vite-plugin-html-template-mpa
Version:
html template map for vite
491 lines (486 loc) • 16.6 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name2 in all)
__defProp(target, name2, { get: all[name2], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// ../../vite-plugin/vite-plugin-html-template-mpa/src/index.ts
var src_exports = {};
__export(src_exports, {
createMinifyHtmlPlugin: () => createMinifyHtmlPlugin,
default: () => createHtmlPlugin,
htmlTemplate: () => htmlTemplate
});
module.exports = __toCommonJS(src_exports);
var import_crypto = require("crypto");
var import_path = __toESM(require("path"));
var import_shelljs = __toESM(require("shelljs"));
var import_vite = require("vite");
var import_fs2 = __toESM(require("fs"));
// ../../vite-plugin/vite-plugin-html-template-mpa/package.json
var name = "vite-plugin-html-template-mpa";
// ../../vite-plugin/vite-plugin-html-template-mpa/src/utils/index.ts
var import_ejs = require("ejs");
var import_fs = require("fs");
var import_html_minifier_terser = require("html-minifier-terser");
async function readHtmlTemplate(templatePath) {
return await import_fs.promises.readFile(templatePath, { encoding: "utf8" });
}
async function getHtmlContent(payload) {
const {
pagesDir,
templatePath,
pageName: pageName2,
pageTitle,
pageEntry,
isMPA,
entry,
extraData,
input,
pages,
jumpTarget,
injectOptions,
addEntryScript,
mpaAutoAddMainTs,
onlyUseEjsAndMinify
} = payload;
let content = "";
const entryJsPath = (() => {
if (isMPA) {
if (pageEntry.includes("src")) {
return `/${pageEntry.replace("/./", "/").replace("//", "/")}`;
}
return ["/", "/index.html"].includes(extraData.url) ? `/${pagesDir}/index/${pageEntry}` : `/${pagesDir}/${pageName2}/${pageEntry}`;
}
return entry;
})();
try {
content = await readHtmlTemplate(templatePath);
} catch (e) {
console.error(e);
}
const inputKeys = Object.keys(input || {});
const pagesKeys = Object.keys(pages || {});
function getHref(item, params) {
const _params = params ? "?" + params : "";
return !isMPA ? `/${pagesDir}/${item}/index.html${_params}` : `/${item}/index.html${_params}`;
}
const links = inputKeys?.length > 1 ? inputKeys.map((item) => {
if (pagesKeys.includes(item)) {
const href = getHref(item, pages[item].urlParams);
return `<a target="${jumpTarget}" href="${href}">${pages[item].title || ""} ${item}</a><br />`;
}
return `<a target="${jumpTarget}" href="${getHref(
item
)}">${item}</a><br />`;
}) : [];
if (!onlyUseEjsAndMinify) {
if (pageName2 === "index" && links?.length) {
content = content.replace(
"</body>",
`${links.join("").replace(/,/g, " ")}
</body>`
);
} else if (isMPA && mpaAutoAddMainTs || addEntryScript) {
content = content.replace(
"</body>",
`<script type="module" src="${entryJsPath}"></script></body>`
);
}
}
const { data, ejsOptions } = injectOptions || {
data: {},
ejsOptions: {}
};
return await (0, import_ejs.render)(
content,
{
title: pageTitle || "",
...data
},
ejsOptions
);
}
function isMpa(viteConfig) {
const input = viteConfig?.build?.rollupOptions?.input ?? void 0;
return typeof input !== "string" && Object.keys(input || {}).length > 1;
}
function getOptions(minify) {
return {
collapseWhitespace: minify,
keepClosingSlash: minify,
removeComments: minify,
removeRedundantAttributes: minify,
removeScriptTypeAttributes: minify,
removeStyleLinkTypeAttributes: minify,
useShortDoctype: minify,
minifyCSS: minify
};
}
async function minifyHtml(html, minify) {
if (typeof minify === "boolean" && !minify) {
return html;
}
let minifyOptions = minify;
if (typeof minify === "boolean" && minify) {
minifyOptions = getOptions(minify);
}
return await (0, import_html_minifier_terser.minify)(html, minifyOptions);
}
function isPlainObject(value) {
if (Object.prototype.toString.call(value) !== "[object Object]") {
return false;
}
const prototype = Object.getPrototypeOf(value);
return prototype === null || prototype === Object.prototype;
}
function pick(obj, keys) {
return keys.reduce((acc, key) => {
if (obj.hasOwnProperty(key)) {
acc[key] = obj[key];
}
return acc;
}, {});
}
function last(array) {
if (!Array.isArray(array) || array.length === 0) {
return void 0;
}
return array[array.length - 1];
}
// ../../vite-plugin/vite-plugin-html-template-mpa/src/index.ts
var resolve = (p) => import_path.default.resolve(process.cwd(), p);
var PREFIX = "src";
var uniqueHash = (0, import_crypto.createHash)("sha256").update(String((/* @__PURE__ */ new Date()).getTime())).digest("hex").substring(0, 16);
var isEmptyObject = (val) => isPlainObject(val) && Object.getOwnPropertyNames(val).length === 0;
var getPageData = (options, pageName2) => {
let page = {};
const commonOptions = pick(options, [
"template",
"title",
"entry",
"filename",
"urlParams",
"inject"
]);
const isSpa = !options.pages || isEmptyObject(options.pages);
if (isSpa) {
return commonOptions;
} else {
page = { ...commonOptions, ...options.pages?.[pageName2] };
return page;
}
};
var pageName;
var isBuild = false;
function htmlTemplate(userOptions = {}) {
const options = {
pagesDir: "src/views",
pages: {},
jumpTarget: "_self",
buildCfg: {
moveHtmlTop: true,
moveHtmlDirTop: false,
buildPrefixName: "",
htmlHash: false,
buildAssetDirName: "",
buildChunkDirName: "",
buildEntryDirName: "",
htmlPrefixSearchValue: "",
htmlPrefixReplaceValue: ""
},
minify: true,
mpaAutoAddMainTs: true,
...userOptions
};
let config;
return {
name,
config(config2, env) {
isBuild = env.command === "build";
},
configResolved(resolvedConfig) {
const {
buildPrefixName,
htmlHash,
buildAssetDirName,
buildChunkDirName,
buildEntryDirName
} = options.buildCfg;
const assetDir = resolvedConfig.build.assetsDir || "assets";
if (!options.onlyUseEjsAndMinify && isMpa(resolvedConfig)) {
const _output = resolvedConfig.build.rollupOptions.output;
if (buildPrefixName) {
const _input = {};
const rollupInput = resolvedConfig.build.rollupOptions.input;
Object.keys(rollupInput).map((key) => {
_input[((isBuild ? buildPrefixName : "") || "") + key] = rollupInput[key];
});
resolvedConfig.build.rollupOptions.input = _input;
}
if (htmlHash) {
const buildAssets = {
entryFileNames: `${assetDir}/[name].js`,
chunkFileNames: `${assetDir}/[name].js`,
assetFileNames: `${assetDir}/[name].[ext]`
};
const buildOutput = resolvedConfig.build.rollupOptions.output;
if (buildOutput) {
resolvedConfig.build.rollupOptions.output = {
...buildOutput,
...buildAssets
};
} else {
resolvedConfig.build.rollupOptions.output = buildAssets;
}
}
if (buildAssetDirName) {
if (htmlHash || !String(_output.assetFileNames)?.includes("[hash]")) {
_output.assetFileNames = `${assetDir}/${buildAssetDirName}/[name].[ext]`;
} else {
_output.assetFileNames = `${assetDir}/${buildAssetDirName}/[name]-[hash].[ext]`;
}
}
if (buildChunkDirName) {
if (htmlHash || !String(_output.chunkFileNames)?.includes("[hash]")) {
_output.chunkFileNames = `${assetDir}/${buildChunkDirName}/[name].js`;
} else {
_output.chunkFileNames = `${assetDir}/${buildChunkDirName}/[name]-[hash].js`;
}
}
if (buildEntryDirName) {
if (htmlHash || !String(_output.entryFileNames)?.includes("[hash]")) {
_output.entryFileNames = `${assetDir}/${buildEntryDirName}/[name].js`;
} else {
_output.entryFileNames = `${assetDir}/${buildEntryDirName}/[name]-[hash].js`;
}
}
resolvedConfig.build.rollupOptions.output = {
...resolvedConfig.build.rollupOptions.output,
..._output
};
} else if (!isBuild && options.template && !resolvedConfig.build.rollupOptions.input) {
resolvedConfig.build.rollupOptions.input = {
main: import_path.default.resolve(resolvedConfig.root, options.template)
};
}
config = resolvedConfig;
},
configureServer(server) {
return () => {
server.middlewares.use(async (req, res, next) => {
if (!req.url?.endsWith(".html") && req.url !== "/") {
return next();
}
const url = options.pagesDir + req.originalUrl;
pageName = (() => {
if (url === "/") {
return "index";
}
return url.match(new RegExp(`${options.pagesDir}/(.*)/`))?.[1] || "index";
})();
const page = getPageData(options, pageName);
const templateOption = page.template;
const templatePath = options.onlyUseEjsAndMinify ? config.build?.rollupOptions?.input?.[pageName] : templateOption ? resolve(templateOption) : isMpa(config) ? resolve("public/index.html") : resolve("index.html");
let content = await getHtmlContent({
pagesDir: options.pagesDir,
pageName,
templatePath,
pageEntry: page.entry || "main",
pageTitle: page.title || "",
injectOptions: page.inject,
isMPA: isMpa(config),
entry: options.entry || "/src/main",
extraData: {
base: config.base,
url
},
addEntryScript: options.addEntryScript || false,
mpaAutoAddMainTs: options.mpaAutoAddMainTs,
input: config.build.rollupOptions.input,
pages: options.pages || {},
jumpTarget: options.jumpTarget,
onlyUseEjsAndMinify: options.onlyUseEjsAndMinify
});
content = await server.transformIndexHtml?.(
url,
content,
req.originalUrl
);
res.end(content);
});
};
},
resolveId(id) {
if (!options.onlyUseEjsAndMinify && id.endsWith(".html")) {
id = (0, import_vite.normalizePath)(id);
if (!isMpa(config)) {
return `${PREFIX}/${import_path.default.basename(id)}`;
} else {
pageName = last(import_path.default.dirname(id).split("/")) || "";
const inputPages = config.build.rollupOptions.input;
for (const key in inputPages) {
const _key = (0, import_vite.normalizePath)(key);
if (inputPages?.[_key] === id) {
return `${PREFIX}/${options.pagesDir.replace(
"src/",
""
)}/${pageName}/index.html`;
}
}
}
}
return null;
},
load(id) {
if (id.endsWith(".html")) {
id = (0, import_vite.normalizePath)(id);
const idNoPrefix = id.slice(PREFIX.length);
pageName = last(import_path.default.dirname(id).split(options.pagesDir)).replace(
/\//g,
""
);
const page = getPageData(options, pageName);
const publicIndexHtml = resolve("public/index.html");
const indexHtml = resolve("index.html");
const templateOption = page.template;
const templatePath = templateOption ? resolve(templateOption) : import_fs2.default.existsSync(publicIndexHtml) ? publicIndexHtml : indexHtml;
return getHtmlContent({
pagesDir: options.pagesDir,
pageName,
templatePath,
pageEntry: page.entry || "main",
entry: options.entry || "/src/main",
pageTitle: page.title || "",
isMPA: isMpa(config),
/**
* { base: '/', url: '/views/test-one/index.html' }
* { base: '/', url: '/views/test-two/index.html' }
* { base: '/', url: '/views/test-twos/index.html' }
*/
extraData: {
base: config.base,
url: isMpa(config) ? idNoPrefix : "/"
},
injectOptions: page.inject,
addEntryScript: options.addEntryScript || false,
mpaAutoAddMainTs: options.mpaAutoAddMainTs,
input: config.build.rollupOptions.input,
pages: options.pages
});
}
return null;
},
transformIndexHtml(data) {
const page = getPageData(options, pageName);
return {
html: data,
tags: page.inject?.tags || []
};
},
closeBundle() {
if (isMpa(config)) {
import_shelljs.default.rm(
"-rf",
resolve(`${config.build?.outDir || "dist"}/index.html`)
);
}
}
};
}
function createMinifyHtmlPlugin(userOptions = {}) {
const options = {
pagesDir: "src/views",
pages: {},
jumpTarget: "_self",
buildCfg: {
moveHtmlTop: true,
moveHtmlDirTop: false,
buildPrefixName: "",
htmlHash: false,
buildAssetDirName: "",
buildChunkDirName: "",
buildEntryDirName: "",
htmlPrefixSearchValue: "",
htmlPrefixReplaceValue: ""
},
minify: true,
mpaAutoAddMainTs: true,
...userOptions
};
let config;
return {
name: "vite:minify-html",
enforce: "post",
configResolved(resolvedConfig) {
config = resolvedConfig;
},
async generateBundle(_, bundle) {
const htmlFiles = Object.keys(bundle).filter((i) => i.endsWith(".html"));
for (const item of htmlFiles) {
const htmlChunk = bundle[item];
const { moveHtmlTop, moveHtmlDirTop, buildPrefixName, htmlHash } = options.buildCfg;
const _pageName = htmlChunk.fileName.replace(/\\/g, "/").split("/");
const htmlName = (buildPrefixName || "") + _pageName[_pageName.length - 2];
if (htmlChunk) {
let _source = htmlChunk.source;
if (htmlHash) {
_source = htmlChunk.source.replace(/\.js/g, `.js?${uniqueHash}`).replace(/.css/g, `.css?${uniqueHash}`);
}
if (options.minify) {
htmlChunk.source = await minifyHtml(_source, options.minify);
} else {
htmlChunk.source = _source;
}
if (options?.buildCfg?.htmlPrefixSearchValue) {
htmlChunk.source = htmlChunk.source.replace(
new RegExp(options.buildCfg.htmlPrefixSearchValue, "g"),
options?.buildCfg?.htmlPrefixReplaceValue || ""
);
}
}
if (isMpa(config)) {
if (moveHtmlTop) {
htmlChunk.fileName = htmlName + ".html";
} else if (moveHtmlDirTop) {
htmlChunk.fileName = htmlName + "/index.html";
}
} else {
htmlChunk.fileName = "index.html";
}
}
}
};
}
function createHtmlPlugin(userOptions = {}) {
return [
htmlTemplate(userOptions),
createMinifyHtmlPlugin(userOptions)
];
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
createMinifyHtmlPlugin,
htmlTemplate
});