Skip to content

Commit

Permalink
fix: 修复 webpack 5 开启 cache 后跳过 js parser 阶段导致的一系列问题 (#14117)
Browse files Browse the repository at this point in the history
* fix: 修复 webpack 开启 cache 后跳过 js parser 阶段导致的一系列问题

* fix: 修复 webpack 开启 cache 后跳过 js parser 阶段导致的一系列问题

---------

Co-authored-by: xuanzebin <[email protected]>
Co-authored-by: chenjiajian <[email protected]>
  • Loading branch information
3 people authored Jul 24, 2023
1 parent 6b50ec3 commit 096701f
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 12 deletions.
95 changes: 94 additions & 1 deletion packages/taro-webpack5-runner/src/plugins/TaroNormalModule.ts
Original file line number Diff line number Diff line change
@@ -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<string>

componentNameSet: Set<string>

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) {
Expand Down Expand Up @@ -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
}
})
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -23,30 +23,55 @@ function isRenderNode (node, ancestors): boolean {
}

export default class TaroNormalModulesPlugin {
isCache = true

onParseCreateElement: Func | undefined

constructor (onParseCreateElement: Func | undefined) {
this.onParseCreateElement = onParseCreateElement
}

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) {
return Promise.resolve(new TaroNormalModule(Object.assign(data,
{ 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
Expand All @@ -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
}
Expand All @@ -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
}
Expand All @@ -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
},
})
})
Expand Down
4 changes: 4 additions & 0 deletions packages/taro-webpack5-runner/src/utils/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()

0 comments on commit 096701f

Please sign in to comment.