lcd-router-webpack-plugin 1.0.0

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 (54) hide show
  1. package/.lecprc.ts +5 -0
  2. package/LICENSE +674 -0
  3. package/lib/Generator.d.ts +16 -0
  4. package/lib/Generator.d.ts.map +1 -0
  5. package/lib/Generator.js +35 -0
  6. package/lib/Generator.js.map +1 -0
  7. package/lib/ReducersGenerator.d.ts +15 -0
  8. package/lib/ReducersGenerator.d.ts.map +1 -0
  9. package/lib/ReducersGenerator.js +62 -0
  10. package/lib/ReducersGenerator.js.map +1 -0
  11. package/lib/RoutersGenerator.d.ts +24 -0
  12. package/lib/RoutersGenerator.d.ts.map +1 -0
  13. package/lib/RoutersGenerator.js +115 -0
  14. package/lib/RoutersGenerator.js.map +1 -0
  15. package/lib/SagasGenerator.d.ts +16 -0
  16. package/lib/SagasGenerator.d.ts.map +1 -0
  17. package/lib/SagasGenerator.js +56 -0
  18. package/lib/SagasGenerator.js.map +1 -0
  19. package/lib/index.d.ts +9 -0
  20. package/lib/index.d.ts.map +1 -0
  21. package/lib/index.js +181 -0
  22. package/lib/index.js.map +1 -0
  23. package/lib/utils/files.d.ts +23 -0
  24. package/lib/utils/files.d.ts.map +1 -0
  25. package/lib/utils/files.js +65 -0
  26. package/lib/utils/files.js.map +1 -0
  27. package/lib/utils/index.d.ts +5 -0
  28. package/lib/utils/index.d.ts.map +1 -0
  29. package/lib/utils/index.js +50 -0
  30. package/lib/utils/index.js.map +1 -0
  31. package/lib/utils/paths.d.ts +38 -0
  32. package/lib/utils/paths.d.ts.map +1 -0
  33. package/lib/utils/paths.js +81 -0
  34. package/lib/utils/paths.js.map +1 -0
  35. package/lib/utils/resolver.d.ts +25 -0
  36. package/lib/utils/resolver.d.ts.map +1 -0
  37. package/lib/utils/resolver.js +41 -0
  38. package/lib/utils/resolver.js.map +1 -0
  39. package/lib/utils/types.d.ts +75 -0
  40. package/lib/utils/types.d.ts.map +1 -0
  41. package/lib/utils/types.js +6 -0
  42. package/lib/utils/types.js.map +1 -0
  43. package/package.json +29 -0
  44. package/src/Generator.ts +46 -0
  45. package/src/ReducersGenerator.ts +90 -0
  46. package/src/RoutersGenerator.ts +295 -0
  47. package/src/SagasGenerator.ts +101 -0
  48. package/src/index.ts +204 -0
  49. package/src/utils/files.ts +54 -0
  50. package/src/utils/index.ts +4 -0
  51. package/src/utils/paths.ts +77 -0
  52. package/src/utils/resolver.ts +37 -0
  53. package/src/utils/types.ts +76 -0
  54. package/tsconfig.json +14 -0
