@mpxjs/webpack-plugin
Version:
mpx compile core
186 lines (164 loc) • 6.29 kB
JavaScript
const compiler = require('./compiler')
const bindThis = require('./bind-this')
const parseRequest = require('../utils/parse-request')
const { matchCondition } = require('../utils/match-condition')
const loaderUtils = require('loader-utils')
const { MPX_DISABLE_EXTRACTOR_CACHE } = require('../utils/const')
const RecordRuntimeInfoDependency = require('../dependencies/RecordRuntimeInfoDependency')
const { createTemplateEngine, createSetupTemplate } = require('@mpxjs/template-engine')
const { stringify } = require('./dynamic')
module.exports = function (raw) {
this.cacheable()
const { resourcePath, queryObj, rawResourcePath } = parseRequest(this.resource)
const mpx = this.getMpx()
const projectRoot = mpx.projectRoot
const mode = mpx.mode
const env = mpx.env
const defs = mpx.defs
const i18n = mpx.i18n
const externalClasses = mpx.externalClasses
const decodeHTMLText = mpx.decodeHTMLText
const globalSrcMode = mpx.srcMode
const localSrcMode = queryObj.mode
const packageName = queryObj.packageRoot || mpx.currentPackageRoot || 'main'
const wxsContentMap = mpx.wxsContentMap
const optimizeRenderRules = mpx.optimizeRenderRules
const usingComponentsInfo = queryObj.usingComponentsInfo || {}
const componentPlaceholder = queryObj.componentPlaceholder || []
const hasComment = queryObj.hasComment
const isNative = queryObj.isNative
const ctorType = queryObj.ctorType
const hasScoped = queryObj.hasScoped
const runtimeCompile = queryObj.isDynamic
const moduleId = queryObj.moduleId || mpx.getModuleId(resourcePath)
let optimizeRenderLevel = 0
for (const rule of optimizeRenderRules) {
if (matchCondition(resourcePath, rule)) {
optimizeRenderLevel = rule.level || 1
break
}
}
const warn = (msg) => {
this.emitWarning(
new Error('[template compiler][' + this.resource + ']: ' + msg)
)
}
const error = (msg) => {
this.emitError(
new Error('[template compiler][' + this.resource + ']: ' + msg)
)
}
const { root, meta } = compiler.parse(raw, {
warn,
error,
runtimeCompile,
componentPlaceholder,
hasComment,
isNative,
ctorType,
mode,
env,
srcMode: localSrcMode || globalSrcMode,
defs,
decodeHTMLText,
externalClasses,
hasScoped,
moduleId,
usingComponentsInfo,
// 这里需传递rawResourcePath和wxsContentMap保持一致
filePath: rawResourcePath,
i18n,
checkUsingComponents: matchCondition(resourcePath, mpx.checkUsingComponentsRules),
globalComponents: Object.keys(mpx.globalComponents),
forceProxyEvent: matchCondition(resourcePath, mpx.forceProxyEventRules) || runtimeCompile,
hasVirtualHost: matchCondition(resourcePath, mpx.autoVirtualHostRules),
dynamicTemplateRuleRunner: mpx.dynamicTemplateRuleRunner
})
if (meta.wxsContentMap) {
for (const module in meta.wxsContentMap) {
wxsContentMap[`${rawResourcePath}~${module}`] = meta.wxsContentMap[module]
}
}
let result = runtimeCompile ? '' : compiler.serialize(root)
if (isNative) {
return result
}
let resultSource = ''
for (const module in meta.wxsModuleMap) {
const src = loaderUtils.urlToRequest(meta.wxsModuleMap[module], projectRoot)
resultSource += `var ${module} = require(${loaderUtils.stringifyRequest(this, src)});\n`
}
resultSource += `global.currentInject = {
moduleId: ${JSON.stringify(moduleId)}
};\n`
if (runtimeCompile) {
resultSource += 'global.currentInject.dynamic = true;\n'
}
const rawCode = runtimeCompile ? '' : compiler.genNode(root)
if (rawCode) {
try {
const ignoreMap = Object.assign({
_i: true,
_c: true,
_sc: true,
_r: true
}, meta.wxsModuleMap)
const bindResult = optimizeRenderLevel === 2
? bindThis.transformSimple(rawCode, {
ignoreMap
})
: bindThis.transform(rawCode, {
needCollect: true,
renderReduce: optimizeRenderLevel === 1,
ignoreMap
})
resultSource += `global.currentInject.render = function (_i, _c, _r, _sc) {
${bindResult.code}
_r(${optimizeRenderLevel === 2 ? 'true' : ''});
};\n`
if ((mode === 'tt' || mode === 'swan') && bindResult.propKeys) {
resultSource += `global.currentInject.propKeys = ${JSON.stringify(bindResult.propKeys)};\n`
}
} catch (e) {
error(`Invalid render function generated by the template, please check!
Template result:
${result}
Error code:
${rawCode}
Error Detail:
${e.stack}`)
return result
}
}
if (meta.computed) {
resultSource += bindThis.transform(`global.currentInject.injectComputed = {${meta.computed.join(',')}};`).code + '\n'
}
if (meta.refs) {
resultSource += `global.currentInject.getRefsData = function () { return ${JSON.stringify(meta.refs)}; };\n`
}
if (meta.options) {
resultSource += `global.currentInject.injectOptions = ${JSON.stringify(meta.options)};\n`
}
this.emitFile(resourcePath, '', undefined, {
skipEmit: true,
extractedResultSource: resultSource
})
if (queryObj.mpxCustomElement) {
this.cacheable(false)
const templateEngine = createTemplateEngine(mpx.mode)
result += `${createSetupTemplate()}\n` + templateEngine.buildTemplate(mpx.getPackageInjectedTemplateConfig(packageName))
}
// 运行时编译的组件直接返回基础模板的内容,并产出动态文本内容
if (runtimeCompile) {
// 包含了运行时组件的template模块必须每次都创建(但并不是每次都需要build),用于收集组件节点信息,传递信息以禁用父级extractor的缓存
this.emitFile(MPX_DISABLE_EXTRACTOR_CACHE, '', undefined, { skipEmit: true })
const templateInfo = {
templateAst: stringify(root),
...meta.runtimeInfo
}
// 以 package 为维度存储,meta 上的数据也只是存储了这个组件的 template 上获取的信息,需要在 dependency 里面再次进行合并操作
this._module.addPresentationalDependency(new RecordRuntimeInfoDependency(packageName, resourcePath, { type: 'template', info: templateInfo }))
// 运行时组件的模版直接返回空,在生成模版静态文件的时候(beforeModuleAssets)再动态注入
}
return result
}