diff --git a/packages/webpack-plugin/lib/dependencies/RecordGlobalComponentsDependency.js b/packages/webpack-plugin/lib/dependencies/RecordGlobalComponentsDependency.js index c9c31edfe4..409535530d 100644 --- a/packages/webpack-plugin/lib/dependencies/RecordGlobalComponentsDependency.js +++ b/packages/webpack-plugin/lib/dependencies/RecordGlobalComponentsDependency.js @@ -1,11 +1,11 @@ const NullDependency = require('webpack/lib/dependencies/NullDependency') const makeSerializable = require('webpack/lib/util/makeSerializable') -const addQuery = require('../utils/add-query') class RecordGlobalComponentsDependency extends NullDependency { - constructor (usingComponents, context) { + constructor (globalComponents, globalComponentsInfo, context) { super() - this.usingComponents = usingComponents + this.globalComponents = globalComponents + this.globalComponentsInfo = globalComponentsInfo this.context = context } @@ -15,26 +15,25 @@ class RecordGlobalComponentsDependency extends NullDependency { mpxAction (module, compilation, callback) { const mpx = compilation.__mpx__ - const { usingComponents, context } = this - Object.keys(usingComponents).forEach((key) => { - const request = usingComponents[key] - mpx.usingComponents[key] = addQuery(request, { - context - }) - }) + const { globalComponents, globalComponentsInfo } = this + + mpx.globalComponents = globalComponents + mpx.globalComponentsInfo = globalComponentsInfo return callback() } serialize (context) { const { write } = context - write(this.usingComponents) + write(this.globalComponents) + write(this.globalComponentsInfo) write(this.context) super.serialize(context) } deserialize (context) { const { read } = context - this.usingComponents = read() + this.globalComponents = read() + this.globalComponentsInfo = read() this.context = read() super.deserialize(context) } diff --git a/packages/webpack-plugin/lib/dependencies/RecordRuntimeInfoDependency.js b/packages/webpack-plugin/lib/dependencies/RecordRuntimeInfoDependency.js index db737b71c9..1be8ff84ee 100644 --- a/packages/webpack-plugin/lib/dependencies/RecordRuntimeInfoDependency.js +++ b/packages/webpack-plugin/lib/dependencies/RecordRuntimeInfoDependency.js @@ -23,7 +23,7 @@ class RecordRuntimeInfoDependency extends NullDependency { template: {}, json: {}, style: [], - moduleId: '_' + mpx.pathHash(this.resourcePath) + moduleId: mpx.getModuleId(this.resourcePath) } const infoConfig = componentInfo[this.blockType] diff --git a/packages/webpack-plugin/lib/index.js b/packages/webpack-plugin/lib/index.js index b6cb0f9560..8a240b95b3 100644 --- a/packages/webpack-plugin/lib/index.js +++ b/packages/webpack-plugin/lib/index.js @@ -63,7 +63,7 @@ const async = require('async') const { parseQuery } = require('loader-utils') const stringifyLoadersAndResource = require('./utils/stringify-loaders-resource') const emitFile = require('./utils/emit-file') -const { MPX_PROCESSED_FLAG, MPX_DISABLE_EXTRACTOR_CACHE } = require('./utils/const') +const { MPX_PROCESSED_FLAG, MPX_DISABLE_EXTRACTOR_CACHE, MPX_APP_MODULE_ID } = require('./utils/const') const isEmptyObject = require('./utils/is-empty-object') const DynamicPlugin = require('./resolver/DynamicPlugin') const { isReact, isWeb } = require('./utils/env') @@ -179,6 +179,7 @@ class MpxWebpackPlugin { include: () => true } options.customOutputPath = options.customOutputPath || null + options.customComponentModuleId = options.customComponentModuleId || null options.nativeConfig = Object.assign({ cssLangs: ['css', 'less', 'stylus', 'scss', 'sass'] }, options.nativeConfig) @@ -679,7 +680,8 @@ class MpxWebpackPlugin { assetsModulesMap: new Map(), // 记录与asset相关联的ast,用于体积分析和esCheck,避免重复parse assetsASTsMap: new Map(), - usingComponents: {}, + globalComponents: {}, + globalComponentsInfo: {}, // todo es6 map读写性能高于object,之后会逐步替换 wxsAssetsCache: new Map(), addEntryPromiseMap: new Map(), @@ -745,6 +747,15 @@ class MpxWebpackPlugin { compilation.addEntry(compiler.context, dep, { name }, callback) return dep }, + getModuleId: (filePath, isApp = false) => { + if (isApp) return MPX_APP_MODULE_ID + const customComponentModuleId = this.options.customComponentModuleId + if (typeof customComponentModuleId === 'function') { + const customModuleId = customComponentModuleId(filePath) + if (customModuleId) return customModuleId + } + return '_' + mpx.pathHash(filePath) + }, getEntryNode: (module, type) => { const entryNodeModulesMap = mpx.entryNodeModulesMap let entryNode = entryNodeModulesMap.get(module) diff --git a/packages/webpack-plugin/lib/json-compiler/index.js b/packages/webpack-plugin/lib/json-compiler/index.js index f0386554a9..755d7863d5 100644 --- a/packages/webpack-plugin/lib/json-compiler/index.js +++ b/packages/webpack-plugin/lib/json-compiler/index.js @@ -10,7 +10,6 @@ const addQuery = require('../utils/add-query') const getJSONContent = require('../utils/get-json-content') const createHelpers = require('../helpers') const createJSONHelper = require('./helper') -const RecordGlobalComponentsDependency = require('../dependencies/RecordGlobalComponentsDependency') const RecordIndependentDependency = require('../dependencies/RecordIndependentDependency') const RecordRuntimeInfoDependency = require('../dependencies/RecordRuntimeInfoDependency') const { MPX_DISABLE_EXTRACTOR_CACHE, RESOLVE_IGNORED_ERR, JSON_JS_EXT } = require('../utils/const') @@ -208,8 +207,8 @@ module.exports = function (content) { warn: emitWarning, error: emitError, data: { - // polyfill global usingComponents & record globalComponents - globalComponents: mpx.usingComponents + // polyfill global usingComponents + globalComponents: mpx.globalComponents } } if (!isApp) { @@ -222,14 +221,6 @@ module.exports = function (content) { rulesRunner(json) } - if (isApp) { - Object.assign(mpx.usingComponents, json.usingComponents) - // 在 rulesRunner 运行后保存全局注册组件 - // todo 其余地方在使用mpx.usingComponents时存在缓存问题,要规避该问题需要在所有使用mpx.usingComponents的loader中添加app resourcePath作为fileDependency,但对于缓存有效率影响巨大 - // todo 需要考虑一种精准控制缓存的方式,仅在全局组件发生变更时才使相关使用方的缓存失效,例如按需在相关模块上动态添加request query? - this._module.addPresentationalDependency(new RecordGlobalComponentsDependency(mpx.usingComponents, this.context)) - } - const processComponents = (components, context, callback) => { if (components) { async.eachOf(components, (component, name, callback) => { diff --git a/packages/webpack-plugin/lib/loader.js b/packages/webpack-plugin/lib/loader.js index 430c60da1b..56fee5db9d 100644 --- a/packages/webpack-plugin/lib/loader.js +++ b/packages/webpack-plugin/lib/loader.js @@ -1,11 +1,9 @@ -const JSON5 = require('json5') const parseComponent = require('./parser') const createHelpers = require('./helpers') const parseRequest = require('./utils/parse-request') const { matchCondition } = require('./utils/match-condition') const addQuery = require('./utils/add-query') const async = require('async') -const getJSONContent = require('./utils/get-json-content') const normalize = require('./utils/normalize') const getEntryName = require('./utils/get-entry-name') const AppEntryDependency = require('./dependencies/AppEntryDependency') @@ -13,12 +11,11 @@ const RecordResourceMapDependency = require('./dependencies/RecordResourceMapDep const CommonJsVariableDependency = require('./dependencies/CommonJsVariableDependency') const DynamicEntryDependency = require('./dependencies/DynamicEntryDependency') const tsWatchRunLoaderFilter = require('./utils/ts-loader-watch-run-loader-filter') -const { MPX_APP_MODULE_ID } = require('./utils/const') const { isReact } = require('./utils/env') +const preProcessJson = require('./utils/pre-process-json') const path = require('path') const processWeb = require('./web') const processReact = require('./react') -const getRulesRunner = require('./platform') const genMpxCustomElement = require('./runtime-render/gen-mpx-custom-element') module.exports = function (content) { @@ -89,7 +86,7 @@ module.exports = function (content) { const loaderContext = this const isProduction = this.minimize || process.env.NODE_ENV === 'production' const filePath = this.resourcePath - const moduleId = ctorType === 'app' ? MPX_APP_MODULE_ID : '_' + mpx.pathHash(filePath) + const moduleId = mpx.getModuleId(resourcePath, ctorType === 'app') const parts = parseComponent(content, { filePath, @@ -106,55 +103,36 @@ module.exports = function (content) { async.waterfall([ (callback) => { - getJSONContent(parts.json || {}, null, loaderContext, (err, content) => { + preProcessJson({ + json: parts.json || {}, + srcMode, + emitWarning, + emitError, + ctorType, + resourcePath, + loaderContext + }, (err, jsonInfo) => { if (err) return callback(err) - if (parts.json) parts.json.content = content - callback() + callback(null, jsonInfo) }) }, - (callback) => { + (jsonInfo, callback) => { + const { + componentPlaceholder, + componentGenerics, + usingComponentsInfo, + jsonContent + } = jsonInfo const hasScoped = parts.styles.some(({ scoped }) => scoped) || autoScope const templateAttrs = parts.template && parts.template.attrs const hasComment = templateAttrs && templateAttrs.comments const isNative = false - let usingComponents = [].concat(Object.keys(mpx.usingComponents)) - let componentPlaceholder = [] - let componentGenerics = {} - - if (parts.json && parts.json.content) { - const rulesRunnerOptions = { - mode, - srcMode, - type: 'json', - waterfall: true, - warn: emitWarning, - error: emitError - } - if (ctorType !== 'app') { - rulesRunnerOptions.mainKey = pagesMap[resourcePath] ? 'page' : 'component' - } - const rulesRunner = getRulesRunner(rulesRunnerOptions) - try { - const ret = JSON5.parse(parts.json.content) - if (rulesRunner) rulesRunner(ret) - if (ret.usingComponents) { - usingComponents = usingComponents.concat(Object.keys(ret.usingComponents)) - } - if (ret.componentPlaceholder) { - componentPlaceholder = componentPlaceholder.concat(Object.values(ret.componentPlaceholder)) - } - if (ret.componentGenerics) { - componentGenerics = Object.assign({}, ret.componentGenerics) - } - } catch (e) { - return callback(e) - } - } // 处理mode为web时输出vue格式文件 if (mode === 'web') { return processWeb({ parts, + jsonContent, loaderContext, pagesMap, componentsMap, @@ -166,7 +144,7 @@ module.exports = function (content) { hasScoped, hasComment, isNative, - usingComponents, + usingComponentsInfo: JSON.stringify(usingComponentsInfo), componentGenerics, autoScope, callback @@ -176,6 +154,7 @@ module.exports = function (content) { if (isReact(mode)) { return processReact({ parts, + jsonContent, loaderContext, pagesMap, componentsMap, @@ -187,7 +166,7 @@ module.exports = function (content) { hasScoped, hasComment, isNative, - usingComponents, + usingComponentsInfo: JSON.stringify(usingComponentsInfo), componentGenerics, autoScope, callback @@ -255,7 +234,7 @@ module.exports = function (content) { isNative, ctorType, moduleId, - usingComponents, + usingComponentsInfo: JSON.stringify(usingComponentsInfo), componentPlaceholder // 添加babel处理渲染函数中可能包含的...展开运算符 // 由于...运算符应用范围极小以及babel成本极高,先关闭此特性后续看情况打开 diff --git a/packages/webpack-plugin/lib/native-loader.js b/packages/webpack-plugin/lib/native-loader.js index 89f50eb10c..402ac11e03 100644 --- a/packages/webpack-plugin/lib/native-loader.js +++ b/packages/webpack-plugin/lib/native-loader.js @@ -1,16 +1,14 @@ const path = require('path') -const JSON5 = require('json5') const parseRequest = require('./utils/parse-request') const config = require('./config') const createHelpers = require('./helpers') -const getJSONContent = require('./utils/get-json-content') const async = require('async') const { matchCondition } = require('./utils/match-condition') -const { JSON_JS_EXT, MPX_APP_MODULE_ID } = require('./utils/const') -const getRulesRunner = require('./platform') +const { JSON_JS_EXT } = require('./utils/const') const getEntryName = require('./utils/get-entry-name') const AppEntryDependency = require('./dependencies/AppEntryDependency') const RecordResourceMapDependency = require('./dependencies/RecordResourceMapDependency') +const preProcessJson = require('./utils/pre-process-json') // todo native-loader考虑与mpx-loader或加强复用,原生组件约等于4个区块都为src的.mpx文件 module.exports = function (content) { @@ -25,6 +23,7 @@ module.exports = function (content) { const loaderContext = this const isProduction = this.minimize || process.env.NODE_ENV === 'production' const filePath = this.resourcePath + const moduleId = mpx.getModuleId(filePath) const { resourcePath, queryObj } = parseRequest(this.resource) const packageRoot = queryObj.packageRoot || mpx.currentPackageRoot const mode = mpx.mode @@ -114,7 +113,29 @@ module.exports = function (content) { new Error('[native-loader][' + this.resource + ']: ' + msg) ) } + let ctorType = pagesMap[resourcePath] + ? 'page' + : componentsMap[resourcePath] + ? 'component' + : 'app' + // 处理构造器类型 + const ctor = ctorType === 'page' + ? (mpx.forceUsePageCtor || mode === 'ali') ? 'Page' : 'Component' + : ctorType === 'component' + ? 'Component' + : 'App' + + // 支持资源query传入isPage或isComponent支持页面/组件单独编译 + if (ctorType === 'app' && (queryObj.isComponent || queryObj.isPage)) { + const entryName = getEntryName(this) || mpx.getOutputPath(resourcePath, queryObj.isComponent ? 'component' : 'page') + ctorType = queryObj.isComponent ? 'component' : 'page' + this._module.addPresentationalDependency(new RecordResourceMapDependency(resourcePath, ctorType, entryName, packageRoot)) + } + if (ctorType === 'app') { + const appName = getEntryName(this) + if (appName) this._module.addPresentationalDependency(new AppEntryDependency(resourcePath, appName)) + } // 先读取json获取usingComponents信息 async.waterfall([ (callback) => { @@ -141,66 +162,29 @@ module.exports = function (content) { }, callback) }, (callback) => { - getJSONContent({ - src: typeResourceMap.json, - useJSONJS - }, null, this, callback) - }, (content, callback) => { - let componentPlaceholder = [] - let json - try { - json = JSON5.parse(content) - } catch (e) { - return callback(e) - } - let usingComponents = Object.keys(mpx.usingComponents) - const rulesRunnerOptions = { - mode, + preProcessJson({ + json: { + src: typeResourceMap.json, + useJSONJS + }, srcMode, - type: 'json', - waterfall: true, - warn: emitWarning, - error: emitError - } - - let ctorType = pagesMap[resourcePath] - ? 'page' - : componentsMap[resourcePath] - ? 'component' - : 'app' - - // 支持资源query传入isPage或isComponent支持页面/组件单独编译 - if (ctorType === 'app' && (queryObj.isComponent || queryObj.isPage)) { - const entryName = getEntryName(this) || mpx.getOutputPath(resourcePath, queryObj.isComponent ? 'component' : 'page') - ctorType = queryObj.isComponent ? 'component' : 'page' - this._module.addPresentationalDependency(new RecordResourceMapDependency(resourcePath, ctorType, entryName, packageRoot)) - } - - // 处理构造器类型 - const ctor = ctorType === 'page' - ? (mpx.forceUsePageCtor || mode === 'ali') ? 'Page' : 'Component' - : ctorType === 'component' - ? 'Component' - : 'App' - - if (ctorType === 'app') { - const appName = getEntryName(this) - if (appName) this._module.addPresentationalDependency(new AppEntryDependency(resourcePath, appName)) - } - - const moduleId = ctorType === 'app' ? MPX_APP_MODULE_ID : '_' + mpx.pathHash(filePath) + emitWarning, + emitError, + ctorType, + resourcePath, + loaderContext + }, (err, jsonInfo) => { + if (err) return callback(err) + callback(null, jsonInfo) + }) + }, + (jsonInfo, callback) => { + const { + componentPlaceholder, + componentGenerics, + usingComponentsInfo + } = jsonInfo - if (ctorType !== 'app') { - rulesRunnerOptions.mainKey = pagesMap[resourcePath] ? 'page' : 'component' - } - const rulesRunner = getRulesRunner(rulesRunnerOptions) - if (rulesRunner) rulesRunner(json) - if (json.usingComponents) { - usingComponents = usingComponents.concat(Object.keys(json.usingComponents)) - } - if (json.componentPlaceholder) { - componentPlaceholder = componentPlaceholder.concat(Object.values(json.componentPlaceholder)) - } const { getRequire } = createHelpers(loaderContext) @@ -222,8 +206,9 @@ module.exports = function (content) { isNative, ctorType, moduleId, - usingComponents, - componentPlaceholder + componentGenerics, + componentPlaceholder, + usingComponentsInfo: JSON.stringify(usingComponentsInfo) }) break case 'styles': diff --git a/packages/webpack-plugin/lib/platform/json/wx/index.js b/packages/webpack-plugin/lib/platform/json/wx/index.js index 2562b9ff44..c149329c1f 100644 --- a/packages/webpack-plugin/lib/platform/json/wx/index.js +++ b/packages/webpack-plugin/lib/platform/json/wx/index.js @@ -84,10 +84,9 @@ module.exports = function getSpec ({ warn, error }) { return input } - function fillGlobalComponents (input, { globalComponents }) { - if (globalComponents) { - Object.assign(globalComponents, input.usingComponents) - } + function fillGlobalComponents (input, { globalComponents }, meta) { + // 通过meta进行globalComponents的透传 + meta.usingComponents = input.usingComponents return input } @@ -165,8 +164,6 @@ module.exports = function getSpec ({ warn, error }) { swan: componentNameCapitalToHyphen('usingComponents') }, { - // todo ali 2.0已支持全局组件,待移除 - ali: addGlobalComponents, swan: addGlobalComponents, qq: addGlobalComponents, tt: addGlobalComponents, @@ -373,8 +370,6 @@ module.exports = function getSpec ({ warn, error }) { }, { test: 'usingComponents', - // todo ali 2.0已支持全局组件,待移除 - ali: fillGlobalComponents, qq: fillGlobalComponents, swan: fillGlobalComponents, tt: fillGlobalComponents, @@ -382,8 +377,6 @@ module.exports = function getSpec ({ warn, error }) { }, { test: 'usingComponents', - // todo ali 2.0已支持全局组件,待移除 - ali: deletePath({ noLog: true }), qq: deletePath({ noLog: true }), swan: deletePath({ noLog: true }), tt: deletePath({ noLog: true }), diff --git a/packages/webpack-plugin/lib/react/index.js b/packages/webpack-plugin/lib/react/index.js index a7e9e34a2d..8018d5f1ad 100644 --- a/packages/webpack-plugin/lib/react/index.js +++ b/packages/webpack-plugin/lib/react/index.js @@ -8,6 +8,7 @@ const RecordLoaderContentDependency = require('../dependencies/RecordLoaderConte module.exports = function ({ parts, + jsonContent, loaderContext, pagesMap, componentsMap, @@ -19,7 +20,7 @@ module.exports = function ({ hasScoped, hasComment, isNative, - usingComponents, + usingComponentsInfo, componentGenerics, autoScope, callback @@ -50,7 +51,7 @@ module.exports = function ({ srcMode, moduleId, ctorType, - usingComponents, + usingComponentsInfo, componentGenerics }, callback) }, @@ -63,7 +64,7 @@ module.exports = function ({ }, callback) }, (callback) => { - processJSON(parts.json, { + processJSON(jsonContent, { loaderContext, ctorType, pagesMap, diff --git a/packages/webpack-plugin/lib/react/processJSON.js b/packages/webpack-plugin/lib/react/processJSON.js index 25ff813e9d..1f8fcbf2a3 100644 --- a/packages/webpack-plugin/lib/react/processJSON.js +++ b/packages/webpack-plugin/lib/react/processJSON.js @@ -12,9 +12,8 @@ const createJSONHelper = require('../json-compiler/helper') const getRulesRunner = require('../platform/index') const { RESOLVE_IGNORED_ERR } = require('../utils/const') const RecordResourceMapDependency = require('../dependencies/RecordResourceMapDependency') -const RecordGlobalComponentsDependency = require('../dependencies/RecordGlobalComponentsDependency') -module.exports = function (json, { +module.exports = function (jsonContent, { loaderContext, ctorType, pagesMap, @@ -81,12 +80,11 @@ module.exports = function (json, { } const isApp = ctorType === 'app' - if (!json) { + if (!jsonContent) { return callback() } - // 由于json需要提前读取在template处理中使用,src的场景已经在loader中处理了,此处无需考虑json.src的场景 try { - jsonObj = JSON5.parse(json.content) + jsonObj = JSON5.parse(jsonContent) // 处理runner const rulesRunnerOptions = { mode, @@ -96,8 +94,8 @@ module.exports = function (json, { warn: emitWarning, error: emitError, data: { - // polyfill global usingComponents & record globalComponents - globalComponents: mpx.usingComponents + // polyfill global usingComponents + globalComponents: mpx.globalComponents } } @@ -110,12 +108,6 @@ module.exports = function (json, { if (rulesRunner) { rulesRunner(jsonObj) } - if (isApp) { - // 收集全局组件 - Object.assign(mpx.usingComponents, jsonObj.usingComponents) - // 在 rulesRunner 运行后保存全局注册组件 - loaderContext._module.addPresentationalDependency(new RecordGlobalComponentsDependency(mpx.usingComponents, loaderContext.context)) - } } catch (e) { return callback(e) } diff --git a/packages/webpack-plugin/lib/react/processTemplate.js b/packages/webpack-plugin/lib/react/processTemplate.js index 7f1ca743d0..8e9c94d0d5 100644 --- a/packages/webpack-plugin/lib/react/processTemplate.js +++ b/packages/webpack-plugin/lib/react/processTemplate.js @@ -14,7 +14,7 @@ module.exports = function (template, { srcMode, moduleId, ctorType, - usingComponents, + usingComponentsInfo, componentGenerics }, callback) { const mpx = loaderContext.getMpx() @@ -64,7 +64,7 @@ module.exports = function (template, { const { root, meta } = templateCompiler.parse(template.content, { warn, error, - usingComponents, + usingComponentsInfo, // processTemplate中无其他地方使用,直接透传 string 类型 hasComment, isNative, ctorType, diff --git a/packages/webpack-plugin/lib/style-compiler/index.js b/packages/webpack-plugin/lib/style-compiler/index.js index fd662fe8ae..586561e2a4 100644 --- a/packages/webpack-plugin/lib/style-compiler/index.js +++ b/packages/webpack-plugin/lib/style-compiler/index.js @@ -19,7 +19,7 @@ module.exports = function (css, map) { const { resourcePath, queryObj } = parseRequest(this.resource) const mpx = this.getMpx() const mpxStyleOptions = (queryObj.mpxStyleOptions && JSON.parse(queryObj.mpxStyleOptions)) || {} - const id = queryObj.moduleId || mpxStyleOptions.mid || '_' + mpx.pathHash(resourcePath) + const id = queryObj.moduleId || mpxStyleOptions.mid || mpx.getModuleId(resourcePath) const appInfo = mpx.appInfo const defs = mpx.defs const mode = mpx.mode diff --git a/packages/webpack-plugin/lib/style-compiler/plugins/scope-id.js b/packages/webpack-plugin/lib/style-compiler/plugins/scope-id.js index 43f43e76fa..3c2745fcca 100644 --- a/packages/webpack-plugin/lib/style-compiler/plugins/scope-id.js +++ b/packages/webpack-plugin/lib/style-compiler/plugins/scope-id.js @@ -20,6 +20,7 @@ module.exports = ({ id }) => { } return } + if (node.selector === ':host') return node.selector = selectorParser(selectors => { selectors.each(selector => { let node = null diff --git a/packages/webpack-plugin/lib/template-compiler/compiler.js b/packages/webpack-plugin/lib/template-compiler/compiler.js index c8d127bdfd..8c5a5cbda3 100644 --- a/packages/webpack-plugin/lib/template-compiler/compiler.js +++ b/packages/webpack-plugin/lib/template-compiler/compiler.js @@ -116,6 +116,7 @@ let i18nInjectableComputed = [] let hasOptionalChaining = false let processingTemplate = false const rulesResultMap = new Map() +let usingComponents = [] function updateForScopesMap () { forScopesMap = {} @@ -633,6 +634,9 @@ function parse (template, options) { processingTemplate = false rulesResultMap.clear() + if (typeof options.usingComponentsInfo === 'string') options.usingComponentsInfo = JSON.parse(options.usingComponentsInfo) + usingComponents = Object.keys(options.usingComponentsInfo) + const _warn = content => { const currentElementRuleResult = rulesResultMap.get(currentEl) || rulesResultMap.set(currentEl, { warnArray: [], @@ -654,7 +658,7 @@ function parse (template, options) { type: 'template', testKey: 'tag', data: { - usingComponents: options.usingComponents + usingComponents }, warn: _warn, error: _error @@ -804,7 +808,7 @@ function parse (template, options) { if (!tagNames.has('component') && options.checkUsingComponents) { const arr = [] - options.usingComponents.forEach((item) => { + usingComponents.forEach((item) => { if (!tagNames.has(item) && !options.globalComponents.includes(item) && !options.componentPlaceholder.includes(item)) { arr.push(item) } @@ -980,7 +984,7 @@ function processComponentIs (el, options) { const range = getAndRemoveAttr(el, 'range').val const isInRange = makeMap(range || '') - el.components = (options.usingComponents || []).filter(i => { + el.components = (usingComponents).filter(i => { if (!range) return true return isInRange(i) }) @@ -2154,7 +2158,7 @@ function isRealNode (el) { } function isComponentNode (el, options) { - return options.usingComponents.indexOf(el.tag) !== -1 || el.tag === 'component' + return usingComponents.indexOf(el.tag) !== -1 || el.tag === 'component' } function isReactComponent (el, options) { @@ -2276,8 +2280,10 @@ function postProcessAliComponentRootView (el, options, meta) { { condition: /^style$/, action: 'move' }, { condition: /^slot$/, action: 'move' } ] + const tagName = el.tag + const mid = options.usingComponentsInfo[tagName]?.mid || moduleId const processAppendAttrsRules = [ - { name: 'class', value: `${MPX_ROOT_VIEW} host-${moduleId}` } + { name: 'class', value: `${MPX_ROOT_VIEW} host-${mid}` } ] const newAttrs = [] const allAttrs = cloneAttrsList(el.attrsList) @@ -2672,11 +2678,19 @@ function closeElement (el, meta, options) { } const isTemplate = postProcessTemplate(el) || processingTemplate - if (!isNative && !isTemplate) { - if (isComponentNode(el, options) && !hasVirtualHost && mode === 'ali') { + if (!isTemplate) { + if (!isNative) { + postProcessComponentIs(el, (child) => { + if (!hasVirtualHost && mode === 'ali') { + postProcessAliComponentRootView(child, options) + } else { + postProcessIf(child) + } + }) + } + if (isComponentNode(el, options) && !hasVirtualHost && mode === 'ali' && el.tag !== 'component') { postProcessAliComponentRootView(el, options, meta) } - postProcessComponentIs(el) } if (runtimeCompile) { @@ -2723,7 +2737,7 @@ function cloneAttrsList (attrsList) { }) } -function postProcessComponentIs (el) { +function postProcessComponentIs (el, postProcessChild) { if (el.is && el.components) { let tempNode if (el.for || el.if || el.elseif || el.else) { @@ -2745,7 +2759,7 @@ function postProcessComponentIs (el) { }) newChild.exps = el.exps addChild(tempNode, newChild) - postProcessIf(newChild) + postProcessChild(newChild) }) if (!el.parent) { diff --git a/packages/webpack-plugin/lib/template-compiler/index.js b/packages/webpack-plugin/lib/template-compiler/index.js index c5c116bc17..4bb489f53f 100644 --- a/packages/webpack-plugin/lib/template-compiler/index.js +++ b/packages/webpack-plugin/lib/template-compiler/index.js @@ -24,14 +24,14 @@ module.exports = function (raw) { const packageName = queryObj.packageRoot || mpx.currentPackageRoot || 'main' const wxsContentMap = mpx.wxsContentMap const optimizeRenderRules = mpx.optimizeRenderRules - const usingComponents = queryObj.usingComponents || [] + 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.pathHash(resourcePath) + const moduleId = queryObj.moduleId || mpx.getModuleId(resourcePath) let optimizeRenderLevel = 0 for (const rule of optimizeRenderRules) { @@ -57,7 +57,6 @@ module.exports = function (raw) { warn, error, runtimeCompile, - usingComponents, componentPlaceholder, hasComment, isNative, @@ -70,11 +69,12 @@ module.exports = function (raw) { externalClasses, hasScoped, moduleId, + usingComponentsInfo, // 这里需传递resourcePath和wxsContentMap保持一致 filePath: resourcePath, i18n, checkUsingComponents: matchCondition(resourcePath, mpx.checkUsingComponentsRules), - globalComponents: Object.keys(mpx.usingComponents), + globalComponents: Object.keys(mpx.globalComponents), forceProxyEvent: matchCondition(resourcePath, mpx.forceProxyEventRules) || runtimeCompile, hasVirtualHost: matchCondition(resourcePath, mpx.autoVirtualHostRules), dynamicTemplateRuleRunner: mpx.dynamicTemplateRuleRunner diff --git a/packages/webpack-plugin/lib/utils/pre-process-json.js b/packages/webpack-plugin/lib/utils/pre-process-json.js new file mode 100644 index 0000000000..0510cccf15 --- /dev/null +++ b/packages/webpack-plugin/lib/utils/pre-process-json.js @@ -0,0 +1,113 @@ +const RecordGlobalComponentsDependency = require('../dependencies/RecordGlobalComponentsDependency') +const JSON5 = require('json5') +const isUrlRequest = require('./is-url-request') +const parseRequest = require('./parse-request') +const addQuery = require('./add-query') +const resolve = require('./resolve') +const getJSONContent = require('./get-json-content') +const getRulesRunner = require('../platform') +const async = require('async') + +module.exports = function ({ + json, + srcMode, + emitWarning, + emitError, + ctorType, + resourcePath, + loaderContext +}, callback) { + const mpx = loaderContext.getMpx() + const context = loaderContext.context + const mode = mpx.mode + const pagesMap = mpx.pagesMap + async.waterfall([ + (callback) => { + getJSONContent(json, null, loaderContext, callback) + }, + (jsonContent, callback) => { + if (!jsonContent) return callback(null, {}) + let componentPlaceholder = [] + let componentGenerics = {} + const usingComponentsInfo = {} + const usingComponents = {} + const finalCallback = (err) => { + if (err) return callback(err) + if (ctorType === 'app') { + // 在 rulesRunner 运行后保存全局注册组件 + // todo 其余地方在使用mpx.globalComponents时存在缓存问题,要规避该问题需要在所有使用mpx.globalComponents的loader中添加app resourcePath作为fileDependency,但对于缓存有效率影响巨大 + // todo 需要考虑一种精准控制缓存的方式,仅在全局组件发生变更时才使相关使用方的缓存失效,例如按需在相关模块上动态添加request query? + loaderContext._module.addPresentationalDependency(new RecordGlobalComponentsDependency(usingComponents, usingComponentsInfo, context)) + } + callback(null, { + componentPlaceholder, + componentGenerics, + usingComponentsInfo: Object.assign({}, usingComponentsInfo, mpx.globalComponentsInfo), + jsonContent + }) + } + try { + const ret = JSON5.parse(jsonContent) + const rulesMeta = {} + const rulesRunnerOptions = { + mode, + srcMode, + type: 'json', + waterfall: true, + warn: emitWarning, + error: emitError, + meta: rulesMeta + } + if (ctorType !== 'app') { + rulesRunnerOptions.mainKey = pagesMap[resourcePath] ? 'page' : 'component' + } + const rulesRunner = getRulesRunner(rulesRunnerOptions) + try { + if (rulesRunner) rulesRunner(ret) + } catch (e) { + return finalCallback(e) + } + // 不支持全局组件的平台,runRules 时会删除 app.json 中的 usingComponents, 同时 fillGlobalComponents 方法会对 rulesMeta 赋值 usingComponents,通过 rulesMeta 来重新获取 globalComponents + // page | component 时 直接获取 ret.usingComponents 内容 + Object.assign(usingComponents, ret.usingComponents || rulesMeta.usingComponents) + + if (ret.componentPlaceholder) { + componentPlaceholder = componentPlaceholder.concat(Object.values(ret.componentPlaceholder)) + } + if (ret.componentGenerics) { + componentGenerics = Object.assign({}, ret.componentGenerics) + } + if (usingComponents) { + const setUsingComponentInfo = (name, moduleId) => { + usingComponentsInfo[name] = { mid: moduleId } + } + async.eachOf(usingComponents, (component, name, callback) => { + if (ctorType === 'app') { + usingComponents[name] = addQuery(component, { + context + }) + } + if (!isUrlRequest(component)) { + const moduleId = mpx.getModuleId(component, ctorType === 'app') + setUsingComponentInfo(name, moduleId) + return callback() + } + resolve(context, component, loaderContext, (err, resource) => { + if (err) return callback(err) + const { rawResourcePath } = parseRequest(resource) + const moduleId = mpx.getModuleId(rawResourcePath, ctorType === 'app') + setUsingComponentInfo(name, moduleId) + callback() + }) + }, (err) => { + finalCallback(err) + }) + } else { + finalCallback() + } + } catch (err) { + finalCallback(err) + } + } + ], callback) +} diff --git a/packages/webpack-plugin/lib/web/index.js b/packages/webpack-plugin/lib/web/index.js index b7c10984d9..51c0df16a4 100644 --- a/packages/webpack-plugin/lib/web/index.js +++ b/packages/webpack-plugin/lib/web/index.js @@ -8,6 +8,7 @@ const RecordLoaderContentDependency = require('../dependencies/RecordLoaderConte module.exports = function ({ parts, + jsonContent, loaderContext, pagesMap, componentsMap, @@ -19,7 +20,7 @@ module.exports = function ({ hasScoped, hasComment, isNative, - usingComponents, + usingComponentsInfo, componentGenerics, autoScope, callback @@ -27,7 +28,7 @@ module.exports = function ({ if (ctorType === 'app' && !queryObj.isApp) { return async.waterfall([ (callback) => { - processJSON(parts.json, { + processJSON(jsonContent, { loaderContext, ctorType, pagesMap, @@ -68,7 +69,7 @@ module.exports = function ({ srcMode, moduleId, ctorType, - usingComponents, + usingComponentsInfo, componentGenerics }, callback) }, @@ -80,7 +81,7 @@ module.exports = function ({ }, callback) }, (callback) => { - processJSON(parts.json, { + processJSON(jsonContent, { loaderContext, ctorType, pagesMap, diff --git a/packages/webpack-plugin/lib/web/processJSON.js b/packages/webpack-plugin/lib/web/processJSON.js index ea6f3ecac4..49abb713b0 100644 --- a/packages/webpack-plugin/lib/web/processJSON.js +++ b/packages/webpack-plugin/lib/web/processJSON.js @@ -12,9 +12,8 @@ const createJSONHelper = require('../json-compiler/helper') const getRulesRunner = require('../platform/index') const { RESOLVE_IGNORED_ERR } = require('../utils/const') const RecordResourceMapDependency = require('../dependencies/RecordResourceMapDependency') -const RecordGlobalComponentsDependency = require('../dependencies/RecordGlobalComponentsDependency') -module.exports = function (json, { +module.exports = function (jsonContent, { loaderContext, ctorType, pagesMap, @@ -82,12 +81,11 @@ module.exports = function (json, { const isApp = ctorType === 'app' - if (!json) { + if (!jsonContent) { return callback() } - // 由于json需要提前读取在template处理中使用,src的场景已经在loader中处理了,此处无需考虑json.src的场景 try { - jsonObj = JSON5.parse(json.content) + jsonObj = JSON5.parse(jsonContent) // 处理runner const rulesRunnerOptions = { mode, @@ -97,8 +95,8 @@ module.exports = function (json, { warn: emitWarning, error: emitError, data: { - // polyfill global usingComponents & record globalComponents - globalComponents: mpx.usingComponents + // polyfill global usingComponents + globalComponents: mpx.globalComponents } } @@ -111,12 +109,6 @@ module.exports = function (json, { if (rulesRunner) { rulesRunner(jsonObj) } - if (isApp) { - // 收集全局组件 - Object.assign(mpx.usingComponents, jsonObj.usingComponents) - // 在 rulesRunner 运行后保存全局注册组件 - loaderContext._module.addPresentationalDependency(new RecordGlobalComponentsDependency(mpx.usingComponents, loaderContext.context)) - } } catch (e) { return callback(e) } diff --git a/packages/webpack-plugin/lib/web/processTemplate.js b/packages/webpack-plugin/lib/web/processTemplate.js index 6fadcc54ce..8129163e2f 100644 --- a/packages/webpack-plugin/lib/web/processTemplate.js +++ b/packages/webpack-plugin/lib/web/processTemplate.js @@ -12,7 +12,7 @@ module.exports = function (template, { srcMode, moduleId, ctorType, - usingComponents, + usingComponentsInfo, componentGenerics }, callback) { const mpx = loaderContext.getMpx() @@ -73,7 +73,7 @@ module.exports = function (template, { const { root, meta } = templateCompiler.parse(template.content, { warn, error, - usingComponents, + usingComponentsInfo, // processTemplate中无其他地方使用,直接透传 string 类型 hasComment, isNative, ctorType, diff --git a/packages/webpack-plugin/test/platform/util.js b/packages/webpack-plugin/test/platform/util.js index 4551ac2ac7..99588f2b35 100644 --- a/packages/webpack-plugin/test/platform/util.js +++ b/packages/webpack-plugin/test/platform/util.js @@ -8,6 +8,7 @@ const warnFn = jest.fn(console.warn) function compileTemplate (template, { srcMode = 'wx', mode = 'ali', env = '' } = {}) { const parsed = compiler.parse(template, { usingComponents: [], + usingComponentsInfo: {}, externalClasses: [], srcMode, mode, diff --git a/packages/webpack-plugin/test/platform/wx/json/app.spec.js b/packages/webpack-plugin/test/platform/wx/json/app.spec.js index 9c616bd429..a0aca6bbc9 100644 --- a/packages/webpack-plugin/test/platform/wx/json/app.spec.js +++ b/packages/webpack-plugin/test/platform/wx/json/app.spec.js @@ -74,7 +74,12 @@ describe('json should transform app json correct', function () { } } const output = compileJson(input) - expect(output).toEqual({}) + expect(output).toEqual({ + usingComponents: { + 'mpx-dialog': '@mpxjs/cube-ui/components/mpx-dialog', + 'mpx-toast': '@mpxjs/cube-ui/components/mpx-toast' + } + }) expect(warnFn).not.toHaveBeenCalled() }) }) diff --git a/packages/webpack-plugin/test/platform/wx/json/component.spec.js b/packages/webpack-plugin/test/platform/wx/json/component.spec.js index 15f444d270..27b92b3c8b 100644 --- a/packages/webpack-plugin/test/platform/wx/json/component.spec.js +++ b/packages/webpack-plugin/test/platform/wx/json/component.spec.js @@ -23,9 +23,7 @@ describe('json should transform component json correct', function () { expect(output).toEqual({ component: true, usingComponents: { - 'my-list': './list', - 'mpx-dialog': '@mpxjs/cube-ui/components/mpx-dialog', - 'mpx-toast': '@mpxjs/cube-ui/components/mpx-toast' + 'my-list': './list' } }) expect(warnFn).not.toHaveBeenCalled() diff --git a/packages/webpack-plugin/test/platform/wx/json/page.spec.js b/packages/webpack-plugin/test/platform/wx/json/page.spec.js index f339f79103..6a2c67775d 100644 --- a/packages/webpack-plugin/test/platform/wx/json/page.spec.js +++ b/packages/webpack-plugin/test/platform/wx/json/page.spec.js @@ -40,9 +40,7 @@ describe('json should transform page json correct', function () { }) expect(output).toEqual({ usingComponents: { - 'my-list': './list', - 'mpx-dialog': '@mpxjs/cube-ui/components/mpx-dialog', - 'mpx-toast': '@mpxjs/cube-ui/components/mpx-toast' + 'my-list': './list' } }) expect(warnFn).not.toHaveBeenCalled() diff --git a/test/e2e/miniprogram-project/package.json b/test/e2e/miniprogram-project/package.json index 646968a4f9..d2c858788d 100644 --- a/test/e2e/miniprogram-project/package.json +++ b/test/e2e/miniprogram-project/package.json @@ -40,7 +40,7 @@ "@babel/preset-env": "^7.10.4", "@babel/runtime-corejs3": "^7.10.4", "@mpxjs/miniprogram-simulate": "1.4.9", - "@mpxjs/mpx-jest": "0.0.27", + "@mpxjs/mpx-jest": "0.0.31", "@mpxjs/webpack-plugin": "^2.9.58", "@mpxjs/template-engine": "^2.8.7", "@typescript-eslint/eslint-plugin": "^5.2.0", diff --git a/test/e2e/miniprogram-project/src/store/index.js b/test/e2e/miniprogram-project/src/store/index.js index b0fdb38997..53b2d7e3fa 100644 --- a/test/e2e/miniprogram-project/src/store/index.js +++ b/test/e2e/miniprogram-project/src/store/index.js @@ -1,4 +1,4 @@ -import {createStore} from '@mpxjs/core' +import { createStore } from '@mpxjs/store' const store = createStore({ state: { diff --git a/test/e2e/plugin-project/config/mpxPlugin.conf.js b/test/e2e/plugin-project/config/mpxPlugin.conf.js index 0b4e01515c..e2cc002e9e 100644 --- a/test/e2e/plugin-project/config/mpxPlugin.conf.js +++ b/test/e2e/plugin-project/config/mpxPlugin.conf.js @@ -14,9 +14,9 @@ module.exports = { writeMode: 'changed', // 是否需要对样式加scope,因为只有ali平台没有样式隔离,只对ali平台生效,提供include和exclude,和webpack的rules规则相同 - // autoScopeRules: { - // include: [resolve('src')] - // }, + autoScopeRules: { + include: [resolve('src')] + }, // 批量指定文件mode,用法如下,指定平台,提供include/exclude指定文件,即include的文件会默认被认为是该平台的,include/exclude的规则和webpack的rules的相同 modeRules: { diff --git a/test/e2e/plugin-project/package.json b/test/e2e/plugin-project/package.json index c3b018cc7e..18e10d4baa 100644 --- a/test/e2e/plugin-project/package.json +++ b/test/e2e/plugin-project/package.json @@ -7,6 +7,7 @@ "watch": "node ./build/build.js -w", "watch:prod": "node ./build/build.js -w -p", "build": "node ./build/build.js -p", + "build:ali": "npm run build --mode=ali", "build:dev": "node ./build/build.js", "copyPlugin": "sh ./copyWebpackPlugin.sh", "watch:cross": "npm run watch --modes=wx,ali,web", @@ -41,9 +42,9 @@ "@babel/preset-env": "^7.10.4", "@babel/runtime-corejs3": "^7.10.4", "@mpxjs/miniprogram-simulate": "^1.4.17", - "@mpxjs/mpx-jest": "0.0.27", - "@mpxjs/webpack-plugin": "^2.9.58", + "@mpxjs/mpx-jest": "^0.0.31", "@mpxjs/template-engine": "^2.8.7", + "@mpxjs/webpack-plugin": "^2.9.58", "@typescript-eslint/eslint-plugin": "^5.2.0", "@typescript-eslint/parser": "^5.2.0", "autoprefixer": "^6.3.1", diff --git a/test/e2e/plugin-project/src/plugin/components/list.mpx b/test/e2e/plugin-project/src/plugin/components/list.mpx index 0d6f7da8fb..1db5eea0ad 100644 --- a/test/e2e/plugin-project/src/plugin/components/list.mpx +++ b/test/e2e/plugin-project/src/plugin/components/list.mpx @@ -13,8 +13,10 @@