8 Star 29 Fork 6

K. / webpack-miniprogram-plugin

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
miniprogram-plugin.js 11.00 KB
一键复制 编辑 原始数据 按行查看 历史
K. 提交于 2019-05-21 08:31 . 1.0.4 add mockMain entry
const pathUtil = require('path');
const fs = require('fs');
const rimraf = require("rimraf");
const webpack = require('webpack');
const {
ConcatSource,
RawSource,
PrefixSource,
OriginalSource,
LineToLineMappedSource,
} = require('webpack-sources');
const MultiEntryPlugin = require('webpack/lib/MultiEntryPlugin');
const SingleEntryPlugin = require('webpack/lib/SingleEntryPlugin');
const _ = require('lodash');
const {
readJsonSync,
filterExts,
replaceDS,
removeExt,
filterExcludeItems,
warn, error, highLight, right,
} = require('./utils');
const MiniProgramResources = require('./miniprogram-resources');
const {exit} = process;
class MiniProgramPlugin {
constructor(srcDir, distDir, options, isBreakInPrepare) {
this.__isBreakInPrepare = !!isBreakInPrepare;
this.__modulePath = __filename;
this.__moduleDir = pathUtil.dirname(__filename);
this.__isPrepare = false;
this.webpackContext = null;
this.webpackEntryScripts = {};
this.webpackEntryAssets = {};
this.webpackEntryScriptChunks = {};
this.webpackExclude = {
count: 0,
chunks: [],
scripts: [],
assets: [],
};
this.root = process.cwd();
this.srcDir = srcDir;
this.srcPath = pathUtil.resolve(this.root, srcDir);
this.distDir = distDir;
this.distPath = pathUtil.resolve(this.root, distDir);
this.pluginName = 'MiniProgramWebpackPlugin';
this.options = {
main: 'app.json',
mockMain: false,
debug: false,
assets: 'assets',
assetsChunkName: '__assets_chunk_name__',
bootstrapModuleName: 'bootstrap.js',
exts: {
json: true,
wxml: true,
wxss: false,
},
scriptExts: {js: true},
checkStatExts: {wxss: true},
app: {
exts: {wxml: false},
files: [
'project.config.json',
],
},
page: {
// exts: {wxml: true},
atta: [],
},
component: {
// exts: {wxml: true},
atta: [],
},
custom: {
// 'app': {
// exts: {styl: true, wxss: false},
// atta: []
// }
// 'pages/index/index': {
// exts: [],
// atta: ['sitemap.json', 'project.config.js'],
// },
},
};
if (_.isObjectLike(options)) {
this.options = _.merge(this.options, options);
}
}
prepare(compiler, context, entry) {
if (this.__isPrepare) return this;
const {options} = compiler;
const {output} = options;
if (output.libraryTarget !== 'var')
throw new Error(`MiniProgram webpack plugin must set as output.libraryTarget = 'var'`);
this.webpackContext = pathUtil.resolve(context);
this.initWebpackEntry(entry);
if (!!this.__isBreakInPrepare) {
console.log(`MiniProgram webpack plugin (${highLight(this.__moduleDir)}) exit in prepare `);
console.log(this.options);
exit();
}
return this;
}
apply(compiler) {
let isFirst = true;
compiler.hooks.entryOption.tap(this.pluginName, (context, entry) => {
this.processTip('compiler.hooks.entryOption');
this.prepare(compiler, context, entry);
});
compiler.hooks.beforeCompile.tapAsync(this.pluginName, (compilation, callback) => {
this.processTip('compiler.hooks.beforeCompile');
callback();
});
compiler.hooks.thisCompilation.tap(this.pluginName, (compilation, params) => {
this.processTip('compiler.hooks.thisCompilation');
const {remove} = this.newResources().seekApp().diffEntries();
this.webpackExclude = remove; // 重新写入一次
});
compiler.hooks.compilation.tap(this.pluginName, (compilation, params) => {
this.processTip('compiler.hooks.compilation');
const findChunkIndex = (chunkName) => compilation.chunks.findIndex(
({name}) => name === chunkName,
);
const excludeChunk = (chunkName) => {
const chunkIndex = findChunkIndex(chunkName);
if (chunkIndex > -1) {
compilation.chunks.splice(chunkIndex, 1);
if (this.webpackEntryScriptChunks[chunkName])
this.processTip(`excludeEntryScript`, error(chunkName));
else
this.processTip(`excludeChunk`, error(chunkName));
}
};
compilation.hooks.beforeChunkAssets.tap(this.pluginName, () => {
this.processTip('compilation.hooks.beforeChunkAssets');
excludeChunk(this.options.assetsChunkName);
});
compilation.hooks.optimizeChunksBasic.tap(this.pluginName, (chunks, chunkGroups) => {
this.processTip('compilation.hooks.optimizeChunksBasic');
if (this.webpackExclude.count > 0) {
if (this.webpackExclude.chunks.length > 0) {
this.webpackExclude.chunks.forEach(chunkName => {
excludeChunk(chunkName);
});
}
}
});
compilation.hooks.additionalAssets.tapAsync(this.pluginName, callback => {
// 这里附加的内容不在 entry
compilation.assets[this.options.bootstrapModuleName] = new ConcatSource(
fs.readFileSync(pathUtil.resolve(this.__moduleDir, './miniprogram-bootstrap.js'), 'utf8'),
);
callback();
});
compilation.hooks.optimizeAssets.tap(this.pluginName, (assets) => {
this.processTip('compilation.hooks.optimizeAssets');
if (this.webpackExclude.assets.length > 0) {
this.webpackExclude.assets.forEach(key => {
if (assets[key]) {
this.processTip(`excludeAsset`, error(key));
delete (assets[key]);
}
});
}
});
compilation.hooks.needAdditionalPass.tap(this.pluginName, () => {
this.processTip('compilation.hooks.needAdditionalPass');
const {add, remove} = this.resources.diffEntries();
return add.count > 0;
});
compilation.mainTemplate.hooks.render.tap('MiniTemplate', (
source,
chunk,
hash,
moduleTemplate,
dependencyTemplates,
) => {
this.processTip('compilation.mainTemplate.hooks.render');
const {name} = chunk;
if (!this.webpackEntryScriptChunks[name]) return source;
if (!chunk.hasEntryModule()) return source;
const sourceBody = compilation.mainTemplate.hooks.modules.call(
new RawSource(''),
chunk,
hash,
moduleTemplate,
dependencyTemplates,
);
const relativePath = (path) => {
return replaceDS(pathUtil.relative(pathUtil.dirname(name), `${path}`));
};
const modules = this.getRequireModules(chunk);
const newSource = new ConcatSource();
newSource.add(`require('./${relativePath((this.options.bootstrapModuleName))}')(${JSON.stringify(chunk.entryModule.id)}, `);
if (modules.length > 0) {
newSource.add('Object.assign(');
modules.forEach(m => {
newSource.add(`require('./${relativePath(m)}').modules, `);
});
newSource.add(sourceBody);
newSource.add(')');
} else {
newSource.add(sourceBody);
}
newSource.add(')');
return newSource;
});
});
compiler.hooks.emit.tapAsync(this.pluginName, (compilation, callback) => {
this.processTip('compiler.hooks.emit');
callback();
});
compiler.hooks.afterEmit.tapAsync(this.pluginName, (compilation, callback) => {
this.processTip('compiler.hooks.afterEmit');
callback();
});
compiler.hooks.additionalPass.tapAsync(this.pluginName, (callback) => {
this.processTip('compiler.hooks.additionalPass');
this.addEntries(compiler);
callback();
});
compiler.hooks.afterCompile.tap(this.pluginName, (compilation) => {
this.processTip('compiler.hooks.afterCompile');
});
}
addEntries(compiler) {
if (this.resources && compiler) {
const {add, remove} = this.resources.diffEntries();
add.scripts.forEach(key => {
const path = this.srcOf(key);
const chunkName = removeExt(key);
(new SingleEntryPlugin(this.srcPath, path, chunkName)).apply(compiler);
this.webpackEntryScripts[key] = path;
this.webpackEntryScriptChunks[chunkName] = path;
});
const mergeAssets = {};
const assets = add.assets.map(key => {
mergeAssets[key] = this.srcOf(key);
return mergeAssets[key];
});
const assetsEntry = new MultiEntryPlugin(this.srcPath, assets, this.options.assetsChunkName);
assetsEntry.apply(compiler);
_.merge(this.webpackEntryAssets, mergeAssets);
}
return this;
}
newResources() {
this.resources = new MiniProgramResources(this, this.resources);
return this.resources;
}
rootOf(...path) {
return pathUtil.resolve(this.root, ...path);
}
srcOf(...path) {
return pathUtil.resolve(this.srcPath, ...path);
}
distOf(...path) {
return pathUtil.resolve(this.distPath, ...path);
}
relativeOfSrc(path) {
return pathUtil.relative(this.srcPath, path);
}
relativeOfRoot(path) {
return pathUtil.relative(this.root, path);
}
relativeOfDist(path) {
return pathUtil.relative(this.distPath, path);
}
makeKey(path, withExt) {
if (!withExt)
return replaceDS(removeExt(this.relativeOfSrc(path)));
else
return replaceDS(this.relativeOfSrc(path));
}
processTip(process, ...subItems) {
if (!!this.options.debug) {
let msgs = [`${highLight(this.pluginName)}/${warn(process)}`];
if (subItems.length > 0)
msgs = msgs.concat(subItems);
console.log(...msgs);
}
return this;
}
isScript(path) {
return this.isScriptExt(pathUtil.extname(path));
}
isScriptExt(ext) {
return !!this.options.scriptExts[_.trim(ext, '.').toLowerCase()];
}
isJson(path) {
return this.isJsonExt(pathUtil.extname(path));
}
isJsonExt(ext) {
return _.trim(ext, '.').toLowerCase() === 'json';
}
getExts(type, key) {
const {options} = this;
let exts = Object.assign({}, options.exts);
if (options[type].exts) {
Object.assign(exts, options[type].exts);
}
if (options.custom[key] && options.custom[key].exts) {
Object.assign(exts, options.custom[key].exts);
}
return filterExts(exts);
}
getAdditionFiles(type, key) {
let files = [];
const {options} = this;
if (options.custom[key] && options.custom[key].files && Array.isArray(options.custom[key].files) && options.custom[key].files.length > 0) {
files.push(...options.custom[key].atta);
}
if (options[type] && options[type].files && Array.isArray(options[type].files) && options[type].files.length > 0) {
files.push(...options[type].files);
}
return _.uniq(files);
}
addWebpackEntry(item) {
const path = this.rootOf(item);
if (this.isScript(path)) {
const key = this.makeKey(path, true);
const notExtKey = removeExt(key);
if (!this.webpackEntryScripts[key])
this.webpackEntryScripts[key] = path;
if (!this.webpackEntryScriptChunks[notExtKey])
this.webpackEntryScriptChunks[notExtKey] = path;
}
return this;
}
initWebpackEntry(entry) {
if (_.isString(entry)) {
this.addWebpackEntry(entry);
} else {
_.values(entry).forEach(item => {
if (Array.isArray(item)) {
item.forEach(subItem => {
this.addWebpackEntry(subItem);
});
} else if (_.isString(item)) {
this.addWebpackEntry(item);
}
});
}
return this;
}
getRequireModules(chunk) {
const modules = [];
if (chunk.hasEntryModule()) {
const {id, groupsIterable} = chunk;
for (const entrypoint of groupsIterable) {
for (const {name} of entrypoint.chunks) {
if (name !== chunk.name) {
modules.push(name);
// 依赖 chunk 最后被打包的位置
}
}
}
}
return modules;
}
}
module.exports = MiniProgramPlugin;
JavaScript
1
https://gitee.com/janpoem/webpack-miniprogram-plugin.git
git@gitee.com:janpoem/webpack-miniprogram-plugin.git
janpoem
webpack-miniprogram-plugin
webpack-miniprogram-plugin
master

搜索帮助