mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-27 13:03:50 +08:00
refactor: add vite-plugin-html. Delete updateHtml related logic
This commit is contained in:
21
build/vite/cdn.ts
Normal file
21
build/vite/cdn.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
const css = ['//cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.css'];
|
||||
|
||||
// TODO use esm?
|
||||
const js = [
|
||||
'//cdn.bootcdn.net/ajax/libs/vue/3.0.0/vue.global.prod.js',
|
||||
'//cdn.bootcdn.net/ajax/libs/vue-router/4.0.0-beta.13/vue-router.global.min.js',
|
||||
'//cdn.bootcdn.net/ajax/libs/vuex/4.0.0-beta.4/vuex.global.prod.js',
|
||||
'//cdn.bootcdn.net/ajax/libs/axios/0.19.2/axios.min.js',
|
||||
'//cdn.bootcdn.net/ajax/libs/qs/6.9.4/qs.min.js',
|
||||
'//cdn.bootcdn.net/ajax/libs/nprogress/0.2.0/nprogress.min.js',
|
||||
// '//cdn.bootcdn.net/ajax/libs/lodash.js/4.17.15/lodash.min.js',
|
||||
// '//cdn.bootcdn.net/ajax/libs/crypto-js/3.3.0/crypto-js.min.js',
|
||||
// '//cdn.bootcdn.net/ajax/libs/vue-i18n/8.18.1/vue-i18n.min.js',
|
||||
];
|
||||
|
||||
export const externals = ['vue', 'vuex', 'vue-router', 'axios', 'qs', 'nprogress'];
|
||||
|
||||
export const cdnConf = {
|
||||
css,
|
||||
js,
|
||||
};
|
12
build/vite/hm.ts
Normal file
12
build/vite/hm.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
// 百度统计代码 用于站点部署
|
||||
// 只在build:site开启
|
||||
export const hmScript = `<script>
|
||||
var _hmt = _hmt || [];
|
||||
(function() {
|
||||
var hm = document.createElement("script");
|
||||
hm.src = "https://hm.baidu.com/hm.js?384d6046e02f6ac4ea075357bd0e9b43";
|
||||
var s = document.getElementsByTagName("script")[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
</script>
|
||||
`;
|
96
build/vite/plugin/context/transform.ts
Normal file
96
build/vite/plugin/context/transform.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
// 修改自
|
||||
// https://github.com/luxueyan/vite-transform-globby-import/blob/master/src/index.ts
|
||||
|
||||
// TODO 目前还不能监听文件新增及删除 内容已经改变,缓存问题?
|
||||
import { join } from 'path';
|
||||
import { lstatSync } from 'fs';
|
||||
import glob from 'glob';
|
||||
import { createResolver, Resolver } from 'vite/dist/node/resolver.js';
|
||||
import { Transform } from 'vite/dist/node/transform.js';
|
||||
|
||||
const modulesDir: string = join(process.cwd(), '/node_modules/');
|
||||
|
||||
interface SharedConfig {
|
||||
root?: string;
|
||||
alias?: Record<string, string>;
|
||||
resolvers?: Resolver[];
|
||||
}
|
||||
|
||||
function template(template: string) {
|
||||
return (data: { [x: string]: any }) => {
|
||||
return template.replace(/#([^#]+)#/g, (_, g1) => data[g1] || g1);
|
||||
};
|
||||
}
|
||||
|
||||
const globbyTransform = function (config: SharedConfig): Transform {
|
||||
const resolver = createResolver(
|
||||
config.root || process.cwd(),
|
||||
config.resolvers || [],
|
||||
config.alias || {}
|
||||
);
|
||||
const cache = new Map();
|
||||
|
||||
const urlMap = new Map();
|
||||
return {
|
||||
test({ path }) {
|
||||
const filePath = path.replace('\u0000', ''); // why some path startsWith '\u0000'?
|
||||
try {
|
||||
return (
|
||||
!filePath.startsWith(modulesDir) &&
|
||||
/\.(vue|js|jsx|ts|tsx)$/.test(filePath) &&
|
||||
lstatSync(filePath).isFile()
|
||||
);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
transform({ code, path, isBuild }) {
|
||||
let result = cache.get(path);
|
||||
if (!result) {
|
||||
const reg = /import\s+([\w\s{}*]+)\s+from\s+(['"])globby(\?path)?!([^'"]+)\2/g;
|
||||
const match = code.match(reg);
|
||||
if (!match) return code;
|
||||
const lastImport = urlMap.get(path);
|
||||
if (lastImport && match) {
|
||||
code = code.replace(lastImport, match[0]);
|
||||
}
|
||||
result = code.replace(reg, (_, g1, g2, g3, g4) => {
|
||||
const filePath = path.replace('\u0000', ''); // why some path startsWith '\u0000'?
|
||||
// resolve path
|
||||
const resolvedFilePath = g4.startsWith('.')
|
||||
? resolver.resolveRelativeRequest(filePath, g4)
|
||||
: { pathname: resolver.requestToFile(g4) };
|
||||
const files = glob.sync(resolvedFilePath.pathname, { dot: true });
|
||||
let templateStr = 'import #name# from #file#'; // import default
|
||||
let name = g1;
|
||||
const m = g1.match(/\{\s*(\w+)(\s+as\s+(\w+))?\s*\}/); // import module
|
||||
const m2 = g1.match(/\*\s+as\s+(\w+)/); // import * as all module
|
||||
if (m) {
|
||||
templateStr = `import { ${m[1]} as #name# } from #file#`;
|
||||
name = m[3] || m[1];
|
||||
} else if (m2) {
|
||||
templateStr = 'import * as #name# from #file#';
|
||||
name = m2[1];
|
||||
}
|
||||
const temRender = template(templateStr);
|
||||
|
||||
const groups: Array<string>[] = [];
|
||||
const replaceFiles = files.map((f, i) => {
|
||||
const file = g2 + resolver.fileToRequest(f) + g2;
|
||||
groups.push([name + i, file]);
|
||||
return temRender({ name: name + i, file });
|
||||
});
|
||||
urlMap.set(path, replaceFiles.join('\n'));
|
||||
return (
|
||||
replaceFiles.join('\n') +
|
||||
(g3 ? '\n' + groups.map((v) => `${v[0]}._path = ${v[1]}`).join('\n') : '') +
|
||||
`\nconst ${name} = { ${groups.map((v) => v[0]).join(',')} }\n`
|
||||
);
|
||||
});
|
||||
if (isBuild) cache.set(path, result);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
};
|
||||
};
|
||||
export default globbyTransform;
|
36
build/vite/plugin/gzip/compress.ts
Normal file
36
build/vite/plugin/gzip/compress.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { gzip } from 'zlib';
|
||||
import { readFileSync, writeFileSync } from 'fs';
|
||||
import { GzipPluginOptions } from './types';
|
||||
import viteConfig from '../../../../vite.config';
|
||||
import { readAllFile, getCwdPath, isBuildGzip, isSiteMode } from '../../../utils';
|
||||
|
||||
export function startGzip(
|
||||
fileContent: string | Buffer,
|
||||
options: GzipPluginOptions = {}
|
||||
): Promise<Buffer> {
|
||||
return new Promise((resolve, reject) => {
|
||||
gzip(fileContent, options.gzipOptions || {}, (err, result) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 手动压缩css
|
||||
export async function startGzipStyle() {
|
||||
if (isBuildGzip() || isSiteMode()) {
|
||||
const outDir = viteConfig.outDir || 'dist';
|
||||
const assets = viteConfig.assetsDir || '_assets';
|
||||
const allCssFile = readAllFile(getCwdPath(outDir, assets), /\.(css)$/);
|
||||
for (const path of allCssFile) {
|
||||
const source = readFileSync(path);
|
||||
const content = await startGzip(source);
|
||||
const ds = path.split('/');
|
||||
const fileName = ds[ds.length - 1];
|
||||
writeFileSync(getCwdPath(outDir, assets, `${fileName}.gz`), content);
|
||||
}
|
||||
}
|
||||
}
|
195
build/vite/plugin/gzip/index.ts
Normal file
195
build/vite/plugin/gzip/index.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
// 修改自https://github.com/kryops/rollup-plugin-gzip
|
||||
// 因为rollup-plugin-gzip不支持vite
|
||||
// vite对css打包独立的。所以不能在打包的时候顺带打包css
|
||||
|
||||
import { readFile, writeFile } from 'fs';
|
||||
import { basename } from 'path';
|
||||
import { promisify } from 'util';
|
||||
import { gzip } from 'zlib';
|
||||
|
||||
import { OutputAsset, OutputChunk, OutputOptions, Plugin } from 'rollup';
|
||||
import { GzipPluginOptions } from './types';
|
||||
|
||||
const isFunction = (arg: unknown): arg is (...args: any[]) => any => typeof arg === 'function';
|
||||
const isRegExp = (arg: unknown): arg is RegExp =>
|
||||
Object.prototype.toString.call(arg) === '[object RegExp]';
|
||||
|
||||
export type StringMappingOption = (originalString: string) => string;
|
||||
export type CustomCompressionOption = (
|
||||
content: string | Buffer
|
||||
) => string | Buffer | Promise<string | Buffer>;
|
||||
|
||||
const readFilePromise = promisify(readFile);
|
||||
const writeFilePromise = promisify(writeFile);
|
||||
|
||||
// functionality partially copied from rollup
|
||||
|
||||
/**
|
||||
* copied from https://github.com/rollup/rollup/blob/master/src/rollup/index.ts#L450
|
||||
*/
|
||||
function isOutputChunk(file: OutputAsset | OutputChunk): file is OutputChunk {
|
||||
return typeof (file as OutputChunk).code === 'string';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string/buffer content from a file object.
|
||||
* Important for adding source map comments
|
||||
*
|
||||
* Copied partially from rollup.writeOutputFile
|
||||
* https://github.com/rollup/rollup/blob/master/src/rollup/index.ts#L454
|
||||
*/
|
||||
function getOutputFileContent(
|
||||
outputFileName: string,
|
||||
outputFile: OutputAsset | OutputChunk,
|
||||
outputOptions: OutputOptions
|
||||
): string | Buffer {
|
||||
if (isOutputChunk(outputFile)) {
|
||||
let source: string | Buffer;
|
||||
source = outputFile.code;
|
||||
if (outputOptions.sourcemap && outputFile.map) {
|
||||
const url =
|
||||
outputOptions.sourcemap === 'inline'
|
||||
? outputFile.map.toUrl()
|
||||
: `${basename(outputFileName)}.map`;
|
||||
|
||||
// https://github.com/rollup/rollup/blob/master/src/utils/sourceMappingURL.ts#L1
|
||||
source += `//# source` + `MappingURL=${url}\n`;
|
||||
}
|
||||
return source;
|
||||
} else {
|
||||
return typeof outputFile.source === 'string'
|
||||
? outputFile.source
|
||||
: // just to be sure, as it is typed string | Uint8Array in rollup 2.0.0
|
||||
Buffer.from(outputFile.source);
|
||||
}
|
||||
}
|
||||
|
||||
// actual plugin code
|
||||
|
||||
function gzipPlugin(options: GzipPluginOptions = {}): Plugin {
|
||||
// check for old options
|
||||
if ('algorithm' in options) {
|
||||
console.warn(
|
||||
'[rollup-plugin-gzip] The "algorithm" option is not supported any more! ' +
|
||||
'Use "customCompression" instead to specify a different compression algorithm.'
|
||||
);
|
||||
}
|
||||
if ('options' in options) {
|
||||
console.warn('[rollup-plugin-gzip] The "options" option was renamed to "gzipOptions"!');
|
||||
}
|
||||
if ('additional' in options) {
|
||||
console.warn('[rollup-plugin-gzip] The "additional" option was renamed to "additionalFiles"!');
|
||||
}
|
||||
if ('delay' in options) {
|
||||
console.warn('[rollup-plugin-gzip] The "delay" option was renamed to "additionalFilesDelay"!');
|
||||
}
|
||||
|
||||
const compressGzip: CustomCompressionOption = (fileContent) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
gzip(fileContent, options.gzipOptions || {}, (err, result) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const doCompress = options.customCompression || compressGzip;
|
||||
|
||||
const mapFileName: StringMappingOption = isFunction(options.fileName)
|
||||
? (options.fileName as StringMappingOption)
|
||||
: (fileName: string) => fileName + (options.fileName || '.gz');
|
||||
|
||||
const plugin: Plugin = {
|
||||
name: 'gzip',
|
||||
|
||||
generateBundle(outputOptions, bundle) {
|
||||
return Promise.all(
|
||||
Object.keys(bundle)
|
||||
.map((fileName) => {
|
||||
const fileEntry = bundle[fileName];
|
||||
|
||||
// file name filter option check
|
||||
|
||||
const fileNameFilter = options.filter || /\.(js|mjs|json|css|html)$/;
|
||||
|
||||
if (isRegExp(fileNameFilter) && !fileName.match(fileNameFilter)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (
|
||||
isFunction(fileNameFilter) &&
|
||||
!(fileNameFilter as (x: string) => boolean)(fileName)
|
||||
) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const fileContent = getOutputFileContent(fileName, fileEntry, outputOptions);
|
||||
|
||||
// minSize option check
|
||||
if (options.minSize && options.minSize > fileContent.length) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return Promise.resolve(doCompress(fileContent))
|
||||
.then((compressedContent) => {
|
||||
const compressedFileName = mapFileName(fileName);
|
||||
bundle[compressedFileName] = {
|
||||
type: 'asset', // Rollup >= 1.21
|
||||
name: compressedFileName,
|
||||
fileName: compressedFileName,
|
||||
isAsset: true, // Rollup < 1.21
|
||||
source: compressedContent,
|
||||
};
|
||||
})
|
||||
.catch((err: any) => {
|
||||
console.error(err);
|
||||
return Promise.reject('[rollup-plugin-gzip] Error compressing file ' + fileName);
|
||||
});
|
||||
})
|
||||
.concat([
|
||||
(() => {
|
||||
if (!options.additionalFiles || !options.additionalFiles.length)
|
||||
return Promise.resolve();
|
||||
|
||||
const compressAdditionalFiles = () =>
|
||||
Promise.all(
|
||||
options.additionalFiles!.map((filePath) =>
|
||||
readFilePromise(filePath)
|
||||
.then((fileContent) => doCompress(fileContent))
|
||||
.then((compressedContent) => {
|
||||
return writeFilePromise(mapFileName(filePath), compressedContent);
|
||||
})
|
||||
.catch(() => {
|
||||
return Promise.reject(
|
||||
'[rollup-plugin-gzip] Error compressing additional file ' +
|
||||
filePath +
|
||||
'. Please check the spelling of your configured additionalFiles. ' +
|
||||
'You might also have to increase the value of the additionalFilesDelay option.'
|
||||
);
|
||||
})
|
||||
)
|
||||
) as Promise<any>;
|
||||
|
||||
// additional files can be processed outside of rollup after a delay
|
||||
// for older plugins or plugins that write to disk (curcumventing rollup) without awaiting
|
||||
const additionalFilesDelay = options.additionalFilesDelay || 0;
|
||||
|
||||
if (additionalFilesDelay) {
|
||||
setTimeout(compressAdditionalFiles, additionalFilesDelay);
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
return compressAdditionalFiles();
|
||||
}
|
||||
})(),
|
||||
])
|
||||
) as Promise<any>;
|
||||
},
|
||||
};
|
||||
|
||||
return plugin;
|
||||
}
|
||||
|
||||
export default gzipPlugin;
|
56
build/vite/plugin/gzip/types.ts
Normal file
56
build/vite/plugin/gzip/types.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import type { ZlibOptions } from 'zlib';
|
||||
|
||||
export type StringMappingOption = (originalString: string) => string;
|
||||
export type CustomCompressionOption = (
|
||||
content: string | Buffer
|
||||
) => string | Buffer | Promise<string | Buffer>;
|
||||
|
||||
export interface GzipPluginOptions {
|
||||
/**
|
||||
* Control which of the output files to compress
|
||||
*
|
||||
* Defaults to `/\.(js|mjs|json|css|html)$/`
|
||||
*/
|
||||
filter?: RegExp | ((fileName: string) => boolean);
|
||||
|
||||
/**
|
||||
* GZIP compression options, see https://nodejs.org/api/zlib.html#zlib_class_options
|
||||
*/
|
||||
gzipOptions?: ZlibOptions;
|
||||
|
||||
/**
|
||||
* Specified the minimum size in Bytes for a file to get compressed.
|
||||
* Files that are smaller than this threshold will not be compressed.
|
||||
* This does not apply to the files specified through `additionalFiles`!
|
||||
*/
|
||||
minSize?: number;
|
||||
|
||||
/**
|
||||
* This option allows you to compress additional files outside of the main rollup bundling process.
|
||||
* The processing is delayed to make sure the files are written on disk; the delay is controlled
|
||||
* through `additionalFilesDelay`.
|
||||
*/
|
||||
additionalFiles?: string[];
|
||||
|
||||
/**
|
||||
* This options sets a delay (ms) before the plugin compresses the files specified through `additionalFiles`.
|
||||
* Increase this value if your artifacts take a long time to generate.
|
||||
*
|
||||
* Defaults to `2000`
|
||||
*/
|
||||
additionalFilesDelay?: number;
|
||||
|
||||
/**
|
||||
* Set a custom compression algorithm. The function can either return the compressed contents synchronously,
|
||||
* or otherwise return a promise for asynchronous processing.
|
||||
*/
|
||||
customCompression?: CustomCompressionOption;
|
||||
|
||||
/**
|
||||
* Set a custom file name convention for the compressed files. Can be a suffix string or a function
|
||||
* returning the file name.
|
||||
*
|
||||
* Defaults to `.gz`
|
||||
*/
|
||||
fileName?: string | StringMappingOption;
|
||||
}
|
76
build/vite/plugin/index.ts
Normal file
76
build/vite/plugin/index.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import type { Plugin as VitePlugin } from 'vite';
|
||||
import type { Plugin as rollupPlugin } from 'rollup';
|
||||
|
||||
import { createMockServer } from 'vite-plugin-mock';
|
||||
import ViteHtmlPlugin from 'vite-plugin-html';
|
||||
import PurgeIcons from 'vite-plugin-purge-icons';
|
||||
|
||||
import visualizer from 'rollup-plugin-visualizer';
|
||||
import gzipPlugin from './gzip/index';
|
||||
|
||||
import { hmScript } from '../hm';
|
||||
|
||||
const pkg = require('../../../package.json');
|
||||
|
||||
import { isDevFn, isProdFn, isSiteMode, ViteEnv, isReportMode, isBuildGzip } from '../../utils';
|
||||
import { GLOB_CONFIG_FILE_NAME } from '../../constant';
|
||||
|
||||
// gen vite plugins
|
||||
export function createVitePlugins(viteEnv: ViteEnv) {
|
||||
const { VITE_USE_MOCK, VITE_GLOB_APP_TITLE, VITE_PUBLIC_PATH } = viteEnv;
|
||||
|
||||
const vitePlugins: VitePlugin[] = [];
|
||||
|
||||
// vite-plugin-html
|
||||
vitePlugins.push(
|
||||
ViteHtmlPlugin({
|
||||
// html title
|
||||
title: VITE_GLOB_APP_TITLE,
|
||||
minify: isProdFn(),
|
||||
options: {
|
||||
// Package and insert additional configuration files
|
||||
injectConfig: isProdFn()
|
||||
? `<script src='${VITE_PUBLIC_PATH || './'}${GLOB_CONFIG_FILE_NAME}?v=${
|
||||
pkg.version
|
||||
}-${new Date().getTime()}'></script>`
|
||||
: '',
|
||||
// Insert Baidu statistics code
|
||||
hmScript: isSiteMode() ? hmScript : '',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
// vite-plugin-purge-icons
|
||||
vitePlugins.push(PurgeIcons());
|
||||
|
||||
// vite-plugin-mock
|
||||
if (isDevFn() && VITE_USE_MOCK) {
|
||||
// open mock
|
||||
vitePlugins.push(
|
||||
createMockServer({
|
||||
ignore: /^\_/,
|
||||
mockPath: 'mock',
|
||||
})
|
||||
);
|
||||
}
|
||||
return vitePlugins;
|
||||
}
|
||||
|
||||
// gen rollup plugins
|
||||
export function createRollupPlugin() {
|
||||
const rollupPlugins: rollupPlugin[] = [];
|
||||
|
||||
if (isProdFn()) {
|
||||
if (isReportMode()) {
|
||||
// rollup-plugin-visualizer
|
||||
rollupPlugins.push(
|
||||
visualizer({ filename: './build/.cache/stats.html', open: true }) as Plugin
|
||||
);
|
||||
}
|
||||
if (isBuildGzip() || isSiteMode()) {
|
||||
// rollup-plugin-gizp
|
||||
rollupPlugins.push(gzipPlugin());
|
||||
}
|
||||
}
|
||||
return rollupPlugins;
|
||||
}
|
19
build/vite/proxy.ts
Normal file
19
build/vite/proxy.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
type ProxyItem = [string, string];
|
||||
|
||||
type ProxyList = ProxyItem[];
|
||||
|
||||
const reg = /^https:\/\//;
|
||||
export function createProxy(list: ProxyList = []) {
|
||||
const ret: any = {};
|
||||
for (const [prefix, target] of list) {
|
||||
const isHttps = reg.test(target);
|
||||
|
||||
ret[prefix] = {
|
||||
target: target,
|
||||
changeOrigin: true,
|
||||
rewrite: (path: string) => path.replace(new RegExp(`^${prefix}`), ''),
|
||||
...(isHttps ? { secure: false } : {}),
|
||||
};
|
||||
}
|
||||
return ret;
|
||||
}
|
Reference in New Issue
Block a user