@@ -0,0 +1,75 @@
1
+ export interface Options {
2
+ /**
3
+ * 组件上下文, 默认取 compiler.context
4
+ * @default compiler.context
5
+ */
6
+ context?: string;
7
+ /**
8
+ * 是否禁用内置路由
9
+ * 默认会将 react-router-dom 转换到 rrc-loader-helper/router
10
+ * 如果你是新项目,建议将该配置配置为true,因为内置路由针对react-router做了大量的改动,可能会引起一些无法预测的故障
11
+ * @default false
12
+ */
13
+ disableBuiltInRoute?: boolean;
14
+ /**
15
+ * 是否开启 webpack prefetch预加载
16
+ * @default false
17
+ */
18
+ enablePreFetch?: boolean;
19
+ /**
20
+ * 指定页面路径 不进行注册路由
21
+ * @default []
22
+ */
23
+ externals?: string[];
24
+ /**
25
+ * 页面目录
26
+ * @default component
27
+ */
28
+ pageDir: string;
29
+ /**
30
+ * shineout 主题
31
+ */
32
+ theme?: {
33
+ hash: string;
34
+ };
35
+ /**
36
+ * reducer 文件名称
37
+ * @default reducers
38
+ */
39
+ reducerName: string;
40
+ /**
41
+ * loading 组件路径
42
+ */
43
+ loading?: string;
44
+ /**
45
+ * 一个神奇的属性,用于处理 /xxx/order/list 这种路由过长的问题
46
+ * 也可以用来作为父路由的默认路由目录
47
+ * 会生成一个新路由 /xxx/order,实际目录指向为 /xxx/order/list
48
+ * @default list
49
+ */
50
+ index?: string;
51
+ /**
52
+ * 渲染出错时的组件地址
53
+ */
54
+ errorComponent?: string;
55
+ /**
56
+ * 是否允许页面内生成自定义路由
57
+ * 例如在 component/order/detail/me.json 中 配置 route: '/:id',此时会生成动态路由 /order/detail/:id
58
+ * 非常不推荐使用的一个属性,因为此功能受 me.json 中 exact 属性影响, 仅当 exact: true 时候,才不会拼接路由
59
+ * 当开启 dangerousRoute 后, 不再受 exact 影响, 都会生成 拼接路由 /order/detail/:id
60
+ * @default false
61
+ */
62
+ dangerousRoute?: boolean;
63
+ }
64
+ export interface MeJSON {
65
+ theme?: string;
66
+ sync?: boolean;
67
+ mobx?: boolean;
68
+ keepAlive?: boolean;
69
+ retain?: boolean;
70
+ title?: string;
71
+ linkExtendable?: boolean;
72
+ injectStore?: boolean | string;
73
+ route?: string;
74
+ }
75
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/utils/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,OAAO;IACtB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,KAAK,CAAC,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","names":[],"sources":["../../types.ts"],"sourcesContent":["export interface Options {\n /**\n * 组件上下文, 默认取 compiler.context\n * @default compiler.context\n */\n context?: string;\n /**\n * 是否禁用内置路由\n * 默认会将 react-router-dom 转换到 rrc-loader-helper/router\n * 如果你是新项目,建议将该配置配置为true,因为内置路由针对react-router做了大量的改动,可能会引起一些无法预测的故障\n * @default false\n */\n disableBuiltInRoute?: boolean;\n /**\n * 是否开启 webpack prefetch预加载\n * @default false\n */\n enablePreFetch?: boolean;\n /**\n * 指定页面路径 不进行注册路由\n * @default []\n */\n externals?: string[];\n /**\n * 页面目录\n * @default component\n */\n pageDir: string;\n /**\n * shineout 主题\n */\n theme?: {\n hash: string;\n };\n /**\n * reducer 文件名称\n * @default reducers\n */\n reducerName: string;\n /**\n * loading 组件路径\n */\n loading?: string;\n /**\n * 一个神奇的属性,用于处理 /xxx/order/list 这种路由过长的问题\n * 也可以用来作为父路由的默认路由目录\n * 会生成一个新路由 /xxx/order,实际目录指向为 /xxx/order/list\n * @default list\n */\n index?: string;\n /**\n * 渲染出错时的组件地址\n */\n errorComponent?: string;\n\n /**\n * 是否允许页面内生成自定义路由\n * 例如在 component/order/detail/me.json 中 配置 route: '/:id',此时会生成动态路由 /order/detail/:id\n * 非常不推荐使用的一个属性,因为此功能受 me.json 中 exact 属性影响, 仅当 exact: true 时候,才不会拼接路由\n * 当开启 dangerousRoute 后, 不再受 exact 影响, 都会生成 拼接路由 /order/detail/:id\n * @default false\n */\n dangerousRoute?: boolean;\n}\n\nexport interface MeJSON {\n theme?: string;\n sync?: boolean;\n mobx?: boolean;\n keepAlive?: boolean;\n retain?: boolean;\n title?: string;\n linkExtendable?: boolean;\n injectStore?: boolean | string;\n route?: string;\n}\n"],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "lcd-router-webpack-plugin",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "lib/index.js",
6
+ "keywords": [],
7
+ "author": "",
8
+ "license": "ISC",
9
+ "dependencies": {
10
+ "@babel/helper-plugin-utils": "7.19.0",
11
+ "chokidar": "3.5.1",
12
+ "deepmerge": "4.2.2",
13
+ "enhanced-resolve": "5.9.3",
14
+ "fs-extra": "10.0.0",
15
+ "globby": "11.1.0",
16
+ "string-format": "^2.0.0",
17
+ "sync-fetch": "0.5.2"
18
+ },
19
+ "devDependencies": {
20
+ "@babel/core": "^7.23.0"
21
+ },
22
+ "peerDependencies": {
23
+ "webpack": ">=5"
24
+ },
25
+ "scripts": {
26
+ "build": "lecp",
27
+ "test": "echo \"Error: no test specified\" && exit 1"
28
+ }
29
+ }
@@ -0,0 +1,46 @@
1
+ import { types } from '@babel/core';
2
+ export class Generator<T> {
3
+ protected cache: Map<string, T>;
4
+ constructor() {
5
+ this.cache = new Map<string, T>();
6
+ }
7
+
8
+ set(name: string, value: T) {
9
+ this.cache.set(name, value);
10
+ }
11
+
12
+ protected identifierString(arg: string) {
13
+ return arg.replace(/(\/|-)/g, '_');
14
+ }
15
+
16
+ protected importDefaultDeclaration(name: string, source: string) {
17
+ return types.importDeclaration(
18
+ [types.importDefaultSpecifier(types.identifier(this.identifierString(name)))],
19
+ types.stringLiteral(source),
20
+ );
21
+ }
22
+
23
+ /**
24
+ * 创建一条 import 语句, 例如:
25
+ * importNamedDeclaration(['a', 'b'], 'xxx/c') 会生成 import {a, b} from 'xxx/c'
26
+ *
27
+ * 也支持 alias, 例如:
28
+ * importNamedDeclaration([['a', 'aliasName'], 'b'], 'xxx/c') 会生成 import {a as aliasName, b} from 'xxx/c'
29
+ */
30
+ protected importNamedDeclaration(
31
+ names: Array<string | [name: string, alias: string]>,
32
+ source: string,
33
+ ) {
34
+ return types.importDeclaration(
35
+ names.map((x) =>
36
+ typeof x === 'string'
37
+ ? types.importSpecifier(types.identifier(x), types.identifier(x))
38
+ : types.importSpecifier(
39
+ types.identifier(this.identifierString(x[1])),
40
+ types.identifier(x[0]),
41
+ ),
42
+ ),
43
+ types.stringLiteral(source),
44
+ );
45
+ }
46
+ }
@@ -0,0 +1,90 @@
1
+ import fs from 'fs';
2
+ import babelCore, { transformSync, types } from '@babel/core';
3
+ import type { MeJSON } from './utils';
4
+ import { Generator } from './Generator';
5
+
6
+ interface GetReducersBabelPluginItem {
7
+ meJSON: MeJSON;
8
+ namespace: string;
9
+ isExternal: boolean;
10
+ meJSONPath: string;
11
+ }
12
+ export class ReducersGenerator extends Generator<GetReducersBabelPluginItem> {
13
+ constructor() {
14
+ super();
15
+ }
16
+
17
+ // TODO: 这里类型 加了个 any, 不太希望写else, 所以导致的问题,ts类型推导似乎不合理
18
+ objectPropertyValue(type: 'call' | 'string' | 'identifier', value: string): any {
19
+ if (type === 'call') {
20
+ return types.callExpression(types.identifier('decorate'), [
21
+ types.identifier(this.identifierString(value)),
22
+ types.stringLiteral(value),
23
+ ]);
24
+ }
25
+ if (type === 'string') {
26
+ return types.stringLiteral('');
27
+ }
28
+ if (type === 'identifier') {
29
+ return types.identifier(this.identifierString(value));
30
+ }
31
+ }
32
+
33
+ writeCodeToFile(absPath: string) {
34
+ const items = Array.from(this.cache.values());
35
+ const code = transformSync('', {
36
+ plugins: [
37
+ () => {
38
+ return {
39
+ name: 'babel-plugin-replace-react-redux',
40
+ visitor: {
41
+ Program: {
42
+ enter: (path) => {
43
+ const codes: (
44
+ | babelCore.types.ImportDeclaration
45
+ | babelCore.types.ExportDefaultDeclaration
46
+ )[] = [
47
+ this.importDefaultDeclaration(
48
+ 'decorate',
49
+ 'rrc-loader-helper/lib/reducer-decorate',
50
+ ),
51
+ ];
52
+ items
53
+ .filter((x) => x.meJSON.sync || x.isExternal)
54
+ .map((x) =>
55
+ this.importNamedDeclaration([['reducer', x.namespace]], x.meJSONPath),
56
+ )
57
+ .forEach((x) => codes.push(x));
58
+
59
+ const properties = items.map((x) => {
60
+ if (x.isExternal || x.meJSON.sync) {
61
+ if (x.meJSON.mobx) {
62
+ return types.objectProperty(
63
+ types.stringLiteral(x.namespace),
64
+ this.objectPropertyValue('call', x.namespace),
65
+ );
66
+ }
67
+ return types.objectProperty(
68
+ types.stringLiteral(x.namespace),
69
+ this.objectPropertyValue('identifier', x.namespace),
70
+ );
71
+ }
72
+ return types.objectProperty(
73
+ types.stringLiteral(x.namespace),
74
+ this.objectPropertyValue('string', x.namespace),
75
+ );
76
+ });
77
+
78
+ codes.push(types.exportDefaultDeclaration(types.objectExpression(properties)));
79
+
80
+ path.pushContainer('body', codes);
81
+ },
82
+ },
83
+ },
84
+ };
85
+ },
86
+ ],
87
+ });
88
+ fs.writeFileSync(absPath, code?.code!, { encoding: 'utf8' });
89
+ }
90
+ }
@@ -0,0 +1,295 @@
1
+ import fs from 'fs';
2
+ import { transformSync, types } from '@babel/core';
3
+ import type { Options, MeJSON, PathService } from './utils';
4
+ import { Generator } from './Generator';
5
+
6
+ interface Item {
7
+ meJSONPath: string;
8
+ meJSON: MeJSON;
9
+ isExternal: boolean;
10
+ path: string;
11
+ page: string;
12
+ exact: boolean;
13
+ }
14
+
15
+ interface TransformItem extends Item, MeJSON {}
16
+
17
+ export class RoutersGenerator extends Generator<Item> {
18
+ config: Options;
19
+ pathService: PathService;
20
+
21
+ constructor(config: Options, pathService: PathService) {
22
+ super();
23
+ this.config = config;
24
+ this.pathService = pathService;
25
+ }
26
+
27
+ private errorComponentVariableDeclaration() {
28
+ return types.variableDeclaration('const', [
29
+ types.variableDeclarator(
30
+ types.identifier('ErrorComponent'),
31
+ types.arrowFunctionExpression(
32
+ [types.identifier('props')],
33
+ types.blockStatement([
34
+ types.returnStatement(
35
+ types.jsxElement(
36
+ types.jsxOpeningElement(types.jsxIdentifier('div'), [], false),
37
+ types.jsxClosingElement(types.jsxIdentifier('div')),
38
+ [
39
+ types.jsxElement(
40
+ types.jsxOpeningElement(types.jsxIdentifier('h1'), [], false),
41
+ types.jsxClosingElement(types.jsxIdentifier('h1')),
42
+ [types.jsxText('页面渲染发生异常')],
43
+ ),
44
+ types.jsxElement(
45
+ types.jsxOpeningElement(types.jsxIdentifier('span'), [], false),
46
+ types.jsxClosingElement(types.jsxIdentifier('span')),
47
+ [
48
+ types.jsxExpressionContainer(
49
+ types.optionalMemberExpression(
50
+ types.memberExpression(
51
+ types.identifier('props'),
52
+ types.identifier('errorStack'),
53
+ ),
54
+ types.identifier('message'),
55
+ false,
56
+ true,
57
+ ),
58
+ ),
59
+ ],
60
+ ),
61
+ ],
62
+ false,
63
+ ),
64
+ ),
65
+ ]),
66
+ ),
67
+ ),
68
+ ]);
69
+ }
70
+
71
+ private loadingVariableDeclaration() {
72
+ return types.variableDeclaration('const', [
73
+ types.variableDeclarator(
74
+ types.identifier('Loading'),
75
+ types.arrowFunctionExpression(
76
+ [],
77
+ types.blockStatement([
78
+ types.returnStatement(
79
+ types.jsxFragment(types.jsxOpeningFragment(), types.jsxClosingFragment(), []),
80
+ ),
81
+ ]),
82
+ ),
83
+ ),
84
+ ]);
85
+ }
86
+
87
+ private createDowngradeValue(meJSON: MeJSON) {
88
+ return <T extends keyof MeJSON>(name: T, defaultValue: MeJSON[T]) => {
89
+ /** @ts-ignore */
90
+ return meJSON[name] ?? this.config[name] ?? defaultValue;
91
+ };
92
+ }
93
+
94
+ private transform(item: Item): TransformItem {
95
+ const downgradeValue = this.createDowngradeValue(item.meJSON);
96
+ // 路由参数
97
+ let route = item.meJSON.route ?? '';
98
+ const keepAlive = downgradeValue('keepAlive', false);
99
+ const retain = downgradeValue('retain', false);
100
+ const title = downgradeValue('title', undefined);
101
+ // FUCK keep-alive 特性的坑, 为了兼容同一个页面多次打开 动态拓展 redux 的reducer 节点
102
+ const linkExtendable = downgradeValue('linkExtendable', false);
103
+ route = linkExtendable ? `${route}/:id?` : route;
104
+ let injectStore = downgradeValue('injectStore', false);
105
+ if (injectStore === true) injectStore = 'store';
106
+ return {
107
+ ...item,
108
+ route,
109
+ keepAlive,
110
+ retain,
111
+ title,
112
+ linkExtendable,
113
+ injectStore,
114
+ };
115
+ }
116
+
117
+ private pageImportExpression(item: TransformItem) {
118
+ const returnObjectProperties = [
119
+ types.objectProperty(types.identifier('module'), types.identifier('module')),
120
+ ];
121
+ if (item.meJSON.sync) {
122
+ returnObjectProperties.push(
123
+ types.objectProperty(types.identifier('sync'), types.booleanLiteral(true)),
124
+ );
125
+ } else {
126
+ returnObjectProperties.push(
127
+ types.objectProperty(types.identifier('sync'), types.booleanLiteral(false)),
128
+ types.objectProperty(types.identifier('reducers'), types.identifier('reducers')),
129
+ );
130
+ }
131
+ const sourceAst = types.stringLiteral(item.meJSONPath);
132
+
133
+ if (this.config.enablePreFetch) {
134
+ types.addComment(sourceAst, 'leading', 'webpackPrefetch: true');
135
+ }
136
+
137
+ return types.callExpression(
138
+ types.memberExpression(
139
+ types.callExpression(types.import(), [sourceAst]),
140
+ types.identifier('then'),
141
+ ),
142
+ [
143
+ types.arrowFunctionExpression(
144
+ [types.identifier('module')],
145
+ types.blockStatement([
146
+ // TODO
147
+ types.returnStatement(types.objectExpression(returnObjectProperties)),
148
+ ]),
149
+ ),
150
+ ],
151
+ );
152
+ }
153
+
154
+ private loadableCallExpression(item: TransformItem) {
155
+ return types.callExpression(types.identifier('Loadable'), [
156
+ types.objectExpression([
157
+ types.objectProperty(
158
+ types.identifier('title'),
159
+ item.title ? types.stringLiteral(item.title) : types.nullLiteral(),
160
+ ),
161
+ types.objectProperty(types.identifier('keepAlive'), types.booleanLiteral(item.keepAlive!)),
162
+ types.objectProperty(types.identifier('route'), types.stringLiteral(item.route!)),
163
+ types.objectProperty(types.identifier('retain'), types.booleanLiteral(item.retain!)),
164
+ types.objectProperty(
165
+ types.identifier('injectStore'),
166
+ typeof item.injectStore === 'boolean'
167
+ ? types.booleanLiteral(item.injectStore)
168
+ : types.stringLiteral(item.injectStore!),
169
+ ),
170
+ types.objectProperty(types.identifier('page'), types.stringLiteral(item.page)),
171
+ types.objectProperty(types.identifier('loading'), types.identifier('Loading')),
172
+ types.objectProperty(
173
+ types.identifier('errorComponent'),
174
+ types.identifier('ErrorComponent'),
175
+ ),
176
+ types.objectProperty(
177
+ types.identifier('metaInfo'),
178
+ types.callExpression(
179
+ types.memberExpression(types.identifier('JSON'), types.identifier('parse')),
180
+ [types.stringLiteral(JSON.stringify(item.meJSON))],
181
+ ),
182
+ ),
183
+ types.objectProperty(
184
+ types.identifier('loader'),
185
+ types.arrowFunctionExpression([], this.pageImportExpression(item)),
186
+ ),
187
+ ]),
188
+ ]);
189
+ }
190
+
191
+ writeCodeToFile(absPath: string) {
192
+ const items = Array.from(this.cache.values());
193
+ const code = transformSync('', {
194
+ plugins: [
195
+ () => {
196
+ return {
197
+ name: 'babel-plugin-replace-react-redux',
198
+ visitor: {
199
+ Program: {
200
+ enter: (path) => {
201
+ const ast: (
202
+ | types.ImportDeclaration
203
+ | types.VariableDeclaration
204
+ | types.ExportDefaultDeclaration
205
+ | types.ExpressionStatement
206
+ )[] = [
207
+ this.importNamedDeclaration([], 'rrc-loader-helper/lib/fake-react'),
208
+ this.importDefaultDeclaration('Loadable', 'rrc-loader-helper/lib/loadable'),
209
+ this.importDefaultDeclaration('React', 'react'),
210
+ ];
211
+
212
+ /** 如果用户没有指定loading组件,那么自动生成一个空 loading */
213
+ if (this.config.loading) {
214
+ ast.push(this.importDefaultDeclaration('Loading', this.config.loading));
215
+ } else {
216
+ ast.push(this.loadingVariableDeclaration());
217
+ }
218
+
219
+ if (this.config.errorComponent) {
220
+ ast.push(
221
+ this.importDefaultDeclaration('ErrorComponent', this.config.errorComponent),
222
+ );
223
+ } else {
224
+ ast.push(this.errorComponentVariableDeclaration());
225
+ }
226
+
227
+ if (this.config.theme?.hash) {
228
+ ast.push(
229
+ this.importNamedDeclaration(
230
+ ['toGlobalTheme'],
231
+ this.pathService.getThemeJSCacheFileAbs(this.config.theme?.hash),
232
+ ),
233
+ );
234
+ ast.push(
235
+ types.expressionStatement(
236
+ types.optionalCallExpression(types.identifier('toGlobalTheme'), [], true),
237
+ ),
238
+ );
239
+ }
240
+
241
+ const _ = items
242
+ .filter((x) => !x.isExternal)
243
+ .map((x) => this.transform(x))
244
+ .map((x) => {
245
+ const path =
246
+ this.config.dangerousRoute || !x.exact
247
+ ? `/${x.path}${x.route}`
248
+ : `/${x.path}`;
249
+ return types.objectExpression([
250
+ types.objectProperty(
251
+ types.identifier('exact'),
252
+ types.booleanLiteral(x.exact),
253
+ ),
254
+ types.objectProperty(
255
+ types.identifier('keepAlive'),
256
+ types.booleanLiteral(x.keepAlive!),
257
+ ),
258
+ types.objectProperty(types.identifier('key'), types.stringLiteral(path)),
259
+ types.objectProperty(types.identifier('path'), types.stringLiteral(path)),
260
+ types.objectProperty(
261
+ types.identifier('component'),
262
+ this.loadableCallExpression(x),
263
+ ),
264
+ ]);
265
+ });
266
+ ast.push(
267
+ types.exportDefaultDeclaration(
268
+ types.arrowFunctionExpression(
269
+ [types.identifier('reducers')],
270
+ types.blockStatement([types.returnStatement(types.arrayExpression(_))]),
271
+ ),
272
+ ),
273
+ );
274
+
275
+ path.pushContainer('body', ast);
276
+ },
277
+ },
278
+ },
279
+ };
280
+ },
281
+ ],
282
+ });
283
+
284
+ fs.writeFileSync(
285
+ absPath,
286
+ [
287
+ JSON.stringify(`👹 pages total count is ${Array.from(this.cache.keys()).length}`),
288
+ code?.code!,
289
+ ].join(';\r\n'),
290
+ {
291
+ encoding: 'utf8',
292
+ },
293
+ );
294
+ }
295
+ }
@@ -0,0 +1,101 @@
1
+ import { Generator } from './Generator';
2
+ import type { MeJSON } from './utils';
3
+ import { transformSync, types } from '@babel/core';
4
+ import fs from 'fs';
5
+ interface Item {
6
+ namespace: string;
7
+ meJSONPath: string;
8
+ isExternal: boolean;
9
+ meJSON: MeJSON;
10
+ }
11
+ export class SagasGenerator extends Generator<Item> {
12
+ constructor() {
13
+ super();
14
+ }
15
+
16
+ private setCtxExpressionStatement(namespace: string) {
17
+ return types.expressionStatement(
18
+ types.yieldExpression(
19
+ types.callExpression(types.identifier('setCtx'), [
20
+ types.objectExpression([
21
+ types.objectProperty(types.identifier('mobxStyle'), types.booleanLiteral(true)),
22
+ types.objectProperty(types.identifier('page'), types.stringLiteral(namespace)),
23
+ types.objectProperty(types.identifier('url'), types.stringLiteral('')),
24
+ ]),
25
+ ]),
26
+ ),
27
+ );
28
+ }
29
+
30
+ private callExpressionStatement(namespace: string) {
31
+ return types.expressionStatement(
32
+ types.yieldExpression(
33
+ types.callExpression(types.identifier('call'), [
34
+ types.memberExpression(
35
+ types.identifier(this.identifierString(namespace)),
36
+ types.identifier('saga'),
37
+ ),
38
+ ]),
39
+ ),
40
+ );
41
+ }
42
+
43
+ writeCodeToFile(absPath: string) {
44
+ const items = Array.from(this.cache.values());
45
+ const code = transformSync('', {
46
+ plugins: [
47
+ () => {
48
+ return {
49
+ visitor: {
50
+ Program: {
51
+ enter: (path) => {
52
+ const ast: (types.ImportDeclaration | types.ExportDefaultDeclaration)[] = [
53
+ this.importNamedDeclaration(['setCtx'], 'rrc-loader-helper/lib/retain'),
54
+ this.importNamedDeclaration(
55
+ ['call'],
56
+ 'rrc-loader-helper/lib/sagas/redux-saga/effects',
57
+ ),
58
+ ];
59
+
60
+ items
61
+ .filter((x) => x.meJSON.sync || x.isExternal)
62
+ .forEach((x) =>
63
+ // 如果是mobx, 取 reducer, 否则取 saga
64
+ ast.push(
65
+ this.importNamedDeclaration(
66
+ [[x.meJSON.mobx ? 'reducer' : 'saga', x.namespace]],
67
+ x.meJSONPath,
68
+ ),
69
+ ),
70
+ );
71
+
72
+ const arrayItem = items.map((x) => {
73
+ if (!x.isExternal && !x.meJSON.sync) {
74
+ return types.stringLiteral('');
75
+ }
76
+ if (x.meJSON.mobx) {
77
+ return types.functionExpression(
78
+ null,
79
+ [],
80
+ types.blockStatement([
81
+ this.setCtxExpressionStatement(x.namespace),
82
+ this.callExpressionStatement(x.namespace),
83
+ ]),
84
+ true,
85
+ false,
86
+ );
87
+ }
88
+ return types.identifier(this.identifierString(x.namespace));
89
+ });
90
+ ast.push(types.exportDefaultDeclaration(types.arrayExpression(arrayItem)));
91
+ path.pushContainer('body', ast);
92
+ },
93
+ },
94
+ },
95
+ };
96
+ },
97
+ ],
98
+ });
99
+ fs.writeFileSync(absPath, code?.code!, { encoding: 'utf8' });
100
+ }
101
+ }