@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.
Files changed (42) hide show
  1. package/dist/compile/hmr-compiler-loader.d.ts +9 -0
  2. package/dist/compile/hmr-compiler-loader.js +162 -0
  3. package/dist/compile/hmr-compiler-loader.js.map +1 -0
  4. package/dist/compile/hmr-compiler-plugin.d.ts +2 -1
  5. package/dist/compile/hmr-compiler-plugin.js +19 -1
  6. package/dist/compile/hmr-compiler-plugin.js.map +1 -1
  7. package/dist/compile/lazy-compiler-loader.d.ts +17 -0
  8. package/dist/compile/lazy-compiler-loader.js +34 -0
  9. package/dist/compile/lazy-compiler-loader.js.map +1 -0
  10. package/dist/constants.d.ts +1 -0
  11. package/dist/constants.js +2 -1
  12. package/dist/constants.js.map +1 -1
  13. package/dist/index.d.ts +1 -0
  14. package/dist/index.js +32 -14
  15. package/dist/index.js.map +1 -1
  16. package/dist/log.d.ts +3 -0
  17. package/dist/log.js +11 -0
  18. package/dist/log.js.map +1 -0
  19. package/dist/patch/hmr-patch-plugin.js +1 -1
  20. package/dist/patch/hmr-patch-plugin.js.map +1 -1
  21. package/dist/patch/hmr-patch-runtime-plugin.js +29 -58
  22. package/dist/patch/hmr-patch-runtime-plugin.js.map +1 -1
  23. package/dist/server/hmr-server-plugin.js +19 -6
  24. package/dist/server/hmr-server-plugin.js.map +1 -1
  25. package/package.json +8 -6
  26. package/src/compile/hmr-compiler-loader.ts +141 -0
  27. package/src/compile/hmr-compiler-plugin.ts +35 -1
  28. package/src/compile/lazy-compiler-loader.ts +51 -0
  29. package/src/constants.ts +2 -0
  30. package/src/index.ts +24 -12
  31. package/src/log.ts +5 -0
  32. package/src/patch/hmr-patch-plugin.ts +2 -2
  33. package/src/patch/hmr-patch-runtime-plugin.ts +44 -75
  34. package/src/server/hmr-server-plugin.ts +9 -3
  35. package/dist/compile/hmr-compile-loader.d.ts +0 -11
  36. package/dist/compile/hmr-compile-loader.js +0 -52
  37. package/dist/compile/hmr-compile-loader.js.map +0 -1
  38. package/dist/compile/inject-app-hmr-runtime.d.ts +0 -1
  39. package/dist/compile/inject-app-hmr-runtime.js +0 -79
  40. package/dist/compile/inject-app-hmr-runtime.js.map +0 -1
  41. package/src/compile/hmr-compile-loader.ts +0 -72
  42. 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
- applyPatch: function(patch) {
17
- if (!patch) return Promise.resolve();
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
- if (patch.type === 'bootstrap') {
24
- var installedModules = [];
25
- Object.keys(patch.modules).forEach(function(moduleId) {
26
- if (!Object.prototype.hasOwnProperty.call(${webpack_1.default.RuntimeGlobals.moduleFactories}, moduleId)) {
27
- ${webpack_1.default.RuntimeGlobals.moduleFactories}[moduleId] = patch.modules[moduleId];
28
- installedModules.push(moduleId);
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;;;;EAIrB,iBAAO,CAAC,cAAc,CAAC,OAAO;;;;;wBAKR,iBAAO,CAAC,cAAc,CAAC,WAAW;;;;;;oDAMN,iBAAO,CAAC,cAAc,CAAC,eAAe;YAC9E,iBAAO,CAAC,cAAc,CAAC,eAAe;;;;;;;;;;;;;;;;;;;;0BAoBxB,iBAAO,CAAC,cAAc,CAAC,WAAW;;;;;;;;;;;;;;;;;;;QAmBpD,iBAAO,CAAC,cAAc,CAAC,OAAO;;;;;;;;;;YAU1B,iBAAO,CAAC,cAAc,CAAC,WAAW;;;;;;;;;;;;;;;;;;;;;CAqB7C,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,aAAa,CAAC,GAAG,CAC/B,WAAW,EACX,aAAa,CAAC,EAAE;gBACZ,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,CACJ,CAAA;QACL,CAAC,CACJ,CAAA;IACL,CAAC;CACJ;AA7BD,sDA6BC"}
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.afterEmit.tap(PLUGIN_NAME, compilation => {
15
- var _a;
16
- const isSuccessful = compilation.errors.length === 0 && Boolean(compilation.hash);
17
- if (isSuccessful && !this.isServerRunning)
18
- this.start((_a = this.options.port) !== null && _a !== void 0 ? _a : 8080, this.options.host);
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":";;;AAAA,8DAAgE;AAEhE,wEAAgE;AAEhE,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,SAAS,CAAC,GAAG,CAAC,WAAW,EAAE,WAAW,CAAC,EAAE;;YACpD,MAAM,YAAY,GAAG,WAAW,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;YACjF,IAAI,YAAY,IAAI,CAAC,IAAI,CAAC,eAAe;gBAAE,IAAI,CAAC,KAAK,CAAC,MAAA,IAAI,CAAC,OAAO,CAAC,IAAI,mCAAI,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QACvG,CAAC,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,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;AAtBD,0CAsBC"}
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.5",
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/helper": "^3.0.0",
23
- "@tarojs/service": "^3.0.0",
24
- "@tarojs/taro": "^3.0.0",
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.5",
46
- "@taro-hmr-compile/react-refresh": "0.0.1-alpha.5",
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
- constructor(private readonly options: HmrCompilerPluginOptions) {}
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
@@ -3,3 +3,5 @@ export const HMR_OUTPUT_PATH = 'hmr'
3
3
  export const HMR_PATCH_ASSET = `${HMR_OUTPUT_PATH}/patch.js`
4
4
 
5
5
  export const DEFAULT_HMR_PATCH_HISTORY_LIMIT = 20
6
+
7
+ export const DEFAULT_DEBUG_NAMESPACES = 'taro-hmr-compile:*'
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 {setupReactRefresh} from '@taro-hmr-compile/react-refresh/compiler'
9
- import {emptyDirFile} from '@taro-hmr-compile/core/compiler'
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
- setupReactRefresh(ctx)
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(({ opts }) => {
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('hmr-compile')
54
+ .rule('lazy-compiler')
43
55
  .test(/\.[jt]sx?$/)
44
56
  .include.add(ctx.paths.sourcePath)
45
57
  .end()
46
- .use('hmr-compile-loader')
47
- .loader(require.resolve('./compile/hmr-compile-loader'))
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
@@ -0,0 +1,5 @@
1
+ import createDebug from 'debug'
2
+
3
+ export const log = createDebug('taro-hmr-compile:webpack')
4
+
5
+ export const enableLog = (namespaces: string) => createDebug.enable(namespaces)
@@ -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 '../patch/hmr-patch-manager'
11
- import type {PatchSource, UpdatePatchSource} from '../patch/hmr-patch-manager'
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'