@taro-hmr-compile/plugin-webpack 0.0.1 → 0.0.2

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.
@@ -4,9 +4,9 @@ interface CreateShellOpt {
4
4
  page: string;
5
5
  framework: 'react';
6
6
  compileUrl: string;
7
- memo: boolean;
8
7
  }
9
8
  export declare const createReactShell: (opt: CreateShellOpt) => string;
9
+ export declare const wrapReactPage: (source: string, pageName: string) => string;
10
10
  interface LoaderOptions {
11
11
  appPath: string;
12
12
  sourcePath: string;
@@ -1,19 +1,56 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
5
38
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.createReactShell = void 0;
39
+ exports.wrapReactPage = exports.createReactShell = void 0;
7
40
  const path_1 = __importDefault(require("path"));
8
41
  const utils_1 = require("../utils");
9
42
  const compile_state_1 = require("./compile-state");
43
+ const generator_1 = __importDefault(require("@babel/generator"));
44
+ const parser_1 = require("@babel/parser");
45
+ const template_1 = __importDefault(require("@babel/template"));
46
+ const traverse_1 = __importDefault(require("@babel/traverse"));
47
+ const t = __importStar(require("@babel/types"));
10
48
  const createReactShell = (opt) => {
11
- const { page, compileUrl, memo } = opt;
49
+ const { page, compileUrl } = opt;
12
50
  return `
13
51
  import { PageShell } from ${JSON.stringify(require.resolve('@taro-hmr-compile/core/runtime'))}
14
- ${memo ? "import React from 'react'" : ''}
15
52
  const Page = () => <PageShell page={${JSON.stringify(page)}} compileUrl={${JSON.stringify(compileUrl)}}/>
16
- export default ${memo ? 'React.memo(Page)' : 'Page'}
53
+ export default Page
17
54
  `;
18
55
  };
19
56
  exports.createReactShell = createReactShell;
@@ -22,16 +59,73 @@ const createShell = (opt) => {
22
59
  return (0, exports.createReactShell)(opt);
23
60
  throw new Error(`unsupported framework: ${opt.framework}`);
24
61
  };
25
- const isMemoPage = (source) => {
26
- return /\bexport\s+default\s+(?:(?:React\.)?memo|observer)\s*\(/.test(source);
62
+ const wrapReactPage = (source, pageName) => {
63
+ const ast = (0, parser_1.parse)(source, { sourceType: 'module', plugins: ['typescript', 'jsx'] });
64
+ const runtimeRequest = require.resolve('@taro-hmr-compile/core/runtime');
65
+ (0, traverse_1.default)(ast, {
66
+ Program(programPath) {
67
+ const hasWithHmrPage = () => programPath.node.body.some(node => {
68
+ if (!t.isImportDeclaration(node) || node.source.value !== runtimeRequest)
69
+ return false;
70
+ return node.specifiers.some(specifier => {
71
+ return t.isImportSpecifier(specifier) && t.isIdentifier(specifier.imported, { name: 'withHmrPage' });
72
+ });
73
+ });
74
+ if (hasWithHmrPage())
75
+ return;
76
+ const withHmrPage = programPath.scope.generateUidIdentifier('withHmrPage');
77
+ programPath.traverse({
78
+ ExportDefaultDeclaration(exportPath) {
79
+ if (hasWithHmrPage())
80
+ return;
81
+ const declaration = exportPath.node.declaration;
82
+ // 跳过只有类型声明、没有函数实现 的 TypeScript 节点
83
+ if (t.isTSDeclareFunction(declaration))
84
+ return;
85
+ const component = (() => {
86
+ // 具名函数或类保留原声明,通过名称引用,避免破坏模块内的其他引用。
87
+ if ((t.isFunctionDeclaration(declaration) || t.isClassDeclaration(declaration)) && declaration.id)
88
+ return t.identifier(declaration.id.name);
89
+ // 标识符、箭头函数、observer(...) 等表达式可以直接作为包装参数。
90
+ if (t.isExpression(declaration))
91
+ return declaration;
92
+ // 匿名函数或类声明需要先转成表达式,才能作为包装参数。
93
+ return t.toExpression(declaration);
94
+ })();
95
+ const defaultExport = template_1.default.statement(`export default %%WRAPPER%%(%%COMPONENT%%, %%PAGE_NAME%%);`)({
96
+ WRAPPER: withHmrPage,
97
+ COMPONENT: component,
98
+ PAGE_NAME: t.stringLiteral(pageName),
99
+ });
100
+ const hasNamedDeclaration = (t.isFunctionDeclaration(declaration) || t.isClassDeclaration(declaration)) && Boolean(declaration.id);
101
+ // 先插入 runtime 导入,让替换后重新入队的默认导出也能识别为已处理。
102
+ programPath.unshiftContainer('body', template_1.default.statement(`import { withHmrPage as %%WRAPPER%% } from %%RUNTIME%%;`)({
103
+ WRAPPER: withHmrPage,
104
+ RUNTIME: t.stringLiteral(runtimeRequest),
105
+ }));
106
+ // 具名函数或类保留声明,再追加包装后的默认导出。
107
+ if (hasNamedDeclaration)
108
+ exportPath.replaceWithMultiple([declaration, defaultExport]);
109
+ // 表达式或匿名声明直接替换为包装后的默认导出。
110
+ if (!hasNamedDeclaration)
111
+ exportPath.replaceWith(defaultExport);
112
+ exportPath.skip();
113
+ },
114
+ });
115
+ },
116
+ });
117
+ return (0, generator_1.default)(ast).code;
27
118
  };
119
+ exports.wrapReactPage = wrapReactPage;
28
120
  const lazyCompilerLoader = function (source) {
29
121
  const { appPath, framework, appConfig, sourcePath, compileUrl, lazyCompileExclude = [] } = this.getOptions();
30
122
  const { dir: resourceDir, name: resourceName } = path_1.default.parse(path_1.default.relative(sourcePath, this.resourcePath));
31
123
  const resourcePath = path_1.default.join(resourceDir, resourceName).replace(/\\/g, '/');
32
124
  const isPageEntry = (0, utils_1.getAllPages)(appConfig).includes(resourcePath);
33
- if (!isPageEntry || (0, compile_state_1.isPageCompiled)(appPath, resourcePath))
125
+ if (!isPageEntry)
34
126
  return source;
127
+ if ((0, compile_state_1.isPageCompiled)(appPath, resourcePath))
128
+ return (0, exports.wrapReactPage)(source, resourcePath);
35
129
  const isExcluded = lazyCompileExclude.some(excludePath => {
36
130
  if (!excludePath)
37
131
  return false;
@@ -39,8 +133,8 @@ const lazyCompilerLoader = function (source) {
39
133
  return normalizedPath.endsWith('/') ? resourcePath.startsWith(normalizedPath) : resourcePath === normalizedPath;
40
134
  });
41
135
  if (isExcluded)
42
- return source;
43
- return createShell({ page: resourcePath, framework, compileUrl, memo: isMemoPage(source) });
136
+ return (0, exports.wrapReactPage)(source, resourcePath);
137
+ return createShell({ page: resourcePath, framework, compileUrl });
44
138
  };
45
139
  exports.default = lazyCompilerLoader;
46
140
  //# sourceMappingURL=lazy-compiler-loader.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"lazy-compiler-loader.js","sourceRoot":"","sources":["../../src/compile/lazy-compiler-loader.ts"],"names":[],"mappings":";;;;;;AACA,gDAAwB;AACxB,oCAAoC;AACpC,mDAA8C;AAWvC,MAAM,gBAAgB,GAAG,CAAC,GAAmB,EAAE,EAAE;IACpD,MAAM,EAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAC,GAAG,GAAG,CAAA;IACpC,OAAO;gCACqB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,gCAAgC,CAAC,CAAC;MAC3F,IAAI,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,EAAE;0CACH,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,iBAAiB,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC;qBACpF,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,MAAM;KAClD,CAAA;AACL,CAAC,CAAA;AARY,QAAA,gBAAgB,oBAQ5B;AAED,MAAM,WAAW,GAAG,CAAC,GAAmB,EAAE,EAAE;IACxC,IAAI,GAAG,CAAC,SAAS,KAAK,OAAO;QAAE,OAAO,IAAA,wBAAgB,EAAC,GAAG,CAAC,CAAA;IAC3D,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,CAAC,SAAS,EAAE,CAAC,CAAA;AAC9D,CAAC,CAAA;AAED,MAAM,UAAU,GAAG,CAAC,MAAc,EAAE,EAAE;IAClC,OAAO,yDAAyD,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;AACjF,CAAC,CAAA;AAYD,MAAM,kBAAkB,GAA4C,UAAU,MAAM;IAChF,MAAM,EAAC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,kBAAkB,GAAG,EAAE,EAAC,GAAG,IAAI,CAAC,UAAU,EAAE,CAAA;IAE1G,MAAM,EAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,YAAY,EAAC,GAAG,cAAI,CAAC,KAAK,CAAC,cAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,CAAA;IAEvG,MAAM,YAAY,GAAG,cAAI,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;IAE7E,MAAM,WAAW,GAAG,IAAA,mBAAW,EAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAA;IAEjE,IAAI,CAAC,WAAW,IAAI,IAAA,8BAAc,EAAC,OAAO,EAAE,YAAY,CAAC;QAAE,OAAO,MAAM,CAAA;IAExE,MAAM,UAAU,GAAG,kBAAkB,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE;QACrD,IAAI,CAAC,WAAW;YAAE,OAAO,KAAK,CAAA;QAC9B,MAAM,cAAc,GAAG,cAAI,CAAC,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;QAChG,OAAO,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,YAAY,KAAK,cAAc,CAAA;IACnH,CAAC,CAAC,CAAA;IAEF,IAAI,UAAU;QAAE,OAAO,MAAM,CAAA;IAE7B,OAAO,WAAW,CAAC,EAAC,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,EAAC,CAAC,CAAA;AAC7F,CAAC,CAAA;AAED,kBAAe,kBAAkB,CAAA"}
1
+ {"version":3,"file":"lazy-compiler-loader.js","sourceRoot":"","sources":["../../src/compile/lazy-compiler-loader.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,gDAAwB;AACxB,oCAAoC;AACpC,mDAA8C;AAE9C,iEAAwC;AACxC,0CAAmC;AACnC,+DAAsC;AACtC,+DAAsC;AACtC,gDAAiC;AAS1B,MAAM,gBAAgB,GAAG,CAAC,GAAmB,EAAE,EAAE;IACpD,MAAM,EAAC,IAAI,EAAE,UAAU,EAAC,GAAG,GAAG,CAAA;IAC9B,OAAO;gCACqB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,gCAAgC,CAAC,CAAC;0CACvD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,iBAAiB,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC;;KAEpG,CAAA;AACL,CAAC,CAAA;AAPY,QAAA,gBAAgB,oBAO5B;AAED,MAAM,WAAW,GAAG,CAAC,GAAmB,EAAE,EAAE;IACxC,IAAI,GAAG,CAAC,SAAS,KAAK,OAAO;QAAE,OAAO,IAAA,wBAAgB,EAAC,GAAG,CAAC,CAAA;IAC3D,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,CAAC,SAAS,EAAE,CAAC,CAAA;AAC9D,CAAC,CAAA;AAEM,MAAM,aAAa,GAAG,CAAC,MAAc,EAAE,QAAgB,EAAU,EAAE;IACtE,MAAM,GAAG,GAAG,IAAA,cAAK,EAAC,MAAM,EAAE,EAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,YAAY,EAAE,KAAK,CAAC,EAAC,CAAC,CAAA;IACjF,MAAM,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,gCAAgC,CAAC,CAAA;IAExE,IAAA,kBAAQ,EAAC,GAAG,EAAE;QACV,OAAO,CAAC,WAAW;YACf,MAAM,cAAc,GAAG,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;gBAC3D,IAAI,CAAC,CAAC,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,KAAK,cAAc;oBAAE,OAAO,KAAK,CAAA;gBAEtF,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE;oBACpC,OAAO,CAAC,CAAC,iBAAiB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAC,IAAI,EAAE,aAAa,EAAC,CAAC,CAAA;gBACtG,CAAC,CAAC,CAAA;YACN,CAAC,CAAC,CAAA;YAEF,IAAI,cAAc,EAAE;gBAAE,OAAM;YAE5B,MAAM,WAAW,GAAG,WAAW,CAAC,KAAK,CAAC,qBAAqB,CAAC,aAAa,CAAC,CAAA;YAE1E,WAAW,CAAC,QAAQ,CAAC;gBACjB,wBAAwB,CAAC,UAAU;oBAC/B,IAAI,cAAc,EAAE;wBAAE,OAAM;oBAC5B,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,CAAC,WAAW,CAAA;oBAE/C,kCAAkC;oBAClC,IAAI,CAAC,CAAC,mBAAmB,CAAC,WAAW,CAAC;wBAAE,OAAM;oBAE9C,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE;wBACpB,mCAAmC;wBACnC,IAAI,CAAC,CAAC,CAAC,qBAAqB,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC,IAAI,WAAW,CAAC,EAAE;4BAAE,OAAO,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC,IAAI,CAAC,CAAA;wBAE3I,yCAAyC;wBACzC,IAAI,CAAC,CAAC,YAAY,CAAC,WAAW,CAAC;4BAAE,OAAO,WAAW,CAAA;wBAEnD,6BAA6B;wBAC7B,OAAO,CAAC,CAAC,YAAY,CAAC,WAAW,CAAC,CAAA;oBACtC,CAAC,CAAC,EAAE,CAAA;oBACJ,MAAM,aAAa,GAAG,kBAAQ,CAAC,SAAS,CAAC,2DAA2D,CAAC,CAAC;wBAClG,OAAO,EAAE,WAAW;wBACpB,SAAS,EAAE,SAAS;wBACpB,SAAS,EAAE,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC;qBACvC,CAAC,CAAA;oBAEF,MAAM,mBAAmB,GAAG,CAAC,CAAC,CAAC,qBAAqB,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC,IAAI,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC,CAAA;oBAElI,wCAAwC;oBACxC,WAAW,CAAC,gBAAgB,CAAC,MAAM,EAAE,kBAAQ,CAAC,SAAS,CAAC,yDAAyD,CAAC,CAAC;wBAC/G,OAAO,EAAE,WAAW;wBACpB,OAAO,EAAE,CAAC,CAAC,aAAa,CAAC,cAAc,CAAC;qBAC3C,CAAC,CAAC,CAAA;oBAEH,0BAA0B;oBAC1B,IAAI,mBAAmB;wBAAE,UAAU,CAAC,mBAAmB,CAAC,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC,CAAA;oBAErF,yBAAyB;oBACzB,IAAI,CAAC,mBAAmB;wBAAE,UAAU,CAAC,WAAW,CAAC,aAAa,CAAC,CAAA;oBAE/D,UAAU,CAAC,IAAI,EAAE,CAAA;gBACrB,CAAC;aACJ,CAAC,CAAA;QACN,CAAC;KACJ,CAAC,CAAA;IAEF,OAAO,IAAA,mBAAS,EAAC,GAAG,CAAC,CAAC,IAAI,CAAA;AAC9B,CAAC,CAAA;AA/DY,QAAA,aAAa,iBA+DzB;AAYD,MAAM,kBAAkB,GAA4C,UAAU,MAAM;IAChF,MAAM,EAAC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,kBAAkB,GAAG,EAAE,EAAC,GAAG,IAAI,CAAC,UAAU,EAAE,CAAA;IAE1G,MAAM,EAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,YAAY,EAAC,GAAG,cAAI,CAAC,KAAK,CAAC,cAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,CAAA;IAEvG,MAAM,YAAY,GAAG,cAAI,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;IAE7E,MAAM,WAAW,GAAG,IAAA,mBAAW,EAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAA;IAEjE,IAAI,CAAC,WAAW;QAAE,OAAO,MAAM,CAAA;IAE/B,IAAI,IAAA,8BAAc,EAAC,OAAO,EAAE,YAAY,CAAC;QAAE,OAAO,IAAA,qBAAa,EAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IAErF,MAAM,UAAU,GAAG,kBAAkB,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE;QACrD,IAAI,CAAC,WAAW;YAAE,OAAO,KAAK,CAAA;QAC9B,MAAM,cAAc,GAAG,cAAI,CAAC,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;QAChG,OAAO,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,YAAY,KAAK,cAAc,CAAA;IACnH,CAAC,CAAC,CAAA;IAEF,IAAI,UAAU;QAAE,OAAO,IAAA,qBAAa,EAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IAE1D,OAAO,WAAW,CAAC,EAAC,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAC,CAAC,CAAA;AACnE,CAAC,CAAA;AAED,kBAAe,kBAAkB,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taro-hmr-compile/plugin-webpack",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -45,7 +45,7 @@
45
45
  "@babel/template": "^7.29.7",
46
46
  "@babel/traverse": "^7.29.8",
47
47
  "@babel/types": "^7.29.8",
48
- "@taro-hmr-compile/core": "0.0.1",
48
+ "@taro-hmr-compile/core": "0.0.2",
49
49
  "debug": "^4.4.3",
50
50
  "lodash": "^4.18.1",
51
51
  "tapable": "^2.3.3",
@@ -3,22 +3,25 @@ import path from "path";
3
3
  import {getAllPages} from '../utils'
4
4
  import {isPageCompiled} from './compile-state'
5
5
  import type {AppConfig} from "@tarojs/taro";
6
+ import generator from '@babel/generator'
7
+ import {parse} from '@babel/parser'
8
+ import template from '@babel/template'
9
+ import traverse from '@babel/traverse'
10
+ import * as t from '@babel/types'
6
11
 
7
12
 
8
13
  interface CreateShellOpt {
9
14
  page: string
10
15
  framework: 'react'
11
16
  compileUrl: string
12
- memo: boolean
13
17
  }
14
18
 
15
19
  export const createReactShell = (opt: CreateShellOpt) => {
16
- const {page, compileUrl, memo} = opt
20
+ const {page, compileUrl} = opt
17
21
  return `
18
22
  import { PageShell } from ${JSON.stringify(require.resolve('@taro-hmr-compile/core/runtime'))}
19
- ${memo ? "import React from 'react'" : ''}
20
23
  const Page = () => <PageShell page={${JSON.stringify(page)}} compileUrl={${JSON.stringify(compileUrl)}}/>
21
- export default ${memo ? 'React.memo(Page)' : 'Page'}
24
+ export default Page
22
25
  `
23
26
  }
24
27
 
@@ -27,8 +30,69 @@ const createShell = (opt: CreateShellOpt) => {
27
30
  throw new Error(`unsupported framework: ${opt.framework}`)
28
31
  }
29
32
 
30
- const isMemoPage = (source: string) => {
31
- return /\bexport\s+default\s+(?:(?:React\.)?memo|observer)\s*\(/.test(source)
33
+ export const wrapReactPage = (source: string, pageName: string): string => {
34
+ const ast = parse(source, {sourceType: 'module', plugins: ['typescript', 'jsx']})
35
+ const runtimeRequest = require.resolve('@taro-hmr-compile/core/runtime')
36
+
37
+ traverse(ast, {
38
+ Program(programPath) {
39
+ const hasWithHmrPage = () => programPath.node.body.some(node => {
40
+ if (!t.isImportDeclaration(node) || node.source.value !== runtimeRequest) return false
41
+
42
+ return node.specifiers.some(specifier => {
43
+ return t.isImportSpecifier(specifier) && t.isIdentifier(specifier.imported, {name: 'withHmrPage'})
44
+ })
45
+ })
46
+
47
+ if (hasWithHmrPage()) return
48
+
49
+ const withHmrPage = programPath.scope.generateUidIdentifier('withHmrPage')
50
+
51
+ programPath.traverse({
52
+ ExportDefaultDeclaration(exportPath) {
53
+ if (hasWithHmrPage()) return
54
+ const declaration = exportPath.node.declaration
55
+
56
+ // 跳过只有类型声明、没有函数实现 的 TypeScript 节点
57
+ if (t.isTSDeclareFunction(declaration)) return
58
+
59
+ const component = (() => {
60
+ // 具名函数或类保留原声明,通过名称引用,避免破坏模块内的其他引用。
61
+ if ((t.isFunctionDeclaration(declaration) || t.isClassDeclaration(declaration)) && declaration.id) return t.identifier(declaration.id.name)
62
+
63
+ // 标识符、箭头函数、observer(...) 等表达式可以直接作为包装参数。
64
+ if (t.isExpression(declaration)) return declaration
65
+
66
+ // 匿名函数或类声明需要先转成表达式,才能作为包装参数。
67
+ return t.toExpression(declaration)
68
+ })()
69
+ const defaultExport = template.statement(`export default %%WRAPPER%%(%%COMPONENT%%, %%PAGE_NAME%%);`)({
70
+ WRAPPER: withHmrPage,
71
+ COMPONENT: component,
72
+ PAGE_NAME: t.stringLiteral(pageName),
73
+ })
74
+
75
+ const hasNamedDeclaration = (t.isFunctionDeclaration(declaration) || t.isClassDeclaration(declaration)) && Boolean(declaration.id)
76
+
77
+ // 先插入 runtime 导入,让替换后重新入队的默认导出也能识别为已处理。
78
+ programPath.unshiftContainer('body', template.statement(`import { withHmrPage as %%WRAPPER%% } from %%RUNTIME%%;`)({
79
+ WRAPPER: withHmrPage,
80
+ RUNTIME: t.stringLiteral(runtimeRequest),
81
+ }))
82
+
83
+ // 具名函数或类保留声明,再追加包装后的默认导出。
84
+ if (hasNamedDeclaration) exportPath.replaceWithMultiple([declaration, defaultExport])
85
+
86
+ // 表达式或匿名声明直接替换为包装后的默认导出。
87
+ if (!hasNamedDeclaration) exportPath.replaceWith(defaultExport)
88
+
89
+ exportPath.skip()
90
+ },
91
+ })
92
+ },
93
+ })
94
+
95
+ return generator(ast).code
32
96
  }
33
97
 
34
98
 
@@ -50,7 +114,9 @@ const lazyCompilerLoader: LoaderDefinitionFunction<LoaderOptions> = function (so
50
114
 
51
115
  const isPageEntry = getAllPages(appConfig).includes(resourcePath)
52
116
 
53
- if (!isPageEntry || isPageCompiled(appPath, resourcePath)) return source
117
+ if (!isPageEntry) return source
118
+
119
+ if (isPageCompiled(appPath, resourcePath)) return wrapReactPage(source, resourcePath)
54
120
 
55
121
  const isExcluded = lazyCompileExclude.some(excludePath => {
56
122
  if (!excludePath) return false
@@ -58,9 +124,9 @@ const lazyCompilerLoader: LoaderDefinitionFunction<LoaderOptions> = function (so
58
124
  return normalizedPath.endsWith('/') ? resourcePath.startsWith(normalizedPath) : resourcePath === normalizedPath
59
125
  })
60
126
 
61
- if (isExcluded) return source
127
+ if (isExcluded) return wrapReactPage(source, resourcePath)
62
128
 
63
- return createShell({page: resourcePath, framework, compileUrl, memo: isMemoPage(source)})
129
+ return createShell({page: resourcePath, framework, compileUrl})
64
130
  }
65
131
 
66
132
  export default lazyCompilerLoader