vite-plugin-require-resolve
Version:
A vite plugin deals with require.resolve() assets, for node-browser mixed-context environment like nw.js etc
142 lines (141 loc) • 6.6 kB
JavaScript
import path from 'path';
import fs from 'fs-extra';
import pc from 'picocolors';
import md5File from 'md5-file';
import strip_comments from 'strip-comments';
const REG_EXP_G = /require\.resolve\(['"][(\.\/)(\.\.\/)][^'"]+['"]\)/g;
const REG_EXP_FOR_MATCH = /require\.resolve\(['"]([(\.\/)(\.\.\/)][^'"]+)['"]\)/;
const SIZE_GB = 1024 * 1024 * 1024 * 1;
const SIZE_MB = 1024 * 1024 * 1;
const SIZE_KB = 1024 * 1;
const Helper = {
AssetsFolderName: '',
RelativeAssetsDir: '',
AbsoluteAssetsDir: '',
ResolvedCodeFileList: [],
BlackAssetFileList: [],
AssetRequireMap: new Map(),
OutputAssetList: [],
GetSizeDesc: (InSize) => {
if (InSize >= SIZE_GB) {
return (InSize / SIZE_GB).toFixed(2) + 'GB';
}
else if (InSize >= SIZE_MB) {
return (InSize / SIZE_MB).toFixed(2) + 'MB';
}
else {
return (InSize / SIZE_KB).toFixed(2) + 'KB';
}
},
};
export default function VitePluginRequireResolve() {
return {
name: 'vite-plugin-require-resolve',
apply: 'build',
enforce: 'pre',
async configResolved(ResolvedConfig) {
Helper.AssetsFolderName = ResolvedConfig.build.assetsDir;
Helper.RelativeAssetsDir = path.join(ResolvedConfig.build.outDir, ResolvedConfig.build.assetsDir).replace(/\\/g, '/');
Helper.AbsoluteAssetsDir = path.resolve(ResolvedConfig.root, ResolvedConfig.build.outDir, ResolvedConfig.build.assetsDir);
},
async transform(Code, FileID) {
const CodeFileAbsolutePath = FileID.replace(/(\?.*)$/, '');
if (Helper.ResolvedCodeFileList.find(Item => Item === CodeFileAbsolutePath)) {
return null;
}
const CodeWithoutComments = strip_comments(Code);
const Matches = CodeWithoutComments.match(REG_EXP_G);
if (!Matches) {
Helper.ResolvedCodeFileList.push(CodeFileAbsolutePath);
return null;
}
let ReturnCode = Code;
const CodeFileAbsolutePathParsed = path.parse(CodeFileAbsolutePath);
const ResolvedStatementMap = new Map();
const BlackStatementList = [];
for (const RequireResolveStatement of Matches) {
if (ResolvedStatementMap.has(RequireResolveStatement) || BlackStatementList.includes(RequireResolveStatement)) {
continue;
}
const AssetDirMatches = RequireResolveStatement.match(REG_EXP_FOR_MATCH);
if (!AssetDirMatches || !AssetDirMatches[1]) {
continue;
}
const AssetFileAbsolutePath = path.resolve(CodeFileAbsolutePathParsed.dir, AssetDirMatches[1]);
if (Helper.BlackAssetFileList.includes(AssetFileAbsolutePath)) {
continue;
}
const AssetFileParsedPath = path.parse(AssetFileAbsolutePath);
const ExistedStatement = Helper.AssetRequireMap.get(AssetFileAbsolutePath);
if (ExistedStatement) {
ResolvedStatementMap.set(RequireResolveStatement, ExistedStatement);
ReturnCode = ReturnCode.replaceAll(RequireResolveStatement, ExistedStatement);
continue;
}
if (!fs.existsSync(AssetFileAbsolutePath)) {
console.log('\r' +
pc.bgYellow(pc.black('WARN')) +
pc.yellow(' [vite-plugin-require-resolve] ') +
pc.yellow(`${RequireResolveStatement} can't find target file, in ${CodeFileAbsolutePath}`));
Helper.BlackAssetFileList.push(AssetFileAbsolutePath);
BlackStatementList.push(RequireResolveStatement);
continue;
}
const MD5 = md5File.sync(AssetFileAbsolutePath).substring(0, 8);
const OutputName = AssetFileParsedPath.name + '-' + MD5 + AssetFileParsedPath.ext;
const NewStatement = `require.resolve('./${Helper.AssetsFolderName}/${OutputName}')`;
const OutputAsset = Helper.OutputAssetList.find(Item => Item.OutputName === OutputName);
if (OutputAsset) {
if (OutputAsset.AssetPath !== AssetFileAbsolutePath) {
OutputAsset.AssetPath = AssetFileAbsolutePath;
}
}
else {
Helper.OutputAssetList.push({
AssetPath: AssetFileAbsolutePath,
OutputName: OutputName,
});
}
Helper.AssetRequireMap.set(AssetFileAbsolutePath, NewStatement);
ResolvedStatementMap.set(RequireResolveStatement, NewStatement);
ReturnCode = ReturnCode.replaceAll(RequireResolveStatement, NewStatement);
}
Helper.ResolvedCodeFileList.push(CodeFileAbsolutePath);
return ReturnCode;
},
async closeBundle() {
const Tasks = [];
const ExpiredAssetList = [];
for (const OutputAsset of Helper.OutputAssetList) {
if (!fs.existsSync(OutputAsset.AssetPath)) {
ExpiredAssetList.push(OutputAsset.AssetPath);
continue;
}
Tasks.push({
Src: OutputAsset.AssetPath,
Dest: path.resolve(Helper.AbsoluteAssetsDir, OutputAsset.OutputName),
Name: OutputAsset.OutputName,
});
}
Helper.OutputAssetList = Helper.OutputAssetList.filter(Item => !ExpiredAssetList.includes(Item.AssetPath));
const OutputResult = [];
await Promise.all(Tasks.map(Task => (async () => {
await fs.copyFile(Task.Src, Task.Dest);
OutputResult.push({
Name: Task.Name,
Size: (await fs.stat(Task.Src)).size,
});
})()));
for (const Result of OutputResult) {
console.log('\r' +
pc.black(Helper.RelativeAssetsDir + '/') +
pc.green(Result.Name) + '\t' +
pc.gray(Helper.GetSizeDesc(Result.Size)));
}
Helper.ResolvedCodeFileList = [];
Helper.BlackAssetFileList = [];
Helper.AssetRequireMap.clear();
},
};
}
module.exports = exports.default;