From 096701f38ce3386a10f1c1d5345bae257404f440 Mon Sep 17 00:00:00 2001 From: xuanzebin <38971117+xuanzebin@users.noreply.github.com> Date: Mon, 24 Jul 2023 16:02:55 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20webpack=205=20?= =?UTF-8?q?=E5=BC=80=E5=90=AF=20cache=20=E5=90=8E=E8=B7=B3=E8=BF=87=20js?= =?UTF-8?q?=20parser=20=E9=98=B6=E6=AE=B5=E5=AF=BC=E8=87=B4=E7=9A=84?= =?UTF-8?q?=E4=B8=80=E7=B3=BB=E5=88=97=E9=97=AE=E9=A2=98=20(#14117)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 修复 webpack 开启 cache 后跳过 js parser 阶段导致的一系列问题 * fix: 修复 webpack 开启 cache 后跳过 js parser 阶段导致的一系列问题 --------- Co-authored-by: xuanzebin Co-authored-by: chenjiajian <798095202@qq.com> --- .../src/plugins/TaroNormalModule.ts | 95 ++++++++++++++++++- .../src/plugins/TaroNormalModulesPlugin.ts | 57 ++++++++--- .../src/utils/component.ts | 4 + 3 files changed, 144 insertions(+), 12 deletions(-) diff --git a/packages/taro-webpack5-runner/src/plugins/TaroNormalModule.ts b/packages/taro-webpack5-runner/src/plugins/TaroNormalModule.ts index 00be087c0fe4..f0d194e16e51 100644 --- a/packages/taro-webpack5-runner/src/plugins/TaroNormalModule.ts +++ b/packages/taro-webpack5-runner/src/plugins/TaroNormalModule.ts @@ -1,7 +1,72 @@ import { META_TYPE } from '@tarojs/helper' +import { isEmpty } from 'lodash' import webpack from 'webpack' -export default class TaroNormalModule extends webpack.NormalModule { +import { componentConfig, componentNameSet, elementNameSet } from '../utils/component' + +export class TaroBaseNormalModule extends webpack.NormalModule { + elementNameSet: Set + + componentNameSet: Set + + collectProps: { [name: string]: string } + + constructor (data) { + super(data) + + this.collectProps = {} + this.elementNameSet = new Set() + this.componentNameSet = new Set() + } + + clear () { + this.collectProps = {} + this.elementNameSet.clear() + this.componentNameSet.clear() + } + + serialize (context) { + const { write } = context + + write(this.collectProps) + write(this.elementNameSet) + write(this.componentNameSet) + + super.serialize(context) + } + + deserialize (context) { + const { read } = context + + this.collectProps = read() + this.elementNameSet = read() + this.componentNameSet = read() + + if (!isEmpty(this.collectProps)) { + for (const key in this.collectProps) { + const attrs = componentConfig.thirdPartyComponents.get(key) + const value = this.collectProps[key] + + if (!attrs) continue + + value.split('|').forEach(item => { + attrs.add(item) + }) + } + } + + for (const elementName of this.elementNameSet) { + elementNameSet.add(elementName) + } + for (const componentName of this.componentNameSet) { + componentNameSet.add(componentName) + } + + return super.deserialize(context) + } +} + +export default class TaroNormalModule extends TaroBaseNormalModule { name: string miniType: META_TYPE constructor (data) { @@ -52,3 +117,31 @@ webpack.util.serialization.register(TaroNormalModule, '@tarojs/webpack5-runner/d return obj } }) + +webpack.util.serialization.register(TaroBaseNormalModule, '@tarojs/webpack5-runner/dist/plugins/TaroNormalModule', 'TaroBaseNormalModule', { + serialize (obj, context) { + obj.serialize(context) + }, + deserialize (context) { + const obj = new TaroBaseNormalModule({ + // will be deserialized by Module + layer: null, + type: '', + // will be filled by updateCacheModule + resource: '', + context: '', + request: null, + userRequest: null, + rawRequest: null, + loaders: null, + matchResource: null, + parser: null, + parserOptions: null, + generator: null, + generatorOptions: null, + resolveOptions: null + }) + obj.deserialize(context) + return obj + } +}) diff --git a/packages/taro-webpack5-runner/src/plugins/TaroNormalModulesPlugin.ts b/packages/taro-webpack5-runner/src/plugins/TaroNormalModulesPlugin.ts index a7cd66ca643b..93749df9be70 100644 --- a/packages/taro-webpack5-runner/src/plugins/TaroNormalModulesPlugin.ts +++ b/packages/taro-webpack5-runner/src/plugins/TaroNormalModulesPlugin.ts @@ -1,6 +1,6 @@ import TaroSingleEntryDependency from '../dependencies/TaroSingleEntryDependency' -import { componentConfig } from '../utils/component' -import TaroNormalModule from './TaroNormalModule' +import { componentConfig, componentNameSet, elementNameSet } from '../utils/component' +import TaroNormalModule, { TaroBaseNormalModule } from './TaroNormalModule' import type { Func } from '@tarojs/taro/types/compile' import type { Compiler } from 'webpack' @@ -23,6 +23,8 @@ function isRenderNode (node, ancestors): boolean { } export default class TaroNormalModulesPlugin { + isCache = true + onParseCreateElement: Func | undefined constructor (onParseCreateElement: Func | undefined) { @@ -30,7 +32,24 @@ export default class TaroNormalModulesPlugin { } apply (compiler: Compiler) { - compiler.hooks.compilation.tap(PLUGIN_NAME, (_, { normalModuleFactory }) => { + compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation, { normalModuleFactory }) => { + // cache 开启后,会跳过 JavaScript parser 环节,因此需要收集组件信息,在 finishModules 阶段处理 + compilation.hooks.finishModules.tap(PLUGIN_NAME, (_) => { + if (!this.isCache) return + + for (const name of elementNameSet) { + this.onParseCreateElement?.(name, componentConfig) + } + + for (const name of componentNameSet) { + if (name === 'CustomWrapper' && !componentConfig.thirdPartyComponents.get('custom-wrapper')) { + componentConfig.thirdPartyComponents.set('custom-wrapper', new Set()) + + return + } + } + }) + normalModuleFactory.hooks.createModule.tapPromise(PLUGIN_NAME, (data, { dependencies }) => { const dependency = dependencies[0] if (dependency instanceof TaroSingleEntryDependency) { @@ -38,15 +57,21 @@ export default class TaroNormalModulesPlugin { { miniType: dependency.miniType, name: dependency.name } ))) } - return Promise.resolve() + return Promise.resolve(new TaroBaseNormalModule(data)) }) // react 的第三方组件支持 normalModuleFactory.hooks.parser.for('javascript/auto').tap(PLUGIN_NAME, (parser) => { parser.hooks.program.tap(PLUGIN_NAME, (ast) => { + this.isCache = false + + const currentModule = parser.state.current as TaroBaseNormalModule + currentModule.clear() + walk.ancestor(ast, { CallExpression: (node, ancestors) => { const callee = node.callee + if (callee.type === 'MemberExpression') { if (callee.property.name !== 'createElement') { return @@ -62,9 +87,8 @@ export default class TaroNormalModulesPlugin { !(nameOfCallee && nameOfCallee.includes('createElementVNode')) && !(nameOfCallee && nameOfCallee.includes('createElementBlock')) && !(nameOfCallee && nameOfCallee.includes('resolveComponent')) && // 收集使用解析函数的组件名称 + // 兼容 Vue 2.0 渲染函数及 JSX !isRenderNode(node, ancestors) - // TODO: 兼容 vue 2.0 渲染函数及 JSX,函数名 h 与 _c 在压缩后太常见,需要做更多限制后才能兼容 - // nameOfCallee !== 'h' && nameOfCallee !== '_c' ) { return } @@ -73,11 +97,18 @@ export default class TaroNormalModulesPlugin { const [type, prop] = node.arguments const componentName = type.name - type.value && this.onParseCreateElement?.(type.value, componentConfig) + if (type.value) { + this.onParseCreateElement?.(type.value, componentConfig) + currentModule.elementNameSet.add(type.value) + } - if (componentName === 'CustomWrapper' && !componentConfig.thirdPartyComponents.get('custom-wrapper')) { - componentConfig.thirdPartyComponents.set('custom-wrapper', new Set()) + if (componentName) { + currentModule.componentNameSet.add(componentName) + if (componentName === 'CustomWrapper' && !componentConfig.thirdPartyComponents.get('custom-wrapper')) { + componentConfig.thirdPartyComponents.set('custom-wrapper', new Set()) + } } + if (componentConfig.thirdPartyComponents.size === 0) { return } @@ -87,9 +118,13 @@ export default class TaroNormalModulesPlugin { return } - prop.properties + const props = prop.properties .filter(p => p.type === 'Property' && p.key.type === 'Identifier' && p.key.name !== 'children' && p.key.name !== 'id') - .forEach(p => attrs.add(p.key.name)) + const res = props.map(p => p.key.name).join('|') + + props.forEach(p => attrs.add(p.key.name)) + + currentModule.collectProps[type.value] = res }, }) }) diff --git a/packages/taro-webpack5-runner/src/utils/component.ts b/packages/taro-webpack5-runner/src/utils/component.ts index ccb7bc1723a5..2d93b6356388 100644 --- a/packages/taro-webpack5-runner/src/utils/component.ts +++ b/packages/taro-webpack5-runner/src/utils/component.ts @@ -6,3 +6,7 @@ export const componentConfig: IComponentConfig = { thirdPartyComponents: new Map(), includeAll: false } + +// 用户 cache 功能开启时,记录 parser 过程中的组件信息 +export const elementNameSet = new Set() +export const componentNameSet = new Set()