import externalGlobals from "rollup-plugin-external-globals"; import fs from "fs"; import path from "path"; import { Plugin, UserConfig } from "vite"; import { load as CheerioLoad } from "cheerio"; export interface Module { name: string; var: string; path: string | string[]; css?: string | string[]; } export interface Options { modules: (Module | ((prodUrl: string) => Module))[]; prodUrl?: string; } /** * get npm module version * @param name * @returns */ function getModuleVersion(name: string): string { const pwd = process.cwd(); const pkgFile = path.join(pwd, "node_modules", name, "package.json"); if (fs.existsSync(pkgFile)) { const pkgJson = JSON.parse(fs.readFileSync(pkgFile, "utf8")); return pkgJson.version; } /*如果没有找到,无所谓 */ return name; } /** * 是否完整的 url * @param path * @returns */ function isFullPath(path: string) { return path.startsWith("http:") || path.startsWith("https:") || path.startsWith("//") ? true : false; } function renderUrl( url: string, data: { name: string; version: string; path: string; } ) { const { path } = data; if (isFullPath(path)) { url = path; } else if (/^\.\//.test(path)) { console.log(path); return path; } return url .replace(/\{name\}/g, data.name) .replace(/\{version\}/g, data.version) .replace(/\{path\}/g, path); } function PluginImportToCDN(options: Options): Plugin[] { const { modules = [], prodUrl = "https://cdn.jsdelivr.net/npm/{name}@{version}/{path}" } = options; let isBuild = false; const data = modules.map(m => { let v: Module; if (typeof m === "function") { v = m(prodUrl); } else { v = m; } const version = getModuleVersion(v.name); let pathList: string[] = []; if (!Array.isArray(v.path)) { pathList.push(v.path); } else { pathList = v.path; } const data = { ...v, version }; pathList = pathList.map(p => { if (!version && !isFullPath(p)) { throw new Error( `modules: ${data.name} package.json file does not exist` ); } return renderUrl(prodUrl, { ...data, path: p }); }); let css = v.css || []; if (!Array.isArray(css) && css) { css = [css]; } const cssList = !Array.isArray(css) ? [] : css.map(c => renderUrl(prodUrl, { ...data, path: c }) ); return { ...v, version, pathList, cssList }; }); const externalMap: { [name: string]: string; } = {}; data.forEach(v => { externalMap[v.name] = v.var; }); const externalLibs = Object.keys(externalMap); const plugins: Plugin[] = [ { name: "vite-plugin-cdn-import", config(_, { command }) { const userConfig: UserConfig = { build: { rollupOptions: {} } }; if (command === "build") { isBuild = true; userConfig!.build!.rollupOptions = { external: [...externalLibs], plugins: [externalGlobals(externalMap)] }; } else { isBuild = false; } return userConfig; }, transformIndexHtml(html) { const cssCode = data .map(v => v.cssList .map(css => ``) .join("\n") ) .filter(v => v) .join("\n"); const jsCode = !isBuild ? "" : data .map(p => p.pathList .map(url => ``) .join("\n") ) .join("\n"); fs.writeFileSync("./html.html", html, "utf-8"); const $ = CheerioLoad(html); const $body = $("body"); const $mainJs = $("script[type=module]"); const mainJs = $mainJs.prop("outerHTML"); $mainJs.remove(); const appDiv = $body.html(); $body.empty(); $body.html(`${appDiv}${jsCode}${mainJs}`); $("title").after(`${cssCode}`); return $.html(); } } ]; return plugins; } export { PluginImportToCDN as Plugin, Options }; export default PluginImportToCDN;