@mpxjs/webpack-plugin
Version:
mpx compile core
152 lines (134 loc) • 4.63 kB
JavaScript
const async = require('async')
const JSON5 = require('json5')
const getEntryName = require('../utils/get-entry-name')
const FlagPluginDependency = require('../dependencies/FlagPluginDependency')
const RemoveEntryDependency = require('../dependencies/RemoveEntryDependency')
const createJSONHelper = require('./helper')
const { MPX_DISABLE_EXTRACTOR_CACHE, RESOLVE_IGNORED_ERR } = require('../utils/const')
module.exports = function (source) {
// 该loader中会在每次编译中动态添加entry,不能缓存,否则watch不好使
const nativeCallback = this.async()
const mpx = this.getMpx()
if (!mpx) {
return nativeCallback(null, source)
}
// json模块必须每次都创建(但并不是每次都需要build),用于动态添加编译入口,传递信息以禁用父级extractor的缓存
this.emitFile(MPX_DISABLE_EXTRACTOR_CACHE, '', undefined, { skipEmit: true })
this._module.addPresentationalDependency(new FlagPluginDependency())
const emitWarning = (msg) => {
this.emitWarning(
new Error('[plugin loader][' + this.resource + ']: ' + msg)
)
}
const emitError = (msg) => {
this.emitError(
new Error('[plugin loader][' + this.resource + ']: ' + msg)
)
}
const {
processPage,
processDynamicEntry,
processComponent,
processJsExport
} = createJSONHelper({
loaderContext: this,
emitWarning,
emitError
})
const context = this.context
const relativePath = this._compilation.outputOptions.publicPath || ''
const mode = mpx.mode
const srcMode = mpx.srcMode
const entryName = getEntryName(this)
// 最终输出中不需要为plugin.json产生chunk,而是使用extractor输出,删除plugin.json对应的entrypoint
if (entryName) this._module.addPresentationalDependency(new RemoveEntryDependency(entryName))
// 新模式下plugin.json输出依赖于extractor
const callback = (err, processOutput) => {
if (err) return nativeCallback(err)
let output = `var pluginEntry = ${JSON.stringify(pluginEntry, null, 2)};\n`
if (processOutput) output = processOutput(output)
output += 'module.exports = JSON.stringify(pluginEntry, null, 2);\n'
nativeCallback(null, output)
}
let pluginEntry
try {
pluginEntry = JSON5.parse(source)
} catch (err) {
return callback(err)
}
const processMain = (main, callback) => {
if (!main) return callback()
processJsExport(main, context, '', (err, entry) => {
if (err === RESOLVE_IGNORED_ERR) {
delete pluginEntry.main
return callback()
}
if (err) return callback(err)
pluginEntry.main = entry
callback()
})
}
const processComponents = (components, callback) => {
if (!components) return callback()
async.eachOf(components, (component, name, callback) => {
processComponent(component, context, { relativePath }, (err, entry) => {
if (err === RESOLVE_IGNORED_ERR) {
delete components[name]
return callback()
}
if (err) return callback(err)
components[name] = entry
callback()
})
}, callback)
}
const processPages = (pages, callback) => {
if (!pages) return callback()
if (srcMode === 'ali') {
const reversedMap = {}
const publicPages = pluginEntry.publicPages || {}
Object.keys(publicPages).forEach((key) => {
const item = publicPages[key]
reversedMap[item] = key
})
pages = pages.reduce((target, page, index) => {
const key = reversedMap[page] || `__private_page_${index}__`
target[key] = page
return target
}, {})
}
if (mode === 'ali') {
pluginEntry.publicPages = {}
pluginEntry.pages = []
}
async.eachOf(pages, (page, key, callback) => {
processPage(page, context, '', (err, entry) => {
if (err === RESOLVE_IGNORED_ERR) {
delete pages[key]
return callback()
}
if (err) return callback(err)
if (mode === 'ali') {
pluginEntry.pages.push(entry)
if (!/^__private_page_\d+__$/.test(key)) {
pluginEntry.publicPages[key] = entry
}
} else {
pages[key] = entry
}
callback()
})
}, callback)
}
async.parallel([
(callback) => {
return processMain(pluginEntry.main, callback)
}, (callback) => {
return processComponents(pluginEntry.publicComponents, callback)
}, (callback) => {
return processPages(pluginEntry.pages, callback)
}
], (err) => {
return callback(err, processDynamicEntry)
})
}