@4399ywkf/core 4.0.85 → 5.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.
- package/dist/cli/index.js +2180 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config/index.d.ts +37 -0
- package/dist/config/index.js +143 -0
- package/dist/config/index.js.map +1 -0
- package/dist/index.d.ts +496 -6
- package/dist/index.js +3671 -49
- package/dist/index.js.map +1 -0
- package/dist/plugin/index.d.ts +110 -0
- package/dist/plugin/index.js +277 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/router/index.d.ts +126 -0
- package/dist/router/index.js +390 -0
- package/dist/router/index.js.map +1 -0
- package/dist/rspack/index.d.ts +29 -0
- package/dist/rspack/index.js +1574 -0
- package/dist/rspack/index.js.map +1 -0
- package/dist/runtime/index.d.ts +120 -0
- package/dist/runtime/index.js +406 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/schema-BuqmN_ra.d.ts +390 -0
- package/dist/types-BZV_2QtD.d.ts +97 -0
- package/package.json +88 -37
- package/README.md +0 -3
- package/compiled/dotenv/LICENSE +0 -23
- package/compiled/dotenv/index.js +0 -1
- package/compiled/dotenv/lib/main.d.ts +0 -73
- package/compiled/dotenv/package.json +0 -1
- package/compiled/dotenv/types/index.d.ts +0 -59
- package/compiled/dotenv-expand/LICENSE +0 -24
- package/compiled/dotenv-expand/index.js +0 -1
- package/compiled/dotenv-expand/lib/main.d.ts +0 -29
- package/compiled/dotenv-expand/package.json +0 -1
- package/compiled/just-diff/LICENSE +0 -21
- package/compiled/just-diff/index.d.ts +0 -20
- package/compiled/just-diff/index.js +0 -1
- package/compiled/just-diff/package.json +0 -1
- package/dist/config/config.d.ts +0 -62
- package/dist/config/config.js +0 -240
- package/dist/config/utils.d.ts +0 -8
- package/dist/config/utils.js +0 -40
- package/dist/constants.d.ts +0 -9
- package/dist/constants.js +0 -45
- package/dist/route/defineRoutes.d.ts +0 -1
- package/dist/route/defineRoutes.js +0 -61
- package/dist/route/route.d.ts +0 -3
- package/dist/route/route.js +0 -27
- package/dist/route/routeUtils.d.ts +0 -8
- package/dist/route/routeUtils.js +0 -46
- package/dist/route/routesConfig.d.ts +0 -6
- package/dist/route/routesConfig.js +0 -125
- package/dist/route/routesConvention.d.ts +0 -5
- package/dist/route/routesConvention.js +0 -88
- package/dist/route/utils.d.ts +0 -8
- package/dist/route/utils.js +0 -59
- package/dist/service/command.d.ts +0 -30
- package/dist/service/command.js +0 -39
- package/dist/service/env.d.ts +0 -4
- package/dist/service/env.js +0 -47
- package/dist/service/generatePlugin.d.ts +0 -4
- package/dist/service/generatePlugin.js +0 -102
- package/dist/service/generator.d.ts +0 -71
- package/dist/service/generator.js +0 -40
- package/dist/service/hook.d.ts +0 -16
- package/dist/service/hook.js +0 -52
- package/dist/service/path.d.ts +0 -15
- package/dist/service/path.js +0 -55
- package/dist/service/plugin.d.ts +0 -61
- package/dist/service/plugin.js +0 -174
- package/dist/service/pluginAPI.d.ts +0 -49
- package/dist/service/pluginAPI.js +0 -233
- package/dist/service/service.d.ts +0 -147
- package/dist/service/service.js +0 -544
- package/dist/service/servicePlugin.d.ts +0 -3
- package/dist/service/servicePlugin.js +0 -37
- package/dist/service/telemetry.d.ts +0 -32
- package/dist/service/telemetry.js +0 -127
- package/dist/service/utils.d.ts +0 -2
- package/dist/service/utils.js +0 -36
- package/dist/types.d.ts +0 -116
- package/dist/types.js +0 -77
package/dist/index.js
CHANGED
|
@@ -1,51 +1,3673 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
1
|
+
// src/config/schema.ts
|
|
2
|
+
function defineConfig(config) {
|
|
3
|
+
return config;
|
|
4
|
+
}
|
|
5
|
+
var defaultConfig = {
|
|
6
|
+
appName: "app",
|
|
7
|
+
appCName: "\u5E94\u7528",
|
|
8
|
+
dev: {
|
|
9
|
+
port: 3e3,
|
|
10
|
+
host: "localhost",
|
|
11
|
+
proxy: {},
|
|
12
|
+
https: false
|
|
13
|
+
},
|
|
14
|
+
output: {
|
|
15
|
+
path: "dist",
|
|
16
|
+
publicPath: "/",
|
|
17
|
+
clean: true
|
|
18
|
+
},
|
|
19
|
+
html: {
|
|
20
|
+
title: "\u5E94\u7528",
|
|
21
|
+
template: "public/index.html",
|
|
22
|
+
favicon: "public/favicon.ico",
|
|
23
|
+
mountRoot: "root"
|
|
24
|
+
},
|
|
25
|
+
style: {
|
|
26
|
+
cssModules: true,
|
|
27
|
+
less: { enabled: true, lessOptions: { javascriptEnabled: true } },
|
|
28
|
+
sass: { enabled: true, sassOptions: {} },
|
|
29
|
+
tailwindcss: true
|
|
30
|
+
},
|
|
31
|
+
router: {
|
|
32
|
+
basename: "/",
|
|
33
|
+
conventional: false,
|
|
34
|
+
pagesDir: "src/pages",
|
|
35
|
+
exclude: [
|
|
36
|
+
/\/components?\//,
|
|
37
|
+
/\/models\//,
|
|
38
|
+
/\/utils?\//,
|
|
39
|
+
/^_/,
|
|
40
|
+
/\.d\.ts$/,
|
|
41
|
+
/\.(test|spec|e2e)\.(ts|tsx|js|jsx)$/
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
microFrontend: {
|
|
45
|
+
enabled: false,
|
|
46
|
+
name: "app",
|
|
47
|
+
framework: "qiankun"
|
|
48
|
+
},
|
|
49
|
+
performance: {
|
|
50
|
+
rsdoctor: false,
|
|
51
|
+
splitChunks: true,
|
|
52
|
+
dropConsole: false
|
|
53
|
+
},
|
|
54
|
+
tools: {},
|
|
55
|
+
env: {
|
|
56
|
+
publicEnvFile: "config/env/.env.public",
|
|
57
|
+
envDir: "config/env"
|
|
58
|
+
},
|
|
59
|
+
alias: {},
|
|
60
|
+
plugins: []
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// src/config/loader.ts
|
|
64
|
+
import { existsSync } from "fs";
|
|
65
|
+
import { resolve, extname } from "path";
|
|
66
|
+
import { pathToFileURL } from "url";
|
|
67
|
+
import deepmerge from "deepmerge";
|
|
68
|
+
var CONFIG_FILES = [
|
|
69
|
+
"ywkf.config.ts",
|
|
70
|
+
"ywkf.config.mts",
|
|
71
|
+
"ywkf.config.js",
|
|
72
|
+
"ywkf.config.mjs"
|
|
73
|
+
];
|
|
74
|
+
function findConfigFile(cwd) {
|
|
75
|
+
for (const file of CONFIG_FILES) {
|
|
76
|
+
const configPath = resolve(cwd, file);
|
|
77
|
+
if (existsSync(configPath)) {
|
|
78
|
+
return configPath;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
async function loadTsConfig(configPath) {
|
|
84
|
+
const jiti = (await import("jiti")).default;
|
|
85
|
+
const loader = jiti(configPath, {
|
|
86
|
+
interopDefault: true
|
|
87
|
+
});
|
|
88
|
+
const config = loader(configPath);
|
|
89
|
+
return config.default || config;
|
|
90
|
+
}
|
|
91
|
+
async function loadJsConfig(configPath) {
|
|
92
|
+
const fileUrl = pathToFileURL(configPath).href;
|
|
93
|
+
const module = await import(fileUrl);
|
|
94
|
+
return module.default || module;
|
|
95
|
+
}
|
|
96
|
+
async function loadConfigFile(configPath) {
|
|
97
|
+
const ext = extname(configPath);
|
|
98
|
+
if (ext === ".ts" || ext === ".mts") {
|
|
99
|
+
return loadTsConfig(configPath);
|
|
100
|
+
}
|
|
101
|
+
return loadJsConfig(configPath);
|
|
102
|
+
}
|
|
103
|
+
function mergeConfig(userConfig, baseConfig = defaultConfig) {
|
|
104
|
+
return deepmerge(baseConfig, userConfig, {
|
|
105
|
+
arrayMerge: (_, sourceArray) => sourceArray
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
async function resolveConfig(cwd) {
|
|
109
|
+
const configPath = findConfigFile(cwd);
|
|
110
|
+
if (!configPath) {
|
|
111
|
+
console.warn(
|
|
112
|
+
"\u26A0\uFE0F \u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6 (ywkf.config.ts)\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E"
|
|
113
|
+
);
|
|
114
|
+
return {
|
|
115
|
+
config: defaultConfig,
|
|
116
|
+
configPath: null
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
const userConfig = await loadConfigFile(configPath);
|
|
121
|
+
const config = mergeConfig(userConfig);
|
|
122
|
+
return { config, configPath };
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error("\u274C \u914D\u7F6E\u6587\u4EF6\u52A0\u8F7D\u5931\u8D25:", error);
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function createPathResolver(cwd) {
|
|
129
|
+
return {
|
|
130
|
+
resolveApp: (relativePath) => resolve(cwd, relativePath),
|
|
131
|
+
cwd
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/rspack/index.ts
|
|
136
|
+
import { RsdoctorRspackPlugin } from "@rsdoctor/rspack-plugin";
|
|
137
|
+
|
|
138
|
+
// src/rspack/dev.ts
|
|
139
|
+
import { merge } from "webpack-merge";
|
|
140
|
+
import { createRequire as createRequire2 } from "module";
|
|
141
|
+
|
|
142
|
+
// src/rspack/base.ts
|
|
143
|
+
import { rspack } from "@rspack/core";
|
|
144
|
+
import { createRequire } from "module";
|
|
145
|
+
import { fileURLToPath } from "url";
|
|
146
|
+
import { dirname, join as join5 } from "path";
|
|
147
|
+
|
|
148
|
+
// src/generator/plugin.ts
|
|
149
|
+
import { watch, existsSync as existsSync5 } from "fs";
|
|
150
|
+
import { join as join4 } from "path";
|
|
151
|
+
|
|
152
|
+
// src/generator/generator.ts
|
|
153
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync2 } from "fs";
|
|
154
|
+
import { join as join3 } from "path";
|
|
155
|
+
|
|
156
|
+
// src/router/generator.ts
|
|
157
|
+
import { existsSync as existsSync2, readdirSync, statSync, mkdirSync, writeFileSync } from "fs";
|
|
158
|
+
import { join, relative } from "path";
|
|
159
|
+
var EXCLUDED_DIRS = /* @__PURE__ */ new Set([
|
|
160
|
+
"components",
|
|
161
|
+
"hooks",
|
|
162
|
+
"utils",
|
|
163
|
+
"services",
|
|
164
|
+
"models",
|
|
165
|
+
"assets",
|
|
166
|
+
"types",
|
|
167
|
+
"constants",
|
|
168
|
+
"styles"
|
|
169
|
+
]);
|
|
170
|
+
var CONVENTION_FILES = {
|
|
171
|
+
page: /^page\.(tsx?|jsx?)$/,
|
|
172
|
+
layout: /^layout\.(tsx?|jsx?)$/,
|
|
173
|
+
error: /^error\.(tsx?|jsx?)$/,
|
|
174
|
+
loading: /^loading\.(tsx?|jsx?)$/,
|
|
175
|
+
catchAll: /^\$\.(tsx?|jsx?)$/
|
|
176
|
+
};
|
|
177
|
+
var ConventionalRouteGenerator = class {
|
|
178
|
+
options;
|
|
179
|
+
constructor(options) {
|
|
180
|
+
this.options = options;
|
|
181
|
+
}
|
|
182
|
+
generate() {
|
|
183
|
+
const { pagesDir } = this.options;
|
|
184
|
+
if (!existsSync2(pagesDir)) {
|
|
185
|
+
console.warn(`[ywkf] \u9875\u9762\u76EE\u5F55\u4E0D\u5B58\u5728: ${pagesDir}`);
|
|
186
|
+
return [];
|
|
187
|
+
}
|
|
188
|
+
return this.scanDirectory(pagesDir, "/");
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* 扫描目录,生成路由树
|
|
192
|
+
*/
|
|
193
|
+
scanDirectory(dir, routePath) {
|
|
194
|
+
const entries = readdirSync(dir);
|
|
195
|
+
const layoutFile = entries.find((e) => CONVENTION_FILES.layout.test(e));
|
|
196
|
+
const pageFile = entries.find((e) => CONVENTION_FILES.page.test(e));
|
|
197
|
+
const errorFile = entries.find((e) => CONVENTION_FILES.error.test(e));
|
|
198
|
+
const loadingFile = entries.find((e) => CONVENTION_FILES.loading.test(e));
|
|
199
|
+
const catchAllFile = entries.find((e) => CONVENTION_FILES.catchAll.test(e));
|
|
200
|
+
const subDirs = entries.filter((e) => {
|
|
201
|
+
if (e.startsWith(".")) return false;
|
|
202
|
+
if (EXCLUDED_DIRS.has(e)) return false;
|
|
203
|
+
return statSync(join(dir, e)).isDirectory();
|
|
204
|
+
});
|
|
205
|
+
const childRoutes = [];
|
|
206
|
+
for (const subDir of subDirs) {
|
|
207
|
+
const subDirPath = join(dir, subDir);
|
|
208
|
+
if (subDir.startsWith("__")) {
|
|
209
|
+
const pathlessRoutes = this.scanDirectory(subDirPath, routePath);
|
|
210
|
+
const pathlessLayout = readdirSync(subDirPath).find(
|
|
211
|
+
(e) => CONVENTION_FILES.layout.test(e)
|
|
212
|
+
);
|
|
213
|
+
if (pathlessLayout) {
|
|
214
|
+
const pathlessChildren = this.scanDirectory(subDirPath, routePath);
|
|
215
|
+
childRoutes.push({
|
|
216
|
+
path: routePath,
|
|
217
|
+
name: this.generateRouteName(subDir.slice(2)),
|
|
218
|
+
layoutFile: relative(this.options.pagesDir, join(subDirPath, pathlessLayout)),
|
|
219
|
+
isLayout: true,
|
|
220
|
+
pathless: true,
|
|
221
|
+
children: pathlessChildren.filter(
|
|
222
|
+
(r) => r.layoutFile !== relative(this.options.pagesDir, join(subDirPath, pathlessLayout))
|
|
223
|
+
)
|
|
224
|
+
});
|
|
225
|
+
} else {
|
|
226
|
+
childRoutes.push(...pathlessRoutes);
|
|
227
|
+
}
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
const subRoutePath = this.resolveRoutePath(subDir, routePath);
|
|
231
|
+
childRoutes.push(...this.scanDirectory(subDirPath, subRoutePath));
|
|
232
|
+
}
|
|
233
|
+
const routeName = this.generateRouteName(routePath);
|
|
234
|
+
const relPath = (file) => relative(this.options.pagesDir, join(dir, file));
|
|
235
|
+
if (layoutFile) {
|
|
236
|
+
const layoutChildren = [];
|
|
237
|
+
if (pageFile) {
|
|
238
|
+
layoutChildren.push({
|
|
239
|
+
path: routePath,
|
|
240
|
+
file: relPath(pageFile),
|
|
241
|
+
name: routeName + "Index",
|
|
242
|
+
index: true
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
if (catchAllFile) {
|
|
246
|
+
layoutChildren.push({
|
|
247
|
+
path: "*",
|
|
248
|
+
file: relPath(catchAllFile),
|
|
249
|
+
name: routeName + "CatchAll"
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
layoutChildren.push(...childRoutes);
|
|
253
|
+
return [
|
|
254
|
+
{
|
|
255
|
+
path: routePath,
|
|
256
|
+
layoutFile: relPath(layoutFile),
|
|
257
|
+
errorFile: errorFile ? relPath(errorFile) : void 0,
|
|
258
|
+
loadingFile: loadingFile ? relPath(loadingFile) : void 0,
|
|
259
|
+
name: routeName,
|
|
260
|
+
isLayout: true,
|
|
261
|
+
children: layoutChildren
|
|
262
|
+
}
|
|
263
|
+
];
|
|
264
|
+
}
|
|
265
|
+
const result = [];
|
|
266
|
+
if (pageFile) {
|
|
267
|
+
result.push({
|
|
268
|
+
path: routePath,
|
|
269
|
+
file: relPath(pageFile),
|
|
270
|
+
errorFile: errorFile ? relPath(errorFile) : void 0,
|
|
271
|
+
name: routeName
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
if (catchAllFile) {
|
|
275
|
+
result.push({
|
|
276
|
+
path: "*",
|
|
277
|
+
file: relPath(catchAllFile),
|
|
278
|
+
name: routeName + "CatchAll"
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
result.push(...childRoutes);
|
|
282
|
+
return result;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* 解析目录名到路由路径
|
|
286
|
+
*
|
|
287
|
+
* - [id] → :id
|
|
288
|
+
* - [id$] → :id?
|
|
289
|
+
* - [...slug] → *
|
|
290
|
+
* - user.profile → user/profile
|
|
291
|
+
*/
|
|
292
|
+
resolveRoutePath(dirName, parentPath) {
|
|
293
|
+
let segment;
|
|
294
|
+
if (dirName.match(/^\[\.\.\.(.+)\]$/)) {
|
|
295
|
+
segment = "*";
|
|
296
|
+
} else if (dirName.match(/^\[(.+)\$\]$/)) {
|
|
297
|
+
const param = dirName.slice(1, -2);
|
|
298
|
+
segment = `:${param}?`;
|
|
299
|
+
} else if (dirName.match(/^\[(.+)\]$/)) {
|
|
300
|
+
const param = dirName.slice(1, -1);
|
|
301
|
+
segment = `:${param}`;
|
|
302
|
+
} else if (dirName.includes(".")) {
|
|
303
|
+
segment = dirName.replace(/\./g, "/");
|
|
304
|
+
} else {
|
|
305
|
+
segment = dirName;
|
|
306
|
+
}
|
|
307
|
+
return parentPath === "/" ? `/${segment}` : `${parentPath}/${segment}`;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* 生成有效的 JS 标识符名称
|
|
311
|
+
*/
|
|
312
|
+
generateRouteName(path) {
|
|
313
|
+
const name = path.split("/").filter(Boolean).map(
|
|
314
|
+
(s) => s.replace(/^:/, "Param").replace(/\?$/, "Optional").replace(/^\*$/, "CatchAll")
|
|
315
|
+
).map((s) => {
|
|
316
|
+
const cleaned = s.replace(/[^a-zA-Z0-9]/g, "");
|
|
317
|
+
if (!cleaned) return "";
|
|
318
|
+
return cleaned.charAt(0).toUpperCase() + cleaned.slice(1);
|
|
319
|
+
}).join("");
|
|
320
|
+
if (!name || /^\d/.test(name)) {
|
|
321
|
+
return "Page" + (name || "Root");
|
|
322
|
+
}
|
|
323
|
+
return name;
|
|
324
|
+
}
|
|
325
|
+
// ===== 代码生成 =====
|
|
326
|
+
generateCode() {
|
|
327
|
+
const routes = this.generate();
|
|
328
|
+
const lazyImports = [];
|
|
329
|
+
const errorImports = [];
|
|
330
|
+
const loadingImports = [];
|
|
331
|
+
this.collectImports(routes, lazyImports, errorImports, loadingImports);
|
|
332
|
+
return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/core \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u4FEE\u6539
|
|
333
|
+
// Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
334
|
+
|
|
335
|
+
import React, { lazy, Suspense } from "react";
|
|
336
|
+
import { createBrowserRouter, type RouteObject } from "react-router";
|
|
337
|
+
|
|
338
|
+
// \u61D2\u52A0\u8F7D\u9875\u9762\u7EC4\u4EF6
|
|
339
|
+
${lazyImports.join("\n")}
|
|
340
|
+
${errorImports.length > 0 ? "\n// \u9519\u8BEF\u8FB9\u754C\u7EC4\u4EF6\n" + errorImports.join("\n") : ""}
|
|
341
|
+
${loadingImports.length > 0 ? "\n// \u52A0\u8F7D\u72B6\u6001\u7EC4\u4EF6\n" + loadingImports.join("\n") : ""}
|
|
342
|
+
|
|
343
|
+
// \u9ED8\u8BA4\u52A0\u8F7D\u72B6\u6001
|
|
344
|
+
const DefaultLoading = () => <div style={{ padding: 24, textAlign: "center" }}>\u52A0\u8F7D\u4E2D...</div>;
|
|
345
|
+
|
|
346
|
+
// \u61D2\u52A0\u8F7D\u5305\u88C5
|
|
347
|
+
function LazyRoute({
|
|
348
|
+
Component,
|
|
349
|
+
Loading = DefaultLoading,
|
|
350
|
+
}: {
|
|
351
|
+
Component: React.LazyExoticComponent<React.ComponentType<unknown>>;
|
|
352
|
+
Loading?: React.ComponentType;
|
|
353
|
+
}) {
|
|
354
|
+
return (
|
|
355
|
+
<Suspense fallback={<Loading />}>
|
|
356
|
+
<Component />
|
|
357
|
+
</Suspense>
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// \u8DEF\u7531\u914D\u7F6E
|
|
362
|
+
export const routes: RouteObject[] = ${this.emitRouteArray(routes)};
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* \u521B\u5EFA\u8DEF\u7531\u5B9E\u4F8B
|
|
366
|
+
*/
|
|
367
|
+
export function createRouter(basename?: string) {
|
|
368
|
+
return createBrowserRouter(routes, { basename: basename || "/" });
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
export default routes;
|
|
372
|
+
`;
|
|
373
|
+
}
|
|
374
|
+
collectImports(routes, lazyImports, errorImports, loadingImports) {
|
|
375
|
+
for (const route of routes) {
|
|
376
|
+
const name = route.name;
|
|
377
|
+
const toImportPath = (f) => f.replace(/\.(tsx?|jsx?)$/, "");
|
|
378
|
+
if (route.isLayout && route.layoutFile) {
|
|
379
|
+
lazyImports.push(
|
|
380
|
+
`const ${name}Layout = lazy(() => import("@/pages/${toImportPath(route.layoutFile)}"));`
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
if (route.file) {
|
|
384
|
+
lazyImports.push(
|
|
385
|
+
`const ${name}Page = lazy(() => import("@/pages/${toImportPath(route.file)}"));`
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
if (route.errorFile) {
|
|
389
|
+
errorImports.push(
|
|
390
|
+
`const ${name}Error = lazy(() => import("@/pages/${toImportPath(route.errorFile)}"));`
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
if (route.loadingFile) {
|
|
394
|
+
loadingImports.push(
|
|
395
|
+
`const ${name}Loading = lazy(() => import("@/pages/${toImportPath(route.loadingFile)}"));`
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
if (route.children) {
|
|
399
|
+
this.collectImports(route.children, lazyImports, errorImports, loadingImports);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
emitRouteArray(routes, parentPath) {
|
|
404
|
+
const items = routes.map((r) => this.emitRouteObject(r, parentPath));
|
|
405
|
+
return `[
|
|
406
|
+
${items.join(",\n ")}
|
|
407
|
+
]`;
|
|
408
|
+
}
|
|
409
|
+
emitRouteObject(route, parentPath) {
|
|
410
|
+
const parts = [];
|
|
411
|
+
const name = route.name;
|
|
412
|
+
if (route.index) {
|
|
413
|
+
parts.push("index: true");
|
|
414
|
+
} else if (route.pathless) {
|
|
415
|
+
} else if (route.path === "*") {
|
|
416
|
+
parts.push(`path: "*"`);
|
|
417
|
+
} else if (parentPath && route.path.startsWith(parentPath) && parentPath !== "/") {
|
|
418
|
+
const rel = route.path.slice(parentPath.length + 1);
|
|
419
|
+
parts.push(`path: "${rel || ""}"`);
|
|
420
|
+
} else {
|
|
421
|
+
parts.push(`path: "${route.path}"`);
|
|
422
|
+
}
|
|
423
|
+
if (route.isLayout && route.layoutFile) {
|
|
424
|
+
const loadingProp = route.loadingFile ? ` Loading={${name}Loading}` : "";
|
|
425
|
+
parts.push(`element: <LazyRoute Component={${name}Layout}${loadingProp} />`);
|
|
426
|
+
} else if (route.file) {
|
|
427
|
+
parts.push(`element: <LazyRoute Component={${name}Page} />`);
|
|
428
|
+
}
|
|
429
|
+
if (route.errorFile) {
|
|
430
|
+
parts.push(`errorElement: <LazyRoute Component={${name}Error} />`);
|
|
431
|
+
}
|
|
432
|
+
if (route.children && route.children.length > 0) {
|
|
433
|
+
const childParent = route.pathless ? parentPath : route.path;
|
|
434
|
+
parts.push(`children: ${this.emitRouteArray(route.children, childParent)}`);
|
|
435
|
+
}
|
|
436
|
+
return `{
|
|
437
|
+
${parts.join(",\n ")}
|
|
438
|
+
}`;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* 写入路由文件
|
|
442
|
+
*/
|
|
443
|
+
write() {
|
|
444
|
+
const { outputDir } = this.options;
|
|
445
|
+
const code = this.generateCode();
|
|
446
|
+
if (!existsSync2(outputDir)) {
|
|
447
|
+
mkdirSync(outputDir, { recursive: true });
|
|
448
|
+
}
|
|
449
|
+
writeFileSync(join(outputDir, "routes.tsx"), code, "utf-8");
|
|
450
|
+
console.log(`[ywkf] \u7EA6\u5B9A\u5F0F\u8DEF\u7531\u5DF2\u751F\u6210`);
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
function generateConventionalRoutes(options) {
|
|
454
|
+
new ConventionalRouteGenerator(options).write();
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// src/generator/templates/entry.ts
|
|
458
|
+
function generateEntry(config, injections = {}) {
|
|
459
|
+
const imports = [
|
|
460
|
+
`import "@/index.css";`,
|
|
461
|
+
`import { runApp } from "./bootstrap";`,
|
|
462
|
+
...injections.imports || []
|
|
463
|
+
];
|
|
464
|
+
const topLevel = injections.topLevel || [];
|
|
465
|
+
const exports = injections.exports || [];
|
|
466
|
+
const hasPluginExports = exports.length > 0;
|
|
467
|
+
const startupCode = hasPluginExports ? `// \u72EC\u7ACB\u8FD0\u884C\u6A21\u5F0F
|
|
468
|
+
if (shouldRunIndependently !== false) {
|
|
469
|
+
runApp();
|
|
470
|
+
}` : `// \u542F\u52A8\u5E94\u7528
|
|
471
|
+
runApp();`;
|
|
472
|
+
return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/core \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u4FEE\u6539
|
|
473
|
+
// Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
474
|
+
|
|
475
|
+
${imports.join("\n")}
|
|
476
|
+
|
|
477
|
+
${topLevel.length > 0 ? topLevel.join("\n") + "\n" : ""}${exports.length > 0 ? exports.join("\n") + "\n\n" : ""}${startupCode}
|
|
478
|
+
`;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// src/generator/templates/bootstrap.ts
|
|
482
|
+
function generateBootstrap(config, injections = {}) {
|
|
483
|
+
const { appName, router } = config;
|
|
484
|
+
const routerImport = router.conventional ? `import { createRouter } from "./routes";` : `import { createRouter } from "@/routes";`;
|
|
485
|
+
const imports = [
|
|
486
|
+
`import { bootstrap, type AppConfig } from "@4399ywkf/core/runtime";`,
|
|
487
|
+
routerImport,
|
|
488
|
+
...injections.imports || []
|
|
489
|
+
];
|
|
490
|
+
const topLevel = injections.topLevel || [];
|
|
491
|
+
const exports = injections.exports || [];
|
|
492
|
+
return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/core \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u4FEE\u6539
|
|
493
|
+
// Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
494
|
+
|
|
495
|
+
${imports.join("\n")}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* \u5E94\u7528\u540D\u79F0
|
|
499
|
+
*/
|
|
500
|
+
export const APP_NAME = "${appName}";
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* \u8DEF\u7531 basename
|
|
504
|
+
*/
|
|
505
|
+
export const BASENAME = "${router.basename || "/"}";
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* \u83B7\u53D6\u7528\u6237\u81EA\u5B9A\u4E49\u914D\u7F6E\uFF08\u5982\u679C\u5B58\u5728\uFF09
|
|
509
|
+
* \u7528\u6237\u53EF\u5728 src/app.config.ts \u4E2D\u5BFC\u51FA\u914D\u7F6E\u6765\u8986\u76D6\u9ED8\u8BA4\u503C
|
|
510
|
+
*/
|
|
511
|
+
async function getUserConfig(): Promise<Partial<AppConfig>> {
|
|
512
|
+
try {
|
|
513
|
+
// webpackIgnore \u6CE8\u91CA\u8BA9\u6253\u5305\u5668\u8DF3\u8FC7\u6A21\u5757\u5B58\u5728\u6027\u68C0\u67E5
|
|
514
|
+
const userConfigModule = await import(/* webpackIgnore: true */ "@/app.config");
|
|
515
|
+
return userConfigModule.default || {};
|
|
516
|
+
} catch {
|
|
517
|
+
// \u6587\u4EF6\u4E0D\u5B58\u5728\u65F6\u8FD4\u56DE\u7A7A\u914D\u7F6E
|
|
518
|
+
return {};
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* \u521B\u5EFA\u5E94\u7528\u914D\u7F6E
|
|
524
|
+
*/
|
|
525
|
+
export function createAppConfig(userConfig: Partial<AppConfig> = {}): AppConfig {
|
|
526
|
+
const router = createRouter(BASENAME);
|
|
527
|
+
|
|
528
|
+
const defaultConfig: AppConfig = {
|
|
529
|
+
appName: APP_NAME,
|
|
530
|
+
router,
|
|
531
|
+
basename: BASENAME,
|
|
532
|
+
rootId: APP_NAME,
|
|
533
|
+
strictMode: true,
|
|
534
|
+
antd: {
|
|
535
|
+
enabled: true,
|
|
536
|
+
},
|
|
537
|
+
providers: [],
|
|
538
|
+
lifecycle: {
|
|
539
|
+
onMounted() {
|
|
540
|
+
console.log(\`[\${APP_NAME}] \u5E94\u7528\u5DF2\u6302\u8F7D\`);
|
|
541
|
+
},
|
|
542
|
+
onUnmount() {
|
|
543
|
+
console.log(\`[\${APP_NAME}] \u5E94\u7528\u5DF2\u5378\u8F7D\`);
|
|
544
|
+
},
|
|
545
|
+
onError(error) {
|
|
546
|
+
console.error(\`[\${APP_NAME}] \u5168\u5C40\u9519\u8BEF:\`, error);
|
|
547
|
+
},
|
|
548
|
+
},
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
// \u6DF1\u5EA6\u5408\u5E76\u7528\u6237\u914D\u7F6E
|
|
552
|
+
return {
|
|
553
|
+
...defaultConfig,
|
|
554
|
+
...userConfig,
|
|
555
|
+
antd: { ...defaultConfig.antd, ...userConfig.antd },
|
|
556
|
+
providers: [...(defaultConfig.providers || []), ...(userConfig.providers || [])],
|
|
557
|
+
lifecycle: { ...defaultConfig.lifecycle, ...userConfig.lifecycle },
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* \u542F\u52A8\u5E94\u7528
|
|
563
|
+
*/
|
|
564
|
+
export async function runApp(): Promise<void> {
|
|
565
|
+
const userConfig = await getUserConfig();
|
|
566
|
+
await bootstrap(createAppConfig(userConfig));
|
|
567
|
+
}
|
|
568
|
+
${topLevel.length > 0 ? "\n" + topLevel.join("\n") : ""}
|
|
569
|
+
${exports.length > 0 ? "\n" + exports.join("\n") : ""}
|
|
570
|
+
`;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// src/generator/templates/env-types.ts
|
|
574
|
+
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
575
|
+
import { join as join2 } from "path";
|
|
576
|
+
function generateEnvTypes(cwd, config) {
|
|
577
|
+
const envVars = collectEnvVars(cwd, config);
|
|
578
|
+
const envInterface = envVars.map((key) => ` readonly ${key}: string;`).join("\n");
|
|
579
|
+
return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/core \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u4FEE\u6539
|
|
580
|
+
// Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
581
|
+
|
|
582
|
+
/// <reference types="react" />
|
|
583
|
+
/// <reference types="react-dom" />
|
|
584
|
+
|
|
585
|
+
declare namespace NodeJS {
|
|
586
|
+
interface ProcessEnv {
|
|
587
|
+
${envInterface}
|
|
588
|
+
readonly NODE_ENV: "development" | "production" | "test";
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
declare module "*.svg" {
|
|
593
|
+
import * as React from "react";
|
|
594
|
+
const ReactComponent: React.FunctionComponent<
|
|
595
|
+
React.SVGProps<SVGSVGElement> & { title?: string }
|
|
596
|
+
>;
|
|
597
|
+
export default ReactComponent;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
declare module "*.png" {
|
|
601
|
+
const src: string;
|
|
602
|
+
export default src;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
declare module "*.jpg" {
|
|
606
|
+
const src: string;
|
|
607
|
+
export default src;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
declare module "*.jpeg" {
|
|
611
|
+
const src: string;
|
|
612
|
+
export default src;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
declare module "*.gif" {
|
|
616
|
+
const src: string;
|
|
617
|
+
export default src;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
declare module "*.webp" {
|
|
621
|
+
const src: string;
|
|
622
|
+
export default src;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
declare module "*.css" {
|
|
626
|
+
const classes: { readonly [key: string]: string };
|
|
627
|
+
export default classes;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
declare module "*.less" {
|
|
631
|
+
const classes: { readonly [key: string]: string };
|
|
632
|
+
export default classes;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
declare module "*.scss" {
|
|
636
|
+
const classes: { readonly [key: string]: string };
|
|
637
|
+
export default classes;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
declare module "*.md" {
|
|
641
|
+
const content: string;
|
|
642
|
+
export default content;
|
|
643
|
+
}
|
|
644
|
+
`;
|
|
645
|
+
}
|
|
646
|
+
function collectEnvVars(cwd, config) {
|
|
647
|
+
const envVars = /* @__PURE__ */ new Set();
|
|
648
|
+
const publicEnvPath = join2(cwd, config.env.publicEnvFile || "config/env/.env.public");
|
|
649
|
+
if (existsSync3(publicEnvPath)) {
|
|
650
|
+
const content = readFileSync(publicEnvPath, "utf-8");
|
|
651
|
+
parseEnvFile(content, envVars);
|
|
652
|
+
}
|
|
653
|
+
const devEnvPath = join2(cwd, config.env.envDir || "config/env", ".env.development");
|
|
654
|
+
if (existsSync3(devEnvPath)) {
|
|
655
|
+
const content = readFileSync(devEnvPath, "utf-8");
|
|
656
|
+
parseEnvFile(content, envVars);
|
|
657
|
+
}
|
|
658
|
+
const prodEnvPath = join2(cwd, config.env.envDir || "config/env", ".env.production");
|
|
659
|
+
if (existsSync3(prodEnvPath)) {
|
|
660
|
+
const content = readFileSync(prodEnvPath, "utf-8");
|
|
661
|
+
parseEnvFile(content, envVars);
|
|
662
|
+
}
|
|
663
|
+
return Array.from(envVars).sort();
|
|
664
|
+
}
|
|
665
|
+
function parseEnvFile(content, envVars) {
|
|
666
|
+
const lines = content.split("\n");
|
|
667
|
+
for (const line of lines) {
|
|
668
|
+
const trimmed = line.trim();
|
|
669
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
const match = trimmed.match(/^([A-Z_][A-Z0-9_]*)\s*=/);
|
|
673
|
+
if (match) {
|
|
674
|
+
envVars.add(match[1]);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// src/generator/templates/route-types.ts
|
|
680
|
+
function generateRouteTypes() {
|
|
681
|
+
return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/core \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u4FEE\u6539
|
|
682
|
+
// Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
683
|
+
|
|
684
|
+
import type { RouteObject } from "react-router";
|
|
685
|
+
|
|
686
|
+
declare module "@ywkf/routes" {
|
|
687
|
+
export const routes: RouteObject[];
|
|
688
|
+
export function createRouter(basename?: string): ReturnType<typeof import("react-router").createBrowserRouter>;
|
|
689
|
+
export default routes;
|
|
690
|
+
}
|
|
691
|
+
`;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// src/generator/generator.ts
|
|
695
|
+
var YwkfGenerator = class {
|
|
696
|
+
context;
|
|
697
|
+
pluginHooks;
|
|
698
|
+
lastGeneratedContent = /* @__PURE__ */ new Map();
|
|
699
|
+
constructor(context, pluginHooks = []) {
|
|
700
|
+
this.context = {
|
|
701
|
+
...context,
|
|
702
|
+
outputDir: context.outputDir || join3(context.cwd, ".ywkf")
|
|
703
|
+
};
|
|
704
|
+
this.pluginHooks = pluginHooks;
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* 生成所有文件
|
|
708
|
+
*/
|
|
709
|
+
async generate() {
|
|
710
|
+
const { outputDir } = this.context;
|
|
711
|
+
this.ensureDir(outputDir);
|
|
712
|
+
this.ensureDir(join3(outputDir, "types"));
|
|
713
|
+
this.generateConfigSnapshot();
|
|
714
|
+
this.generateRoutes();
|
|
715
|
+
this.generateBootstrapFile();
|
|
716
|
+
this.generateEntryFile();
|
|
717
|
+
this.generateTypes();
|
|
718
|
+
await this.generatePluginFiles();
|
|
719
|
+
for (const hooks of this.pluginHooks) {
|
|
720
|
+
if (hooks.afterGenerate) {
|
|
721
|
+
await hooks.afterGenerate(this.context);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
console.log(`[ywkf] \u5DF2\u751F\u6210 .ywkf \u76EE\u5F55`);
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* 生成配置快照
|
|
728
|
+
*/
|
|
729
|
+
generateConfigSnapshot() {
|
|
730
|
+
const { outputDir, config } = this.context;
|
|
731
|
+
const configPath = join3(outputDir, "config.json");
|
|
732
|
+
const serializableConfig = JSON.parse(
|
|
733
|
+
JSON.stringify(config, (key, value) => {
|
|
734
|
+
if (typeof value === "function") {
|
|
735
|
+
return "[Function]";
|
|
736
|
+
}
|
|
737
|
+
return value;
|
|
738
|
+
})
|
|
739
|
+
);
|
|
740
|
+
this.writeFileIfChanged(
|
|
741
|
+
configPath,
|
|
742
|
+
JSON.stringify(serializableConfig, null, 2)
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* 生成约定式路由
|
|
747
|
+
*/
|
|
748
|
+
generateRoutes() {
|
|
749
|
+
const { cwd, outputDir, config } = this.context;
|
|
750
|
+
if (!config.router.conventional) {
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
const pagesDir = join3(cwd, config.router.pagesDir || "src/pages");
|
|
754
|
+
const generator = new ConventionalRouteGenerator({
|
|
755
|
+
pagesDir,
|
|
756
|
+
outputDir,
|
|
757
|
+
basename: config.router.basename
|
|
758
|
+
});
|
|
759
|
+
const content = generator.generateCode();
|
|
760
|
+
const routesPath = join3(outputDir, "routes.tsx");
|
|
761
|
+
this.writeFileIfChanged(routesPath, content);
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* 生成启动文件
|
|
765
|
+
*/
|
|
766
|
+
generateBootstrapFile() {
|
|
767
|
+
const { outputDir, config } = this.context;
|
|
768
|
+
const injections = this.collectInjections("bootstrap");
|
|
769
|
+
let content = generateBootstrap(config, injections);
|
|
770
|
+
for (const hooks of this.pluginHooks) {
|
|
771
|
+
if (hooks.modifyBootstrapCode) {
|
|
772
|
+
const result = hooks.modifyBootstrapCode(content, this.context);
|
|
773
|
+
if (result) {
|
|
774
|
+
content = result;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
const bootstrapPath = join3(outputDir, "bootstrap.tsx");
|
|
779
|
+
this.writeFileIfChanged(bootstrapPath, content);
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* 生成入口文件
|
|
783
|
+
*/
|
|
784
|
+
generateEntryFile() {
|
|
785
|
+
const { outputDir, config } = this.context;
|
|
786
|
+
const injections = this.collectInjections("entry");
|
|
787
|
+
let content = generateEntry(config, injections);
|
|
788
|
+
for (const hooks of this.pluginHooks) {
|
|
789
|
+
if (hooks.modifyEntryCode) {
|
|
790
|
+
const result = hooks.modifyEntryCode(content, this.context);
|
|
791
|
+
if (result) {
|
|
792
|
+
content = result;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
const entryPath = join3(outputDir, "index.tsx");
|
|
797
|
+
this.writeFileIfChanged(entryPath, content);
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* 收集插件代码注入
|
|
801
|
+
*/
|
|
802
|
+
collectInjections(type) {
|
|
803
|
+
const result = {
|
|
804
|
+
imports: [],
|
|
805
|
+
topLevel: [],
|
|
806
|
+
exports: []
|
|
807
|
+
};
|
|
808
|
+
const hookName = type === "entry" ? "injectEntry" : "injectBootstrap";
|
|
809
|
+
for (const hooks of this.pluginHooks) {
|
|
810
|
+
const hook = hooks[hookName];
|
|
811
|
+
if (hook) {
|
|
812
|
+
const injection = hook(this.context);
|
|
813
|
+
if (injection) {
|
|
814
|
+
result.imports?.push(...injection.imports || []);
|
|
815
|
+
result.topLevel?.push(...injection.topLevel || []);
|
|
816
|
+
result.exports?.push(...injection.exports || []);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
return result;
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* 生成插件附加文件
|
|
824
|
+
*/
|
|
825
|
+
async generatePluginFiles() {
|
|
826
|
+
const { outputDir } = this.context;
|
|
827
|
+
for (const hooks of this.pluginHooks) {
|
|
828
|
+
if (hooks.generateFiles) {
|
|
829
|
+
const files = hooks.generateFiles(this.context);
|
|
830
|
+
if (files) {
|
|
831
|
+
for (const file of files) {
|
|
832
|
+
const filePath = join3(outputDir, file.path);
|
|
833
|
+
const dir = join3(filePath, "..");
|
|
834
|
+
this.ensureDir(dir);
|
|
835
|
+
this.writeFileIfChanged(filePath, file.content);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* 生成类型定义
|
|
843
|
+
*/
|
|
844
|
+
generateTypes() {
|
|
845
|
+
const { outputDir, config, cwd } = this.context;
|
|
846
|
+
const envTypesContent = generateEnvTypes(cwd, config);
|
|
847
|
+
this.writeFileIfChanged(
|
|
848
|
+
join3(outputDir, "types", "env.d.ts"),
|
|
849
|
+
envTypesContent
|
|
850
|
+
);
|
|
851
|
+
if (config.router.conventional) {
|
|
852
|
+
const routeTypesContent = generateRouteTypes();
|
|
853
|
+
this.writeFileIfChanged(
|
|
854
|
+
join3(outputDir, "types", "routes.d.ts"),
|
|
855
|
+
routeTypesContent
|
|
856
|
+
);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* 确保目录存在
|
|
861
|
+
*/
|
|
862
|
+
ensureDir(dir) {
|
|
863
|
+
if (!existsSync4(dir)) {
|
|
864
|
+
mkdirSync2(dir, { recursive: true });
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
/**
|
|
868
|
+
* 只在内容变化时写入文件(避免触发不必要的热更新)
|
|
869
|
+
*/
|
|
870
|
+
writeFileIfChanged(filePath, content) {
|
|
871
|
+
const normalizedContent = this.normalizeContent(content);
|
|
872
|
+
const lastContent = this.lastGeneratedContent.get(filePath);
|
|
873
|
+
if (lastContent === normalizedContent) {
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
if (existsSync4(filePath)) {
|
|
877
|
+
const existingContent = readFileSync2(filePath, "utf-8");
|
|
878
|
+
if (this.normalizeContent(existingContent) === normalizedContent) {
|
|
879
|
+
this.lastGeneratedContent.set(filePath, normalizedContent);
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
writeFileSync2(filePath, content, "utf-8");
|
|
884
|
+
this.lastGeneratedContent.set(filePath, normalizedContent);
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* 标准化内容(去掉时间戳等动态部分)
|
|
888
|
+
*/
|
|
889
|
+
normalizeContent(content) {
|
|
890
|
+
return content.replace(/\/\/ Generated at: .+/g, "").replace(/\/\*\* Generated at: .+ \*\//g, "");
|
|
891
|
+
}
|
|
892
|
+
};
|
|
893
|
+
|
|
894
|
+
// src/generator/plugin.ts
|
|
895
|
+
var YwkfGeneratorPlugin = class {
|
|
896
|
+
options;
|
|
897
|
+
hasGenerated = false;
|
|
898
|
+
isWatching = false;
|
|
899
|
+
generator = null;
|
|
900
|
+
pluginHooks = [];
|
|
901
|
+
initialized = false;
|
|
902
|
+
constructor(options) {
|
|
903
|
+
this.options = options;
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* 解析插件配置为插件实例
|
|
907
|
+
*/
|
|
908
|
+
async resolvePlugin(pluginConfig) {
|
|
909
|
+
try {
|
|
910
|
+
let plugin;
|
|
911
|
+
let options = {};
|
|
912
|
+
if (typeof pluginConfig === "string") {
|
|
913
|
+
const module = await import(pluginConfig);
|
|
914
|
+
plugin = module.default || module;
|
|
915
|
+
} else if (Array.isArray(pluginConfig)) {
|
|
916
|
+
const [pluginOrName, pluginOptions] = pluginConfig;
|
|
917
|
+
options = pluginOptions;
|
|
918
|
+
if (typeof pluginOrName === "string") {
|
|
919
|
+
const module = await import(pluginOrName);
|
|
920
|
+
plugin = module.default || module;
|
|
921
|
+
} else {
|
|
922
|
+
plugin = pluginOrName;
|
|
923
|
+
}
|
|
924
|
+
} else {
|
|
925
|
+
plugin = pluginConfig;
|
|
926
|
+
}
|
|
927
|
+
if (typeof plugin === "function") {
|
|
928
|
+
plugin = plugin(options);
|
|
929
|
+
}
|
|
930
|
+
return plugin;
|
|
931
|
+
} catch (error) {
|
|
932
|
+
console.error(`[ywkf] \u89E3\u6790\u63D2\u4EF6\u5931\u8D25: ${error}`);
|
|
933
|
+
return null;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* 初始化插件钩子
|
|
938
|
+
*/
|
|
939
|
+
async initialize() {
|
|
940
|
+
if (this.initialized) {
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
const { cwd, config, isDev, pluginConfigs = [] } = this.options;
|
|
944
|
+
const context = {
|
|
945
|
+
cwd,
|
|
946
|
+
isDev,
|
|
947
|
+
isProd: !isDev,
|
|
948
|
+
config,
|
|
949
|
+
logger: {
|
|
950
|
+
info: (msg) => console.log(`[ywkf] ${msg}`),
|
|
951
|
+
warn: (msg) => console.warn(`[ywkf] ${msg}`),
|
|
952
|
+
error: (msg) => console.error(`[ywkf] ${msg}`),
|
|
953
|
+
debug: (msg) => {
|
|
954
|
+
if (process.env.DEBUG) {
|
|
955
|
+
console.log(`[ywkf:debug] ${msg}`);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
for (const pluginConfig of pluginConfigs) {
|
|
961
|
+
const plugin = await this.resolvePlugin(pluginConfig);
|
|
962
|
+
if (plugin) {
|
|
963
|
+
const hooks = await plugin.setup(context);
|
|
964
|
+
this.pluginHooks.push(hooks);
|
|
965
|
+
console.log(`[ywkf] \u63D2\u4EF6\u5DF2\u52A0\u8F7D: ${plugin.name}`);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
this.generator = new YwkfGenerator(
|
|
969
|
+
{
|
|
970
|
+
cwd: this.options.cwd,
|
|
971
|
+
config: this.options.config,
|
|
972
|
+
outputDir: this.options.outputDir,
|
|
973
|
+
isDev: this.options.isDev
|
|
974
|
+
},
|
|
975
|
+
this.pluginHooks
|
|
976
|
+
);
|
|
977
|
+
this.initialized = true;
|
|
978
|
+
}
|
|
979
|
+
apply(compiler) {
|
|
980
|
+
const pluginName = "YwkfGeneratorPlugin";
|
|
981
|
+
compiler.hooks.beforeCompile.tapAsync(pluginName, async (params, callback) => {
|
|
982
|
+
try {
|
|
983
|
+
await this.initialize();
|
|
984
|
+
if (!this.hasGenerated && this.generator) {
|
|
985
|
+
await this.generator.generate();
|
|
986
|
+
this.hasGenerated = true;
|
|
987
|
+
}
|
|
988
|
+
callback();
|
|
989
|
+
} catch (error) {
|
|
990
|
+
callback(error);
|
|
991
|
+
}
|
|
992
|
+
});
|
|
993
|
+
if (this.options.isDev && !this.isWatching) {
|
|
994
|
+
this.watchPages();
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
/**
|
|
998
|
+
* 监听页面目录变化
|
|
999
|
+
*/
|
|
1000
|
+
watchPages() {
|
|
1001
|
+
const { config, cwd } = this.options;
|
|
1002
|
+
const pagesDir = join4(cwd, config.router.pagesDir || "src/pages");
|
|
1003
|
+
if (!existsSync5(pagesDir)) {
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
this.isWatching = true;
|
|
1007
|
+
let debounceTimer = null;
|
|
1008
|
+
const watcher = watch(
|
|
1009
|
+
pagesDir,
|
|
1010
|
+
{ recursive: true },
|
|
1011
|
+
(eventType, filename) => {
|
|
1012
|
+
if (!filename?.match(/\.(tsx?|jsx?)$/)) {
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
if (debounceTimer) {
|
|
1016
|
+
clearTimeout(debounceTimer);
|
|
1017
|
+
}
|
|
1018
|
+
debounceTimer = setTimeout(async () => {
|
|
1019
|
+
console.log(`[ywkf] \u68C0\u6D4B\u5230\u9875\u9762\u53D8\u5316: ${filename}`);
|
|
1020
|
+
if (this.generator) {
|
|
1021
|
+
await this.generator.generate();
|
|
1022
|
+
}
|
|
1023
|
+
}, 500);
|
|
1024
|
+
}
|
|
1025
|
+
);
|
|
1026
|
+
process.on("exit", () => {
|
|
1027
|
+
watcher.close();
|
|
1028
|
+
});
|
|
1029
|
+
}
|
|
1030
|
+
};
|
|
1031
|
+
|
|
1032
|
+
// src/rspack/base.ts
|
|
1033
|
+
var require2 = createRequire(import.meta.url);
|
|
1034
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
1035
|
+
var coreNodeModules = join5(__dirname, "../../node_modules");
|
|
1036
|
+
function createBaseConfig(config, cwd) {
|
|
1037
|
+
const { resolveApp } = createPathResolver(cwd);
|
|
1038
|
+
const {
|
|
1039
|
+
appName,
|
|
1040
|
+
appCName,
|
|
1041
|
+
output,
|
|
1042
|
+
html,
|
|
1043
|
+
alias: userAlias,
|
|
1044
|
+
microFrontend,
|
|
1045
|
+
router
|
|
1046
|
+
} = config;
|
|
1047
|
+
const ywkfOutputDir = resolveApp(".ywkf");
|
|
1048
|
+
const defaultAlias = {
|
|
1049
|
+
"@": resolveApp("src"),
|
|
1050
|
+
"@config": resolveApp("config"),
|
|
1051
|
+
"@store": resolveApp("store"),
|
|
1052
|
+
"@locales": resolveApp("locales"),
|
|
1053
|
+
"@public": resolveApp("public"),
|
|
1054
|
+
// 约定式路由生成的文件
|
|
1055
|
+
"@ywkf/routes": join5(ywkfOutputDir, "routes.tsx"),
|
|
1056
|
+
// 请求层(由 reactQueryPlugin 生成)
|
|
1057
|
+
"@ywkf/request": join5(ywkfOutputDir, "request.ts"),
|
|
1058
|
+
// Store 工具(由 zustandPlugin 生成)
|
|
1059
|
+
"@ywkf/store": join5(ywkfOutputDir, "store.ts")
|
|
1060
|
+
};
|
|
1061
|
+
const alias = { ...defaultAlias, ...userAlias };
|
|
1062
|
+
return {
|
|
1063
|
+
// 使用 .ywkf/index.tsx 作为入口(由框架生成)
|
|
1064
|
+
entry: [join5(ywkfOutputDir, "index.tsx")],
|
|
1065
|
+
resolve: {
|
|
1066
|
+
extensions: [".ts", ".tsx", ".jsx", ".js", ".json", ".mjs"],
|
|
1067
|
+
symlinks: false,
|
|
1068
|
+
alias: {
|
|
1069
|
+
...alias,
|
|
1070
|
+
"process/browser.js": require2.resolve("process/browser.js"),
|
|
1071
|
+
"process/browser": require2.resolve("process/browser.js"),
|
|
1072
|
+
// 确保 React 系列包从用户项目的 node_modules 解析
|
|
1073
|
+
react: resolveApp("node_modules/react"),
|
|
1074
|
+
"react-dom": resolveApp("node_modules/react-dom"),
|
|
1075
|
+
"react-router": resolveApp("node_modules/react-router")
|
|
1076
|
+
},
|
|
1077
|
+
fallback: {
|
|
1078
|
+
path: require2.resolve("path-browserify"),
|
|
1079
|
+
process: require2.resolve("process/browser.js"),
|
|
1080
|
+
buffer: require2.resolve("buffer/"),
|
|
1081
|
+
util: require2.resolve("util/"),
|
|
1082
|
+
stream: require2.resolve("stream-browserify"),
|
|
1083
|
+
crypto: require2.resolve("crypto-browserify"),
|
|
1084
|
+
zlib: require2.resolve("browserify-zlib"),
|
|
1085
|
+
querystring: require2.resolve("querystring-es3"),
|
|
1086
|
+
url: require2.resolve("url/"),
|
|
1087
|
+
assert: require2.resolve("assert/"),
|
|
1088
|
+
fs: false,
|
|
1089
|
+
net: false,
|
|
1090
|
+
tls: false,
|
|
1091
|
+
os: false,
|
|
1092
|
+
https: false,
|
|
1093
|
+
http: false
|
|
1094
|
+
}
|
|
1095
|
+
},
|
|
1096
|
+
resolveLoader: {
|
|
1097
|
+
modules: [coreNodeModules, "node_modules"]
|
|
1098
|
+
},
|
|
1099
|
+
module: {
|
|
1100
|
+
rules: [
|
|
1101
|
+
{
|
|
1102
|
+
test: /\.m?js$/,
|
|
1103
|
+
resolve: {
|
|
1104
|
+
fullySpecified: false
|
|
1105
|
+
}
|
|
1106
|
+
},
|
|
1107
|
+
{
|
|
1108
|
+
test: /\.[jt]sx?$/,
|
|
1109
|
+
include: [
|
|
1110
|
+
resolveApp("src"),
|
|
1111
|
+
resolveApp("config"),
|
|
1112
|
+
resolveApp("store"),
|
|
1113
|
+
resolveApp("packages"),
|
|
1114
|
+
ywkfOutputDir
|
|
1115
|
+
// 约定式路由生成的文件
|
|
1116
|
+
],
|
|
1117
|
+
exclude: [resolveApp("node_modules")],
|
|
1118
|
+
use: [
|
|
1119
|
+
{
|
|
1120
|
+
loader: "builtin:swc-loader",
|
|
1121
|
+
options: {
|
|
1122
|
+
jsc: {
|
|
1123
|
+
parser: {
|
|
1124
|
+
syntax: "typescript",
|
|
1125
|
+
tsx: true,
|
|
1126
|
+
decorators: true
|
|
1127
|
+
},
|
|
1128
|
+
transform: {
|
|
1129
|
+
react: {
|
|
1130
|
+
runtime: "automatic"
|
|
1131
|
+
}
|
|
1132
|
+
},
|
|
1133
|
+
target: "es2015"
|
|
1134
|
+
},
|
|
1135
|
+
sourceMaps: true
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
]
|
|
1139
|
+
},
|
|
1140
|
+
{
|
|
1141
|
+
test: /\.(png|jpg|jpeg|gif|webp|m3u8|exr|hdr|json|woff2)$/,
|
|
1142
|
+
include: [resolveApp("public")],
|
|
1143
|
+
exclude: [resolveApp("src"), resolveApp("store")],
|
|
1144
|
+
type: "asset",
|
|
1145
|
+
parser: {
|
|
1146
|
+
dataUrlCondition: {
|
|
1147
|
+
maxSize: 10 * 1024
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
},
|
|
1151
|
+
{
|
|
1152
|
+
test: /\.(md)$/,
|
|
1153
|
+
include: [resolveApp("src")],
|
|
1154
|
+
type: "asset/source"
|
|
1155
|
+
},
|
|
1156
|
+
{
|
|
1157
|
+
test: /\.svg$/,
|
|
1158
|
+
use: ["@svgr/webpack"]
|
|
1159
|
+
}
|
|
1160
|
+
]
|
|
1161
|
+
},
|
|
1162
|
+
plugins: [
|
|
1163
|
+
new rspack.ProvidePlugin({
|
|
1164
|
+
process: "process/browser.js",
|
|
1165
|
+
Buffer: ["buffer", "Buffer"]
|
|
1166
|
+
}),
|
|
1167
|
+
new rspack.DefinePlugin({
|
|
1168
|
+
"process.env": JSON.stringify(process.env)
|
|
1169
|
+
}),
|
|
1170
|
+
new rspack.HtmlRspackPlugin({
|
|
1171
|
+
template: resolveApp(html.template ?? "public/index.html"),
|
|
1172
|
+
filename: "index.html",
|
|
1173
|
+
inject: "body",
|
|
1174
|
+
hash: true,
|
|
1175
|
+
minify: process.env.NODE_ENV === "production",
|
|
1176
|
+
favicon: resolveApp(html.favicon ?? "public/favicon.ico"),
|
|
1177
|
+
templateParameters: {
|
|
1178
|
+
title: html.title ?? appCName,
|
|
1179
|
+
mountRoot: html.mountRoot ?? appName
|
|
1180
|
+
}
|
|
1181
|
+
}),
|
|
1182
|
+
new rspack.CopyRspackPlugin({
|
|
1183
|
+
patterns: [
|
|
1184
|
+
{
|
|
1185
|
+
from: resolveApp("public/images"),
|
|
1186
|
+
to: "public/images",
|
|
1187
|
+
noErrorOnMissing: true
|
|
1188
|
+
}
|
|
1189
|
+
]
|
|
1190
|
+
}),
|
|
1191
|
+
// .ywkf 目录生成插件(传递用户配置的插件列表)
|
|
1192
|
+
new YwkfGeneratorPlugin({
|
|
1193
|
+
cwd,
|
|
1194
|
+
config,
|
|
1195
|
+
outputDir: ywkfOutputDir,
|
|
1196
|
+
isDev: process.env.NODE_ENV !== "production",
|
|
1197
|
+
pluginConfigs: config.plugins
|
|
1198
|
+
})
|
|
1199
|
+
].filter(Boolean),
|
|
1200
|
+
output: {
|
|
1201
|
+
assetModuleFilename: "images/[hash][ext]",
|
|
1202
|
+
library: microFrontend.enabled ? `${appCName}-[name]` : void 0,
|
|
1203
|
+
chunkFilename: "[name].[contenthash].js",
|
|
1204
|
+
libraryTarget: microFrontend.enabled ? "umd" : void 0,
|
|
1205
|
+
globalObject: microFrontend.enabled ? "window" : void 0,
|
|
1206
|
+
chunkLoadingGlobal: microFrontend.enabled ? `chunk_global_${appName}` : void 0,
|
|
1207
|
+
publicPath: output.publicPath ?? "/",
|
|
1208
|
+
path: resolveApp(output.path ?? "dist")
|
|
1209
|
+
},
|
|
1210
|
+
ignoreWarnings: [
|
|
1211
|
+
{
|
|
1212
|
+
module: /@testing-library\/react/,
|
|
1213
|
+
message: /export 'act'/
|
|
1214
|
+
}
|
|
1215
|
+
],
|
|
1216
|
+
devtool: "source-map"
|
|
1217
|
+
};
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
// src/rspack/dev.ts
|
|
1221
|
+
var require3 = createRequire2(import.meta.url);
|
|
1222
|
+
function convertProxyToArray(proxy) {
|
|
1223
|
+
if (!proxy || Object.keys(proxy).length === 0) {
|
|
1224
|
+
return void 0;
|
|
1225
|
+
}
|
|
1226
|
+
return Object.entries(proxy).map(([context, target]) => {
|
|
1227
|
+
if (typeof target === "string") {
|
|
1228
|
+
return {
|
|
1229
|
+
context: [context],
|
|
1230
|
+
target,
|
|
1231
|
+
changeOrigin: true,
|
|
1232
|
+
secure: false
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
1235
|
+
return {
|
|
1236
|
+
context: [context],
|
|
1237
|
+
changeOrigin: true,
|
|
1238
|
+
secure: false,
|
|
1239
|
+
...target
|
|
1240
|
+
};
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
function createDevConfig(config, cwd) {
|
|
1244
|
+
const baseConfig = createBaseConfig(config, cwd);
|
|
1245
|
+
const { resolveApp } = createPathResolver(cwd);
|
|
1246
|
+
const { dev: dev2, style } = config;
|
|
1247
|
+
const styleRules = {
|
|
1248
|
+
rules: [
|
|
1249
|
+
// Less - antd
|
|
1250
|
+
{
|
|
1251
|
+
test: /\.less$/,
|
|
1252
|
+
include: [
|
|
1253
|
+
/[\\/]node_modules[\\/].*antd/,
|
|
1254
|
+
/[\\/]node_modules[\\/]@4399ywkf[\\/]design/
|
|
1255
|
+
],
|
|
1256
|
+
use: [
|
|
1257
|
+
{ loader: "style-loader" },
|
|
1258
|
+
{
|
|
1259
|
+
loader: "css-loader",
|
|
1260
|
+
options: { sourceMap: true }
|
|
1261
|
+
},
|
|
1262
|
+
{
|
|
1263
|
+
loader: "less-loader",
|
|
1264
|
+
options: {
|
|
1265
|
+
sourceMap: true,
|
|
1266
|
+
lessOptions: {
|
|
1267
|
+
strictMath: false,
|
|
1268
|
+
math: "always",
|
|
1269
|
+
javascriptEnabled: true
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
]
|
|
1274
|
+
},
|
|
1275
|
+
// Less - 项目文件
|
|
1276
|
+
...style.less?.enabled !== false ? [
|
|
1277
|
+
{
|
|
1278
|
+
test: /\.less$/,
|
|
1279
|
+
include: [resolveApp("src")],
|
|
1280
|
+
exclude: [resolveApp("node_modules")],
|
|
1281
|
+
oneOf: [
|
|
1282
|
+
{
|
|
1283
|
+
test: /\.module\.less$/,
|
|
1284
|
+
use: [
|
|
1285
|
+
{ loader: "style-loader" },
|
|
1286
|
+
{
|
|
1287
|
+
loader: "css-loader",
|
|
1288
|
+
options: {
|
|
1289
|
+
sourceMap: true,
|
|
1290
|
+
modules: {
|
|
1291
|
+
localIdentName: "[path][name]__[local]--[hash:base64:5]"
|
|
1292
|
+
},
|
|
1293
|
+
importLoaders: 2
|
|
1294
|
+
}
|
|
1295
|
+
},
|
|
1296
|
+
{
|
|
1297
|
+
loader: "less-loader",
|
|
1298
|
+
options: {
|
|
1299
|
+
sourceMap: true,
|
|
1300
|
+
lessOptions: {
|
|
1301
|
+
javascriptEnabled: true,
|
|
1302
|
+
math: "always",
|
|
1303
|
+
...style.less?.lessOptions
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
]
|
|
1308
|
+
},
|
|
1309
|
+
{
|
|
1310
|
+
use: [
|
|
1311
|
+
{ loader: "style-loader" },
|
|
1312
|
+
{
|
|
1313
|
+
loader: "css-loader",
|
|
1314
|
+
options: { sourceMap: true }
|
|
1315
|
+
},
|
|
1316
|
+
{
|
|
1317
|
+
loader: "less-loader",
|
|
1318
|
+
options: {
|
|
1319
|
+
sourceMap: true,
|
|
1320
|
+
lessOptions: {
|
|
1321
|
+
javascriptEnabled: true,
|
|
1322
|
+
...style.less?.lessOptions
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
]
|
|
1327
|
+
}
|
|
1328
|
+
]
|
|
1329
|
+
}
|
|
1330
|
+
] : [],
|
|
1331
|
+
// Sass
|
|
1332
|
+
...style.sass?.enabled !== false ? [
|
|
1333
|
+
{
|
|
1334
|
+
test: /\.s[ac]ss$/i,
|
|
1335
|
+
include: [resolveApp("src")],
|
|
1336
|
+
exclude: [resolveApp("node_modules")],
|
|
1337
|
+
oneOf: [
|
|
1338
|
+
{
|
|
1339
|
+
test: /\.module\.s[ac]ss$/,
|
|
1340
|
+
use: [
|
|
1341
|
+
{ loader: "style-loader" },
|
|
1342
|
+
{
|
|
1343
|
+
loader: "css-loader",
|
|
1344
|
+
options: {
|
|
1345
|
+
sourceMap: true,
|
|
1346
|
+
modules: {
|
|
1347
|
+
localIdentName: "[path][name]__[local]--[hash:base64:5]"
|
|
1348
|
+
},
|
|
1349
|
+
importLoaders: 2
|
|
1350
|
+
}
|
|
1351
|
+
},
|
|
1352
|
+
{
|
|
1353
|
+
loader: "sass-loader",
|
|
1354
|
+
options: {
|
|
1355
|
+
sourceMap: true,
|
|
1356
|
+
sassOptions: {
|
|
1357
|
+
outputStyle: "compressed",
|
|
1358
|
+
...style.sass?.sassOptions
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
]
|
|
1363
|
+
},
|
|
1364
|
+
{
|
|
1365
|
+
use: [
|
|
1366
|
+
{ loader: "style-loader" },
|
|
1367
|
+
{
|
|
1368
|
+
loader: "css-loader",
|
|
1369
|
+
options: { sourceMap: true }
|
|
1370
|
+
},
|
|
1371
|
+
{
|
|
1372
|
+
loader: "sass-loader",
|
|
1373
|
+
options: {
|
|
1374
|
+
sourceMap: true,
|
|
1375
|
+
sassOptions: {
|
|
1376
|
+
outputStyle: "compressed",
|
|
1377
|
+
...style.sass?.sassOptions
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
]
|
|
1382
|
+
}
|
|
1383
|
+
]
|
|
1384
|
+
}
|
|
1385
|
+
] : [],
|
|
1386
|
+
// TailwindCSS
|
|
1387
|
+
...style.tailwindcss ? [
|
|
1388
|
+
{
|
|
1389
|
+
test: /\.css$/,
|
|
1390
|
+
include: [resolveApp("src/index.css")],
|
|
1391
|
+
use: [
|
|
1392
|
+
"style-loader",
|
|
1393
|
+
"css-loader",
|
|
1394
|
+
{
|
|
1395
|
+
loader: "postcss-loader",
|
|
1396
|
+
options: {
|
|
1397
|
+
postcssOptions: {
|
|
1398
|
+
config: resolveApp("postcss.config.js")
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
]
|
|
1403
|
+
}
|
|
1404
|
+
] : [],
|
|
1405
|
+
// 其他 CSS
|
|
1406
|
+
{
|
|
1407
|
+
test: /\.css$/,
|
|
1408
|
+
include: [resolveApp("src"), resolveApp("node_modules")],
|
|
1409
|
+
exclude: style.tailwindcss ? [resolveApp("src/index.css")] : [],
|
|
1410
|
+
use: [
|
|
1411
|
+
{ loader: "style-loader" },
|
|
1412
|
+
{
|
|
1413
|
+
loader: "css-loader",
|
|
1414
|
+
options: { sourceMap: true }
|
|
1415
|
+
}
|
|
1416
|
+
]
|
|
1417
|
+
}
|
|
1418
|
+
]
|
|
1419
|
+
};
|
|
1420
|
+
const devConfig = {
|
|
1421
|
+
mode: "development",
|
|
1422
|
+
devtool: "inline-source-map",
|
|
1423
|
+
output: {
|
|
1424
|
+
filename: "[name].bundle.js",
|
|
1425
|
+
clean: false
|
|
1426
|
+
},
|
|
1427
|
+
stats: "errors-only",
|
|
1428
|
+
devServer: {
|
|
1429
|
+
host: dev2.host,
|
|
1430
|
+
port: dev2.port,
|
|
1431
|
+
compress: true,
|
|
1432
|
+
historyApiFallback: true,
|
|
1433
|
+
hot: true,
|
|
1434
|
+
allowedHosts: "all",
|
|
1435
|
+
client: {
|
|
1436
|
+
overlay: false,
|
|
1437
|
+
progress: true
|
|
1438
|
+
},
|
|
1439
|
+
proxy: convertProxyToArray(dev2.proxy)
|
|
1440
|
+
},
|
|
1441
|
+
module: styleRules
|
|
1442
|
+
};
|
|
1443
|
+
return merge(baseConfig, devConfig);
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
// src/rspack/prod.ts
|
|
1447
|
+
import { rspack as rspack2 } from "@rspack/core";
|
|
1448
|
+
import { merge as merge2 } from "webpack-merge";
|
|
1449
|
+
function createProdConfig(config, cwd) {
|
|
1450
|
+
const baseConfig = createBaseConfig(config, cwd);
|
|
1451
|
+
const { resolveApp } = createPathResolver(cwd);
|
|
1452
|
+
const { style, performance, output } = config;
|
|
1453
|
+
const styleRules = {
|
|
1454
|
+
rules: [
|
|
1455
|
+
// Less - antd
|
|
1456
|
+
{
|
|
1457
|
+
test: /\.less$/,
|
|
1458
|
+
include: [
|
|
1459
|
+
/[\\/]node_modules[\\/].*antd/,
|
|
1460
|
+
/[\\/]node_modules[\\/]@4399ywkf[\\/]design/
|
|
1461
|
+
],
|
|
1462
|
+
use: [
|
|
1463
|
+
{ loader: rspack2.CssExtractRspackPlugin.loader },
|
|
1464
|
+
{
|
|
1465
|
+
loader: "css-loader",
|
|
1466
|
+
options: { sourceMap: true }
|
|
1467
|
+
},
|
|
1468
|
+
{
|
|
1469
|
+
loader: "less-loader",
|
|
1470
|
+
options: {
|
|
1471
|
+
sourceMap: true,
|
|
1472
|
+
lessOptions: {
|
|
1473
|
+
strictMath: false,
|
|
1474
|
+
math: "always",
|
|
1475
|
+
javascriptEnabled: true
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
]
|
|
1480
|
+
},
|
|
1481
|
+
// Less - 项目文件
|
|
1482
|
+
...style.less?.enabled !== false ? [
|
|
1483
|
+
{
|
|
1484
|
+
test: /\.less$/,
|
|
1485
|
+
include: [resolveApp("src")],
|
|
1486
|
+
exclude: [resolveApp("node_modules")],
|
|
1487
|
+
oneOf: [
|
|
1488
|
+
{
|
|
1489
|
+
test: /\.module\.less$/,
|
|
1490
|
+
use: [
|
|
1491
|
+
{ loader: rspack2.CssExtractRspackPlugin.loader },
|
|
1492
|
+
{
|
|
1493
|
+
loader: "css-loader",
|
|
1494
|
+
options: {
|
|
1495
|
+
sourceMap: true,
|
|
1496
|
+
modules: {
|
|
1497
|
+
localIdentName: "[path][name]__[local]--[hash:base64:5]"
|
|
1498
|
+
},
|
|
1499
|
+
importLoaders: 2
|
|
1500
|
+
}
|
|
1501
|
+
},
|
|
1502
|
+
{
|
|
1503
|
+
loader: "less-loader",
|
|
1504
|
+
options: {
|
|
1505
|
+
sourceMap: true,
|
|
1506
|
+
lessOptions: {
|
|
1507
|
+
javascriptEnabled: true,
|
|
1508
|
+
...style.less?.lessOptions
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
]
|
|
1513
|
+
},
|
|
1514
|
+
{
|
|
1515
|
+
use: [
|
|
1516
|
+
{ loader: rspack2.CssExtractRspackPlugin.loader },
|
|
1517
|
+
{
|
|
1518
|
+
loader: "css-loader",
|
|
1519
|
+
options: { sourceMap: true }
|
|
1520
|
+
},
|
|
1521
|
+
{
|
|
1522
|
+
loader: "less-loader",
|
|
1523
|
+
options: {
|
|
1524
|
+
sourceMap: true,
|
|
1525
|
+
lessOptions: {
|
|
1526
|
+
javascriptEnabled: true,
|
|
1527
|
+
...style.less?.lessOptions
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
]
|
|
1532
|
+
}
|
|
1533
|
+
]
|
|
1534
|
+
}
|
|
1535
|
+
] : [],
|
|
1536
|
+
// Sass
|
|
1537
|
+
...style.sass?.enabled !== false ? [
|
|
1538
|
+
{
|
|
1539
|
+
test: /\.s[ac]ss$/i,
|
|
1540
|
+
include: [resolveApp("src")],
|
|
1541
|
+
exclude: [resolveApp("node_modules")],
|
|
1542
|
+
oneOf: [
|
|
1543
|
+
{
|
|
1544
|
+
test: /\.module\.s[ac]ss$/,
|
|
1545
|
+
use: [
|
|
1546
|
+
{ loader: rspack2.CssExtractRspackPlugin.loader },
|
|
1547
|
+
{
|
|
1548
|
+
loader: "css-loader",
|
|
1549
|
+
options: {
|
|
1550
|
+
sourceMap: true,
|
|
1551
|
+
modules: {
|
|
1552
|
+
localIdentName: "[path][name]__[local]--[hash:base64:5]"
|
|
1553
|
+
},
|
|
1554
|
+
importLoaders: 2
|
|
1555
|
+
}
|
|
1556
|
+
},
|
|
1557
|
+
{
|
|
1558
|
+
loader: "sass-loader",
|
|
1559
|
+
options: {
|
|
1560
|
+
sourceMap: true,
|
|
1561
|
+
sassOptions: {
|
|
1562
|
+
outputStyle: "compressed",
|
|
1563
|
+
...style.sass?.sassOptions
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
]
|
|
1568
|
+
},
|
|
1569
|
+
{
|
|
1570
|
+
use: [
|
|
1571
|
+
{ loader: rspack2.CssExtractRspackPlugin.loader },
|
|
1572
|
+
{
|
|
1573
|
+
loader: "css-loader",
|
|
1574
|
+
options: { sourceMap: true }
|
|
1575
|
+
},
|
|
1576
|
+
{
|
|
1577
|
+
loader: "sass-loader",
|
|
1578
|
+
options: {
|
|
1579
|
+
sourceMap: true,
|
|
1580
|
+
sassOptions: {
|
|
1581
|
+
outputStyle: "compressed",
|
|
1582
|
+
...style.sass?.sassOptions
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
]
|
|
1587
|
+
}
|
|
1588
|
+
]
|
|
1589
|
+
}
|
|
1590
|
+
] : [],
|
|
1591
|
+
// TailwindCSS
|
|
1592
|
+
...style.tailwindcss ? [
|
|
1593
|
+
{
|
|
1594
|
+
test: /\.css$/,
|
|
1595
|
+
include: [resolveApp("src/index.css")],
|
|
1596
|
+
use: [
|
|
1597
|
+
{ loader: rspack2.CssExtractRspackPlugin.loader },
|
|
1598
|
+
"css-loader",
|
|
1599
|
+
{
|
|
1600
|
+
loader: "postcss-loader",
|
|
1601
|
+
options: {
|
|
1602
|
+
postcssOptions: {
|
|
1603
|
+
config: resolveApp("postcss.config.js")
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
]
|
|
1608
|
+
}
|
|
1609
|
+
] : [],
|
|
1610
|
+
// src 目录中的其他 CSS
|
|
1611
|
+
{
|
|
1612
|
+
test: /\.css$/,
|
|
1613
|
+
include: [resolveApp("src")],
|
|
1614
|
+
exclude: style.tailwindcss ? [resolveApp("src/index.css")] : [],
|
|
1615
|
+
use: [
|
|
1616
|
+
{ loader: rspack2.CssExtractRspackPlugin.loader },
|
|
1617
|
+
{
|
|
1618
|
+
loader: "css-loader",
|
|
1619
|
+
options: { sourceMap: true }
|
|
1620
|
+
}
|
|
1621
|
+
]
|
|
1622
|
+
},
|
|
1623
|
+
// node_modules CSS
|
|
1624
|
+
{
|
|
1625
|
+
test: /\.css$/,
|
|
1626
|
+
include: [resolveApp("node_modules")],
|
|
1627
|
+
use: [
|
|
1628
|
+
{ loader: "style-loader" },
|
|
1629
|
+
{
|
|
1630
|
+
loader: "css-loader",
|
|
1631
|
+
options: { sourceMap: true }
|
|
1632
|
+
}
|
|
1633
|
+
]
|
|
1634
|
+
}
|
|
1635
|
+
]
|
|
1636
|
+
};
|
|
1637
|
+
const prodConfig = {
|
|
1638
|
+
mode: "production",
|
|
1639
|
+
devtool: "hidden-source-map",
|
|
1640
|
+
output: {
|
|
1641
|
+
filename: "[name].[contenthash].bundle.js",
|
|
1642
|
+
chunkFilename: "[name].[contenthash].chunk.js",
|
|
1643
|
+
clean: output.clean
|
|
1644
|
+
},
|
|
1645
|
+
module: styleRules,
|
|
1646
|
+
plugins: [
|
|
1647
|
+
new rspack2.CssExtractRspackPlugin({
|
|
1648
|
+
filename: "[name].[contenthash:8].css",
|
|
1649
|
+
chunkFilename: "[name].[contenthash:8].chunk.css"
|
|
1650
|
+
})
|
|
1651
|
+
],
|
|
1652
|
+
optimization: {
|
|
1653
|
+
minimize: true,
|
|
1654
|
+
minimizer: [
|
|
1655
|
+
new rspack2.SwcJsMinimizerRspackPlugin({
|
|
1656
|
+
exclude: [resolveApp("node_modules")],
|
|
1657
|
+
minimizerOptions: {
|
|
1658
|
+
compress: {
|
|
1659
|
+
pure_funcs: performance.dropConsole ? ["console.log", "console.info", "console.debug", "console.warn"] : ["console.info", "console.debug"],
|
|
1660
|
+
drop_console: false,
|
|
1661
|
+
drop_debugger: true
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
}),
|
|
1665
|
+
new rspack2.LightningCssMinimizerRspackPlugin()
|
|
1666
|
+
]
|
|
1667
|
+
}
|
|
1668
|
+
};
|
|
1669
|
+
return merge2(baseConfig, prodConfig);
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
// src/rspack/index.ts
|
|
1673
|
+
function createRspackConfig(config, cwd, options = {}) {
|
|
1674
|
+
const isDev = options.isDev ?? process.env.NODE_ENV !== "production";
|
|
1675
|
+
let rspackConfig = isDev ? createDevConfig(config, cwd) : createProdConfig(config, cwd);
|
|
1676
|
+
if (config.performance.rsdoctor) {
|
|
1677
|
+
rspackConfig.plugins = [
|
|
1678
|
+
...rspackConfig.plugins || [],
|
|
1679
|
+
new RsdoctorRspackPlugin({})
|
|
1680
|
+
];
|
|
1681
|
+
}
|
|
1682
|
+
if (config.tools.rspack) {
|
|
1683
|
+
const userConfig = config.tools.rspack(rspackConfig, {
|
|
1684
|
+
isDev,
|
|
1685
|
+
isProd: !isDev
|
|
1686
|
+
});
|
|
1687
|
+
if (userConfig) {
|
|
1688
|
+
rspackConfig = userConfig;
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
return rspackConfig;
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
// src/router/plugin.ts
|
|
1695
|
+
import { join as join6 } from "path";
|
|
1696
|
+
import { watch as watch2, existsSync as existsSync6 } from "fs";
|
|
1697
|
+
var ConventionalRoutePlugin = class {
|
|
1698
|
+
options;
|
|
1699
|
+
isWatching = false;
|
|
1700
|
+
hasGenerated = false;
|
|
1701
|
+
lastGeneratedContent = "";
|
|
1702
|
+
constructor(options) {
|
|
1703
|
+
this.options = options;
|
|
1704
|
+
}
|
|
1705
|
+
apply(compiler) {
|
|
1706
|
+
const pluginName = "ConventionalRoutePlugin";
|
|
1707
|
+
compiler.hooks.beforeCompile.tapAsync(pluginName, (params, callback) => {
|
|
1708
|
+
if (!this.hasGenerated) {
|
|
1709
|
+
this.generateRoutes();
|
|
1710
|
+
this.hasGenerated = true;
|
|
1711
|
+
}
|
|
1712
|
+
callback();
|
|
1713
|
+
});
|
|
1714
|
+
if (this.options.watch && !this.isWatching) {
|
|
1715
|
+
this.watchPages();
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
/**
|
|
1719
|
+
* 生成路由文件(带内容比对,避免不必要的重复生成)
|
|
1720
|
+
*/
|
|
1721
|
+
generateRoutes() {
|
|
1722
|
+
try {
|
|
1723
|
+
const generator = new ConventionalRouteGenerator({
|
|
1724
|
+
pagesDir: this.options.pagesDir,
|
|
1725
|
+
outputDir: this.options.outputDir,
|
|
1726
|
+
basename: this.options.basename
|
|
1727
|
+
});
|
|
1728
|
+
const newContent = generator.generateCode();
|
|
1729
|
+
const outputPath = join6(this.options.outputDir, "routes.tsx");
|
|
1730
|
+
const normalizedNew = this.normalizeContent(newContent);
|
|
1731
|
+
const normalizedOld = this.normalizeContent(this.lastGeneratedContent);
|
|
1732
|
+
if (normalizedNew === normalizedOld) {
|
|
1733
|
+
return;
|
|
1734
|
+
}
|
|
1735
|
+
generator.write();
|
|
1736
|
+
this.lastGeneratedContent = newContent;
|
|
1737
|
+
} catch (error) {
|
|
1738
|
+
console.error("[ywkf] \u751F\u6210\u8DEF\u7531\u5931\u8D25:", error);
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
/**
|
|
1742
|
+
* 标准化内容(去掉时间戳等动态部分)
|
|
1743
|
+
*/
|
|
1744
|
+
normalizeContent(content) {
|
|
1745
|
+
return content.replace(/\/\/ Generated at: .+/g, "");
|
|
1746
|
+
}
|
|
1747
|
+
/**
|
|
1748
|
+
* 监听页面目录变化
|
|
1749
|
+
*/
|
|
1750
|
+
watchPages() {
|
|
1751
|
+
if (!existsSync6(this.options.pagesDir)) {
|
|
1752
|
+
return;
|
|
1753
|
+
}
|
|
1754
|
+
this.isWatching = true;
|
|
1755
|
+
let debounceTimer = null;
|
|
1756
|
+
const watcher = watch2(
|
|
1757
|
+
this.options.pagesDir,
|
|
1758
|
+
{ recursive: true },
|
|
1759
|
+
(eventType, filename) => {
|
|
1760
|
+
if (!filename?.match(/\.(tsx?|jsx?)$/)) {
|
|
1761
|
+
return;
|
|
1762
|
+
}
|
|
1763
|
+
if (debounceTimer) {
|
|
1764
|
+
clearTimeout(debounceTimer);
|
|
1765
|
+
}
|
|
1766
|
+
debounceTimer = setTimeout(() => {
|
|
1767
|
+
console.log(`[ywkf] \u68C0\u6D4B\u5230\u9875\u9762\u53D8\u5316: ${filename}`);
|
|
1768
|
+
this.generateRoutes();
|
|
1769
|
+
}, 500);
|
|
1770
|
+
}
|
|
1771
|
+
);
|
|
1772
|
+
process.on("exit", () => {
|
|
1773
|
+
watcher.close();
|
|
1774
|
+
});
|
|
1775
|
+
}
|
|
1776
|
+
};
|
|
1777
|
+
|
|
1778
|
+
// src/runtime/context.tsx
|
|
1779
|
+
import { createContext, useContext } from "react";
|
|
1780
|
+
import { jsx } from "react/jsx-runtime";
|
|
1781
|
+
var defaultContextValue = {
|
|
1782
|
+
appName: "app",
|
|
1783
|
+
basename: "/",
|
|
1784
|
+
isDev: process.env.NODE_ENV === "development",
|
|
1785
|
+
env: {}
|
|
1786
|
+
};
|
|
1787
|
+
var AppContext = createContext(defaultContextValue);
|
|
1788
|
+
function AppContextProvider({
|
|
1789
|
+
value,
|
|
1790
|
+
children
|
|
1791
|
+
}) {
|
|
1792
|
+
const contextValue = {
|
|
1793
|
+
...defaultContextValue,
|
|
1794
|
+
...value,
|
|
1795
|
+
env: {
|
|
1796
|
+
...defaultContextValue.env,
|
|
1797
|
+
...value.env
|
|
1798
|
+
}
|
|
1799
|
+
};
|
|
1800
|
+
return /* @__PURE__ */ jsx(AppContext.Provider, { value: contextValue, children });
|
|
1801
|
+
}
|
|
1802
|
+
function useApp() {
|
|
1803
|
+
const context = useContext(AppContext);
|
|
1804
|
+
if (!context) {
|
|
1805
|
+
throw new Error("useApp must be used within AppContextProvider");
|
|
1806
|
+
}
|
|
1807
|
+
return context;
|
|
1808
|
+
}
|
|
1809
|
+
function useAppName() {
|
|
1810
|
+
return useApp().appName;
|
|
1811
|
+
}
|
|
1812
|
+
function useBasename() {
|
|
1813
|
+
return useApp().basename;
|
|
1814
|
+
}
|
|
1815
|
+
function useEnv() {
|
|
1816
|
+
return useApp().env;
|
|
1817
|
+
}
|
|
1818
|
+
function useIsDev() {
|
|
1819
|
+
return useApp().isDev;
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
// src/runtime/error-boundary.tsx
|
|
1823
|
+
import { Component } from "react";
|
|
1824
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
1825
|
+
var ErrorBoundary = class extends Component {
|
|
1826
|
+
constructor(props) {
|
|
1827
|
+
super(props);
|
|
1828
|
+
this.state = { hasError: false, error: null };
|
|
1829
|
+
}
|
|
1830
|
+
static getDerivedStateFromError(error) {
|
|
1831
|
+
return { hasError: true, error };
|
|
1832
|
+
}
|
|
1833
|
+
componentDidCatch(error, errorInfo) {
|
|
1834
|
+
console.error("[ErrorBoundary] Caught error:", error, errorInfo);
|
|
1835
|
+
this.props.onError?.(error, errorInfo);
|
|
1836
|
+
}
|
|
1837
|
+
reset = () => {
|
|
1838
|
+
this.setState({ hasError: false, error: null });
|
|
1839
|
+
};
|
|
1840
|
+
render() {
|
|
1841
|
+
const { hasError, error } = this.state;
|
|
1842
|
+
const { children, fallback } = this.props;
|
|
1843
|
+
if (hasError && error) {
|
|
1844
|
+
if (typeof fallback === "function") {
|
|
1845
|
+
return fallback(error, this.reset);
|
|
1846
|
+
}
|
|
1847
|
+
if (fallback) {
|
|
1848
|
+
return fallback;
|
|
1849
|
+
}
|
|
1850
|
+
return /* @__PURE__ */ jsx2(DefaultErrorFallback, { error, onReset: this.reset });
|
|
1851
|
+
}
|
|
1852
|
+
return children;
|
|
1853
|
+
}
|
|
1854
|
+
};
|
|
1855
|
+
function DefaultErrorFallback({ error, onReset }) {
|
|
1856
|
+
const isDev = process.env.NODE_ENV === "development";
|
|
1857
|
+
return /* @__PURE__ */ jsx2(
|
|
1858
|
+
"div",
|
|
1859
|
+
{
|
|
1860
|
+
style: {
|
|
1861
|
+
padding: 24,
|
|
1862
|
+
display: "flex",
|
|
1863
|
+
flexDirection: "column",
|
|
1864
|
+
alignItems: "center",
|
|
1865
|
+
justifyContent: "center",
|
|
1866
|
+
minHeight: "100vh",
|
|
1867
|
+
backgroundColor: "#f5f5f5"
|
|
1868
|
+
},
|
|
1869
|
+
children: /* @__PURE__ */ jsxs(
|
|
1870
|
+
"div",
|
|
1871
|
+
{
|
|
1872
|
+
style: {
|
|
1873
|
+
maxWidth: 600,
|
|
1874
|
+
padding: 32,
|
|
1875
|
+
backgroundColor: "#fff",
|
|
1876
|
+
borderRadius: 8,
|
|
1877
|
+
boxShadow: "0 2px 8px rgba(0,0,0,0.1)"
|
|
1878
|
+
},
|
|
1879
|
+
children: [
|
|
1880
|
+
/* @__PURE__ */ jsx2("h1", { style: { color: "#ff4d4f", marginBottom: 16 }, children: "\u9875\u9762\u51FA\u9519\u4E86" }),
|
|
1881
|
+
/* @__PURE__ */ jsx2("p", { style: { color: "#666", marginBottom: 16 }, children: "\u62B1\u6B49\uFF0C\u9875\u9762\u9047\u5230\u4E86\u4E00\u4E9B\u95EE\u9898\u3002\u8BF7\u5C1D\u8BD5\u5237\u65B0\u9875\u9762\u6216\u8054\u7CFB\u7BA1\u7406\u5458\u3002" }),
|
|
1882
|
+
isDev && /* @__PURE__ */ jsxs(
|
|
1883
|
+
"details",
|
|
1884
|
+
{
|
|
1885
|
+
style: {
|
|
1886
|
+
marginBottom: 16,
|
|
1887
|
+
padding: 12,
|
|
1888
|
+
backgroundColor: "#fff2f0",
|
|
1889
|
+
borderRadius: 4,
|
|
1890
|
+
border: "1px solid #ffccc7"
|
|
1891
|
+
},
|
|
1892
|
+
children: [
|
|
1893
|
+
/* @__PURE__ */ jsx2("summary", { style: { cursor: "pointer", fontWeight: "bold" }, children: "\u9519\u8BEF\u8BE6\u60C5\uFF08\u4EC5\u5F00\u53D1\u73AF\u5883\u53EF\u89C1\uFF09" }),
|
|
1894
|
+
/* @__PURE__ */ jsxs(
|
|
1895
|
+
"pre",
|
|
1896
|
+
{
|
|
1897
|
+
style: {
|
|
1898
|
+
marginTop: 8,
|
|
1899
|
+
padding: 8,
|
|
1900
|
+
backgroundColor: "#fff",
|
|
1901
|
+
borderRadius: 4,
|
|
1902
|
+
overflow: "auto",
|
|
1903
|
+
fontSize: 12
|
|
1904
|
+
},
|
|
1905
|
+
children: [
|
|
1906
|
+
error.message,
|
|
1907
|
+
"\n\n",
|
|
1908
|
+
error.stack
|
|
1909
|
+
]
|
|
1910
|
+
}
|
|
1911
|
+
)
|
|
1912
|
+
]
|
|
1913
|
+
}
|
|
1914
|
+
),
|
|
1915
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
|
|
1916
|
+
/* @__PURE__ */ jsx2(
|
|
1917
|
+
"button",
|
|
1918
|
+
{
|
|
1919
|
+
onClick: onReset,
|
|
1920
|
+
style: {
|
|
1921
|
+
padding: "8px 16px",
|
|
1922
|
+
backgroundColor: "#1890ff",
|
|
1923
|
+
color: "#fff",
|
|
1924
|
+
border: "none",
|
|
1925
|
+
borderRadius: 4,
|
|
1926
|
+
cursor: "pointer"
|
|
1927
|
+
},
|
|
1928
|
+
children: "\u91CD\u8BD5"
|
|
1929
|
+
}
|
|
1930
|
+
),
|
|
1931
|
+
/* @__PURE__ */ jsx2(
|
|
1932
|
+
"button",
|
|
1933
|
+
{
|
|
1934
|
+
onClick: () => window.location.reload(),
|
|
1935
|
+
style: {
|
|
1936
|
+
padding: "8px 16px",
|
|
1937
|
+
backgroundColor: "#fff",
|
|
1938
|
+
color: "#666",
|
|
1939
|
+
border: "1px solid #d9d9d9",
|
|
1940
|
+
borderRadius: 4,
|
|
1941
|
+
cursor: "pointer"
|
|
1942
|
+
},
|
|
1943
|
+
children: "\u5237\u65B0\u9875\u9762"
|
|
1944
|
+
}
|
|
1945
|
+
)
|
|
1946
|
+
] })
|
|
1947
|
+
]
|
|
1948
|
+
}
|
|
1949
|
+
)
|
|
1950
|
+
}
|
|
1951
|
+
);
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
// src/runtime/providers.tsx
|
|
1955
|
+
import {
|
|
1956
|
+
StrictMode,
|
|
1957
|
+
Suspense
|
|
1958
|
+
} from "react";
|
|
1959
|
+
import { RouterProvider } from "react-router";
|
|
1960
|
+
import { ConfigProvider } from "antd";
|
|
1961
|
+
import zhCN from "antd/locale/zh_CN.js";
|
|
1962
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
1963
|
+
function DefaultLoading() {
|
|
1964
|
+
return /* @__PURE__ */ jsx3(
|
|
1965
|
+
"div",
|
|
1966
|
+
{
|
|
1967
|
+
style: {
|
|
1968
|
+
display: "flex",
|
|
1969
|
+
alignItems: "center",
|
|
1970
|
+
justifyContent: "center",
|
|
1971
|
+
height: "100vh",
|
|
1972
|
+
fontSize: 16,
|
|
1973
|
+
color: "#999"
|
|
1974
|
+
},
|
|
1975
|
+
children: "\u52A0\u8F7D\u4E2D..."
|
|
1976
|
+
}
|
|
1977
|
+
);
|
|
1978
|
+
}
|
|
1979
|
+
function composeProviders(providers) {
|
|
1980
|
+
const sortedProviders = [...providers].sort(
|
|
1981
|
+
(a, b) => (a.order ?? 100) - (b.order ?? 100)
|
|
1982
|
+
);
|
|
1983
|
+
return function ComposedProviders({ children }) {
|
|
1984
|
+
return sortedProviders.reduceRight((acc, { component: Provider, props }) => {
|
|
1985
|
+
return /* @__PURE__ */ jsx3(Provider, { ...props, children: acc });
|
|
1986
|
+
}, children);
|
|
1987
|
+
};
|
|
1988
|
+
}
|
|
1989
|
+
function RootProvider({ config, children }) {
|
|
1990
|
+
const {
|
|
1991
|
+
appName = "app",
|
|
1992
|
+
router,
|
|
1993
|
+
basename: basename2 = "/",
|
|
1994
|
+
strictMode = true,
|
|
1995
|
+
antd = { enabled: true },
|
|
1996
|
+
providers = [],
|
|
1997
|
+
lifecycle
|
|
1998
|
+
} = config;
|
|
1999
|
+
const appContextValue = {
|
|
2000
|
+
appName,
|
|
2001
|
+
basename: basename2,
|
|
2002
|
+
isDev: process.env.NODE_ENV === "development",
|
|
2003
|
+
env: typeof process !== "undefined" ? process.env : {}
|
|
2004
|
+
};
|
|
2005
|
+
const allProviders = [
|
|
2006
|
+
// 应用上下文 Provider(最外层)
|
|
2007
|
+
{
|
|
2008
|
+
component: AppContextProvider,
|
|
2009
|
+
props: { value: appContextValue },
|
|
2010
|
+
order: 0
|
|
2011
|
+
},
|
|
2012
|
+
// Ant Design ConfigProvider
|
|
2013
|
+
...antd.enabled !== false ? [
|
|
2014
|
+
{
|
|
2015
|
+
component: ConfigProvider,
|
|
2016
|
+
props: {
|
|
2017
|
+
locale: antd.locale || zhCN,
|
|
2018
|
+
theme: antd.theme,
|
|
2019
|
+
...antd.configProvider
|
|
2020
|
+
},
|
|
2021
|
+
order: 10
|
|
2022
|
+
}
|
|
2023
|
+
] : [],
|
|
2024
|
+
// 用户自定义 Providers
|
|
2025
|
+
...providers.map((p) => ({ ...p, order: p.order ?? 50 }))
|
|
2026
|
+
];
|
|
2027
|
+
const ComposedProviders = composeProviders(allProviders);
|
|
2028
|
+
const content = router ? /* @__PURE__ */ jsx3(Suspense, { fallback: /* @__PURE__ */ jsx3(DefaultLoading, {}), children: /* @__PURE__ */ jsx3(RouterProvider, { router }) }) : children;
|
|
2029
|
+
const app = /* @__PURE__ */ jsx3(ErrorBoundary, { onError: lifecycle?.onError, children: /* @__PURE__ */ jsx3(ComposedProviders, { children: content }) });
|
|
2030
|
+
if (strictMode) {
|
|
2031
|
+
return /* @__PURE__ */ jsx3(StrictMode, { children: app });
|
|
2032
|
+
}
|
|
2033
|
+
return app;
|
|
2034
|
+
}
|
|
2035
|
+
function createProvider(component, props, order) {
|
|
2036
|
+
return {
|
|
2037
|
+
component,
|
|
2038
|
+
props,
|
|
2039
|
+
order
|
|
2040
|
+
};
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
// src/runtime/bootstrap.tsx
|
|
2044
|
+
import { createRoot } from "react-dom/client";
|
|
2045
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
2046
|
+
var root = null;
|
|
2047
|
+
async function resolveAppConfig(config) {
|
|
2048
|
+
if (config.router || !config.conventionalRoutes) {
|
|
2049
|
+
return config;
|
|
2050
|
+
}
|
|
2051
|
+
try {
|
|
2052
|
+
const routesModule = await import("@ywkf/routes");
|
|
2053
|
+
const createRouter = routesModule.createRouter || routesModule.default?.createRouter;
|
|
2054
|
+
if (typeof createRouter === "function") {
|
|
2055
|
+
const router = createRouter(config.basename);
|
|
2056
|
+
console.log("[ywkf] \u5DF2\u81EA\u52A8\u6CE8\u5165\u7EA6\u5B9A\u5F0F\u8DEF\u7531");
|
|
2057
|
+
return { ...config, router };
|
|
2058
|
+
} else {
|
|
2059
|
+
console.warn("[ywkf] @ywkf/routes \u672A\u5BFC\u51FA createRouter \u51FD\u6570");
|
|
2060
|
+
}
|
|
2061
|
+
} catch (error) {
|
|
2062
|
+
console.error("[ywkf] \u52A0\u8F7D\u7EA6\u5B9A\u5F0F\u8DEF\u7531\u5931\u8D25:", error);
|
|
2063
|
+
console.warn("[ywkf] \u8BF7\u786E\u4FDD\u5DF2\u542F\u7528\u7EA6\u5B9A\u5F0F\u8DEF\u7531\u4E14 src/pages \u76EE\u5F55\u5B58\u5728");
|
|
2064
|
+
}
|
|
2065
|
+
return config;
|
|
2066
|
+
}
|
|
2067
|
+
async function bootstrap(config) {
|
|
2068
|
+
const { rootId = "root", lifecycle } = config;
|
|
2069
|
+
const resolvedConfig = await resolveAppConfig(config);
|
|
2070
|
+
if (lifecycle?.onBeforeMount) {
|
|
2071
|
+
await lifecycle.onBeforeMount();
|
|
2072
|
+
}
|
|
2073
|
+
const rootElement = document.getElementById(rootId);
|
|
2074
|
+
if (!rootElement) {
|
|
2075
|
+
throw new Error(`\u627E\u4E0D\u5230\u6839\u5143\u7D20 #${rootId}`);
|
|
2076
|
+
}
|
|
2077
|
+
root = createRoot(rootElement);
|
|
2078
|
+
root.render(/* @__PURE__ */ jsx4(RootProvider, { config: resolvedConfig }));
|
|
2079
|
+
if (lifecycle?.onMounted) {
|
|
2080
|
+
setTimeout(() => {
|
|
2081
|
+
lifecycle.onMounted?.();
|
|
2082
|
+
}, 0);
|
|
2083
|
+
}
|
|
2084
|
+
if (lifecycle?.onUnmount) {
|
|
2085
|
+
window.addEventListener("beforeunload", () => {
|
|
2086
|
+
lifecycle.onUnmount?.();
|
|
2087
|
+
});
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
function unmount() {
|
|
2091
|
+
if (root) {
|
|
2092
|
+
root.unmount();
|
|
2093
|
+
root = null;
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
function createMicroApp(getConfig) {
|
|
2097
|
+
let microRoot = null;
|
|
2098
|
+
return {
|
|
2099
|
+
/**
|
|
2100
|
+
* 微前端 bootstrap 生命周期
|
|
2101
|
+
*/
|
|
2102
|
+
async bootstrap() {
|
|
2103
|
+
console.log("[MicroApp] bootstrap");
|
|
2104
|
+
},
|
|
2105
|
+
/**
|
|
2106
|
+
* 微前端 mount 生命周期
|
|
2107
|
+
*/
|
|
2108
|
+
async mount(props) {
|
|
2109
|
+
console.log("[MicroApp] mount", props);
|
|
2110
|
+
const config = getConfig(props);
|
|
2111
|
+
const resolvedConfig = await resolveAppConfig(config);
|
|
2112
|
+
const container = props?.masterProps?.container;
|
|
2113
|
+
const rootId = resolvedConfig.rootId || "root";
|
|
2114
|
+
const rootElement = container ? container.querySelector(`#${rootId}`) : document.getElementById(rootId);
|
|
2115
|
+
if (!rootElement) {
|
|
2116
|
+
throw new Error(`[MicroApp] \u627E\u4E0D\u5230\u6839\u5143\u7D20 #${rootId}`);
|
|
2117
|
+
}
|
|
2118
|
+
if (resolvedConfig.lifecycle?.onBeforeMount) {
|
|
2119
|
+
await resolvedConfig.lifecycle.onBeforeMount();
|
|
2120
|
+
}
|
|
2121
|
+
microRoot = createRoot(rootElement);
|
|
2122
|
+
microRoot.render(/* @__PURE__ */ jsx4(RootProvider, { config: resolvedConfig }));
|
|
2123
|
+
if (resolvedConfig.lifecycle?.onMounted) {
|
|
2124
|
+
setTimeout(() => {
|
|
2125
|
+
resolvedConfig.lifecycle?.onMounted?.();
|
|
2126
|
+
}, 0);
|
|
2127
|
+
}
|
|
2128
|
+
},
|
|
2129
|
+
/**
|
|
2130
|
+
* 微前端 unmount 生命周期
|
|
2131
|
+
*/
|
|
2132
|
+
async unmount(props) {
|
|
2133
|
+
console.log("[MicroApp] unmount", props);
|
|
2134
|
+
const config = getConfig(props);
|
|
2135
|
+
if (config.lifecycle?.onUnmount) {
|
|
2136
|
+
config.lifecycle.onUnmount();
|
|
2137
|
+
}
|
|
2138
|
+
if (microRoot) {
|
|
2139
|
+
microRoot.unmount();
|
|
2140
|
+
microRoot = null;
|
|
2141
|
+
}
|
|
2142
|
+
},
|
|
2143
|
+
/**
|
|
2144
|
+
* 微前端 update 生命周期(可选)
|
|
2145
|
+
*/
|
|
2146
|
+
async update(props) {
|
|
2147
|
+
console.log("[MicroApp] update", props);
|
|
2148
|
+
}
|
|
2149
|
+
};
|
|
2150
|
+
}
|
|
2151
|
+
function isMicroAppEnv() {
|
|
2152
|
+
if (window.__POWERED_BY_QIANKUN__) {
|
|
2153
|
+
return true;
|
|
2154
|
+
}
|
|
2155
|
+
if (window.__GARFISH__) {
|
|
2156
|
+
return true;
|
|
2157
|
+
}
|
|
2158
|
+
return false;
|
|
2159
|
+
}
|
|
2160
|
+
function getMicroAppPublicPath() {
|
|
2161
|
+
if (window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__) {
|
|
2162
|
+
return window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
|
|
2163
|
+
}
|
|
2164
|
+
return "/";
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
// src/plugin/define.ts
|
|
2168
|
+
function definePlugin(plugin) {
|
|
2169
|
+
return plugin;
|
|
2170
|
+
}
|
|
2171
|
+
function createPlugin(factory) {
|
|
2172
|
+
return factory;
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
// src/plugin/manager.ts
|
|
2176
|
+
function createLogger(pluginName) {
|
|
2177
|
+
const prefix = `[${pluginName}]`;
|
|
2178
|
+
return {
|
|
2179
|
+
info: (msg) => console.log(`${prefix} ${msg}`),
|
|
2180
|
+
warn: (msg) => console.warn(`${prefix} ${msg}`),
|
|
2181
|
+
error: (msg) => console.error(`${prefix} ${msg}`),
|
|
2182
|
+
debug: (msg) => {
|
|
2183
|
+
if (process.env.DEBUG) {
|
|
2184
|
+
console.debug(`${prefix} ${msg}`);
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
};
|
|
2188
|
+
}
|
|
2189
|
+
async function resolvePlugin(pluginConfig, cwd) {
|
|
2190
|
+
let plugin;
|
|
2191
|
+
let options = {};
|
|
2192
|
+
if (typeof pluginConfig === "string") {
|
|
2193
|
+
const module = await import(pluginConfig);
|
|
2194
|
+
plugin = module.default || module;
|
|
2195
|
+
} else if (Array.isArray(pluginConfig)) {
|
|
2196
|
+
const [pluginOrName, pluginOptions] = pluginConfig;
|
|
2197
|
+
options = pluginOptions;
|
|
2198
|
+
if (typeof pluginOrName === "string") {
|
|
2199
|
+
const module = await import(pluginOrName);
|
|
2200
|
+
plugin = module.default || module;
|
|
2201
|
+
} else {
|
|
2202
|
+
plugin = pluginOrName;
|
|
2203
|
+
}
|
|
2204
|
+
} else {
|
|
2205
|
+
plugin = pluginConfig;
|
|
2206
|
+
}
|
|
2207
|
+
if (typeof plugin === "function") {
|
|
2208
|
+
plugin = plugin(options);
|
|
2209
|
+
}
|
|
2210
|
+
return { plugin, options };
|
|
2211
|
+
}
|
|
2212
|
+
var PluginManager = class {
|
|
2213
|
+
plugins = /* @__PURE__ */ new Map();
|
|
2214
|
+
context;
|
|
2215
|
+
constructor(config, cwd, isDev) {
|
|
2216
|
+
this.context = {
|
|
2217
|
+
cwd,
|
|
2218
|
+
isDev,
|
|
2219
|
+
isProd: !isDev,
|
|
2220
|
+
config,
|
|
2221
|
+
logger: createLogger("PluginManager")
|
|
2222
|
+
};
|
|
2223
|
+
}
|
|
2224
|
+
/**
|
|
2225
|
+
* 加载并初始化所有插件
|
|
2226
|
+
*/
|
|
2227
|
+
async loadPlugins(pluginConfigs) {
|
|
2228
|
+
for (const pluginConfig of pluginConfigs) {
|
|
2229
|
+
try {
|
|
2230
|
+
const { plugin } = await resolvePlugin(pluginConfig, this.context.cwd);
|
|
2231
|
+
if (this.plugins.has(plugin.name)) {
|
|
2232
|
+
this.context.logger.warn(`\u63D2\u4EF6 ${plugin.name} \u5DF2\u52A0\u8F7D\uFF0C\u8DF3\u8FC7\u91CD\u590D\u52A0\u8F7D`);
|
|
2233
|
+
continue;
|
|
2234
|
+
}
|
|
2235
|
+
const pluginContext = {
|
|
2236
|
+
...this.context,
|
|
2237
|
+
logger: createLogger(plugin.name)
|
|
2238
|
+
};
|
|
2239
|
+
const hooks = await plugin.setup(pluginContext);
|
|
2240
|
+
this.plugins.set(plugin.name, { plugin, hooks });
|
|
2241
|
+
this.context.logger.info(`\u5DF2\u52A0\u8F7D\u63D2\u4EF6: ${plugin.name}`);
|
|
2242
|
+
} catch (error) {
|
|
2243
|
+
this.context.logger.error(
|
|
2244
|
+
`\u52A0\u8F7D\u63D2\u4EF6\u5931\u8D25: ${String(pluginConfig)} - ${error}`
|
|
2245
|
+
);
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
/**
|
|
2250
|
+
* 执行 modifyRspackConfig 钩子
|
|
2251
|
+
*/
|
|
2252
|
+
async applyRspackConfigHooks(config) {
|
|
2253
|
+
let result = config;
|
|
2254
|
+
for (const [name, { hooks }] of this.plugins) {
|
|
2255
|
+
if (hooks.modifyRspackConfig) {
|
|
2256
|
+
try {
|
|
2257
|
+
const modified = await hooks.modifyRspackConfig(result, this.context);
|
|
2258
|
+
if (modified) {
|
|
2259
|
+
result = modified;
|
|
2260
|
+
}
|
|
2261
|
+
} catch (error) {
|
|
2262
|
+
this.context.logger.error(
|
|
2263
|
+
`\u63D2\u4EF6 ${name} modifyRspackConfig \u6267\u884C\u5931\u8D25: ${error}`
|
|
2264
|
+
);
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2268
|
+
return result;
|
|
2269
|
+
}
|
|
2270
|
+
/**
|
|
2271
|
+
* 执行 modifyRoutes 钩子
|
|
2272
|
+
*/
|
|
2273
|
+
async applyRoutesHooks(routes) {
|
|
2274
|
+
let result = routes;
|
|
2275
|
+
for (const [name, { hooks }] of this.plugins) {
|
|
2276
|
+
if (hooks.modifyRoutes) {
|
|
2277
|
+
try {
|
|
2278
|
+
const modified = await hooks.modifyRoutes(result, this.context);
|
|
2279
|
+
if (modified) {
|
|
2280
|
+
result = modified;
|
|
2281
|
+
}
|
|
2282
|
+
} catch (error) {
|
|
2283
|
+
this.context.logger.error(
|
|
2284
|
+
`\u63D2\u4EF6 ${name} modifyRoutes \u6267\u884C\u5931\u8D25: ${error}`
|
|
2285
|
+
);
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
return result;
|
|
2290
|
+
}
|
|
2291
|
+
/**
|
|
2292
|
+
* 执行 modifyAppConfig 钩子
|
|
2293
|
+
*/
|
|
2294
|
+
applyAppConfigHooks(appConfig) {
|
|
2295
|
+
let result = appConfig;
|
|
2296
|
+
for (const [name, { hooks }] of this.plugins) {
|
|
2297
|
+
if (hooks.modifyAppConfig) {
|
|
2298
|
+
try {
|
|
2299
|
+
const modified = hooks.modifyAppConfig(result, this.context);
|
|
2300
|
+
if (modified) {
|
|
2301
|
+
result = modified;
|
|
2302
|
+
}
|
|
2303
|
+
} catch (error) {
|
|
2304
|
+
this.context.logger.error(
|
|
2305
|
+
`\u63D2\u4EF6 ${name} modifyAppConfig \u6267\u884C\u5931\u8D25: ${error}`
|
|
2306
|
+
);
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
return result;
|
|
2311
|
+
}
|
|
2312
|
+
/**
|
|
2313
|
+
* 收集所有插件的 Provider
|
|
2314
|
+
*/
|
|
2315
|
+
collectProviders() {
|
|
2316
|
+
const providers = [];
|
|
2317
|
+
for (const [name, { hooks }] of this.plugins) {
|
|
2318
|
+
if (hooks.addProvider) {
|
|
2319
|
+
try {
|
|
2320
|
+
const result = hooks.addProvider(this.context);
|
|
2321
|
+
if (Array.isArray(result)) {
|
|
2322
|
+
providers.push(...result);
|
|
2323
|
+
} else if (result) {
|
|
2324
|
+
providers.push(result);
|
|
2325
|
+
}
|
|
2326
|
+
} catch (error) {
|
|
2327
|
+
this.context.logger.error(
|
|
2328
|
+
`\u63D2\u4EF6 ${name} addProvider \u6267\u884C\u5931\u8D25: ${error}`
|
|
2329
|
+
);
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
return providers;
|
|
2334
|
+
}
|
|
2335
|
+
/**
|
|
2336
|
+
* 执行 beforeBuild 钩子
|
|
2337
|
+
*/
|
|
2338
|
+
async runBeforeBuild() {
|
|
2339
|
+
for (const [name, { hooks }] of this.plugins) {
|
|
2340
|
+
if (hooks.beforeBuild) {
|
|
2341
|
+
try {
|
|
2342
|
+
await hooks.beforeBuild(this.context);
|
|
2343
|
+
} catch (error) {
|
|
2344
|
+
this.context.logger.error(
|
|
2345
|
+
`\u63D2\u4EF6 ${name} beforeBuild \u6267\u884C\u5931\u8D25: ${error}`
|
|
2346
|
+
);
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
/**
|
|
2352
|
+
* 执行 afterBuild 钩子
|
|
2353
|
+
*/
|
|
2354
|
+
async runAfterBuild(stats) {
|
|
2355
|
+
for (const [name, { hooks }] of this.plugins) {
|
|
2356
|
+
if (hooks.afterBuild) {
|
|
2357
|
+
try {
|
|
2358
|
+
await hooks.afterBuild(this.context, stats);
|
|
2359
|
+
} catch (error) {
|
|
2360
|
+
this.context.logger.error(
|
|
2361
|
+
`\u63D2\u4EF6 ${name} afterBuild \u6267\u884C\u5931\u8D25: ${error}`
|
|
2362
|
+
);
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
/**
|
|
2368
|
+
* 执行 beforeDevServer 钩子
|
|
2369
|
+
*/
|
|
2370
|
+
async runBeforeDevServer() {
|
|
2371
|
+
for (const [name, { hooks }] of this.plugins) {
|
|
2372
|
+
if (hooks.beforeDevServer) {
|
|
2373
|
+
try {
|
|
2374
|
+
await hooks.beforeDevServer(this.context);
|
|
2375
|
+
} catch (error) {
|
|
2376
|
+
this.context.logger.error(
|
|
2377
|
+
`\u63D2\u4EF6 ${name} beforeDevServer \u6267\u884C\u5931\u8D25: ${error}`
|
|
2378
|
+
);
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
/**
|
|
2384
|
+
* 执行 afterDevServer 钩子
|
|
2385
|
+
*/
|
|
2386
|
+
async runAfterDevServer(server) {
|
|
2387
|
+
for (const [name, { hooks }] of this.plugins) {
|
|
2388
|
+
if (hooks.afterDevServer) {
|
|
2389
|
+
try {
|
|
2390
|
+
await hooks.afterDevServer(this.context, server);
|
|
2391
|
+
} catch (error) {
|
|
2392
|
+
this.context.logger.error(
|
|
2393
|
+
`\u63D2\u4EF6 ${name} afterDevServer \u6267\u884C\u5931\u8D25: ${error}`
|
|
2394
|
+
);
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
2399
|
+
/**
|
|
2400
|
+
* 获取所有已加载的插件名称
|
|
2401
|
+
*/
|
|
2402
|
+
getPluginNames() {
|
|
2403
|
+
return Array.from(this.plugins.keys());
|
|
2404
|
+
}
|
|
2405
|
+
/**
|
|
2406
|
+
* 获取所有已加载的插件
|
|
2407
|
+
*/
|
|
2408
|
+
getPlugins() {
|
|
2409
|
+
return Array.from(this.plugins.values()).map((p) => p.plugin);
|
|
2410
|
+
}
|
|
2411
|
+
/**
|
|
2412
|
+
* 获取所有已加载的插件钩子
|
|
2413
|
+
*/
|
|
2414
|
+
getPluginHooks() {
|
|
2415
|
+
return Array.from(this.plugins.values()).map((p) => p.hooks);
|
|
2416
|
+
}
|
|
2417
|
+
};
|
|
2418
|
+
|
|
2419
|
+
// src/plugin/builtin/garfish.ts
|
|
2420
|
+
var garfishPlugin = createPlugin((options = {}) => ({
|
|
2421
|
+
name: "@4399ywkf/plugin-garfish",
|
|
2422
|
+
version: "1.0.0",
|
|
2423
|
+
description: "Garfish \u5FAE\u524D\u7AEF\u63D2\u4EF6",
|
|
2424
|
+
setup(context) {
|
|
2425
|
+
const { appName, master = false, apps = [], sandbox = {} } = options;
|
|
2426
|
+
const { config, logger, isDev } = context;
|
|
2427
|
+
const finalAppName = appName || config.appName;
|
|
2428
|
+
const hooks = {
|
|
2429
|
+
// ===== 构建阶段钩子 =====
|
|
2430
|
+
modifyRspackConfig(rspackConfig) {
|
|
2431
|
+
if (!master) {
|
|
2432
|
+
rspackConfig.output = {
|
|
2433
|
+
...rspackConfig.output,
|
|
2434
|
+
library: finalAppName,
|
|
2435
|
+
libraryTarget: "umd",
|
|
2436
|
+
globalObject: "window",
|
|
2437
|
+
chunkLoadingGlobal: `garfish_${finalAppName}`
|
|
2438
|
+
};
|
|
2439
|
+
logger.info(`\u914D\u7F6E UMD \u8F93\u51FA: ${finalAppName}`);
|
|
2440
|
+
}
|
|
2441
|
+
return rspackConfig;
|
|
2442
|
+
},
|
|
2443
|
+
beforeDevServer() {
|
|
2444
|
+
if (master) {
|
|
2445
|
+
logger.info("Garfish \u4E3B\u5E94\u7528\u6A21\u5F0F");
|
|
2446
|
+
logger.info(`\u5B50\u5E94\u7528\u5217\u8868: ${apps.map((a) => a.name).join(", ")}`);
|
|
2447
|
+
} else {
|
|
2448
|
+
logger.info(`Garfish \u5B50\u5E94\u7528\u6A21\u5F0F: ${finalAppName}`);
|
|
2449
|
+
}
|
|
2450
|
+
},
|
|
2451
|
+
// ===== 代码生成阶段钩子 =====
|
|
2452
|
+
injectEntry(ctx) {
|
|
2453
|
+
if (master) {
|
|
2454
|
+
return {};
|
|
2455
|
+
}
|
|
2456
|
+
return {
|
|
2457
|
+
imports: [
|
|
2458
|
+
`import { microApp, isMicroAppEnv } from "./bootstrap";`
|
|
2459
|
+
],
|
|
2460
|
+
exports: [
|
|
2461
|
+
`// Garfish \u5FAE\u524D\u7AEF\u751F\u547D\u5468\u671F\u5BFC\u51FA`,
|
|
2462
|
+
`export const { mount, unmount } = microApp;`,
|
|
2463
|
+
`export const provider = microApp;`,
|
|
2464
|
+
`export default microApp;`
|
|
2465
|
+
],
|
|
2466
|
+
topLevel: [
|
|
2467
|
+
`// Garfish \u5FAE\u524D\u7AEF\uFF1A\u72EC\u7ACB\u8FD0\u884C\u5224\u65AD`,
|
|
2468
|
+
`const shouldRunIndependently = !isMicroAppEnv();`
|
|
2469
|
+
]
|
|
2470
|
+
};
|
|
2471
|
+
},
|
|
2472
|
+
injectBootstrap(ctx) {
|
|
2473
|
+
const imports = [
|
|
2474
|
+
`import { createMicroApp, isMicroAppEnv } from "@4399ywkf/core/runtime";`
|
|
2475
|
+
];
|
|
2476
|
+
const exports = [
|
|
2477
|
+
`/**`,
|
|
2478
|
+
` * Garfish \u5FAE\u524D\u7AEF\u5E94\u7528\u5B9E\u4F8B`,
|
|
2479
|
+
` */`,
|
|
2480
|
+
`export const microApp = createMicroApp(createAppConfig);`,
|
|
2481
|
+
``,
|
|
2482
|
+
`/**`,
|
|
2483
|
+
` * \u5224\u65AD\u662F\u5426\u5728\u5FAE\u524D\u7AEF\u73AF\u5883`,
|
|
2484
|
+
` */`,
|
|
2485
|
+
`export { isMicroAppEnv };`
|
|
2486
|
+
];
|
|
2487
|
+
const topLevel = [];
|
|
2488
|
+
if (master && apps.length > 0) {
|
|
2489
|
+
imports.push(`import Garfish from "garfish";`);
|
|
2490
|
+
topLevel.push(generateMasterInitCode(apps, config.router.basename));
|
|
2491
|
+
}
|
|
2492
|
+
return { imports, exports, topLevel };
|
|
2493
|
+
},
|
|
2494
|
+
modifyBootstrapCode(code, ctx) {
|
|
2495
|
+
if (!master) {
|
|
2496
|
+
return code.replace(
|
|
2497
|
+
/antd:\s*\{\s*enabled:\s*true/,
|
|
2498
|
+
"antd: { enabled: false"
|
|
2499
|
+
);
|
|
2500
|
+
}
|
|
2501
|
+
return code;
|
|
2502
|
+
},
|
|
2503
|
+
// ===== 运行时阶段钩子 =====
|
|
2504
|
+
modifyAppConfig(appConfig) {
|
|
2505
|
+
return {
|
|
2506
|
+
...appConfig,
|
|
2507
|
+
// 微前端模式下禁用 antd 样式注入(由主应用管理)
|
|
2508
|
+
antd: {
|
|
2509
|
+
...appConfig.antd,
|
|
2510
|
+
enabled: master
|
|
2511
|
+
}
|
|
2512
|
+
};
|
|
2513
|
+
}
|
|
2514
|
+
};
|
|
2515
|
+
return hooks;
|
|
2516
|
+
}
|
|
2517
|
+
}));
|
|
2518
|
+
function generateMasterInitCode(apps, basename2) {
|
|
2519
|
+
const appConfigs = apps.map(
|
|
2520
|
+
(app) => `{
|
|
2521
|
+
name: "${app.name}",
|
|
2522
|
+
entry: "${app.entry}",
|
|
2523
|
+
activeWhen: "${app.activeRule}",
|
|
2524
|
+
${app.basename ? `basename: "${app.basename}",` : ""}
|
|
2525
|
+
}`
|
|
2526
|
+
).join(",\n ");
|
|
2527
|
+
return `
|
|
2528
|
+
/**
|
|
2529
|
+
* Garfish \u4E3B\u5E94\u7528\u521D\u59CB\u5316
|
|
2530
|
+
* \u5728 runApp \u4E4B\u524D\u8C03\u7528
|
|
2531
|
+
*/
|
|
2532
|
+
export async function initGarfishMaster(): Promise<void> {
|
|
2533
|
+
await Garfish.run({
|
|
2534
|
+
basename: "${basename2 || "/"}",
|
|
2535
|
+
apps: [
|
|
2536
|
+
${appConfigs}
|
|
2537
|
+
],
|
|
2538
|
+
});
|
|
2539
|
+
}
|
|
2540
|
+
`;
|
|
2541
|
+
}
|
|
2542
|
+
|
|
2543
|
+
// src/plugin/builtin/tailwind.ts
|
|
2544
|
+
import { existsSync as existsSync7, writeFileSync as writeFileSync3 } from "fs";
|
|
2545
|
+
import { join as join7 } from "path";
|
|
2546
|
+
var tailwindPlugin = createPlugin((options = {}) => ({
|
|
2547
|
+
name: "@4399ywkf/plugin-tailwind",
|
|
2548
|
+
version: "1.0.0",
|
|
2549
|
+
description: "Tailwind CSS \u96C6\u6210\u63D2\u4EF6",
|
|
2550
|
+
setup(context) {
|
|
2551
|
+
const {
|
|
2552
|
+
darkModeSelector = '&:where([data-theme="dark"], [data-theme="dark"] *)',
|
|
2553
|
+
autoConfig = true,
|
|
2554
|
+
extraCSS = ""
|
|
2555
|
+
} = options;
|
|
2556
|
+
const { cwd, logger } = context;
|
|
2557
|
+
if (autoConfig) {
|
|
2558
|
+
ensurePostcssConfig(cwd, logger);
|
|
2559
|
+
}
|
|
2560
|
+
const hooks = {
|
|
2561
|
+
modifyRspackConfig(rspackConfig) {
|
|
2562
|
+
logger.info("Tailwind CSS \u5DF2\u542F\u7528");
|
|
2563
|
+
const rules = rspackConfig.module?.rules || [];
|
|
2564
|
+
const postcssConfigPath = join7(cwd, "postcss.config.js");
|
|
2565
|
+
const isProd = rspackConfig.mode === "production";
|
|
2566
|
+
const tailwindRule = {
|
|
2567
|
+
test: /tailwind\.css$/,
|
|
2568
|
+
use: [
|
|
2569
|
+
isProd ? "mini-css-extract-plugin/loader" : "style-loader",
|
|
2570
|
+
"css-loader",
|
|
2571
|
+
{
|
|
2572
|
+
loader: "postcss-loader",
|
|
2573
|
+
options: {
|
|
2574
|
+
postcssOptions: {
|
|
2575
|
+
config: postcssConfigPath
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
]
|
|
2580
|
+
};
|
|
2581
|
+
rules.unshift(tailwindRule);
|
|
2582
|
+
rspackConfig.module = { ...rspackConfig.module, rules };
|
|
2583
|
+
return rspackConfig;
|
|
2584
|
+
},
|
|
2585
|
+
generateFiles(ctx) {
|
|
2586
|
+
const cssContent = buildTailwindCSS(darkModeSelector, extraCSS);
|
|
2587
|
+
return [
|
|
2588
|
+
{
|
|
2589
|
+
path: "tailwind.css",
|
|
2590
|
+
content: cssContent
|
|
2591
|
+
}
|
|
2592
|
+
];
|
|
2593
|
+
},
|
|
2594
|
+
injectEntry(ctx) {
|
|
2595
|
+
return {
|
|
2596
|
+
imports: [
|
|
2597
|
+
`import "./tailwind.css";`
|
|
2598
|
+
]
|
|
2599
|
+
};
|
|
2600
|
+
}
|
|
2601
|
+
};
|
|
2602
|
+
return hooks;
|
|
2603
|
+
}
|
|
2604
|
+
}));
|
|
2605
|
+
function buildTailwindCSS(darkModeSelector, extraCSS) {
|
|
2606
|
+
return `/* \u6B64\u6587\u4EF6\u7531 @4399ywkf/plugin-tailwind \u81EA\u52A8\u751F\u6210 */
|
|
2607
|
+
@import "tailwindcss";
|
|
2608
|
+
@variant dark (${darkModeSelector});
|
|
2609
|
+
${extraCSS ? `
|
|
2610
|
+
${extraCSS}` : ""}
|
|
2611
|
+
`;
|
|
2612
|
+
}
|
|
2613
|
+
function ensurePostcssConfig(cwd, logger) {
|
|
2614
|
+
const configPath = join7(cwd, "postcss.config.js");
|
|
2615
|
+
if (!existsSync7(configPath)) {
|
|
2616
|
+
writeFileSync3(
|
|
2617
|
+
configPath,
|
|
2618
|
+
`module.exports = {
|
|
2619
|
+
plugins: {
|
|
2620
|
+
"@tailwindcss/postcss": {},
|
|
2621
|
+
},
|
|
2622
|
+
};
|
|
2623
|
+
`,
|
|
2624
|
+
"utf-8"
|
|
2625
|
+
);
|
|
2626
|
+
logger.info("\u5DF2\u81EA\u52A8\u751F\u6210 postcss.config.js");
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2629
|
+
|
|
2630
|
+
// src/plugin/builtin/i18n.ts
|
|
2631
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
2632
|
+
import { join as join8 } from "path";
|
|
2633
|
+
var i18nPlugin = createPlugin((options = {}) => ({
|
|
2634
|
+
name: "@4399ywkf/plugin-i18n",
|
|
2635
|
+
version: "1.0.0",
|
|
2636
|
+
description: "\u56FD\u9645\u5316 (i18next) \u63D2\u4EF6",
|
|
2637
|
+
setup(context) {
|
|
2638
|
+
const {
|
|
2639
|
+
defaultLocale = "zh-CN",
|
|
2640
|
+
locales = ["zh-CN", "en-US"],
|
|
2641
|
+
localesDir = "locales",
|
|
2642
|
+
defaultNS = ["common"],
|
|
2643
|
+
autoScaffold = true
|
|
2644
|
+
} = options;
|
|
2645
|
+
const { cwd, logger } = context;
|
|
2646
|
+
if (autoScaffold) {
|
|
2647
|
+
scaffoldLocaleFiles(cwd, localesDir, locales, defaultNS, logger);
|
|
2648
|
+
}
|
|
2649
|
+
const hooks = {
|
|
2650
|
+
modifyRspackConfig(rspackConfig) {
|
|
2651
|
+
const aliases = rspackConfig.resolve?.alias || {};
|
|
2652
|
+
if (!aliases["@locales"]) {
|
|
2653
|
+
aliases["@locales"] = join8(cwd, localesDir);
|
|
2654
|
+
rspackConfig.resolve = { ...rspackConfig.resolve, alias: aliases };
|
|
2655
|
+
}
|
|
2656
|
+
return rspackConfig;
|
|
2657
|
+
},
|
|
2658
|
+
generateFiles(ctx) {
|
|
2659
|
+
return [
|
|
2660
|
+
{
|
|
2661
|
+
path: "i18n.ts",
|
|
2662
|
+
content: generateI18nCode(defaultLocale, locales, localesDir, defaultNS)
|
|
2663
|
+
}
|
|
2664
|
+
];
|
|
2665
|
+
},
|
|
2666
|
+
injectBootstrap(ctx) {
|
|
2667
|
+
return {
|
|
2668
|
+
imports: [
|
|
2669
|
+
`import { initI18n } from "./i18n";`
|
|
2670
|
+
],
|
|
2671
|
+
topLevel: [
|
|
2672
|
+
`// \u521D\u59CB\u5316 i18n\uFF08\u5728\u5E94\u7528\u542F\u52A8\u524D\uFF09`,
|
|
2673
|
+
`await initI18n();`
|
|
2674
|
+
]
|
|
2675
|
+
};
|
|
2676
|
+
}
|
|
2677
|
+
};
|
|
2678
|
+
return hooks;
|
|
2679
|
+
}
|
|
2680
|
+
}));
|
|
2681
|
+
function generateI18nCode(defaultLocale, locales, localesDir, defaultNS) {
|
|
2682
|
+
return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/plugin-i18n \u81EA\u52A8\u751F\u6210
|
|
2683
|
+
import i18n from "i18next";
|
|
2684
|
+
import LanguageDetector from "i18next-browser-languagedetector";
|
|
2685
|
+
import resourcesToBackend from "i18next-resources-to-backend";
|
|
2686
|
+
import { initReactI18next } from "react-i18next";
|
|
2687
|
+
|
|
2688
|
+
export const DEFAULT_LOCALE = "${defaultLocale}";
|
|
2689
|
+
|
|
2690
|
+
export const SUPPORTED_LOCALES = ${JSON.stringify(locales)} as const;
|
|
2691
|
+
|
|
2692
|
+
export type SupportedLocale = typeof SUPPORTED_LOCALES[number];
|
|
2693
|
+
|
|
2694
|
+
const instance = i18n
|
|
2695
|
+
.use(initReactI18next)
|
|
2696
|
+
.use(LanguageDetector)
|
|
2697
|
+
.use(
|
|
2698
|
+
resourcesToBackend(async (lng: string, ns: string) => {
|
|
2699
|
+
return import(\`@locales/\${normalizeLocale(lng)}/\${ns}.json\`);
|
|
2700
|
+
})
|
|
2701
|
+
);
|
|
2702
|
+
|
|
2703
|
+
function normalizeLocale(locale?: string): string {
|
|
2704
|
+
if (!locale) return DEFAULT_LOCALE;
|
|
2705
|
+
for (const l of SUPPORTED_LOCALES) {
|
|
2706
|
+
if (l.startsWith(locale) || locale.startsWith(l.split("-")[0])) {
|
|
2707
|
+
return l;
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
return DEFAULT_LOCALE;
|
|
2711
|
+
}
|
|
2712
|
+
|
|
2713
|
+
let initialized = false;
|
|
2714
|
+
|
|
2715
|
+
export async function initI18n(lang?: string): Promise<typeof i18n> {
|
|
2716
|
+
if (initialized) return instance;
|
|
2717
|
+
|
|
2718
|
+
await instance.init({
|
|
2719
|
+
defaultNS: ${JSON.stringify(defaultNS)},
|
|
2720
|
+
fallbackLng: DEFAULT_LOCALE,
|
|
2721
|
+
interpolation: { escapeValue: false },
|
|
2722
|
+
lng: lang,
|
|
2723
|
+
});
|
|
2724
|
+
|
|
2725
|
+
initialized = true;
|
|
2726
|
+
return instance;
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2729
|
+
export { instance as i18n };
|
|
2730
|
+
export default instance;
|
|
2731
|
+
`;
|
|
2732
|
+
}
|
|
2733
|
+
function scaffoldLocaleFiles(cwd, localesDir, locales, namespaces, logger) {
|
|
2734
|
+
const baseDir = join8(cwd, localesDir);
|
|
2735
|
+
for (const locale of locales) {
|
|
2736
|
+
const localeDir = join8(baseDir, locale);
|
|
2737
|
+
if (!existsSync8(localeDir)) {
|
|
2738
|
+
mkdirSync3(localeDir, { recursive: true });
|
|
2739
|
+
}
|
|
2740
|
+
for (const ns of namespaces) {
|
|
2741
|
+
const filePath = join8(localeDir, `${ns}.json`);
|
|
2742
|
+
if (!existsSync8(filePath)) {
|
|
2743
|
+
const isDefault = locale === locales[0];
|
|
2744
|
+
const content = isDefault ? JSON.stringify({ welcome: "\u6B22\u8FCE\u4F7F\u7528" }, null, 2) : JSON.stringify({ welcome: "Welcome" }, null, 2);
|
|
2745
|
+
writeFileSync4(filePath, content + "\n", "utf-8");
|
|
2746
|
+
logger.info(`\u5DF2\u751F\u6210\u7FFB\u8BD1\u6587\u4EF6: ${localesDir}/${locale}/${ns}.json`);
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2752
|
+
// src/plugin/builtin/theme.ts
|
|
2753
|
+
var themePlugin = createPlugin((options = {}) => ({
|
|
2754
|
+
name: "@4399ywkf/plugin-theme",
|
|
2755
|
+
version: "1.0.0",
|
|
2756
|
+
description: "antd-style \u4E3B\u9898\u7CFB\u7EDF\u63D2\u4EF6",
|
|
2757
|
+
setup(context) {
|
|
2758
|
+
const {
|
|
2759
|
+
darkMode = true,
|
|
2760
|
+
defaultAppearance = "auto",
|
|
2761
|
+
primaryColor = "blue",
|
|
2762
|
+
prefixCls = "ant",
|
|
2763
|
+
cssVar = true,
|
|
2764
|
+
globalReset = true
|
|
2765
|
+
} = options;
|
|
2766
|
+
const { logger } = context;
|
|
2767
|
+
logger.info(`\u4E3B\u9898\u6A21\u5F0F: ${defaultAppearance}, \u4E3B\u8272: ${primaryColor}`);
|
|
2768
|
+
const hooks = {
|
|
2769
|
+
generateFiles(ctx) {
|
|
2770
|
+
return [
|
|
2771
|
+
{
|
|
2772
|
+
path: "theme.tsx",
|
|
2773
|
+
content: generateThemeProvider({
|
|
2774
|
+
darkMode,
|
|
2775
|
+
defaultAppearance,
|
|
2776
|
+
primaryColor,
|
|
2777
|
+
prefixCls,
|
|
2778
|
+
cssVar,
|
|
2779
|
+
globalReset
|
|
2780
|
+
})
|
|
2781
|
+
}
|
|
2782
|
+
];
|
|
2783
|
+
},
|
|
2784
|
+
injectBootstrap(ctx) {
|
|
2785
|
+
return {
|
|
2786
|
+
imports: [
|
|
2787
|
+
`import { ThemeWrapper } from "./theme";`
|
|
2788
|
+
]
|
|
2789
|
+
};
|
|
2790
|
+
}
|
|
2791
|
+
// Provider 由生成的 theme.tsx 文件在用户层使用
|
|
2792
|
+
// 用户可以在 src/app.config.ts 中通过 providers 引入 ThemeWrapper
|
|
2793
|
+
};
|
|
2794
|
+
return hooks;
|
|
2795
|
+
}
|
|
2796
|
+
}));
|
|
2797
|
+
function generateThemeProvider(opts) {
|
|
2798
|
+
const { darkMode, defaultAppearance, primaryColor, prefixCls, cssVar, globalReset } = opts;
|
|
2799
|
+
return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/plugin-theme \u81EA\u52A8\u751F\u6210
|
|
2800
|
+
import React, { useEffect, type ReactNode } from "react";
|
|
2801
|
+
import { ThemeProvider, createGlobalStyle } from "antd-style";
|
|
2802
|
+
|
|
2803
|
+
${globalReset ? GLOBAL_RESET_CODE : ""}
|
|
2804
|
+
|
|
2805
|
+
const DEFAULT_APPEARANCE = "${defaultAppearance}" as const;
|
|
2806
|
+
|
|
2807
|
+
interface ThemeWrapperProps {
|
|
2808
|
+
children: ReactNode;
|
|
2809
|
+
}
|
|
2810
|
+
|
|
2811
|
+
/**
|
|
2812
|
+
* \u4E3B\u9898\u5305\u88F9\u7EC4\u4EF6
|
|
2813
|
+
* \u7531 themePlugin \u81EA\u52A8\u6CE8\u5165\u5230\u5E94\u7528\u542F\u52A8\u5C42
|
|
2814
|
+
*/
|
|
2815
|
+
export function ThemeWrapper({ children }: ThemeWrapperProps) {
|
|
2816
|
+
${darkMode ? DARK_MODE_EFFECT : ""}
|
|
2817
|
+
|
|
2818
|
+
return (
|
|
2819
|
+
<ThemeProvider
|
|
2820
|
+
prefixCls="${prefixCls}"
|
|
2821
|
+
${defaultAppearance !== "auto" ? `appearance="${defaultAppearance}"` : ""}
|
|
2822
|
+
theme={{
|
|
2823
|
+
cssVar: ${cssVar},
|
|
2824
|
+
token: {
|
|
2825
|
+
colorPrimary: "${primaryColor}",
|
|
2826
|
+
},
|
|
2827
|
+
}}
|
|
2828
|
+
themeMode={DEFAULT_APPEARANCE}
|
|
2829
|
+
>
|
|
2830
|
+
${globalReset ? "<GlobalReset />" : ""}
|
|
2831
|
+
{children}
|
|
2832
|
+
</ThemeProvider>
|
|
2833
|
+
);
|
|
2834
|
+
}
|
|
2835
|
+
|
|
2836
|
+
export default ThemeWrapper;
|
|
2837
|
+
`;
|
|
2838
|
+
}
|
|
2839
|
+
var GLOBAL_RESET_CODE = `
|
|
2840
|
+
const GlobalReset = createGlobalStyle\`
|
|
2841
|
+
*,
|
|
2842
|
+
*::before,
|
|
2843
|
+
*::after {
|
|
2844
|
+
box-sizing: border-box;
|
|
2845
|
+
}
|
|
2846
|
+
|
|
2847
|
+
html, body, #root, #app {
|
|
2848
|
+
height: 100%;
|
|
2849
|
+
margin: 0;
|
|
2850
|
+
padding: 0;
|
|
2851
|
+
}
|
|
2852
|
+
|
|
2853
|
+
body {
|
|
2854
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
2855
|
+
"Helvetica Neue", Arial, "Noto Sans", sans-serif;
|
|
2856
|
+
-webkit-font-smoothing: antialiased;
|
|
2857
|
+
-moz-osx-font-smoothing: grayscale;
|
|
2858
|
+
}
|
|
2859
|
+
\`;
|
|
2860
|
+
`;
|
|
2861
|
+
var DARK_MODE_EFFECT = `
|
|
2862
|
+
useEffect(() => {
|
|
2863
|
+
if (DEFAULT_APPEARANCE !== "auto") {
|
|
2864
|
+
document.documentElement.dataset.theme = DEFAULT_APPEARANCE;
|
|
2865
|
+
return;
|
|
2866
|
+
}
|
|
2867
|
+
|
|
2868
|
+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
2869
|
+
document.documentElement.dataset.theme = mediaQuery.matches ? "dark" : "light";
|
|
2870
|
+
|
|
2871
|
+
function handleChange(e: MediaQueryListEvent) {
|
|
2872
|
+
document.documentElement.dataset.theme = e.matches ? "dark" : "light";
|
|
2873
|
+
}
|
|
2874
|
+
|
|
2875
|
+
mediaQuery.addEventListener("change", handleChange);
|
|
2876
|
+
return () => mediaQuery.removeEventListener("change", handleChange);
|
|
2877
|
+
}, []);
|
|
2878
|
+
`;
|
|
2879
|
+
|
|
2880
|
+
// src/plugin/builtin/mock.ts
|
|
2881
|
+
import { resolve as resolve2, join as join9 } from "path";
|
|
2882
|
+
import { existsSync as existsSync9, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
2883
|
+
var mockPlugin = createPlugin((options = {}) => ({
|
|
2884
|
+
name: "@4399ywkf/plugin-mock",
|
|
2885
|
+
version: "1.0.0",
|
|
2886
|
+
description: "Mock \u6570\u636E\u63D2\u4EF6",
|
|
2887
|
+
setup(context) {
|
|
2888
|
+
const {
|
|
2889
|
+
mockDir = "mock",
|
|
2890
|
+
enableInProd = false,
|
|
2891
|
+
delay = 0,
|
|
2892
|
+
prefix = ""
|
|
2893
|
+
} = options;
|
|
2894
|
+
const { cwd, isDev, logger } = context;
|
|
2895
|
+
if (!isDev && !enableInProd) {
|
|
2896
|
+
logger.info("\u751F\u4EA7\u73AF\u5883\u5DF2\u7981\u7528 Mock");
|
|
2897
|
+
return {};
|
|
2898
|
+
}
|
|
2899
|
+
const mockPath = resolve2(cwd, mockDir);
|
|
2900
|
+
if (!existsSync9(mockPath)) {
|
|
2901
|
+
logger.warn(`Mock \u76EE\u5F55\u4E0D\u5B58\u5728: ${mockPath}`);
|
|
2902
|
+
return {};
|
|
2903
|
+
}
|
|
2904
|
+
const hooks = {
|
|
2905
|
+
modifyRspackConfig(rspackConfig) {
|
|
2906
|
+
if (isDev && rspackConfig.devServer) {
|
|
2907
|
+
const mockFiles = scanMockFiles(mockPath);
|
|
2908
|
+
logger.info(`\u627E\u5230 ${mockFiles.length} \u4E2A Mock \u6587\u4EF6`);
|
|
2909
|
+
rspackConfig.devServer.setupMiddlewares = (middlewares, devServer) => {
|
|
2910
|
+
middlewares.unshift({
|
|
2911
|
+
name: "mock-middleware",
|
|
2912
|
+
middleware: createMockMiddleware(mockPath, { delay, prefix, logger })
|
|
2913
|
+
});
|
|
2914
|
+
return middlewares;
|
|
2915
|
+
};
|
|
2916
|
+
}
|
|
2917
|
+
return rspackConfig;
|
|
2918
|
+
},
|
|
2919
|
+
beforeDevServer() {
|
|
2920
|
+
logger.info(`Mock \u5DF2\u542F\u7528\uFF0C\u76EE\u5F55: ${mockPath}`);
|
|
2921
|
+
if (delay > 0) {
|
|
2922
|
+
logger.info(`\u6A21\u62DF\u5EF6\u8FDF: ${delay}ms`);
|
|
2923
|
+
}
|
|
2924
|
+
}
|
|
2925
|
+
};
|
|
2926
|
+
return hooks;
|
|
2927
|
+
}
|
|
2928
|
+
}));
|
|
2929
|
+
function scanMockFiles(dir) {
|
|
2930
|
+
const files = [];
|
|
2931
|
+
if (!existsSync9(dir)) {
|
|
2932
|
+
return files;
|
|
2933
|
+
}
|
|
2934
|
+
const entries = readdirSync2(dir);
|
|
2935
|
+
for (const entry of entries) {
|
|
2936
|
+
const fullPath = join9(dir, entry);
|
|
2937
|
+
const stat = statSync2(fullPath);
|
|
2938
|
+
if (stat.isFile() && /\.(ts|js|mjs)$/.test(entry)) {
|
|
2939
|
+
files.push(fullPath);
|
|
2940
|
+
} else if (stat.isDirectory()) {
|
|
2941
|
+
files.push(...scanMockFiles(fullPath));
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2944
|
+
return files;
|
|
2945
|
+
}
|
|
2946
|
+
function createMockMiddleware(mockPath, options) {
|
|
2947
|
+
const { delay } = options;
|
|
2948
|
+
scanMockFiles(mockPath);
|
|
2949
|
+
return async (req, res, next) => {
|
|
2950
|
+
if (delay > 0) {
|
|
2951
|
+
await new Promise((resolve4) => setTimeout(resolve4, delay));
|
|
2952
|
+
}
|
|
2953
|
+
next();
|
|
2954
|
+
};
|
|
2955
|
+
}
|
|
2956
|
+
|
|
2957
|
+
// src/plugin/builtin/analytics.ts
|
|
2958
|
+
var analyticsPlugin = createPlugin((options = {}) => ({
|
|
2959
|
+
name: "@4399ywkf/plugin-analytics",
|
|
2960
|
+
version: "1.0.0",
|
|
2961
|
+
description: "\u6784\u5EFA\u5206\u6790\u63D2\u4EF6",
|
|
2962
|
+
setup(context) {
|
|
2963
|
+
const {
|
|
2964
|
+
buildAnalysis = true,
|
|
2965
|
+
timing = true,
|
|
2966
|
+
bundleSize = true,
|
|
2967
|
+
sizeWarningThreshold = 500
|
|
2968
|
+
} = options;
|
|
2969
|
+
const { logger, isProd } = context;
|
|
2970
|
+
let buildStartTime;
|
|
2971
|
+
const hooks = {
|
|
2972
|
+
beforeBuild() {
|
|
2973
|
+
if (timing) {
|
|
2974
|
+
buildStartTime = Date.now();
|
|
2975
|
+
logger.info("\u5F00\u59CB\u6784\u5EFA...");
|
|
2976
|
+
}
|
|
2977
|
+
},
|
|
2978
|
+
afterBuild(_context, stats) {
|
|
2979
|
+
if (timing) {
|
|
2980
|
+
const duration = Date.now() - buildStartTime;
|
|
2981
|
+
logger.info(`\u6784\u5EFA\u5B8C\u6210\uFF0C\u8017\u65F6: ${(duration / 1e3).toFixed(2)}s`);
|
|
2982
|
+
}
|
|
2983
|
+
if (!stats.success && stats.errors) {
|
|
2984
|
+
logger.error(`\u6784\u5EFA\u5931\u8D25\uFF0C\u9519\u8BEF\u6570: ${stats.errors.length}`);
|
|
2985
|
+
}
|
|
2986
|
+
},
|
|
2987
|
+
modifyRspackConfig(config) {
|
|
2988
|
+
if (buildAnalysis && isProd) {
|
|
2989
|
+
logger.info("\u6784\u5EFA\u5206\u6790\u5DF2\u542F\u7528");
|
|
2990
|
+
}
|
|
2991
|
+
return config;
|
|
2992
|
+
}
|
|
2993
|
+
};
|
|
2994
|
+
return hooks;
|
|
2995
|
+
}
|
|
2996
|
+
}));
|
|
2997
|
+
|
|
2998
|
+
// src/plugin/builtin/react-query.ts
|
|
2999
|
+
var reactQueryPlugin = createPlugin((options = {}) => ({
|
|
3000
|
+
name: "@4399ywkf/plugin-react-query",
|
|
3001
|
+
version: "1.0.0",
|
|
3002
|
+
description: "React Query + Axios \u8BF7\u6C42\u5C42\u96C6\u6210",
|
|
3003
|
+
setup(context) {
|
|
3004
|
+
const {
|
|
3005
|
+
staleTime = 5 * 60 * 1e3,
|
|
3006
|
+
gcTime = 10 * 60 * 1e3,
|
|
3007
|
+
retry = 1,
|
|
3008
|
+
devtools = true,
|
|
3009
|
+
baseURL = "",
|
|
3010
|
+
timeout = 15e3
|
|
3011
|
+
} = options;
|
|
3012
|
+
const { logger, isDev } = context;
|
|
3013
|
+
const hooks = {
|
|
3014
|
+
generateFiles(ctx) {
|
|
3015
|
+
logger.info("React Query \u5DF2\u542F\u7528");
|
|
3016
|
+
const files = [
|
|
3017
|
+
{
|
|
3018
|
+
path: "query-client.ts",
|
|
3019
|
+
content: buildQueryClientFile({ staleTime, gcTime, retry, devtools, isDev })
|
|
3020
|
+
},
|
|
3021
|
+
{
|
|
3022
|
+
path: "request.ts",
|
|
3023
|
+
content: buildRequestFile({ baseURL, timeout })
|
|
3024
|
+
}
|
|
3025
|
+
];
|
|
3026
|
+
return files;
|
|
3027
|
+
},
|
|
3028
|
+
injectBootstrap(ctx) {
|
|
3029
|
+
return {
|
|
3030
|
+
imports: [
|
|
3031
|
+
`import { QueryClientProvider } from "@tanstack/react-query";`,
|
|
3032
|
+
`import { queryClient } from "./query-client";`
|
|
3033
|
+
],
|
|
3034
|
+
topLevel: [
|
|
3035
|
+
`// React Query Provider\uFF08\u7531 @4399ywkf/plugin-react-query \u6CE8\u5165\uFF09`
|
|
3036
|
+
]
|
|
3037
|
+
};
|
|
3038
|
+
},
|
|
3039
|
+
modifyBootstrapCode(code) {
|
|
3040
|
+
const providerEntry = ` {
|
|
3041
|
+
component: QueryClientProvider as React.ComponentType<{ children: React.ReactNode }>,
|
|
3042
|
+
props: { client: queryClient },
|
|
3043
|
+
order: 20,
|
|
3044
|
+
}`;
|
|
3045
|
+
if (code.includes("providers: []")) {
|
|
3046
|
+
code = code.replace(
|
|
3047
|
+
"providers: []",
|
|
3048
|
+
`providers: [
|
|
3049
|
+
${providerEntry},
|
|
3050
|
+
]`
|
|
3051
|
+
);
|
|
3052
|
+
} else if (code.includes("providers: [")) {
|
|
3053
|
+
code = code.replace(
|
|
3054
|
+
"providers: [",
|
|
3055
|
+
`providers: [
|
|
3056
|
+
${providerEntry},`
|
|
3057
|
+
);
|
|
3058
|
+
}
|
|
3059
|
+
if (!code.includes("import React")) {
|
|
3060
|
+
code = code.replace(
|
|
3061
|
+
`import { bootstrap`,
|
|
3062
|
+
`import React from "react";
|
|
3063
|
+
import { bootstrap`
|
|
3064
|
+
);
|
|
3065
|
+
}
|
|
3066
|
+
return code;
|
|
3067
|
+
}
|
|
3068
|
+
};
|
|
3069
|
+
return hooks;
|
|
3070
|
+
}
|
|
3071
|
+
}));
|
|
3072
|
+
function buildQueryClientFile(opts) {
|
|
3073
|
+
const { staleTime, gcTime, retry, devtools, isDev } = opts;
|
|
3074
|
+
return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/plugin-react-query \u81EA\u52A8\u751F\u6210
|
|
3075
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
3076
|
+
|
|
3077
|
+
/**
|
|
3078
|
+
* \u5168\u5C40 QueryClient \u5B9E\u4F8B
|
|
3079
|
+
*
|
|
3080
|
+
* \u9ED8\u8BA4\u914D\u7F6E\u53EF\u5728 ywkf.config.ts \u7684 reactQueryPlugin \u9009\u9879\u4E2D\u4FEE\u6539\u3002
|
|
3081
|
+
* \u8FD0\u884C\u65F6\u81EA\u5B9A\u4E49\u53EF\u901A\u8FC7 src/app.config.ts \u8986\u76D6\u3002
|
|
3082
|
+
*/
|
|
3083
|
+
export const queryClient = new QueryClient({
|
|
3084
|
+
defaultOptions: {
|
|
3085
|
+
queries: {
|
|
3086
|
+
staleTime: ${staleTime},
|
|
3087
|
+
gcTime: ${gcTime},
|
|
3088
|
+
retry: ${typeof retry === "boolean" ? retry : retry},
|
|
3089
|
+
refetchOnWindowFocus: false,
|
|
3090
|
+
},
|
|
3091
|
+
mutations: {
|
|
3092
|
+
retry: false,
|
|
3093
|
+
},
|
|
3094
|
+
},
|
|
30
3095
|
});
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
3096
|
+
|
|
3097
|
+
export default queryClient;
|
|
3098
|
+
`;
|
|
3099
|
+
}
|
|
3100
|
+
function buildRequestFile(opts) {
|
|
3101
|
+
const { baseURL, timeout } = opts;
|
|
3102
|
+
return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/plugin-react-query \u81EA\u52A8\u751F\u6210
|
|
3103
|
+
import axios, { type AxiosInstance, type AxiosRequestConfig, type InternalAxiosRequestConfig, type AxiosResponse, type AxiosError } from "axios";
|
|
3104
|
+
|
|
3105
|
+
/**
|
|
3106
|
+
* Axios \u5B9E\u4F8B
|
|
3107
|
+
*
|
|
3108
|
+
* \u57FA\u7840\u914D\u7F6E\u7531\u63D2\u4EF6\u751F\u6210\uFF0C\u53EF\u5728 src/app.config.ts \u4E2D\u901A\u8FC7
|
|
3109
|
+
* \`configureRequest(request)\` \u81EA\u5B9A\u4E49\u62E6\u622A\u5668\u3002
|
|
3110
|
+
*/
|
|
3111
|
+
export const request: AxiosInstance = axios.create({
|
|
3112
|
+
baseURL: "${baseURL}" || import.meta.env.VITE_API_BASE_URL || "",
|
|
3113
|
+
timeout: ${timeout},
|
|
3114
|
+
headers: {
|
|
3115
|
+
"Content-Type": "application/json",
|
|
3116
|
+
},
|
|
3117
|
+
});
|
|
3118
|
+
|
|
3119
|
+
// ============ \u8BF7\u6C42\u62E6\u622A\u5668 ============
|
|
3120
|
+
|
|
3121
|
+
request.interceptors.request.use(
|
|
3122
|
+
(config: InternalAxiosRequestConfig) => {
|
|
3123
|
+
// Token \u6CE8\u5165\uFF08\u4ECE localStorage \u8BFB\u53D6\uFF0C\u53EF\u6309\u9700\u66FF\u6362\uFF09
|
|
3124
|
+
const token = typeof localStorage !== "undefined"
|
|
3125
|
+
? localStorage.getItem("token")
|
|
3126
|
+
: null;
|
|
3127
|
+
|
|
3128
|
+
if (token && config.headers) {
|
|
3129
|
+
config.headers.Authorization = \`Bearer \${token}\`;
|
|
3130
|
+
}
|
|
3131
|
+
|
|
3132
|
+
return config;
|
|
3133
|
+
},
|
|
3134
|
+
(error: AxiosError) => Promise.reject(error)
|
|
3135
|
+
);
|
|
3136
|
+
|
|
3137
|
+
// ============ \u54CD\u5E94\u62E6\u622A\u5668 ============
|
|
3138
|
+
|
|
3139
|
+
request.interceptors.response.use(
|
|
3140
|
+
(response: AxiosResponse) => {
|
|
3141
|
+
// \u76F4\u63A5\u8FD4\u56DE data \u5C42\uFF0C\u51CF\u5C11\u4E1A\u52A1\u4EE3\u7801\u89E3\u6784
|
|
3142
|
+
return response.data;
|
|
3143
|
+
},
|
|
3144
|
+
(error: AxiosError) => {
|
|
3145
|
+
const status = error.response?.status;
|
|
3146
|
+
|
|
3147
|
+
switch (status) {
|
|
3148
|
+
case 401:
|
|
3149
|
+
console.warn("[request] \u672A\u6388\u6743\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55");
|
|
3150
|
+
break;
|
|
3151
|
+
case 403:
|
|
3152
|
+
console.warn("[request] \u65E0\u8BBF\u95EE\u6743\u9650");
|
|
3153
|
+
break;
|
|
3154
|
+
case 404:
|
|
3155
|
+
console.warn("[request] \u8BF7\u6C42\u8D44\u6E90\u4E0D\u5B58\u5728");
|
|
3156
|
+
break;
|
|
3157
|
+
case 500:
|
|
3158
|
+
console.error("[request] \u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF");
|
|
3159
|
+
break;
|
|
3160
|
+
default:
|
|
3161
|
+
if (!error.response) {
|
|
3162
|
+
console.error("[request] \u7F51\u7EDC\u5F02\u5E38\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u8FDE\u63A5");
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
|
|
3166
|
+
return Promise.reject(error);
|
|
3167
|
+
}
|
|
3168
|
+
);
|
|
3169
|
+
|
|
3170
|
+
export default request;
|
|
3171
|
+
`;
|
|
3172
|
+
}
|
|
3173
|
+
|
|
3174
|
+
// src/plugin/builtin/zustand.ts
|
|
3175
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
3176
|
+
import { join as join10 } from "path";
|
|
3177
|
+
var zustandPlugin = createPlugin((options = {}) => ({
|
|
3178
|
+
name: "@4399ywkf/plugin-zustand",
|
|
3179
|
+
version: "1.0.0",
|
|
3180
|
+
description: "Zustand \u72B6\u6001\u7BA1\u7406\u96C6\u6210",
|
|
3181
|
+
setup(context) {
|
|
3182
|
+
const { scaffold = true, storeDir = "store" } = options;
|
|
3183
|
+
const { cwd, logger } = context;
|
|
3184
|
+
const storePath = join10(cwd, storeDir);
|
|
3185
|
+
if (scaffold && !existsSync10(storePath)) {
|
|
3186
|
+
scaffoldStore(storePath, logger);
|
|
3187
|
+
}
|
|
3188
|
+
const hooks = {
|
|
3189
|
+
modifyRspackConfig(rspackConfig) {
|
|
3190
|
+
logger.info("Zustand \u5DF2\u542F\u7528");
|
|
3191
|
+
const resolve4 = rspackConfig.resolve || {};
|
|
3192
|
+
const alias = resolve4.alias || {};
|
|
3193
|
+
alias["@store"] = storePath;
|
|
3194
|
+
resolve4.alias = alias;
|
|
3195
|
+
rspackConfig.resolve = resolve4;
|
|
3196
|
+
return rspackConfig;
|
|
3197
|
+
},
|
|
3198
|
+
generateFiles(ctx) {
|
|
3199
|
+
return [
|
|
3200
|
+
{
|
|
3201
|
+
path: "store.ts",
|
|
3202
|
+
content: buildStoreHelperFile()
|
|
3203
|
+
}
|
|
3204
|
+
];
|
|
3205
|
+
}
|
|
3206
|
+
};
|
|
3207
|
+
return hooks;
|
|
3208
|
+
}
|
|
3209
|
+
}));
|
|
3210
|
+
function scaffoldStore(storePath, logger) {
|
|
3211
|
+
const dirs = [
|
|
3212
|
+
storePath,
|
|
3213
|
+
join10(storePath, "middleware"),
|
|
3214
|
+
join10(storePath, "app"),
|
|
3215
|
+
join10(storePath, "app", "slices"),
|
|
3216
|
+
join10(storePath, "app", "slices", "counter")
|
|
3217
|
+
];
|
|
3218
|
+
for (const dir of dirs) {
|
|
3219
|
+
if (!existsSync10(dir)) {
|
|
3220
|
+
mkdirSync4(dir, { recursive: true });
|
|
3221
|
+
}
|
|
3222
|
+
}
|
|
3223
|
+
writeFileSync5(
|
|
3224
|
+
join10(storePath, "middleware", "createDevtools.ts"),
|
|
3225
|
+
TPL_CREATE_DEVTOOLS,
|
|
3226
|
+
"utf-8"
|
|
3227
|
+
);
|
|
3228
|
+
writeFileSync5(
|
|
3229
|
+
join10(storePath, "app", "slices", "counter", "initialState.ts"),
|
|
3230
|
+
TPL_COUNTER_INITIAL_STATE,
|
|
3231
|
+
"utf-8"
|
|
3232
|
+
);
|
|
3233
|
+
writeFileSync5(
|
|
3234
|
+
join10(storePath, "app", "slices", "counter", "actions.ts"),
|
|
3235
|
+
TPL_COUNTER_ACTIONS,
|
|
3236
|
+
"utf-8"
|
|
3237
|
+
);
|
|
3238
|
+
writeFileSync5(
|
|
3239
|
+
join10(storePath, "app", "initialState.ts"),
|
|
3240
|
+
TPL_APP_INITIAL_STATE,
|
|
3241
|
+
"utf-8"
|
|
3242
|
+
);
|
|
3243
|
+
writeFileSync5(
|
|
3244
|
+
join10(storePath, "app", "store.ts"),
|
|
3245
|
+
TPL_APP_STORE,
|
|
3246
|
+
"utf-8"
|
|
3247
|
+
);
|
|
3248
|
+
writeFileSync5(
|
|
3249
|
+
join10(storePath, "app", "index.tsx"),
|
|
3250
|
+
TPL_APP_INDEX,
|
|
3251
|
+
"utf-8"
|
|
3252
|
+
);
|
|
3253
|
+
writeFileSync5(join10(storePath, "index.ts"), TPL_STORE_INDEX, "utf-8");
|
|
3254
|
+
logger.info("\u5DF2\u751F\u6210 store/ \u811A\u624B\u67B6\uFF08Agent/Slice \u67B6\u6784\uFF09");
|
|
3255
|
+
}
|
|
3256
|
+
var TPL_CREATE_DEVTOOLS = `import type { DevtoolsOptions } from "zustand/middleware";
|
|
3257
|
+
import { devtools as devtoolsMiddleware } from "zustand/middleware";
|
|
3258
|
+
import type { StateCreator, StoreMutatorIdentifier } from "zustand/vanilla";
|
|
3259
|
+
|
|
3260
|
+
/**
|
|
3261
|
+
* \u5C01\u88C5 zustand devtools \u4E2D\u95F4\u4EF6
|
|
3262
|
+
* \u751F\u4EA7\u73AF\u5883\u81EA\u52A8\u7981\u7528\uFF0C\u5F00\u53D1\u73AF\u5883\u542F\u7528 trace
|
|
3263
|
+
*/
|
|
3264
|
+
export const createDevtools =
|
|
3265
|
+
(name: string, options?: DevtoolsOptions) =>
|
|
3266
|
+
<
|
|
3267
|
+
T,
|
|
3268
|
+
Mps extends [StoreMutatorIdentifier, unknown][] = [],
|
|
3269
|
+
Mcs extends [StoreMutatorIdentifier, unknown][] = [],
|
|
3270
|
+
>(
|
|
3271
|
+
initializer: StateCreator<T, [...Mps, ["zustand/devtools", never]], Mcs>,
|
|
3272
|
+
) =>
|
|
3273
|
+
devtoolsMiddleware(initializer, {
|
|
3274
|
+
name,
|
|
3275
|
+
enabled: process.env.NODE_ENV === "development",
|
|
3276
|
+
trace: process.env.NODE_ENV === "development",
|
|
3277
|
+
...options,
|
|
3278
|
+
});
|
|
3279
|
+
`;
|
|
3280
|
+
var TPL_COUNTER_INITIAL_STATE = `export interface CounterState {
|
|
3281
|
+
count: number;
|
|
3282
|
+
loading: boolean;
|
|
3283
|
+
error: string | null;
|
|
3284
|
+
}
|
|
3285
|
+
|
|
3286
|
+
export const initialCounterState: CounterState = {
|
|
3287
|
+
count: 0,
|
|
3288
|
+
loading: false,
|
|
3289
|
+
error: null,
|
|
3290
|
+
};
|
|
3291
|
+
`;
|
|
3292
|
+
var TPL_COUNTER_ACTIONS = `import type { StateCreator } from "zustand/vanilla";
|
|
3293
|
+
import type { AppStore } from "../../store";
|
|
3294
|
+
|
|
3295
|
+
export interface CounterAction {
|
|
3296
|
+
increment: () => void;
|
|
3297
|
+
decrement: () => void;
|
|
3298
|
+
reset: () => void;
|
|
3299
|
+
setLoading: (loading: boolean) => void;
|
|
3300
|
+
setError: (error: string | null) => void;
|
|
3301
|
+
fetchCount: () => Promise<void>;
|
|
3302
|
+
}
|
|
3303
|
+
|
|
3304
|
+
export const createCounterSlice: StateCreator<
|
|
3305
|
+
AppStore,
|
|
3306
|
+
[["zustand/devtools", never]],
|
|
3307
|
+
[],
|
|
3308
|
+
CounterAction
|
|
3309
|
+
> = (set, get) => ({
|
|
3310
|
+
increment: () => set((s) => ({ count: s.count + 1 })),
|
|
3311
|
+
decrement: () => set((s) => ({ count: s.count - 1 })),
|
|
3312
|
+
reset: () => set({ count: 0, error: null }),
|
|
3313
|
+
setLoading: (loading) => set({ loading }),
|
|
3314
|
+
setError: (error) => set({ error }),
|
|
3315
|
+
|
|
3316
|
+
fetchCount: async () => {
|
|
3317
|
+
set({ loading: true, error: null });
|
|
3318
|
+
try {
|
|
3319
|
+
// \u66FF\u6362\u4E3A\u5B9E\u9645 API \u8C03\u7528
|
|
3320
|
+
// import request from "@ywkf/request";
|
|
3321
|
+
// const res = await request.get("/api/count");
|
|
3322
|
+
// set({ count: res.data, loading: false });
|
|
3323
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
3324
|
+
set({ count: 42, loading: false });
|
|
3325
|
+
} catch (err) {
|
|
3326
|
+
set({
|
|
3327
|
+
error: err instanceof Error ? err.message : "\u672A\u77E5\u9519\u8BEF",
|
|
3328
|
+
loading: false,
|
|
3329
|
+
});
|
|
3330
|
+
}
|
|
3331
|
+
},
|
|
51
3332
|
});
|
|
3333
|
+
`;
|
|
3334
|
+
var TPL_APP_INITIAL_STATE = `import {
|
|
3335
|
+
type CounterState,
|
|
3336
|
+
initialCounterState,
|
|
3337
|
+
} from "./slices/counter/initialState";
|
|
3338
|
+
|
|
3339
|
+
// ============== \u805A\u5408\u6240\u6709 Slice \u72B6\u6001 ============== //
|
|
3340
|
+
|
|
3341
|
+
export type AppStoreState = CounterState;
|
|
3342
|
+
|
|
3343
|
+
export const initialState: AppStoreState = {
|
|
3344
|
+
...initialCounterState,
|
|
3345
|
+
};
|
|
3346
|
+
`;
|
|
3347
|
+
var TPL_APP_STORE = `import { subscribeWithSelector } from "zustand/middleware";
|
|
3348
|
+
import { shallow } from "zustand/shallow";
|
|
3349
|
+
import { createWithEqualityFn } from "zustand/traditional";
|
|
3350
|
+
import type { StateCreator } from "zustand/vanilla";
|
|
3351
|
+
|
|
3352
|
+
import { createDevtools } from "../middleware/createDevtools";
|
|
3353
|
+
import { type AppStoreState, initialState } from "./initialState";
|
|
3354
|
+
|
|
3355
|
+
import {
|
|
3356
|
+
type CounterAction,
|
|
3357
|
+
createCounterSlice,
|
|
3358
|
+
} from "./slices/counter/actions";
|
|
3359
|
+
|
|
3360
|
+
// ============== \u805A\u5408 Store \u7C7B\u578B ============== //
|
|
3361
|
+
|
|
3362
|
+
export type AppStore = AppStoreState & CounterAction;
|
|
3363
|
+
|
|
3364
|
+
// ============== \u521B\u5EFA Store ============== //
|
|
3365
|
+
|
|
3366
|
+
const createStore: StateCreator<AppStore, [["zustand/devtools", never]]> = (
|
|
3367
|
+
...parameters
|
|
3368
|
+
) => ({
|
|
3369
|
+
...initialState,
|
|
3370
|
+
...createCounterSlice(...parameters),
|
|
3371
|
+
});
|
|
3372
|
+
|
|
3373
|
+
// ============== \u5B9E\u88C5 useStore ============== //
|
|
3374
|
+
|
|
3375
|
+
const devtools = createDevtools("app");
|
|
3376
|
+
|
|
3377
|
+
export const useAppStore = createWithEqualityFn<AppStore>()(
|
|
3378
|
+
subscribeWithSelector(devtools(createStore)),
|
|
3379
|
+
shallow,
|
|
3380
|
+
);
|
|
3381
|
+
|
|
3382
|
+
export const getAppStoreState = () => useAppStore.getState();
|
|
3383
|
+
`;
|
|
3384
|
+
var TPL_APP_INDEX = `export { useAppStore, getAppStoreState } from "./store";
|
|
3385
|
+
export type { AppStore } from "./store";
|
|
3386
|
+
export { initialState } from "./initialState";
|
|
3387
|
+
export type { AppStoreState } from "./initialState";
|
|
3388
|
+
`;
|
|
3389
|
+
var TPL_STORE_INDEX = `/**
|
|
3390
|
+
* Store \u805A\u5408\u5165\u53E3
|
|
3391
|
+
*
|
|
3392
|
+
* \u67B6\u6784\uFF1A
|
|
3393
|
+
* - store/middleware/ \u2014 \u4E2D\u95F4\u4EF6\uFF08devtools \u7B49\uFF09
|
|
3394
|
+
* - store/{domain}/ \u2014 \u6BCF\u4E2A\u57DF\u4EE3\u8868\u4E00\u4E2A\u4E1A\u52A1\u5B50\u7CFB\u7EDF
|
|
3395
|
+
* - store/{domain}/slices \u2014 \u6BCF\u4E2A slice \u62C6\u4E3A initialState + actions
|
|
3396
|
+
*
|
|
3397
|
+
* \u7EA6\u675F\uFF1A
|
|
3398
|
+
* - initialState\uFF1A\u7EAF\u6570\u636E\u5B9A\u4E49\uFF0C\u4E0D\u542B\u903B\u8F91
|
|
3399
|
+
* - actions\uFF1A\u540C\u6B65\u72B6\u6001\u53D8\u66F4 + \u5F02\u6B65\u526F\u4F5C\u7528
|
|
3400
|
+
* - slice \u4E4B\u95F4\u7981\u6B62\u76F4\u63A5\u4FEE\u6539\u5F7C\u6B64\u72B6\u6001
|
|
3401
|
+
* - \u57DF\u5C42\u8D1F\u8D23\u8DE8 slice \u534F\u8C03
|
|
3402
|
+
*/
|
|
3403
|
+
export { useAppStore, getAppStoreState } from "./app";
|
|
3404
|
+
export type { AppStore, AppStoreState } from "./app";
|
|
3405
|
+
`;
|
|
3406
|
+
function buildStoreHelperFile() {
|
|
3407
|
+
return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/plugin-zustand \u81EA\u52A8\u751F\u6210
|
|
3408
|
+
// \u63D0\u4F9B Zustand \u5DE5\u5177\u51FD\u6570\u7684 re-export\uFF0C\u65B9\u4FBF\u4E1A\u52A1\u4EE3\u7801\u5F15\u7528
|
|
3409
|
+
|
|
3410
|
+
export { useShallow } from "zustand/react/shallow";
|
|
3411
|
+
export type { StateCreator, StoreApi } from "zustand";
|
|
3412
|
+
`;
|
|
3413
|
+
}
|
|
3414
|
+
|
|
3415
|
+
// src/cli/dev.ts
|
|
3416
|
+
import { rspack as rspack3 } from "@rspack/core";
|
|
3417
|
+
import { RspackDevServer } from "@rspack/dev-server";
|
|
3418
|
+
import chalk from "chalk";
|
|
3419
|
+
import ora from "ora";
|
|
3420
|
+
|
|
3421
|
+
// src/cli/env.ts
|
|
3422
|
+
import { existsSync as existsSync11, readFileSync as readFileSync4 } from "fs";
|
|
3423
|
+
import { resolve as resolve3 } from "path";
|
|
3424
|
+
import dotenv from "dotenv";
|
|
3425
|
+
function preloadEnv(cwd, mode) {
|
|
3426
|
+
process.env.NODE_ENV = mode;
|
|
3427
|
+
const defaultPaths = [
|
|
3428
|
+
resolve3(cwd, "config/env/.env.public"),
|
|
3429
|
+
resolve3(cwd, ".env.public"),
|
|
3430
|
+
resolve3(cwd, ".env")
|
|
3431
|
+
];
|
|
3432
|
+
for (const envPath of defaultPaths) {
|
|
3433
|
+
if (existsSync11(envPath)) {
|
|
3434
|
+
dotenv.config({ path: envPath });
|
|
3435
|
+
break;
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3438
|
+
const modeEnvPaths = [
|
|
3439
|
+
resolve3(cwd, `config/env/.env.${mode}`),
|
|
3440
|
+
resolve3(cwd, `.env.${mode}`)
|
|
3441
|
+
];
|
|
3442
|
+
for (const envPath of modeEnvPaths) {
|
|
3443
|
+
if (existsSync11(envPath)) {
|
|
3444
|
+
const envContent = readFileSync4(envPath, "utf-8");
|
|
3445
|
+
const parsed = dotenv.parse(envContent);
|
|
3446
|
+
for (const key in parsed) {
|
|
3447
|
+
process.env[key] = parsed[key];
|
|
3448
|
+
}
|
|
3449
|
+
break;
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
function loadEnv(config, cwd, mode) {
|
|
3454
|
+
const { env } = config;
|
|
3455
|
+
const publicEnvPath = resolve3(cwd, env.publicEnvFile ?? "config/env/.env.public");
|
|
3456
|
+
if (existsSync11(publicEnvPath)) {
|
|
3457
|
+
dotenv.config({ path: publicEnvPath });
|
|
3458
|
+
}
|
|
3459
|
+
const modeEnvPath = resolve3(cwd, env.envDir ?? "config/env", `.env.${mode}`);
|
|
3460
|
+
if (existsSync11(modeEnvPath)) {
|
|
3461
|
+
const envContent = readFileSync4(modeEnvPath, "utf-8");
|
|
3462
|
+
const parsed = dotenv.parse(envContent);
|
|
3463
|
+
for (const key in parsed) {
|
|
3464
|
+
process.env[key] = parsed[key];
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
process.env.NODE_ENV = mode;
|
|
3468
|
+
process.env.APP_NAME = process.env.APP_NAME || config.appName;
|
|
3469
|
+
process.env.APP_CNAME = process.env.APP_CNAME || config.appCName;
|
|
3470
|
+
process.env.OUTPUT_PATH = process.env.OUTPUT_PATH || config.output.path;
|
|
3471
|
+
process.env.PUBLIC_PATH = process.env.PUBLIC_PATH || config.output.publicPath;
|
|
3472
|
+
process.env.APP_HOST = process.env.APP_HOST || config.dev.host;
|
|
3473
|
+
process.env.APP_PORT = process.env.APP_PORT || String(config.dev.port);
|
|
3474
|
+
}
|
|
3475
|
+
|
|
3476
|
+
// src/cli/dev.ts
|
|
3477
|
+
async function dev(options = {}) {
|
|
3478
|
+
const cwd = options.cwd || process.cwd();
|
|
3479
|
+
const spinner = ora("\u6B63\u5728\u542F\u52A8\u5F00\u53D1\u670D\u52A1\u5668...").start();
|
|
3480
|
+
try {
|
|
3481
|
+
preloadEnv(cwd, "development");
|
|
3482
|
+
const { config, configPath } = await resolveConfig(cwd);
|
|
3483
|
+
if (configPath) {
|
|
3484
|
+
spinner.text = `\u5DF2\u52A0\u8F7D\u914D\u7F6E: ${configPath}`;
|
|
3485
|
+
}
|
|
3486
|
+
loadEnv(config, cwd, "development");
|
|
3487
|
+
const pluginManager = new PluginManager(config, cwd, true);
|
|
3488
|
+
if (config.plugins && config.plugins.length > 0) {
|
|
3489
|
+
spinner.text = "\u52A0\u8F7D\u63D2\u4EF6...";
|
|
3490
|
+
await pluginManager.loadPlugins(config.plugins);
|
|
3491
|
+
}
|
|
3492
|
+
await pluginManager.runBeforeDevServer();
|
|
3493
|
+
let rspackConfig = createRspackConfig(config, cwd, { isDev: true });
|
|
3494
|
+
rspackConfig = await pluginManager.applyRspackConfigHooks(rspackConfig);
|
|
3495
|
+
const compiler = rspack3(rspackConfig);
|
|
3496
|
+
const devServerOptions = rspackConfig.devServer || {};
|
|
3497
|
+
const server = new RspackDevServer(devServerOptions, compiler);
|
|
3498
|
+
const host = config.dev.host || "localhost";
|
|
3499
|
+
const port = config.dev.port || 3e3;
|
|
3500
|
+
await server.start();
|
|
3501
|
+
await pluginManager.runAfterDevServer({ host, port });
|
|
3502
|
+
spinner.succeed(chalk.green("\u5F00\u53D1\u670D\u52A1\u5668\u542F\u52A8\u6210\u529F!"));
|
|
3503
|
+
console.log();
|
|
3504
|
+
console.log(
|
|
3505
|
+
` ${chalk.bold("Local:")} ${chalk.cyan(`http://${host}:${port}`)}`
|
|
3506
|
+
);
|
|
3507
|
+
const pluginNames = pluginManager.getPluginNames();
|
|
3508
|
+
if (pluginNames.length > 0) {
|
|
3509
|
+
console.log(` ${chalk.bold("\u63D2\u4EF6:")} ${chalk.dim(pluginNames.join(", "))}`);
|
|
3510
|
+
}
|
|
3511
|
+
console.log();
|
|
3512
|
+
const signals = ["SIGINT", "SIGTERM"];
|
|
3513
|
+
for (const signal of signals) {
|
|
3514
|
+
process.on(signal, async () => {
|
|
3515
|
+
await server.stop();
|
|
3516
|
+
process.exit(0);
|
|
3517
|
+
});
|
|
3518
|
+
}
|
|
3519
|
+
} catch (error) {
|
|
3520
|
+
spinner.fail(chalk.red("\u5F00\u53D1\u670D\u52A1\u5668\u542F\u52A8\u5931\u8D25"));
|
|
3521
|
+
console.error(error);
|
|
3522
|
+
process.exit(1);
|
|
3523
|
+
}
|
|
3524
|
+
}
|
|
3525
|
+
|
|
3526
|
+
// src/cli/build.ts
|
|
3527
|
+
import { rspack as rspack4 } from "@rspack/core";
|
|
3528
|
+
import chalk2 from "chalk";
|
|
3529
|
+
import ora2 from "ora";
|
|
3530
|
+
function formatSize(bytes) {
|
|
3531
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
3532
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`;
|
|
3533
|
+
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
3534
|
+
}
|
|
3535
|
+
function printBuildResult(stats) {
|
|
3536
|
+
const info = stats.toJson({
|
|
3537
|
+
assets: true,
|
|
3538
|
+
errors: true,
|
|
3539
|
+
warnings: true
|
|
3540
|
+
});
|
|
3541
|
+
if (info.errors && info.errors.length > 0) {
|
|
3542
|
+
console.log(chalk2.red("\n\u6784\u5EFA\u9519\u8BEF:"));
|
|
3543
|
+
for (const error of info.errors) {
|
|
3544
|
+
console.log(chalk2.red(` ${error.message}`));
|
|
3545
|
+
}
|
|
3546
|
+
return;
|
|
3547
|
+
}
|
|
3548
|
+
if (info.warnings && info.warnings.length > 0) {
|
|
3549
|
+
console.log(chalk2.yellow("\n\u6784\u5EFA\u8B66\u544A:"));
|
|
3550
|
+
for (const warning of info.warnings) {
|
|
3551
|
+
console.log(chalk2.yellow(` ${warning.message}`));
|
|
3552
|
+
}
|
|
3553
|
+
}
|
|
3554
|
+
console.log(chalk2.bold("\n\u6784\u5EFA\u4EA7\u7269:"));
|
|
3555
|
+
console.log();
|
|
3556
|
+
const assets = info.assets || [];
|
|
3557
|
+
const sortedAssets = assets.filter((asset) => !asset.name.endsWith(".map")).sort((a, b) => b.size - a.size);
|
|
3558
|
+
for (const asset of sortedAssets.slice(0, 15)) {
|
|
3559
|
+
const sizeColor = asset.size > 500 * 1024 ? chalk2.yellow : chalk2.green;
|
|
3560
|
+
console.log(
|
|
3561
|
+
` ${chalk2.dim(asset.name.padEnd(50))} ${sizeColor(formatSize(asset.size))}`
|
|
3562
|
+
);
|
|
3563
|
+
}
|
|
3564
|
+
if (sortedAssets.length > 15) {
|
|
3565
|
+
console.log(chalk2.dim(` ... \u8FD8\u6709 ${sortedAssets.length - 15} \u4E2A\u6587\u4EF6`));
|
|
3566
|
+
}
|
|
3567
|
+
console.log();
|
|
3568
|
+
console.log(
|
|
3569
|
+
chalk2.green(
|
|
3570
|
+
`\u2713 \u6784\u5EFA\u5B8C\u6210\uFF0C\u8017\u65F6 ${((info.time || 0) / 1e3).toFixed(2)}s`
|
|
3571
|
+
)
|
|
3572
|
+
);
|
|
3573
|
+
}
|
|
3574
|
+
async function build(options = {}) {
|
|
3575
|
+
const cwd = options.cwd || process.cwd();
|
|
3576
|
+
const spinner = ora2("\u6B63\u5728\u6784\u5EFA\u751F\u4EA7\u73AF\u5883...").start();
|
|
3577
|
+
try {
|
|
3578
|
+
preloadEnv(cwd, "production");
|
|
3579
|
+
const { config, configPath } = await resolveConfig(cwd);
|
|
3580
|
+
if (configPath) {
|
|
3581
|
+
spinner.text = `\u5DF2\u52A0\u8F7D\u914D\u7F6E: ${configPath}`;
|
|
3582
|
+
}
|
|
3583
|
+
loadEnv(config, cwd, "production");
|
|
3584
|
+
const pluginManager = new PluginManager(config, cwd, false);
|
|
3585
|
+
if (config.plugins && config.plugins.length > 0) {
|
|
3586
|
+
spinner.text = "\u52A0\u8F7D\u63D2\u4EF6...";
|
|
3587
|
+
await pluginManager.loadPlugins(config.plugins);
|
|
3588
|
+
}
|
|
3589
|
+
await pluginManager.runBeforeBuild();
|
|
3590
|
+
let rspackConfig = createRspackConfig(config, cwd, { isDev: false });
|
|
3591
|
+
rspackConfig = await pluginManager.applyRspackConfigHooks(rspackConfig);
|
|
3592
|
+
spinner.text = "\u6B63\u5728\u7F16\u8BD1...";
|
|
3593
|
+
const compiler = rspack4(rspackConfig);
|
|
3594
|
+
const stats = await new Promise((resolve4, reject) => {
|
|
3595
|
+
compiler.run((err, stats2) => {
|
|
3596
|
+
if (err) {
|
|
3597
|
+
reject(err);
|
|
3598
|
+
return;
|
|
3599
|
+
}
|
|
3600
|
+
if (!stats2) {
|
|
3601
|
+
reject(new Error("\u6784\u5EFA\u5931\u8D25: \u65E0\u6CD5\u83B7\u53D6\u7EDF\u8BA1\u4FE1\u606F"));
|
|
3602
|
+
return;
|
|
3603
|
+
}
|
|
3604
|
+
resolve4(stats2);
|
|
3605
|
+
});
|
|
3606
|
+
});
|
|
3607
|
+
await new Promise((resolve4, reject) => {
|
|
3608
|
+
compiler.close((err) => {
|
|
3609
|
+
if (err) reject(err);
|
|
3610
|
+
else resolve4();
|
|
3611
|
+
});
|
|
3612
|
+
});
|
|
3613
|
+
const hasErrors = stats.hasErrors();
|
|
3614
|
+
const statsInfo = stats.toJson({ errors: true });
|
|
3615
|
+
await pluginManager.runAfterBuild({
|
|
3616
|
+
success: !hasErrors,
|
|
3617
|
+
errors: statsInfo.errors?.map((e) => e.message)
|
|
3618
|
+
});
|
|
3619
|
+
if (hasErrors) {
|
|
3620
|
+
spinner.fail(chalk2.red("\u6784\u5EFA\u5931\u8D25"));
|
|
3621
|
+
printBuildResult(stats);
|
|
3622
|
+
process.exit(1);
|
|
3623
|
+
}
|
|
3624
|
+
spinner.succeed(chalk2.green("\u6784\u5EFA\u5B8C\u6210!"));
|
|
3625
|
+
printBuildResult(stats);
|
|
3626
|
+
} catch (error) {
|
|
3627
|
+
spinner.fail(chalk2.red("\u6784\u5EFA\u5931\u8D25"));
|
|
3628
|
+
console.error(error);
|
|
3629
|
+
process.exit(1);
|
|
3630
|
+
}
|
|
3631
|
+
}
|
|
3632
|
+
export {
|
|
3633
|
+
AppContext,
|
|
3634
|
+
AppContextProvider,
|
|
3635
|
+
ConventionalRouteGenerator,
|
|
3636
|
+
ConventionalRoutePlugin,
|
|
3637
|
+
ErrorBoundary,
|
|
3638
|
+
PluginManager,
|
|
3639
|
+
RootProvider,
|
|
3640
|
+
YwkfGenerator,
|
|
3641
|
+
YwkfGeneratorPlugin,
|
|
3642
|
+
analyticsPlugin,
|
|
3643
|
+
bootstrap,
|
|
3644
|
+
build,
|
|
3645
|
+
createBaseConfig,
|
|
3646
|
+
createDevConfig,
|
|
3647
|
+
createMicroApp,
|
|
3648
|
+
createPlugin,
|
|
3649
|
+
createProdConfig,
|
|
3650
|
+
createProvider,
|
|
3651
|
+
createRspackConfig,
|
|
3652
|
+
defaultConfig,
|
|
3653
|
+
defineConfig,
|
|
3654
|
+
definePlugin,
|
|
3655
|
+
dev,
|
|
3656
|
+
garfishPlugin,
|
|
3657
|
+
generateConventionalRoutes,
|
|
3658
|
+
getMicroAppPublicPath,
|
|
3659
|
+
i18nPlugin,
|
|
3660
|
+
isMicroAppEnv,
|
|
3661
|
+
mockPlugin,
|
|
3662
|
+
reactQueryPlugin,
|
|
3663
|
+
tailwindPlugin,
|
|
3664
|
+
themePlugin,
|
|
3665
|
+
unmount,
|
|
3666
|
+
useApp,
|
|
3667
|
+
useAppName,
|
|
3668
|
+
useBasename,
|
|
3669
|
+
useEnv,
|
|
3670
|
+
useIsDev,
|
|
3671
|
+
zustandPlugin
|
|
3672
|
+
};
|
|
3673
|
+
//# sourceMappingURL=index.js.map
|