Files
RentWeAppFront/node_modules/@dcloudio/uni-mp-weixin/lib/independent-plugins/split-independent-chunks-plugin.js

259 lines
10 KiB
JavaScript
Raw Normal View History

2025-11-14 11:39:33 +08:00
const GraphHelpers = require('webpack/lib/GraphHelpers');
const { normalizePath } = require('@dcloudio/uni-cli-shared');
const getSplitChunks = require('@dcloudio/vue-cli-plugin-uni/lib/split-chunks');
const path = require('path');
const mainPath = normalizePath(path.resolve(process.env.UNI_INPUT_DIR, 'main.'));
const mainPkgName = 'mainPkg';
function getAllEntryPointOfChunkGroup (chunkGroup, entryPointSet) {
if (chunkGroup.isInitial()) {
return entryPointSet.add(chunkGroup);
}
const parentChunkGroups = [...chunkGroup.parentsIterable];
parentChunkGroups.forEach(parentChunkGroup => getAllEntryPointOfChunkGroup(parentChunkGroup, entryPointSet));
}
function getChunkToEntryPointsMap (allChunks) {
const chunkToEntryPointsMap = new Map();
allChunks.forEach(chunkItem => {
const chunkGroups = [...chunkItem.groupsIterable];
const tmpEntryPointSet = new Set();
chunkToEntryPointsMap.set(chunkItem, tmpEntryPointSet);
chunkGroups.forEach(chunkGroup => {
getAllEntryPointOfChunkGroup(chunkGroup, tmpEntryPointSet);
});
});
return chunkToEntryPointsMap;
}
function baseTest (module) {
if (module.type === 'css/mini-extract') {
return false;
}
if (module.resource) {
const resource = normalizePath(module.resource);
if (
resource.indexOf('.vue') !== -1 ||
resource.indexOf('.nvue') !== -1 ||
resource.indexOf(mainPath) === 0 // main.js
) {
return false;
}
}
return true;
}
class SplitHandler {
constructor (chunks = [], compilation, cacheGroups, chunkFilter, removeModuleFromChunkFilter = () => true) {
this.chunks = chunks || [];
this.chunkFilter = chunkFilter;
this.cacheGroups = cacheGroups;
this.compilation = compilation;
this.removeModuleFromChunkFilter = removeModuleFromChunkFilter;
this.chunksInfoMap = new Map();
}
addModuleToChunksInfoMap (module, chunks, newChunkName) {
let info = this.chunksInfoMap.get(newChunkName);
if (!info) {
info = {
modules: new Set(),
chunks: new Set(),
};
this.chunksInfoMap.set(newChunkName, info);
}
info.modules.add(module);
chunks.forEach(chunk => info.chunks.add(chunk));
}
checkTest (module, test) {
if (typeof test === 'function') {
if (test(module, module.getChunks())) {
return true;
}
} else if (test instanceof RegExp) {
if (module.nameForCondition && test.test(module.nameForCondition())) {
return true;
}
for (const chunk of module.getChunks()) {
if (chunk.name && test.test(chunk.name)) {
return true;
}
}
}
return false;
}
getHitCacheGroups (module) {
const hitCacheGroups = [];
const cacheGroups = this.cacheGroups;
for (const key of Object.keys(cacheGroups)) {
const cacheInfo = cacheGroups[key];
if (!cacheInfo) {
continue;
}
if (this.checkTest(module, cacheInfo.test)) {
hitCacheGroups.push({ newChunkName: cacheInfo.name, priority: cacheInfo.priority || 0 });
}
}
if (hitCacheGroups.length) {
return hitCacheGroups.sort((b, a) => a.priority - b.priority)[0];
}
return null;
}
start () {
const allModulesSet = new Set();
this.chunks.forEach(chunk => {
chunk.getModules().forEach(module => allModulesSet.add(module));
});
this.splitHandler([...allModulesSet]);
}
filter (module) {
// 获取chunks和this.chunks的交集部分
const filterOne = this.chunks.filter(targetChunk => module.chunksIterable.has(targetChunk)) || [];
// 处理uniapp提供的过滤见split-chunks文件
return filterOne.filter(this.chunkFilter);
}
splitHandler (allModulesUsedByIndependent) {
// 遍历独立分包中用到的模块测试其所在的cacheGroup
// 每个模块在这里至多会生成或加入到一个newChunk中
for (const module of allModulesUsedByIndependent) {
const hitGroup = this.getHitCacheGroups(module);
if (!hitGroup) {
continue;
}
this.addModuleToChunksInfoMap(module, this.filter(module), hitGroup.newChunkName);
}
// 遍历 chunksInfoMap
for (const [chunkName, newChunkInfo] of this.chunksInfoMap) {
const newChunk = this.compilation.addChunk(chunkName);
newChunk.chunkReason = 'split chunk for independent';
for (const module of newChunkInfo.modules) {
GraphHelpers.connectChunkAndModule(newChunk, module);
[...newChunkInfo.chunks].forEach(chunk => {
if (this.removeModuleFromChunkFilter(chunk)) {
chunk.removeModule(module);
chunk.split(newChunk);
}
});
}
}
}
}
class SplitIndependentChunksPlugin {
generateCacheGroups () {
const cacheGroups = {};
Object.keys(process.UNI_SUBPACKAGES).forEach(root => {
const pkgInfo = process.UNI_SUBPACKAGES[root];
if (pkgInfo.independent) {
cacheGroups[root] = {
[root + '/commonsVendor']: {
test: /[\\/]node_modules[\\/]/,
minSize: 0,
minChunks: 1,
name: normalizePath(path.join(root, 'common/library')),
priority: 2,
chunks: 'all',
},
[root + '/commons']: {
priority: 1,
name: normalizePath(path.join(root, 'common/vendor')),
test: (module) => {
if (!baseTest(module)) {
return false;
}
return true;
},
},
};
}
});
const splitChunkConfig = getSplitChunks();
cacheGroups[mainPkgName] = splitChunkConfig.cacheGroups;
return { cacheGroups, chunkFilter: splitChunkConfig.chunks };
}
apply (compiler) {
compiler.hooks.thisCompilation.tap('SplitIndependentChunksPlugin', compilation => {
compilation.hooks.optimizeChunksAdvanced.tap('SplitIndependentChunksPlugin', chunks => {
try {
const independentPkgRoot = Object.values(process.UNI_SUBPACKAGES).filter(rootInfo => rootInfo.independent).map(rootInfo => rootInfo.root);
const allPkgRootMap = {};
const mainPkgChunks = [];
for (const chunk of chunks) {
const chunkName = chunk.name;
if (!chunkName) {
continue;
}
const root = independentPkgRoot.find(root => chunkName.startsWith(root));
if (!root) {
mainPkgChunks.push(chunk);
continue;
}
if (!allPkgRootMap[root]) {
allPkgRootMap[root] = [];
}
allPkgRootMap[root].push(chunk);
}
const { cacheGroups, chunkFilter } = this.generateCacheGroups(compiler);
// 找出chunk所有的entryPoint
const chunkToEntryPointsMap = getChunkToEntryPointsMap(compilation.chunks);
const allChunksUsedByIndependentMap = {};
for (const pkgRoot in allPkgRootMap) {
if (!allChunksUsedByIndependentMap[pkgRoot]) {
allChunksUsedByIndependentMap[pkgRoot] = new Set();
}
for (const [chunkItem, entryPointSet] of chunkToEntryPointsMap) {
const filter = entryPoint => entryPoint.name.startsWith(pkgRoot);
const referenceByPkgRoot = [...entryPointSet].find(filter);
// 当前chunk中存在模块被该独立分包下面的页面引用
// uniapp的entry: main.js + page.vue
if (referenceByPkgRoot) {
allChunksUsedByIndependentMap[pkgRoot].add(chunkItem);
}
}
}
// 先分离独立分包
for (const pkgRoot in allPkgRootMap) {
const chunksOfIndependentPkg = allPkgRootMap[pkgRoot];
// 需要将独立分包中用到外部js模块拆分一份到 pkgRoot/vendor.js 中
const allChunksUsedByIndependent = [...allChunksUsedByIndependentMap[pkgRoot]];
// 收集独立分包下面的所有chunks包内chunks + 包外chunks(主要引用的包外组件)
const outSideChunks = [];
allChunksUsedByIndependent.forEach(chunkItem => {
if (!chunksOfIndependentPkg.includes(chunkItem)) {
outSideChunks.push(chunkItem);
}
});
new SplitHandler([...allChunksUsedByIndependent], compilation, cacheGroups[pkgRoot], chunkFilter, (chunk) => {
if (outSideChunks.includes(chunk)) {
return false;
}
return true;
}).start();
}
// 再分离普通分包和主包
new SplitHandler(mainPkgChunks, compilation, cacheGroups[mainPkgName], chunkFilter).start();
} catch (e) {
console.error('independent.error', 'SplitIndependentChunksPlugin', e);
}
});
});
}
}
module.exports = SplitIndependentChunksPlugin;