@taro-hmr-compile/plugin-webpack 0.0.1-alpha.8 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -0
- package/dist/compile/hmr-compiler-loader.js +44 -7
- package/dist/compile/hmr-compiler-loader.js.map +1 -1
- package/dist/compile/hmr-compiler-plugin.d.ts +7 -7
- package/dist/compile/hmr-compiler-plugin.js +2 -29
- package/dist/compile/hmr-compiler-plugin.js.map +1 -1
- package/dist/compile/hmr-output-plugin.d.ts +28 -0
- package/dist/compile/hmr-output-plugin.js +145 -0
- package/dist/compile/hmr-output-plugin.js.map +1 -0
- package/dist/compile/hmr-style-plugin.d.ts +25 -0
- package/dist/compile/hmr-style-plugin.js +53 -0
- package/dist/compile/hmr-style-plugin.js.map +1 -0
- package/dist/compile/lazy-compiler-loader.d.ts +2 -0
- package/dist/compile/lazy-compiler-loader.js +17 -5
- package/dist/compile/lazy-compiler-loader.js.map +1 -1
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +2 -1
- package/dist/constants.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +18 -6
- package/dist/index.js.map +1 -1
- package/dist/patch/hmr-patch-plugin.d.ts +6 -0
- package/dist/patch/hmr-patch-plugin.js +53 -4
- package/dist/patch/hmr-patch-plugin.js.map +1 -1
- package/dist/patch/hmr-patch-processor.d.ts +2 -1
- package/dist/patch/hmr-patch-processor.js +39 -22
- package/dist/patch/hmr-patch-processor.js.map +1 -1
- package/dist/patch/hmr-patch-runtime-plugin.js +1 -1
- package/dist/patch/types.d.ts +1 -0
- package/dist/patch/utils.d.ts +2 -0
- package/dist/patch/utils.js +11 -5
- package/dist/patch/utils.js.map +1 -1
- package/package.json +4 -2
- package/src/compile/hmr-compiler-loader.ts +43 -7
- package/src/compile/hmr-compiler-plugin.ts +9 -34
- package/src/compile/hmr-output-plugin.ts +158 -0
- package/src/compile/hmr-style-plugin.ts +70 -0
- package/src/compile/lazy-compiler-loader.ts +20 -5
- package/src/constants.ts +2 -0
- package/src/index.ts +23 -9
- package/src/patch/hmr-patch-plugin.ts +53 -5
- package/src/patch/hmr-patch-processor.ts +48 -28
- package/src/patch/hmr-patch-runtime-plugin.ts +1 -1
- package/src/patch/types.ts +1 -0
- package/src/patch/utils.ts +10 -5
- package/src/types/taro-service.d.ts +15 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {Compilation, Compiler} from 'webpack'
|
|
2
2
|
import {RawSource} from 'webpack-sources'
|
|
3
|
-
import
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import {HMR_PATCH_ASSET, HMR_PATCH_STYLE_ASSET} from '../constants'
|
|
4
5
|
import {HmrCompilerPlugin} from '../compile/hmr-compiler-plugin'
|
|
5
6
|
import {HmrPatchRuntimePlugin} from './hmr-patch-runtime-plugin'
|
|
6
7
|
import {HmrPatchProcessor, HmrPatchProcessorOptions} from "./hmr-patch-processor";
|
|
@@ -10,6 +11,7 @@ const HOT_UPDATE_MANIFEST_SUFFIX = '.hot-update.json'
|
|
|
10
11
|
const HOT_UPDATE_SCRIPT_SUFFIX = '.hot-update.js'
|
|
11
12
|
const STYLE_ASSET_PATTERN = /\.(?:css|wxss|acss|ttss|qss|jxss)(?:\.map)?$/i
|
|
12
13
|
const TEMPLATE_RUNTIME_ASSET_PATTERN = /(?:^|\/)(?:base\.wxml|utils\.wxs)$/i
|
|
14
|
+
const STYLE_ENTRY_ASSET = 'app.wxss'
|
|
13
15
|
|
|
14
16
|
type HmrPatchPluginOptions = Partial<HmrPatchProcessorOptions>
|
|
15
17
|
|
|
@@ -25,6 +27,8 @@ export class HmrPatchPlugin {
|
|
|
25
27
|
|
|
26
28
|
private readonly patchProcessor: HmrPatchProcessor
|
|
27
29
|
|
|
30
|
+
private readonly preservedAssets = new Set([HMR_PATCH_ASSET, STYLE_ENTRY_ASSET, HMR_PATCH_STYLE_ASSET])
|
|
31
|
+
|
|
28
32
|
private readonly emittedAssetSources = new Map<string, string>()
|
|
29
33
|
|
|
30
34
|
constructor(options: HmrPatchPluginOptions) {
|
|
@@ -35,13 +39,18 @@ export class HmrPatchPlugin {
|
|
|
35
39
|
new HmrPatchRuntimePlugin().apply(compiler)
|
|
36
40
|
|
|
37
41
|
compiler.hooks.compilation.tap(PLUGIN_NAME, compilation => {
|
|
38
|
-
HmrCompilerPlugin.getCompilationHooks(compilation).chunkModulesRendered.tap(PLUGIN_NAME,
|
|
39
|
-
this.patchProcessor.processChunkModules({compilation,
|
|
42
|
+
HmrCompilerPlugin.getCompilationHooks(compilation).chunkModulesRendered.tap(PLUGIN_NAME, options => {
|
|
43
|
+
this.patchProcessor.processChunkModules({compilation, ...options})
|
|
40
44
|
})
|
|
41
45
|
|
|
42
46
|
// 等 Webpack 生成完整的 hot-update assets 后,再统一转换成固定的 patch.js。
|
|
43
47
|
const stage = compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_REPORT
|
|
44
|
-
compilation.hooks.processAssets.tap({name: PLUGIN_NAME, stage}, () =>
|
|
48
|
+
compilation.hooks.processAssets.tap({name: PLUGIN_NAME, stage}, () => {
|
|
49
|
+
if (compilation.errors.length > 0 || !compilation.hash) return
|
|
50
|
+
this.processStyleAssets(compilation)
|
|
51
|
+
this.processAssets(compilation)
|
|
52
|
+
})
|
|
53
|
+
this.removeHotUpdateAssets(compilation)
|
|
45
54
|
})
|
|
46
55
|
|
|
47
56
|
compiler.hooks.afterEmit.tap(PLUGIN_NAME, compilation => {
|
|
@@ -82,10 +91,40 @@ export class HmrPatchPlugin {
|
|
|
82
91
|
this.previousHash = toHash
|
|
83
92
|
}
|
|
84
93
|
|
|
94
|
+
/** 将 Webpack 汇总后的 App 样式放入固定 patch 文件,App 入口只保留稳定引用。 */
|
|
95
|
+
private processStyleAssets(compilation: Compilation) {
|
|
96
|
+
const entryAsset = compilation.getAsset(STYLE_ENTRY_ASSET)
|
|
97
|
+
const source = entryAsset?.source.source().toString()
|
|
98
|
+
if (!source) return
|
|
99
|
+
|
|
100
|
+
const importPath = path.posix.relative(path.posix.dirname(STYLE_ENTRY_ASSET), HMR_PATCH_STYLE_ASSET)
|
|
101
|
+
const patchImport = importPath.startsWith('.') ? importPath : `./${importPath}`
|
|
102
|
+
const alreadyPatched = source.trim() === `@import "${patchImport}";` || source.trim() === `@import '${patchImport}';`
|
|
103
|
+
|
|
104
|
+
if (!alreadyPatched) {
|
|
105
|
+
const patchDirectory = path.posix.dirname(HMR_PATCH_STYLE_ASSET)
|
|
106
|
+
const sourceDirectory = path.posix.dirname(STYLE_ENTRY_ASSET)
|
|
107
|
+
const patchSource = source.replace(/(@import\s+)(["'])([^"']+)\2/g, (match, prefix: string, quote: string, request: string) => {
|
|
108
|
+
if (/^(?:[a-z]+:|\/|#)/i.test(request)) return match
|
|
109
|
+
const resolved = path.posix.normalize(path.posix.join(sourceDirectory, request))
|
|
110
|
+
const relative = path.posix.relative(patchDirectory, resolved)
|
|
111
|
+
return `${prefix}${quote}${relative.startsWith('.') ? relative : `./${relative}`}${quote}`
|
|
112
|
+
})
|
|
113
|
+
this.emitAsset(compilation, HMR_PATCH_STYLE_ASSET, new RawSource(patchSource))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
this.emitAsset(compilation, STYLE_ENTRY_ASSET, new RawSource(`@import ${JSON.stringify(patchImport)};\n`))
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private emitAsset(compilation: Compilation, assetName: string, source: RawSource) {
|
|
120
|
+
if (compilation.getAsset(assetName)) compilation.updateAsset(assetName, source)
|
|
121
|
+
else compilation.emitAsset(assetName, source)
|
|
122
|
+
}
|
|
123
|
+
|
|
85
124
|
/** 热更新转换完成后,保留固定 patch 与发生变化的模板、平台样式资产。 */
|
|
86
125
|
private removeSupersededAssets(compilation: Compilation) {
|
|
87
126
|
compilation.getAssets().forEach(asset => {
|
|
88
|
-
if (asset.name
|
|
127
|
+
if (this.preservedAssets.has(asset.name)) return
|
|
89
128
|
if (!isPassthroughAsset(asset.name)) return compilation.deleteAsset(asset.name)
|
|
90
129
|
|
|
91
130
|
const source = asset.source.source().toString()
|
|
@@ -108,4 +147,13 @@ export class HmrPatchPlugin {
|
|
|
108
147
|
this.emittedAssetSources.clear()
|
|
109
148
|
this.patchProcessor.reset()
|
|
110
149
|
}
|
|
150
|
+
/** 补丁已从 hot-update assets 生成后,在 Webpack 写盘前从本次 compilation 移除它们。 */
|
|
151
|
+
private removeHotUpdateAssets(compilation: Compilation) {
|
|
152
|
+
compilation.getAssets().forEach(asset => {
|
|
153
|
+
if (asset.name.endsWith(HOT_UPDATE_MANIFEST_SUFFIX) || asset.name.endsWith(HOT_UPDATE_SCRIPT_SUFFIX)) {
|
|
154
|
+
compilation.deleteAsset(asset.name)
|
|
155
|
+
}
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
|
|
111
159
|
}
|
|
@@ -1,23 +1,18 @@
|
|
|
1
|
-
import {type Compilation,} from "webpack";
|
|
2
|
-
import generator from '@babel/generator'
|
|
1
|
+
import {type Chunk, type Compilation,} from "webpack";
|
|
3
2
|
import {
|
|
4
3
|
parseHotUpdateAssets,
|
|
5
4
|
ParseHotUpdateAssetsOptions,
|
|
6
5
|
parseChunkAsset, mergeUpdatePatches, generatePatchAssetSource,
|
|
7
6
|
} from "./utils";
|
|
8
|
-
import
|
|
7
|
+
import isNil from 'lodash/isNil'
|
|
9
8
|
import {ModuleFactory, UpdatePatchSource} from "./types";
|
|
10
9
|
import {DEFAULT_HMR_PATCH_HISTORY_LIMIT} from "../constants";
|
|
11
10
|
|
|
12
|
-
interface ModuleHash {
|
|
13
|
-
moduleId: string;
|
|
14
|
-
factoryHash: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
11
|
interface ProcessChunkModulesOptions {
|
|
18
12
|
source: string
|
|
19
13
|
compilation: Compilation,
|
|
20
|
-
isBootstrapChunk: boolean
|
|
14
|
+
isBootstrapChunk: boolean,
|
|
15
|
+
chunk: Chunk
|
|
21
16
|
}
|
|
22
17
|
|
|
23
18
|
export interface HmrPatchProcessorOptions {
|
|
@@ -31,7 +26,14 @@ interface ProcessUpdateOptions extends ParseHotUpdateAssetsOptions {
|
|
|
31
26
|
|
|
32
27
|
type BootstrapFactories = Map<string, ModuleFactory>
|
|
33
28
|
|
|
34
|
-
type
|
|
29
|
+
type ModuleHashes = Map<string, Set<string>>
|
|
30
|
+
|
|
31
|
+
type ModuleHashesByFullHash = Map<string, ModuleHashes>
|
|
32
|
+
|
|
33
|
+
const areModuleHashesEqual = (left: Set<string> | undefined, right: Set<string> | undefined): boolean => {
|
|
34
|
+
if (!left || !right || left.size !== right.size) return false
|
|
35
|
+
return Array.from(left).every(hash => right.has(hash))
|
|
36
|
+
}
|
|
35
37
|
|
|
36
38
|
export class HmrPatchProcessor {
|
|
37
39
|
private readonly bootstrapFactories: BootstrapFactories = new Map()
|
|
@@ -62,9 +64,9 @@ export class HmrPatchProcessor {
|
|
|
62
64
|
|
|
63
65
|
const changedModuleIds = new Set<string>()
|
|
64
66
|
moduleIds.forEach(moduleId => {
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
if (
|
|
67
|
+
const fromModuleHash = fromModuleHashes.get(moduleId)
|
|
68
|
+
const toModuleHash = toModuleHashes.get(moduleId)
|
|
69
|
+
if (!areModuleHashesEqual(fromModuleHash, toModuleHash)) changedModuleIds.add(moduleId)
|
|
68
70
|
})
|
|
69
71
|
|
|
70
72
|
return changedModuleIds
|
|
@@ -87,7 +89,9 @@ export class HmrPatchProcessor {
|
|
|
87
89
|
return Array.from(tempPatchInfo.removedModules.values()).filter(item => changedModuleIds.has(item))
|
|
88
90
|
})()
|
|
89
91
|
|
|
90
|
-
|
|
92
|
+
const {runtimeModules} = tempPatchInfo
|
|
93
|
+
|
|
94
|
+
this.recentUpdatePatches.push({type: 'update', moduleFactories, runtimeModules, fromHash, toHash, removedModules})
|
|
91
95
|
|
|
92
96
|
const patchHistoryLimit = Math.max(1, this.opts.patchHistoryLimit ?? DEFAULT_HMR_PATCH_HISTORY_LIMIT)
|
|
93
97
|
|
|
@@ -103,28 +107,44 @@ export class HmrPatchProcessor {
|
|
|
103
107
|
}
|
|
104
108
|
|
|
105
109
|
processChunkModules(opt: ProcessChunkModulesOptions) {
|
|
106
|
-
const {
|
|
107
|
-
const
|
|
110
|
+
const {chunk, compilation, isBootstrapChunk, source} = opt
|
|
111
|
+
const {hash: fullHash} = compilation
|
|
112
|
+
const modules = parseChunkAsset(source)
|
|
108
113
|
modules.forEach(moduleFactory => {
|
|
109
114
|
const {moduleId, factory} = moduleFactory
|
|
110
115
|
|
|
111
|
-
if (
|
|
112
|
-
const compilationFactories = this.bootstrapFactoriesByCompilation.get(
|
|
116
|
+
if (isBootstrapChunk) {
|
|
117
|
+
const compilationFactories = this.bootstrapFactoriesByCompilation.get(compilation) ?? new Map()
|
|
113
118
|
compilationFactories.set(moduleId, {moduleId, factory})
|
|
114
|
-
this.bootstrapFactoriesByCompilation.set(
|
|
119
|
+
this.bootstrapFactoriesByCompilation.set(compilation, compilationFactories)
|
|
115
120
|
}
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
if (!fullHash) return
|
|
124
|
+
|
|
125
|
+
const moduleHashes = (() => {
|
|
126
|
+
const hasCurFullHashModuleHashMap = this.moduleHashesByFullHash.has(fullHash)
|
|
127
|
+
if (!hasCurFullHashModuleHashMap) this.moduleHashesByFullHash.set(fullHash, new Map())
|
|
128
|
+
return this.moduleHashesByFullHash.get(fullHash)!
|
|
129
|
+
})()
|
|
130
|
+
|
|
131
|
+
const { chunkGraph, codeGenerationResults } = compilation
|
|
132
|
+
|
|
133
|
+
Array.from(chunkGraph.getChunkModulesIterable(chunk)).forEach(module => {
|
|
134
|
+
const moduleId = chunkGraph.getModuleId(module)
|
|
135
|
+
|
|
136
|
+
if (isNil(moduleId)) return
|
|
116
137
|
|
|
117
|
-
|
|
138
|
+
const normalizedModuleId = String(moduleId)
|
|
118
139
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
})()
|
|
140
|
+
// 获取模块在当前 chunk.runtime 下的 Hash,优先从 codeGenerationResults 获取,如果没有对应的代码生成结果,则回退到 chunkGraph 获取
|
|
141
|
+
const moduleHash = codeGenerationResults?.has(module, chunk.runtime)
|
|
142
|
+
? codeGenerationResults.getHash(module, chunk.runtime)
|
|
143
|
+
: chunkGraph.getModuleHash(module, chunk.runtime)
|
|
124
144
|
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
moduleHashes.set(
|
|
145
|
+
const hashes = moduleHashes.get(normalizedModuleId) ?? new Set<string>()
|
|
146
|
+
hashes.add(moduleHash)
|
|
147
|
+
moduleHashes.set(normalizedModuleId, hashes)
|
|
128
148
|
})
|
|
129
149
|
}
|
|
130
150
|
|
|
@@ -64,7 +64,7 @@ ${webpack.RuntimeGlobals.require}.miniHmr.preparePatch = function(patch, applyHa
|
|
|
64
64
|
currentUpdateChunks = {};
|
|
65
65
|
currentUpdate = Object.assign({}, patch.modules);
|
|
66
66
|
currentUpdateRemovedChunks = [];
|
|
67
|
-
currentUpdateRuntime = [];
|
|
67
|
+
currentUpdateRuntime = patch.runtimeModules ? patch.runtimeModules.slice() : [];
|
|
68
68
|
|
|
69
69
|
if (patch.removedModules) {
|
|
70
70
|
patch.removedModules.forEach(function(moduleId) {
|
package/src/patch/types.ts
CHANGED
package/src/patch/utils.ts
CHANGED
|
@@ -17,7 +17,7 @@ const parseModuleFactories = (expression: ObjectExpression) => {
|
|
|
17
17
|
|
|
18
18
|
const moduleFactories = new Map<string, ModuleFactory>()
|
|
19
19
|
|
|
20
|
-
if (!t.isObjectExpression(expression)) return moduleFactories.values()
|
|
20
|
+
if (!t.isObjectExpression(expression)) return Array.from(moduleFactories.values())
|
|
21
21
|
|
|
22
22
|
expression.properties.forEach(property => {
|
|
23
23
|
if (!t.isObjectProperty(property) || !t.isExpression(property.value)) throw new Error('[taro-hmr-compile] HMR 模块对象包含无法识别的属性')
|
|
@@ -33,7 +33,7 @@ const parseModuleFactories = (expression: ObjectExpression) => {
|
|
|
33
33
|
moduleFactories.set(moduleId, {factory, moduleId})
|
|
34
34
|
})
|
|
35
35
|
|
|
36
|
-
return moduleFactories.values()
|
|
36
|
+
return Array.from(moduleFactories.values())
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
|
|
@@ -50,6 +50,7 @@ export const parseHotUpdateAssets = (opt: ParseHotUpdateAssetsOptions) => {
|
|
|
50
50
|
const {manifests, scripts} = opt
|
|
51
51
|
|
|
52
52
|
const moduleFactories = new Map<string, ModuleFactory>()
|
|
53
|
+
const runtimeModules: t.Expression[] = []
|
|
53
54
|
|
|
54
55
|
scripts.forEach(asset => {
|
|
55
56
|
|
|
@@ -63,11 +64,12 @@ export const parseHotUpdateAssets = (opt: ParseHotUpdateAssetsOptions) => {
|
|
|
63
64
|
|
|
64
65
|
if (!t.isStringLiteral(callee.property) || !callee.property.value.startsWith('webpackHotUpdate')) return
|
|
65
66
|
|
|
66
|
-
const [
|
|
67
|
+
const [, modules, runtime] = nodePath.node.arguments
|
|
67
68
|
|
|
68
69
|
if (!t.isObjectExpression(modules)) return
|
|
69
70
|
|
|
70
71
|
parseModuleFactories(modules).forEach(module => moduleFactories.set(module.moduleId, module))
|
|
72
|
+
if (t.isExpression(runtime)) runtimeModules.push(runtime)
|
|
71
73
|
},
|
|
72
74
|
})
|
|
73
75
|
})
|
|
@@ -83,7 +85,7 @@ export const parseHotUpdateAssets = (opt: ParseHotUpdateAssetsOptions) => {
|
|
|
83
85
|
tempRemoveModules.forEach(removedModule => removedModules.add(String(removedModule)))
|
|
84
86
|
}, [])
|
|
85
87
|
|
|
86
|
-
return {moduleFactories, removedModules}
|
|
88
|
+
return {moduleFactories, runtimeModules, removedModules}
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
/**
|
|
@@ -126,7 +128,8 @@ export const mergeUpdatePatches = (basePatch: UpdatePatchSource, nextPatch: Upda
|
|
|
126
128
|
type: 'update',
|
|
127
129
|
fromHash: basePatch.fromHash,
|
|
128
130
|
toHash: nextPatch.toHash,
|
|
129
|
-
moduleFactories:
|
|
131
|
+
moduleFactories: Array.from(moduleFactories.values()),
|
|
132
|
+
runtimeModules: [...basePatch.runtimeModules, ...nextPatch.runtimeModules],
|
|
130
133
|
removedModules: [...removedModules],
|
|
131
134
|
}
|
|
132
135
|
}
|
|
@@ -147,6 +150,7 @@ export const generatePatchAssetSource = (opt: generatePatchAssetSourceOpts): str
|
|
|
147
150
|
fromHash: "FROM_HASH",
|
|
148
151
|
toHash: "TO_HASH",
|
|
149
152
|
modules: MODULES,
|
|
153
|
+
runtimeModules: RUNTIME_MODULES,
|
|
150
154
|
removedModules: REMOVED_MODULES
|
|
151
155
|
})`)
|
|
152
156
|
const buildPatchAsset = template.program('module.exports = PATCHES;')
|
|
@@ -165,6 +169,7 @@ export const generatePatchAssetSource = (opt: generatePatchAssetSourceOpts): str
|
|
|
165
169
|
FROM_HASH: patch.fromHash,
|
|
166
170
|
TO_HASH: patch.toHash,
|
|
167
171
|
MODULES: t.objectExpression(updateModules),
|
|
172
|
+
RUNTIME_MODULES: t.arrayExpression(patch.runtimeModules),
|
|
168
173
|
REMOVED_MODULES: t.arrayExpression(patch.removedModules.map(moduleId => t.stringLiteral(moduleId))),
|
|
169
174
|
})
|
|
170
175
|
}),
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { IProjectConfig } from '@tarojs/taro/types/compile'
|
|
2
|
+
import type { IComponentConfig } from '@tarojs/taro/types/compile/hooks'
|
|
3
|
+
|
|
4
|
+
declare module '@tarojs/service' {
|
|
5
|
+
interface IPluginContext {
|
|
6
|
+
modifyComponentConfig: (
|
|
7
|
+
callback: (args: {
|
|
8
|
+
componentConfig: IComponentConfig
|
|
9
|
+
config: IProjectConfig
|
|
10
|
+
}) => void,
|
|
11
|
+
) => void
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export {}
|