@taro-hmr-compile/plugin-webpack 0.0.1-alpha.5 → 0.0.1-alpha.7
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/dist/compile/hmr-compiler-loader.d.ts +9 -0
- package/dist/compile/hmr-compiler-loader.js +162 -0
- package/dist/compile/hmr-compiler-loader.js.map +1 -0
- package/dist/compile/hmr-compiler-plugin.d.ts +2 -1
- package/dist/compile/hmr-compiler-plugin.js +19 -1
- package/dist/compile/hmr-compiler-plugin.js.map +1 -1
- package/dist/compile/lazy-compiler-loader.d.ts +17 -0
- package/dist/compile/lazy-compiler-loader.js +34 -0
- package/dist/compile/lazy-compiler-loader.js.map +1 -0
- 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 +1 -0
- package/dist/index.js +32 -14
- package/dist/index.js.map +1 -1
- package/dist/log.d.ts +3 -0
- package/dist/log.js +11 -0
- package/dist/log.js.map +1 -0
- package/dist/patch/hmr-patch-plugin.js +1 -1
- package/dist/patch/hmr-patch-plugin.js.map +1 -1
- package/dist/patch/hmr-patch-runtime-plugin.js +29 -58
- package/dist/patch/hmr-patch-runtime-plugin.js.map +1 -1
- package/dist/server/hmr-server-plugin.js +19 -6
- package/dist/server/hmr-server-plugin.js.map +1 -1
- package/package.json +8 -6
- package/src/compile/hmr-compiler-loader.ts +141 -0
- package/src/compile/hmr-compiler-plugin.ts +35 -1
- package/src/compile/lazy-compiler-loader.ts +51 -0
- package/src/constants.ts +2 -0
- package/src/index.ts +24 -12
- package/src/log.ts +5 -0
- package/src/patch/hmr-patch-plugin.ts +2 -2
- package/src/patch/hmr-patch-runtime-plugin.ts +44 -75
- package/src/server/hmr-server-plugin.ts +9 -3
- package/dist/compile/hmr-compile-loader.d.ts +0 -11
- package/dist/compile/hmr-compile-loader.js +0 -52
- package/dist/compile/hmr-compile-loader.js.map +0 -1
- package/dist/compile/inject-app-hmr-runtime.d.ts +0 -1
- package/dist/compile/inject-app-hmr-runtime.js +0 -79
- package/dist/compile/inject-app-hmr-runtime.js.map +0 -1
- package/src/compile/hmr-compile-loader.ts +0 -72
- package/src/compile/inject-app-hmr-runtime.ts +0 -44
|
@@ -10,80 +10,47 @@ const webpack_1 = __importDefault(require("webpack"));
|
|
|
10
10
|
const PLUGIN_NAME = 'HmrPatchRuntimePlugin';
|
|
11
11
|
const hotRuntimeCode = `
|
|
12
12
|
var applyQueue = Promise.resolve();
|
|
13
|
-
var applySequence = 0;
|
|
14
13
|
|
|
15
14
|
${webpack_1.default.RuntimeGlobals.require}.miniHmr = {
|
|
16
|
-
|
|
17
|
-
if (!patch) return
|
|
15
|
+
shouldApplyBootstrapPatch: function(patch) {
|
|
16
|
+
if (!patch) return false;
|
|
17
|
+
|
|
18
|
+
return Object.keys(patch.modules).some(function(moduleId) {
|
|
19
|
+
return !Object.prototype.hasOwnProperty.call(${webpack_1.default.RuntimeGlobals.moduleFactories}, moduleId);
|
|
20
|
+
});
|
|
21
|
+
},
|
|
22
|
+
applyBootstrapPatch: function(patch) {
|
|
23
|
+
if (!patch) return [];
|
|
18
24
|
|
|
19
|
-
var applyId = ++applySequence;
|
|
20
|
-
var enqueueHash = ${webpack_1.default.RuntimeGlobals.getFullHash}();
|
|
21
25
|
// 微信要求 Page() 在页面脚本求值期间同步注册。bootstrap 只补齐缺失工厂,
|
|
22
26
|
// 不会覆盖现有模块;必须在 Page Chunk startup 继续执行前同步完成。
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
Object.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
console.log('[taro-hmr-compile] bootstrap patch installed', {
|
|
32
|
-
applyId: applyId,
|
|
33
|
-
installedModules: installedModules
|
|
34
|
-
});
|
|
35
|
-
return Promise.resolve(installedModules);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
console.log('[taro-hmr-compile] patch enqueued', {
|
|
39
|
-
applyId: applyId,
|
|
40
|
-
type: patch.type,
|
|
41
|
-
runtimeHash: enqueueHash,
|
|
42
|
-
fromHash: patch.fromHash,
|
|
43
|
-
toHash: patch.toHash
|
|
27
|
+
var installedModules = [];
|
|
28
|
+
Object.keys(patch.modules).forEach(function(moduleId) {
|
|
29
|
+
if (!Object.prototype.hasOwnProperty.call(${webpack_1.default.RuntimeGlobals.moduleFactories}, moduleId)) {
|
|
30
|
+
${webpack_1.default.RuntimeGlobals.moduleFactories}[moduleId] = patch.modules[moduleId];
|
|
31
|
+
installedModules.push(moduleId);
|
|
32
|
+
}
|
|
44
33
|
});
|
|
34
|
+
return installedModules;
|
|
35
|
+
},
|
|
36
|
+
applyUpdatePatch: function(patch) {
|
|
37
|
+
if (!patch) return Promise.resolve();
|
|
38
|
+
|
|
39
|
+
globalThis.log('apply update patch %O', patch);
|
|
45
40
|
|
|
46
41
|
var task = applyQueue.then(function() {
|
|
47
42
|
var runtimeHash = ${webpack_1.default.RuntimeGlobals.getFullHash}();
|
|
48
|
-
console.log('[taro-hmr-compile] patch dequeued', {
|
|
49
|
-
applyId: applyId,
|
|
50
|
-
runtimeHash: runtimeHash,
|
|
51
|
-
fromHash: patch.fromHash,
|
|
52
|
-
toHash: patch.toHash,
|
|
53
|
-
isStaleBaseline: runtimeHash !== patch.fromHash
|
|
54
|
-
});
|
|
55
43
|
|
|
56
|
-
if (runtimeHash === patch.toHash)
|
|
57
|
-
console.log('[taro-hmr-compile] patch skipped at target hash', {
|
|
58
|
-
applyId: applyId,
|
|
59
|
-
runtimeHash: runtimeHash
|
|
60
|
-
});
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
44
|
+
if (runtimeHash === patch.toHash) return;
|
|
63
45
|
|
|
64
46
|
currentUpdateApplyHandlers = [];
|
|
65
47
|
|
|
66
|
-
${webpack_1.default.RuntimeGlobals.require}.miniHmr.preparePatch(
|
|
67
|
-
patch,
|
|
68
|
-
currentUpdateApplyHandlers
|
|
69
|
-
);
|
|
48
|
+
${webpack_1.default.RuntimeGlobals.require}.miniHmr.preparePatch(patch, currentUpdateApplyHandlers);
|
|
70
49
|
|
|
71
50
|
return setStatus('ready')
|
|
72
|
-
.then(function() {
|
|
73
|
-
return hotApply();
|
|
74
|
-
})
|
|
51
|
+
.then(function() { return hotApply(); })
|
|
75
52
|
.then(function(updatedModules) {
|
|
76
|
-
${webpack_1.default.RuntimeGlobals.getFullHash} = function() {
|
|
77
|
-
return patch.toHash;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
console.log('[taro-hmr-compile] patch applied', {
|
|
81
|
-
applyId: applyId,
|
|
82
|
-
fromHash: patch.fromHash,
|
|
83
|
-
toHash: patch.toHash,
|
|
84
|
-
updatedModules: updatedModules
|
|
85
|
-
});
|
|
86
|
-
|
|
53
|
+
${webpack_1.default.RuntimeGlobals.getFullHash} = function() { return patch.toHash; };
|
|
87
54
|
return updatedModules;
|
|
88
55
|
});
|
|
89
56
|
});
|
|
@@ -120,6 +87,10 @@ const injectRuntime = (source, injectedCode) => {
|
|
|
120
87
|
class HmrPatchRuntimePlugin {
|
|
121
88
|
apply(compiler) {
|
|
122
89
|
compiler.hooks.thisCompilation.tap(PLUGIN_NAME, compilation => {
|
|
90
|
+
compilation.hooks.additionalTreeRuntimeRequirements.tap(PLUGIN_NAME, (_chunk, runtimeRequirements) => {
|
|
91
|
+
runtimeRequirements.add(webpack_1.default.RuntimeGlobals.require);
|
|
92
|
+
runtimeRequirements.add(webpack_1.default.RuntimeGlobals.getFullHash);
|
|
93
|
+
});
|
|
123
94
|
compilation.hooks.runtimeModule.tap(PLUGIN_NAME, runtimeModule => {
|
|
124
95
|
const injectedCode = {
|
|
125
96
|
HotModuleReplacementRuntimeModule: hotRuntimeCode,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hmr-patch-runtime-plugin.js","sourceRoot":"","sources":["../../src/patch/hmr-patch-runtime-plugin.ts"],"names":[],"mappings":";;;;;;AAAA,0CAAmC;AACnC,iEAAuC;AACvC,sDAA6B;AAE7B,MAAM,WAAW,GAAG,uBAAuB,CAAA;AAE3C,MAAM,cAAc,GAAG
|
|
1
|
+
{"version":3,"file":"hmr-patch-runtime-plugin.js","sourceRoot":"","sources":["../../src/patch/hmr-patch-runtime-plugin.ts"],"names":[],"mappings":";;;;;;AAAA,0CAAmC;AACnC,iEAAuC;AACvC,sDAA6B;AAE7B,MAAM,WAAW,GAAG,uBAAuB,CAAA;AAE3C,MAAM,cAAc,GAAG;;;EAGrB,iBAAO,CAAC,cAAc,CAAC,OAAO;;;;;qDAKqB,iBAAO,CAAC,cAAc,CAAC,eAAe;;;;;;;;;;kDAUzC,iBAAO,CAAC,cAAc,CAAC,eAAe;UAC9E,iBAAO,CAAC,cAAc,CAAC,eAAe;;;;;;;;;;;;0BAYtB,iBAAO,CAAC,cAAc,CAAC,WAAW;;;;;;QAMpD,iBAAO,CAAC,cAAc,CAAC,OAAO;;;;;YAK1B,iBAAO,CAAC,cAAc,CAAC,WAAW;;;;;;;;;;;CAW7C,CAAC;AAEF,MAAM,uBAAuB,GAAG;EAC9B,iBAAO,CAAC,cAAc,CAAC,OAAO;;;;;;;;;;;;;;CAc/B,CAAA;AAED,MAAM,aAAa,GAAG,CAAC,MAAc,EAAE,YAAoB,EAAE,EAAE;IAC3D,MAAM,OAAO,GAAG,IAAA,cAAK,EAAC,MAAM,EAAE,EAAC,UAAU,EAAE,QAAQ,EAAC,CAAC,CAAA;IACrD,MAAM,QAAQ,GAAG,IAAA,cAAK,EAAC,YAAY,EAAE,EAAC,UAAU,EAAE,QAAQ,EAAC,CAAC,CAAA;IAE5D,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IAEnD,OAAO,IAAA,mBAAQ,EAAC,OAAO,CAAC,CAAC,IAAI,CAAA;AACjC,CAAC,CAAA;AAED,MAAa,qBAAqB;IAC9B,KAAK,CAAC,QAA0B;QAC5B,QAAQ,CAAC,KAAK,CAAC,eAAe,CAAC,GAAG,CAC9B,WAAW,EACX,WAAW,CAAC,EAAE;YACV,WAAW,CAAC,KAAK,CAAC,iCAAiC,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,mBAAmB,EAAE,EAAE;gBACjG,mBAAmB,CAAC,GAAG,CAAC,iBAAO,CAAC,cAAc,CAAC,OAAO,CAAC,CAAA;gBACvD,mBAAmB,CAAC,GAAG,CAAC,iBAAO,CAAC,cAAc,CAAC,WAAW,CAAC,CAAA;YAC/D,CAAC,CAAC,CAAA;YAEF,WAAW,CAAC,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,WAAW,EAAE,aAAa,CAAC,EAAE;gBAC7D,MAAM,YAAY,GAAG;oBACjB,iCAAiC,EAAE,cAAc;oBACjD,8BAA8B,EAAE,uBAAuB;iBAC1D,CAAC,aAAa,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;gBAEjC,IAAI,CAAC,YAAY;oBAAE,OAAM;gBAEzB,MAAM,QAAQ,GAAG,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;gBAE3D,aAAa,CAAC,QAAQ,GAAG,GAAG,EAAE;oBAC1B,MAAM,MAAM,GAAG,QAAQ,EAAE,CAAA;oBAEzB,IAAI,CAAC,MAAM;wBAAE,OAAO,MAAM,CAAA;oBAE1B,OAAO,aAAa,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;gBAC9C,CAAC,CAAA;YACL,CAAC,CAAC,CAAA;QACN,CAAC,CACJ,CAAA;IACL,CAAC;CACJ;AA/BD,sDA+BC"}
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
2
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
12
|
exports.HmrServerPlugin = void 0;
|
|
4
13
|
const compiler_1 = require("@taro-hmr-compile/core/compiler");
|
|
5
14
|
const hmr_compiler_plugin_1 = require("../compile/hmr-compiler-plugin");
|
|
15
|
+
const log_1 = require("../log");
|
|
16
|
+
const serverLog = log_1.log.extend('HmrServerPlugin');
|
|
6
17
|
const PLUGIN_NAME = 'HmrServerPlugin';
|
|
7
18
|
class HmrServerPlugin extends compiler_1.HmrCompileServer {
|
|
8
19
|
constructor(options) {
|
|
@@ -11,15 +22,17 @@ class HmrServerPlugin extends compiler_1.HmrCompileServer {
|
|
|
11
22
|
}
|
|
12
23
|
apply(compiler) {
|
|
13
24
|
this.compiler = compiler;
|
|
14
|
-
compiler.hooks.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
compiler.hooks.beforeCompile.tapPromise(PLUGIN_NAME, () => __awaiter(this, void 0, void 0, function* () {
|
|
26
|
+
if (this.isServerRunning)
|
|
27
|
+
return;
|
|
28
|
+
const { port = 8080, host } = this.options;
|
|
29
|
+
serverLog('start server %O', { port, host });
|
|
30
|
+
yield this.start(port, host);
|
|
31
|
+
}));
|
|
20
32
|
compiler.hooks.watchClose.tap(PLUGIN_NAME, () => this.stop());
|
|
21
33
|
}
|
|
22
34
|
compile(page) {
|
|
35
|
+
serverLog('receive compile %s', page);
|
|
23
36
|
if (!this.compiler)
|
|
24
37
|
throw new Error('[taro-hmr-compile] Compiler 尚未初始化');
|
|
25
38
|
hmr_compiler_plugin_1.HmrCompilerPlugin.getCompilerHooks(this.compiler).compileRequested.call(page);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hmr-server-plugin.js","sourceRoot":"","sources":["../../src/server/hmr-server-plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"hmr-server-plugin.js","sourceRoot":"","sources":["../../src/server/hmr-server-plugin.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,8DAAgE;AAEhE,wEAAgE;AAChE,gCAA2B;AAE3B,MAAM,SAAS,GAAG,SAAG,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAA;AAE/C,MAAM,WAAW,GAAG,iBAAiB,CAAA;AAOrC,MAAa,eAAgB,SAAQ,2BAAgB;IAGjD,YAA6B,OAA+B;QACxD,KAAK,EAAE,CAAA;QADkB,YAAO,GAAP,OAAO,CAAwB;IAE5D,CAAC;IAED,KAAK,CAAC,QAAkB;QACpB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;QAExB,QAAQ,CAAC,KAAK,CAAC,aAAa,CAAC,UAAU,CAAC,WAAW,EAAE,GAAS,EAAE;YAC5D,IAAI,IAAI,CAAC,eAAe;gBAAE,OAAM;YAChC,MAAM,EAAC,IAAI,GAAG,IAAI,EAAE,IAAI,EAAC,GAAG,IAAI,CAAC,OAAO,CAAA;YACxC,SAAS,CAAC,iBAAiB,EAAE,EAAC,IAAI,EAAE,IAAI,EAAC,CAAC,CAAA;YAC1C,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QAChC,CAAC,CAAA,CAAC,CAAA;QAEF,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;IACjE,CAAC;IAEQ,OAAO,CAAC,IAAY;QACzB,SAAS,CAAC,oBAAoB,EAAE,IAAI,CAAC,CAAA;QACrC,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAA;QACxE,uCAAiB,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACjF,CAAC;CACJ;AAzBD,0CAyBC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taro-hmr-compile/plugin-webpack",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.7",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -19,9 +19,10 @@
|
|
|
19
19
|
},
|
|
20
20
|
"type": "commonjs",
|
|
21
21
|
"peerDependencies": {
|
|
22
|
-
"@tarojs/
|
|
23
|
-
"@tarojs/
|
|
24
|
-
"@tarojs/
|
|
22
|
+
"@tarojs/components": ">=3.0.0",
|
|
23
|
+
"@tarojs/helper": ">=3.0.0",
|
|
24
|
+
"@tarojs/service": ">=3.0.0",
|
|
25
|
+
"@tarojs/taro": ">=3.0.0",
|
|
25
26
|
"webpack": "^5.0.0"
|
|
26
27
|
},
|
|
27
28
|
"devDependencies": {
|
|
@@ -32,6 +33,7 @@
|
|
|
32
33
|
"@types/babel__generator": "^7.27.0",
|
|
33
34
|
"@types/babel__template": "^7.4.4",
|
|
34
35
|
"@types/babel__traverse": "^7.28.0",
|
|
36
|
+
"@types/debug": "^4.1.13",
|
|
35
37
|
"@types/node": "^22.20.1",
|
|
36
38
|
"typescript": "^5.8.2",
|
|
37
39
|
"webpack": "^5.110.3"
|
|
@@ -42,8 +44,8 @@
|
|
|
42
44
|
"@babel/template": "^7.29.7",
|
|
43
45
|
"@babel/traverse": "^7.29.8",
|
|
44
46
|
"@babel/types": "^7.29.8",
|
|
45
|
-
"@taro-hmr-compile/core": "0.0.1-alpha.
|
|
46
|
-
"
|
|
47
|
+
"@taro-hmr-compile/core": "0.0.1-alpha.7",
|
|
48
|
+
"debug": "^4.4.3",
|
|
47
49
|
"tapable": "^2.3.3",
|
|
48
50
|
"webpack-sources": "^3.5.1"
|
|
49
51
|
},
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import generator from '@babel/generator'
|
|
2
|
+
import * as parser from '@babel/parser'
|
|
3
|
+
import template from '@babel/template'
|
|
4
|
+
import traverse, {NodePath} from '@babel/traverse'
|
|
5
|
+
import * as t from '@babel/types'
|
|
6
|
+
import type {LoaderDefinitionFunction} from 'webpack'
|
|
7
|
+
import webpack from 'webpack'
|
|
8
|
+
|
|
9
|
+
const CREATE_PAGE_CONFIG = 'createPageConfig'
|
|
10
|
+
|
|
11
|
+
const WITH_HMR_PAGE_CONFIG = 'withHmrPageConfig'
|
|
12
|
+
|
|
13
|
+
const TARO_RUNTIME_REQUEST = '@tarojs/runtime'
|
|
14
|
+
|
|
15
|
+
const LOG_REQUEST = require.resolve('../log')
|
|
16
|
+
|
|
17
|
+
const CORE_RUNTIME_REQUEST = require.resolve('@taro-hmr-compile/core/runtime')
|
|
18
|
+
|
|
19
|
+
export const injectAppHmrRuntime = (source: string, debugNamespaces = '') => {
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
const ast = parser.parse(source, {sourceType: 'module'})
|
|
23
|
+
|
|
24
|
+
const buildHmrCode = template.statements(`
|
|
25
|
+
import { hmrState, mergePatches, enableLog as enableHmrCoreLog } from ${JSON.stringify(CORE_RUNTIME_REQUEST)}
|
|
26
|
+
import { log, enableLog as enableHmrWebpackLog } from ${JSON.stringify(LOG_REQUEST)}
|
|
27
|
+
enableHmrCoreLog(${JSON.stringify(debugNamespaces)})
|
|
28
|
+
enableHmrWebpackLog(${JSON.stringify(debugNamespaces)})
|
|
29
|
+
globalThis.log = log.extend('runtime')
|
|
30
|
+
globalThis.miniHmrApplyPatch = async (patches)=>{
|
|
31
|
+
try {
|
|
32
|
+
const { miniHmr } = __webpack_require__
|
|
33
|
+
const bootstrapPatch = patches.find(patch => patch.type === 'bootstrap')
|
|
34
|
+
|
|
35
|
+
if (miniHmr.shouldApplyBootstrapPatch(bootstrapPatch)) miniHmr.applyBootstrapPatch(bootstrapPatch)
|
|
36
|
+
|
|
37
|
+
const updatePatch = mergePatches(${webpack.RuntimeGlobals.getFullHash}(), patches)
|
|
38
|
+
hmrState.setLastFullHash(updatePatch?.toHash ?? ${webpack.RuntimeGlobals.getFullHash}())
|
|
39
|
+
await miniHmr.applyUpdatePatch(updatePatch)
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error('[taro-hmr-compile] patch apply failed', error)
|
|
42
|
+
return Promise.resolve()
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
`, {placeholderPattern: false})
|
|
46
|
+
|
|
47
|
+
traverse(ast, {
|
|
48
|
+
Program(path) {
|
|
49
|
+
const body = path.get('body')
|
|
50
|
+
const hasInjected = body.some(item => item.isImportDeclaration() && t.isStringLiteral(item.node.source, {value: CORE_RUNTIME_REQUEST}))
|
|
51
|
+
if (hasInjected) return
|
|
52
|
+
|
|
53
|
+
const nodes = buildHmrCode()
|
|
54
|
+
const lastImport = [...body].reverse().find(item => item.isImportDeclaration())
|
|
55
|
+
|
|
56
|
+
if (lastImport) lastImport.insertAfter(nodes)
|
|
57
|
+
if (!lastImport) path.unshiftContainer('body', nodes)
|
|
58
|
+
},
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
return generator(ast).code
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const transformPageConfig = (source: string, sourceFileName?: string) => {
|
|
65
|
+
|
|
66
|
+
const ast = parser.parse(source, {sourceType: 'module', plugins: ['typescript', 'jsx']})
|
|
67
|
+
|
|
68
|
+
const hasImportWithHmrPageConfig = (programPath: NodePath<t.Program>) => {
|
|
69
|
+
return programPath.node.body.some(node => {
|
|
70
|
+
if (!t.isImportDeclaration(node)) return false
|
|
71
|
+
if (!t.isStringLiteral(node.source, {value: CORE_RUNTIME_REQUEST})) return false
|
|
72
|
+
return node.specifiers.some(specifier => {
|
|
73
|
+
return t.isImportSpecifier(specifier) && t.isIdentifier(specifier.imported, {name: WITH_HMR_PAGE_CONFIG})
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
traverse(ast, {
|
|
79
|
+
Program(programPath) {
|
|
80
|
+
|
|
81
|
+
// 判断是否存在 createPageConfig 引用
|
|
82
|
+
const createPageConfigBinding = programPath.scope.getBinding(CREATE_PAGE_CONFIG)
|
|
83
|
+
if (!createPageConfigBinding || !createPageConfigBinding.path.isImportSpecifier()) return
|
|
84
|
+
|
|
85
|
+
// 判断 createPageConfig 引用是否来自 @tarojs/runtime
|
|
86
|
+
const {parentPath: createPageConfigImportDeclaration} = createPageConfigBinding.path
|
|
87
|
+
if (!t.isImportDeclaration(createPageConfigImportDeclaration.node)) return
|
|
88
|
+
if (!t.isStringLiteral(createPageConfigImportDeclaration.node.source, {value: TARO_RUNTIME_REQUEST})) return
|
|
89
|
+
|
|
90
|
+
// 判断是否已经注入了 withHmrPageConfig
|
|
91
|
+
if (hasImportWithHmrPageConfig(programPath)) return
|
|
92
|
+
|
|
93
|
+
// 判断是否存在 withHmrPageConfig 引用
|
|
94
|
+
const hasWithHmrPageConfigBinding = programPath.scope.hasBinding(WITH_HMR_PAGE_CONFIG)
|
|
95
|
+
|
|
96
|
+
// 生成 withHmrPageConfig 的别名
|
|
97
|
+
const wrapperIdentifier = hasWithHmrPageConfigBinding ? programPath.scope.generateUidIdentifier(WITH_HMR_PAGE_CONFIG) : t.identifier(WITH_HMR_PAGE_CONFIG)
|
|
98
|
+
|
|
99
|
+
// 生成 runtime import
|
|
100
|
+
const runtimeImport = (() => {
|
|
101
|
+
if (!hasWithHmrPageConfigBinding) return template.statement(`import { ${WITH_HMR_PAGE_CONFIG} } from ${JSON.stringify(CORE_RUNTIME_REQUEST)};`)()
|
|
102
|
+
return template.statement(`import { ${WITH_HMR_PAGE_CONFIG} as %%WRAPPER%% } from ${JSON.stringify(CORE_RUNTIME_REQUEST)};`)({WRAPPER: wrapperIdentifier})
|
|
103
|
+
})()
|
|
104
|
+
|
|
105
|
+
programPath.traverse({
|
|
106
|
+
CallExpression(CallExpressionPath) {
|
|
107
|
+
const {node: CallExpressionNode} = CallExpressionPath
|
|
108
|
+
if (!t.isIdentifier(CallExpressionNode.callee, {name: CREATE_PAGE_CONFIG})) return
|
|
109
|
+
const [_, pageName] = CallExpressionNode.arguments
|
|
110
|
+
const wrappedPageConfig = template.expression(`%%WRAPPER%%(%%PAGE_CONFIG%%, %%PAGE_NAME%%)`)({
|
|
111
|
+
WRAPPER: wrapperIdentifier,
|
|
112
|
+
PAGE_CONFIG: CallExpressionNode,
|
|
113
|
+
PAGE_NAME: pageName,
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
if (!hasImportWithHmrPageConfig(programPath)) programPath.unshiftContainer('body', runtimeImport)
|
|
117
|
+
|
|
118
|
+
CallExpressionPath.replaceWith(wrappedPageConfig)
|
|
119
|
+
CallExpressionPath.skip()
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
},
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
return generator(ast, {sourceMaps: true, sourceFileName}, source).code
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
interface LoaderOptions {
|
|
130
|
+
miniType: 'PAGE' | 'ENTRY'
|
|
131
|
+
debugNamespaces?: string
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const hmrCompilerLoader: LoaderDefinitionFunction<LoaderOptions> = function (source) {
|
|
135
|
+
const {miniType, debugNamespaces} = this.getOptions()
|
|
136
|
+
if (miniType === 'ENTRY') return injectAppHmrRuntime(source, debugNamespaces)
|
|
137
|
+
if (miniType === 'PAGE') return transformPageConfig(source, this.resourcePath) ?? source
|
|
138
|
+
return source
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export default hmrCompilerLoader
|
|
@@ -9,15 +9,24 @@ import type {Source} from 'webpack-sources'
|
|
|
9
9
|
import {HMR_PATCH_ASSET} from '../constants'
|
|
10
10
|
import {clearCompiledPages, isPageCompiled, markPageCompiled} from './compile-state'
|
|
11
11
|
import {getAllPages} from '../utils'
|
|
12
|
+
import {log} from "../log";
|
|
13
|
+
|
|
14
|
+
const compilerLog = log.extend('HmrCompilerPlugin')
|
|
12
15
|
|
|
13
16
|
const PLUGIN_NAME = 'HmrCompilerPlugin'
|
|
14
17
|
|
|
18
|
+
interface TaroNormalModule {
|
|
19
|
+
miniType?: string
|
|
20
|
+
loaders: Array<{ loader: string; options?: unknown }>
|
|
21
|
+
}
|
|
22
|
+
|
|
15
23
|
type StyleFilename = NonNullable<Compiler['options']['output']['cssFilename']>
|
|
16
24
|
|
|
17
25
|
export interface HmrCompilerPluginOptions {
|
|
18
26
|
paths: IPaths
|
|
19
27
|
appConfig: AppConfig
|
|
20
28
|
styleFilename?: StyleFilename
|
|
29
|
+
debugNamespaces?: string
|
|
21
30
|
}
|
|
22
31
|
|
|
23
32
|
export interface HmrCompilerHooks {
|
|
@@ -51,7 +60,11 @@ export class HmrCompilerPlugin {
|
|
|
51
60
|
// 当前编译批次的文件快照。与 pendingFiles 分离,确保编译期间新到达的请求留给下一批处理。
|
|
52
61
|
private readonly rebuildingFiles = new Set<string>()
|
|
53
62
|
|
|
54
|
-
|
|
63
|
+
private readonly options: HmrCompilerPluginOptions
|
|
64
|
+
|
|
65
|
+
constructor(options: HmrCompilerPluginOptions) {
|
|
66
|
+
this.options = options
|
|
67
|
+
}
|
|
55
68
|
|
|
56
69
|
static getCompilerHooks(compiler: Compiler): HmrCompilerHooks {
|
|
57
70
|
let hooks = HmrCompilerPlugin.compilerHooksMap.get(compiler)
|
|
@@ -95,6 +108,26 @@ export class HmrCompilerPlugin {
|
|
|
95
108
|
const hmrHooks = HmrCompilerPlugin.getCompilationHooks(compilation)
|
|
96
109
|
const javascriptHooks = compiler.webpack.javascript.JavascriptModulesPlugin.getCompilationHooks(compilation)
|
|
97
110
|
|
|
111
|
+
const HMR_COMPILER_LOADER = require.resolve('./hmr-compiler-loader')
|
|
112
|
+
|
|
113
|
+
const TARO_LOADER = '@tarojs/taro-loader'
|
|
114
|
+
|
|
115
|
+
// 尽量晚执行
|
|
116
|
+
compiler.webpack.NormalModule.getCompilationHooks(compilation).loader.tap({name: PLUGIN_NAME, stage: 10_000}, (_loaderContext, normalModule: TaroNormalModule) => {
|
|
117
|
+
if (!normalModule.miniType) return
|
|
118
|
+
|
|
119
|
+
if (normalModule.loaders.some(item => item.loader === HMR_COMPILER_LOADER)) return
|
|
120
|
+
|
|
121
|
+
const taroLoaderIndex = normalModule.loaders.findIndex(item => item.loader.replace(/\\/g, '/').includes(TARO_LOADER))
|
|
122
|
+
|
|
123
|
+
if (taroLoaderIndex < 0) return
|
|
124
|
+
|
|
125
|
+
const loaderOptions = {miniType: normalModule.miniType, debugNamespaces: this.options.debugNamespaces}
|
|
126
|
+
// Loader 从右向左执行;插到 taro-loader 左侧才能处理它生成的入口注册代码。
|
|
127
|
+
normalModule.loaders.splice(taroLoaderIndex, 0, { loader: HMR_COMPILER_LOADER, options: loaderOptions})
|
|
128
|
+
},
|
|
129
|
+
)
|
|
130
|
+
|
|
98
131
|
// 只修改 Page Chunk 的 JS transport。Webpack 的 ModuleGraph / ChunkGraph 保持不变,
|
|
99
132
|
// 因而 hash、HMR diff 和 hot-update 仍以完整模块图为准。
|
|
100
133
|
javascriptHooks.renderChunk.tap({name: PLUGIN_NAME, stage: -10_000}, (source, {chunk}) => {
|
|
@@ -155,6 +188,7 @@ export class HmrCompilerPlugin {
|
|
|
155
188
|
try {
|
|
156
189
|
if (isPageCompiled(this.options.paths.appPath, page)) return
|
|
157
190
|
// loader 在下一轮构建中读取该状态;必须先标记,再触发 watch invalidation。
|
|
191
|
+
compilerLog('start compile %s', page)
|
|
158
192
|
markPageCompiled(this.options.paths.appPath, page)
|
|
159
193
|
this.pendingFiles.add(resolveMainFilePath(path.resolve(this.options.paths.sourcePath, page)))
|
|
160
194
|
this.compiler?.watching?.invalidate()
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type {LoaderDefinitionFunction} from 'webpack'
|
|
2
|
+
import path from "path";
|
|
3
|
+
import {getAllPages} from '../utils'
|
|
4
|
+
import {isPageCompiled} from './compile-state'
|
|
5
|
+
import type {AppConfig} from "@tarojs/taro";
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
interface CreateShellOpt {
|
|
9
|
+
page: string
|
|
10
|
+
framework: 'react'
|
|
11
|
+
compileUrl: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const createReactShell = (opt: CreateShellOpt) => {
|
|
15
|
+
const {page, compileUrl} = opt
|
|
16
|
+
return `
|
|
17
|
+
import { PageShell } from ${JSON.stringify(require.resolve('@taro-hmr-compile/core/runtime'))}
|
|
18
|
+
const Page = () => <PageShell page={${JSON.stringify(page)}} compileUrl={${JSON.stringify(compileUrl)}}/>
|
|
19
|
+
export default Page
|
|
20
|
+
`
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const createShell = (opt: CreateShellOpt) => {
|
|
24
|
+
if (opt.framework === 'react') return createReactShell(opt)
|
|
25
|
+
throw new Error(`unsupported framework: ${opt.framework}`)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
interface LoaderOptions {
|
|
30
|
+
appPath: string
|
|
31
|
+
sourcePath: string
|
|
32
|
+
appConfig: AppConfig
|
|
33
|
+
framework: 'react'
|
|
34
|
+
compileUrl: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const lazyCompilerLoader: LoaderDefinitionFunction<LoaderOptions> = function (source) {
|
|
38
|
+
const {appPath, framework, appConfig, sourcePath, compileUrl} = this.getOptions()
|
|
39
|
+
|
|
40
|
+
const {dir: resourceDir, name: resourceName} = path.parse(path.relative(sourcePath, this.resourcePath))
|
|
41
|
+
|
|
42
|
+
const resourcePath = path.join(resourceDir, resourceName)
|
|
43
|
+
|
|
44
|
+
const isPageEntry = getAllPages(appConfig).includes(resourcePath)
|
|
45
|
+
|
|
46
|
+
if (!isPageEntry || isPageCompiled(appPath, resourcePath)) return source
|
|
47
|
+
|
|
48
|
+
return createShell({page: resourcePath, framework, compileUrl})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export default lazyCompilerLoader
|
package/src/constants.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import type {IPluginContext} from '@tarojs/service'
|
|
2
2
|
import type {AppConfig} from '@tarojs/taro'
|
|
3
|
-
import {HMR_OUTPUT_PATH} from './constants'
|
|
3
|
+
import {HMR_OUTPUT_PATH, DEFAULT_DEBUG_NAMESPACES} from './constants'
|
|
4
4
|
import {getAppConfig} from './utils'
|
|
5
5
|
import {HmrCompilerPlugin} from './compile/hmr-compiler-plugin'
|
|
6
6
|
import {HmrPatchPlugin} from './patch/hmr-patch-plugin'
|
|
7
7
|
import {HmrServerPlugin} from './server/hmr-server-plugin'
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import {emptyDirFile, findAvailablePort} from '@taro-hmr-compile/core/compiler'
|
|
9
|
+
import {enableLog} from './log'
|
|
10
|
+
|
|
10
11
|
|
|
11
12
|
export interface WebpackHmrCompileOpt {
|
|
12
13
|
host: string
|
|
13
14
|
port: number
|
|
14
15
|
patchHistoryLimit: number
|
|
16
|
+
debug: boolean | string
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
export default (ctx: IPluginContext, pluginOpts: Partial<WebpackHmrCompileOpt>) => {
|
|
@@ -24,27 +26,37 @@ export default (ctx: IPluginContext, pluginOpts: Partial<WebpackHmrCompileOpt>)
|
|
|
24
26
|
const appConfig: AppConfig = getAppConfig(ctx)
|
|
25
27
|
|
|
26
28
|
const host = pluginOpts.host ?? '127.0.0.1'
|
|
27
|
-
const port = pluginOpts.port ?? 8080
|
|
28
|
-
const compileUrl = `http://${host}:${port}/compile`
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
const preferredPort = pluginOpts.port ?? 8080
|
|
31
|
+
|
|
32
|
+
const debugNamespaces = (() => {
|
|
33
|
+
if (typeof pluginOpts.debug === 'string') return pluginOpts.debug
|
|
34
|
+
return pluginOpts.debug ? DEFAULT_DEBUG_NAMESPACES : undefined
|
|
35
|
+
})()
|
|
36
|
+
|
|
37
|
+
if (debugNamespaces) enableLog(debugNamespaces)
|
|
31
38
|
|
|
32
39
|
ctx.onBuildStart(() => emptyDirFile(outputPath))
|
|
33
40
|
|
|
34
|
-
ctx.modifyRunnerOpts(({
|
|
41
|
+
ctx.modifyRunnerOpts(({opts}) => {
|
|
35
42
|
opts.output ??= {}
|
|
36
43
|
opts.output.clean = false
|
|
37
44
|
})
|
|
38
45
|
|
|
39
|
-
ctx.modifyWebpackChain(({chain, webpack}) => {
|
|
46
|
+
ctx.modifyWebpackChain(async ({chain, webpack}) => {
|
|
47
|
+
const port = await findAvailablePort(preferredPort, host)
|
|
48
|
+
|
|
49
|
+
const compileUrl = `http://${host}:${port}/compile`
|
|
50
|
+
|
|
51
|
+
if (port !== preferredPort) console.warn(`[taro-hmr-compile] 端口 ${preferredPort} 已被占用,自动使用端口 ${port}`)
|
|
40
52
|
|
|
41
53
|
chain.module
|
|
42
|
-
.rule('
|
|
54
|
+
.rule('lazy-compiler')
|
|
43
55
|
.test(/\.[jt]sx?$/)
|
|
44
56
|
.include.add(ctx.paths.sourcePath)
|
|
45
57
|
.end()
|
|
46
|
-
.use('
|
|
47
|
-
.loader(require.resolve('./compile/
|
|
58
|
+
.use('lazy-compiler-loader')
|
|
59
|
+
.loader(require.resolve('./compile/lazy-compiler-loader'))
|
|
48
60
|
.options({appPath, sourcePath, appConfig, framework, compileUrl})
|
|
49
61
|
.end()
|
|
50
62
|
|
|
@@ -56,7 +68,7 @@ export default (ctx: IPluginContext, pluginOpts: Partial<WebpackHmrCompileOpt>)
|
|
|
56
68
|
|
|
57
69
|
const [{filename: styleFilename}] = chain.plugin('miniCssExtractPlugin')?.get('args') || []
|
|
58
70
|
|
|
59
|
-
chain.plugin('hmr-compiler-plugin').use(HmrCompilerPlugin, [{paths: ctx.paths, appConfig, styleFilename}])
|
|
71
|
+
chain.plugin('hmr-compiler-plugin').use(HmrCompilerPlugin, [{paths: ctx.paths, appConfig, styleFilename, debugNamespaces}])
|
|
60
72
|
|
|
61
73
|
chain.plugin('hmr-plugin').use(webpack.HotModuleReplacementPlugin)
|
|
62
74
|
|
package/src/log.ts
ADDED
|
@@ -7,8 +7,8 @@ import type {Compilation, Compiler} from 'webpack'
|
|
|
7
7
|
import {RawSource} from 'webpack-sources'
|
|
8
8
|
import {DEFAULT_HMR_PATCH_HISTORY_LIMIT, HMR_PATCH_ASSET} from '../constants'
|
|
9
9
|
import {HmrCompilerPlugin} from '../compile/hmr-compiler-plugin'
|
|
10
|
-
import {HmrPatchManager} from '
|
|
11
|
-
import type {PatchSource, UpdatePatchSource} from '
|
|
10
|
+
import {HmrPatchManager} from './hmr-patch-manager'
|
|
11
|
+
import type {PatchSource, UpdatePatchSource} from './hmr-patch-manager'
|
|
12
12
|
import {HmrPatchRuntimePlugin} from './hmr-patch-runtime-plugin'
|
|
13
13
|
|
|
14
14
|
const PLUGIN_NAME = 'HmrPatchPlugin'
|