@4399ywkf/core 4.0.85 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/cli/index.js +2180 -0
  2. package/dist/cli/index.js.map +1 -0
  3. package/dist/config/index.d.ts +37 -0
  4. package/dist/config/index.js +143 -0
  5. package/dist/config/index.js.map +1 -0
  6. package/dist/index.d.ts +496 -6
  7. package/dist/index.js +3671 -49
  8. package/dist/index.js.map +1 -0
  9. package/dist/plugin/index.d.ts +110 -0
  10. package/dist/plugin/index.js +277 -0
  11. package/dist/plugin/index.js.map +1 -0
  12. package/dist/router/index.d.ts +126 -0
  13. package/dist/router/index.js +390 -0
  14. package/dist/router/index.js.map +1 -0
  15. package/dist/rspack/index.d.ts +29 -0
  16. package/dist/rspack/index.js +1574 -0
  17. package/dist/rspack/index.js.map +1 -0
  18. package/dist/runtime/index.d.ts +120 -0
  19. package/dist/runtime/index.js +406 -0
  20. package/dist/runtime/index.js.map +1 -0
  21. package/dist/schema-BuqmN_ra.d.ts +390 -0
  22. package/dist/types-BZV_2QtD.d.ts +97 -0
  23. package/package.json +92 -37
  24. package/README.md +0 -3
  25. package/compiled/dotenv/LICENSE +0 -23
  26. package/compiled/dotenv/index.js +0 -1
  27. package/compiled/dotenv/lib/main.d.ts +0 -73
  28. package/compiled/dotenv/package.json +0 -1
  29. package/compiled/dotenv/types/index.d.ts +0 -59
  30. package/compiled/dotenv-expand/LICENSE +0 -24
  31. package/compiled/dotenv-expand/index.js +0 -1
  32. package/compiled/dotenv-expand/lib/main.d.ts +0 -29
  33. package/compiled/dotenv-expand/package.json +0 -1
  34. package/compiled/just-diff/LICENSE +0 -21
  35. package/compiled/just-diff/index.d.ts +0 -20
  36. package/compiled/just-diff/index.js +0 -1
  37. package/compiled/just-diff/package.json +0 -1
  38. package/dist/config/config.d.ts +0 -62
  39. package/dist/config/config.js +0 -240
  40. package/dist/config/utils.d.ts +0 -8
  41. package/dist/config/utils.js +0 -40
  42. package/dist/constants.d.ts +0 -9
  43. package/dist/constants.js +0 -45
  44. package/dist/route/defineRoutes.d.ts +0 -1
  45. package/dist/route/defineRoutes.js +0 -61
  46. package/dist/route/route.d.ts +0 -3
  47. package/dist/route/route.js +0 -27
  48. package/dist/route/routeUtils.d.ts +0 -8
  49. package/dist/route/routeUtils.js +0 -46
  50. package/dist/route/routesConfig.d.ts +0 -6
  51. package/dist/route/routesConfig.js +0 -125
  52. package/dist/route/routesConvention.d.ts +0 -5
  53. package/dist/route/routesConvention.js +0 -88
  54. package/dist/route/utils.d.ts +0 -8
  55. package/dist/route/utils.js +0 -59
  56. package/dist/service/command.d.ts +0 -30
  57. package/dist/service/command.js +0 -39
  58. package/dist/service/env.d.ts +0 -4
  59. package/dist/service/env.js +0 -47
  60. package/dist/service/generatePlugin.d.ts +0 -4
  61. package/dist/service/generatePlugin.js +0 -102
  62. package/dist/service/generator.d.ts +0 -71
  63. package/dist/service/generator.js +0 -40
  64. package/dist/service/hook.d.ts +0 -16
  65. package/dist/service/hook.js +0 -52
  66. package/dist/service/path.d.ts +0 -15
  67. package/dist/service/path.js +0 -55
  68. package/dist/service/plugin.d.ts +0 -61
  69. package/dist/service/plugin.js +0 -174
  70. package/dist/service/pluginAPI.d.ts +0 -49
  71. package/dist/service/pluginAPI.js +0 -233
  72. package/dist/service/service.d.ts +0 -147
  73. package/dist/service/service.js +0 -544
  74. package/dist/service/servicePlugin.d.ts +0 -3
  75. package/dist/service/servicePlugin.js +0 -37
  76. package/dist/service/telemetry.d.ts +0 -32
  77. package/dist/service/telemetry.js +0 -127
  78. package/dist/service/utils.d.ts +0 -2
  79. package/dist/service/utils.js +0 -36
  80. package/dist/types.d.ts +0 -116
  81. package/dist/types.js +0 -77
@@ -0,0 +1,2180 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli/index.ts
4
+ import { Command } from "commander";
5
+ import chalk3 from "chalk";
6
+
7
+ // src/cli/dev.ts
8
+ import { rspack as rspack3 } from "@rspack/core";
9
+ import { RspackDevServer } from "@rspack/dev-server";
10
+ import chalk from "chalk";
11
+ import ora from "ora";
12
+
13
+ // src/config/loader.ts
14
+ import { existsSync } from "fs";
15
+ import { resolve, extname } from "path";
16
+ import { pathToFileURL } from "url";
17
+ import deepmerge from "deepmerge";
18
+
19
+ // src/config/schema.ts
20
+ var defaultConfig = {
21
+ appName: "app",
22
+ appCName: "\u5E94\u7528",
23
+ dev: {
24
+ port: 3e3,
25
+ host: "localhost",
26
+ proxy: {},
27
+ https: false
28
+ },
29
+ output: {
30
+ path: "dist",
31
+ publicPath: "/",
32
+ clean: true
33
+ },
34
+ html: {
35
+ title: "\u5E94\u7528",
36
+ template: "public/index.html",
37
+ favicon: "public/favicon.ico",
38
+ mountRoot: "root"
39
+ },
40
+ style: {
41
+ cssModules: true,
42
+ less: { enabled: true, lessOptions: { javascriptEnabled: true } },
43
+ sass: { enabled: true, sassOptions: {} },
44
+ tailwindcss: true
45
+ },
46
+ router: {
47
+ basename: "/",
48
+ conventional: false,
49
+ pagesDir: "src/pages",
50
+ exclude: [
51
+ /\/components?\//,
52
+ /\/models\//,
53
+ /\/utils?\//,
54
+ /^_/,
55
+ /\.d\.ts$/,
56
+ /\.(test|spec|e2e)\.(ts|tsx|js|jsx)$/
57
+ ]
58
+ },
59
+ microFrontend: {
60
+ enabled: false,
61
+ name: "app",
62
+ framework: "qiankun"
63
+ },
64
+ performance: {
65
+ rsdoctor: false,
66
+ splitChunks: true,
67
+ dropConsole: false
68
+ },
69
+ tools: {},
70
+ env: {
71
+ publicEnvFile: "config/env/.env.public",
72
+ envDir: "config/env"
73
+ },
74
+ alias: {},
75
+ plugins: []
76
+ };
77
+
78
+ // src/config/loader.ts
79
+ var CONFIG_FILES = [
80
+ "ywkf.config.ts",
81
+ "ywkf.config.mts",
82
+ "ywkf.config.js",
83
+ "ywkf.config.mjs"
84
+ ];
85
+ function findConfigFile(cwd) {
86
+ for (const file of CONFIG_FILES) {
87
+ const configPath = resolve(cwd, file);
88
+ if (existsSync(configPath)) {
89
+ return configPath;
90
+ }
91
+ }
92
+ return null;
93
+ }
94
+ async function loadTsConfig(configPath) {
95
+ const jiti = (await import("jiti")).default;
96
+ const loader = jiti(configPath, {
97
+ interopDefault: true
98
+ });
99
+ const config = loader(configPath);
100
+ return config.default || config;
101
+ }
102
+ async function loadJsConfig(configPath) {
103
+ const fileUrl = pathToFileURL(configPath).href;
104
+ const module = await import(fileUrl);
105
+ return module.default || module;
106
+ }
107
+ async function loadConfigFile(configPath) {
108
+ const ext = extname(configPath);
109
+ if (ext === ".ts" || ext === ".mts") {
110
+ return loadTsConfig(configPath);
111
+ }
112
+ return loadJsConfig(configPath);
113
+ }
114
+ function mergeConfig(userConfig, baseConfig = defaultConfig) {
115
+ return deepmerge(baseConfig, userConfig, {
116
+ arrayMerge: (_, sourceArray) => sourceArray
117
+ });
118
+ }
119
+ async function resolveConfig(cwd) {
120
+ const configPath = findConfigFile(cwd);
121
+ if (!configPath) {
122
+ console.warn(
123
+ "\u26A0\uFE0F \u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6 (ywkf.config.ts)\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E"
124
+ );
125
+ return {
126
+ config: defaultConfig,
127
+ configPath: null
128
+ };
129
+ }
130
+ try {
131
+ const userConfig = await loadConfigFile(configPath);
132
+ const config = mergeConfig(userConfig);
133
+ return { config, configPath };
134
+ } catch (error) {
135
+ console.error("\u274C \u914D\u7F6E\u6587\u4EF6\u52A0\u8F7D\u5931\u8D25:", error);
136
+ throw error;
137
+ }
138
+ }
139
+ function createPathResolver(cwd) {
140
+ return {
141
+ resolveApp: (relativePath) => resolve(cwd, relativePath),
142
+ cwd
143
+ };
144
+ }
145
+
146
+ // src/rspack/index.ts
147
+ import { RsdoctorRspackPlugin } from "@rsdoctor/rspack-plugin";
148
+
149
+ // src/rspack/dev.ts
150
+ import { merge } from "webpack-merge";
151
+ import { createRequire as createRequire2 } from "module";
152
+
153
+ // src/rspack/base.ts
154
+ import { rspack } from "@rspack/core";
155
+ import { createRequire } from "module";
156
+ import { fileURLToPath } from "url";
157
+ import { dirname, join as join5 } from "path";
158
+
159
+ // src/generator/plugin.ts
160
+ import { watch, existsSync as existsSync5 } from "fs";
161
+ import { join as join4 } from "path";
162
+
163
+ // src/generator/generator.ts
164
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync2 } from "fs";
165
+ import { join as join3 } from "path";
166
+
167
+ // src/router/generator.ts
168
+ import { existsSync as existsSync2, readdirSync, statSync, mkdirSync, writeFileSync } from "fs";
169
+ import { join, relative } from "path";
170
+ var EXCLUDED_DIRS = /* @__PURE__ */ new Set([
171
+ "components",
172
+ "hooks",
173
+ "utils",
174
+ "services",
175
+ "models",
176
+ "assets",
177
+ "types",
178
+ "constants",
179
+ "styles"
180
+ ]);
181
+ var CONVENTION_FILES = {
182
+ page: /^page\.(tsx?|jsx?)$/,
183
+ layout: /^layout\.(tsx?|jsx?)$/,
184
+ error: /^error\.(tsx?|jsx?)$/,
185
+ loading: /^loading\.(tsx?|jsx?)$/,
186
+ catchAll: /^\$\.(tsx?|jsx?)$/
187
+ };
188
+ var ConventionalRouteGenerator = class {
189
+ options;
190
+ constructor(options) {
191
+ this.options = options;
192
+ }
193
+ generate() {
194
+ const { pagesDir } = this.options;
195
+ if (!existsSync2(pagesDir)) {
196
+ console.warn(`[ywkf] \u9875\u9762\u76EE\u5F55\u4E0D\u5B58\u5728: ${pagesDir}`);
197
+ return [];
198
+ }
199
+ return this.scanDirectory(pagesDir, "/");
200
+ }
201
+ /**
202
+ * 扫描目录,生成路由树
203
+ */
204
+ scanDirectory(dir, routePath) {
205
+ const entries = readdirSync(dir);
206
+ const layoutFile = entries.find((e) => CONVENTION_FILES.layout.test(e));
207
+ const pageFile = entries.find((e) => CONVENTION_FILES.page.test(e));
208
+ const errorFile = entries.find((e) => CONVENTION_FILES.error.test(e));
209
+ const loadingFile = entries.find((e) => CONVENTION_FILES.loading.test(e));
210
+ const catchAllFile = entries.find((e) => CONVENTION_FILES.catchAll.test(e));
211
+ const subDirs = entries.filter((e) => {
212
+ if (e.startsWith(".")) return false;
213
+ if (EXCLUDED_DIRS.has(e)) return false;
214
+ return statSync(join(dir, e)).isDirectory();
215
+ });
216
+ const childRoutes = [];
217
+ for (const subDir of subDirs) {
218
+ const subDirPath = join(dir, subDir);
219
+ if (subDir.startsWith("__")) {
220
+ const pathlessRoutes = this.scanDirectory(subDirPath, routePath);
221
+ const pathlessLayout = readdirSync(subDirPath).find(
222
+ (e) => CONVENTION_FILES.layout.test(e)
223
+ );
224
+ if (pathlessLayout) {
225
+ const pathlessChildren = this.scanDirectory(subDirPath, routePath);
226
+ childRoutes.push({
227
+ path: routePath,
228
+ name: this.generateRouteName(subDir.slice(2)),
229
+ layoutFile: relative(this.options.pagesDir, join(subDirPath, pathlessLayout)),
230
+ isLayout: true,
231
+ pathless: true,
232
+ children: pathlessChildren.filter(
233
+ (r) => r.layoutFile !== relative(this.options.pagesDir, join(subDirPath, pathlessLayout))
234
+ )
235
+ });
236
+ } else {
237
+ childRoutes.push(...pathlessRoutes);
238
+ }
239
+ continue;
240
+ }
241
+ const subRoutePath = this.resolveRoutePath(subDir, routePath);
242
+ childRoutes.push(...this.scanDirectory(subDirPath, subRoutePath));
243
+ }
244
+ const routeName = this.generateRouteName(routePath);
245
+ const relPath = (file) => relative(this.options.pagesDir, join(dir, file));
246
+ if (layoutFile) {
247
+ const layoutChildren = [];
248
+ if (pageFile) {
249
+ layoutChildren.push({
250
+ path: routePath,
251
+ file: relPath(pageFile),
252
+ name: routeName + "Index",
253
+ index: true
254
+ });
255
+ }
256
+ if (catchAllFile) {
257
+ layoutChildren.push({
258
+ path: "*",
259
+ file: relPath(catchAllFile),
260
+ name: routeName + "CatchAll"
261
+ });
262
+ }
263
+ layoutChildren.push(...childRoutes);
264
+ return [
265
+ {
266
+ path: routePath,
267
+ layoutFile: relPath(layoutFile),
268
+ errorFile: errorFile ? relPath(errorFile) : void 0,
269
+ loadingFile: loadingFile ? relPath(loadingFile) : void 0,
270
+ name: routeName,
271
+ isLayout: true,
272
+ children: layoutChildren
273
+ }
274
+ ];
275
+ }
276
+ const result = [];
277
+ if (pageFile) {
278
+ result.push({
279
+ path: routePath,
280
+ file: relPath(pageFile),
281
+ errorFile: errorFile ? relPath(errorFile) : void 0,
282
+ name: routeName
283
+ });
284
+ }
285
+ if (catchAllFile) {
286
+ result.push({
287
+ path: "*",
288
+ file: relPath(catchAllFile),
289
+ name: routeName + "CatchAll"
290
+ });
291
+ }
292
+ result.push(...childRoutes);
293
+ return result;
294
+ }
295
+ /**
296
+ * 解析目录名到路由路径
297
+ *
298
+ * - [id] → :id
299
+ * - [id$] → :id?
300
+ * - [...slug] → *
301
+ * - user.profile → user/profile
302
+ */
303
+ resolveRoutePath(dirName, parentPath) {
304
+ let segment;
305
+ if (dirName.match(/^\[\.\.\.(.+)\]$/)) {
306
+ segment = "*";
307
+ } else if (dirName.match(/^\[(.+)\$\]$/)) {
308
+ const param = dirName.slice(1, -2);
309
+ segment = `:${param}?`;
310
+ } else if (dirName.match(/^\[(.+)\]$/)) {
311
+ const param = dirName.slice(1, -1);
312
+ segment = `:${param}`;
313
+ } else if (dirName.includes(".")) {
314
+ segment = dirName.replace(/\./g, "/");
315
+ } else {
316
+ segment = dirName;
317
+ }
318
+ return parentPath === "/" ? `/${segment}` : `${parentPath}/${segment}`;
319
+ }
320
+ /**
321
+ * 生成有效的 JS 标识符名称
322
+ */
323
+ generateRouteName(path) {
324
+ const name = path.split("/").filter(Boolean).map(
325
+ (s) => s.replace(/^:/, "Param").replace(/\?$/, "Optional").replace(/^\*$/, "CatchAll")
326
+ ).map((s) => {
327
+ const cleaned = s.replace(/[^a-zA-Z0-9]/g, "");
328
+ if (!cleaned) return "";
329
+ return cleaned.charAt(0).toUpperCase() + cleaned.slice(1);
330
+ }).join("");
331
+ if (!name || /^\d/.test(name)) {
332
+ return "Page" + (name || "Root");
333
+ }
334
+ return name;
335
+ }
336
+ // ===== 代码生成 =====
337
+ generateCode() {
338
+ const routes = this.generate();
339
+ const lazyImports = [];
340
+ const errorImports = [];
341
+ const loadingImports = [];
342
+ this.collectImports(routes, lazyImports, errorImports, loadingImports);
343
+ return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/core \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u4FEE\u6539
344
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
345
+
346
+ import React, { lazy, Suspense } from "react";
347
+ import { createBrowserRouter, type RouteObject } from "react-router";
348
+
349
+ // \u61D2\u52A0\u8F7D\u9875\u9762\u7EC4\u4EF6
350
+ ${lazyImports.join("\n")}
351
+ ${errorImports.length > 0 ? "\n// \u9519\u8BEF\u8FB9\u754C\u7EC4\u4EF6\n" + errorImports.join("\n") : ""}
352
+ ${loadingImports.length > 0 ? "\n// \u52A0\u8F7D\u72B6\u6001\u7EC4\u4EF6\n" + loadingImports.join("\n") : ""}
353
+
354
+ // \u9ED8\u8BA4\u52A0\u8F7D\u72B6\u6001
355
+ const DefaultLoading = () => <div style={{ padding: 24, textAlign: "center" }}>\u52A0\u8F7D\u4E2D...</div>;
356
+
357
+ // \u61D2\u52A0\u8F7D\u5305\u88C5
358
+ function LazyRoute({
359
+ Component,
360
+ Loading = DefaultLoading,
361
+ }: {
362
+ Component: React.LazyExoticComponent<React.ComponentType<unknown>>;
363
+ Loading?: React.ComponentType;
364
+ }) {
365
+ return (
366
+ <Suspense fallback={<Loading />}>
367
+ <Component />
368
+ </Suspense>
369
+ );
370
+ }
371
+
372
+ // \u8DEF\u7531\u914D\u7F6E
373
+ export const routes: RouteObject[] = ${this.emitRouteArray(routes)};
374
+
375
+ /**
376
+ * \u521B\u5EFA\u8DEF\u7531\u5B9E\u4F8B
377
+ */
378
+ export function createRouter(basename?: string) {
379
+ return createBrowserRouter(routes, { basename: basename || "/" });
380
+ }
381
+
382
+ export default routes;
383
+ `;
384
+ }
385
+ collectImports(routes, lazyImports, errorImports, loadingImports) {
386
+ for (const route of routes) {
387
+ const name = route.name;
388
+ const toImportPath = (f) => f.replace(/\.(tsx?|jsx?)$/, "");
389
+ if (route.isLayout && route.layoutFile) {
390
+ lazyImports.push(
391
+ `const ${name}Layout = lazy(() => import("@/pages/${toImportPath(route.layoutFile)}"));`
392
+ );
393
+ }
394
+ if (route.file) {
395
+ lazyImports.push(
396
+ `const ${name}Page = lazy(() => import("@/pages/${toImportPath(route.file)}"));`
397
+ );
398
+ }
399
+ if (route.errorFile) {
400
+ errorImports.push(
401
+ `const ${name}Error = lazy(() => import("@/pages/${toImportPath(route.errorFile)}"));`
402
+ );
403
+ }
404
+ if (route.loadingFile) {
405
+ loadingImports.push(
406
+ `const ${name}Loading = lazy(() => import("@/pages/${toImportPath(route.loadingFile)}"));`
407
+ );
408
+ }
409
+ if (route.children) {
410
+ this.collectImports(route.children, lazyImports, errorImports, loadingImports);
411
+ }
412
+ }
413
+ }
414
+ emitRouteArray(routes, parentPath) {
415
+ const items = routes.map((r) => this.emitRouteObject(r, parentPath));
416
+ return `[
417
+ ${items.join(",\n ")}
418
+ ]`;
419
+ }
420
+ emitRouteObject(route, parentPath) {
421
+ const parts = [];
422
+ const name = route.name;
423
+ if (route.index) {
424
+ parts.push("index: true");
425
+ } else if (route.pathless) {
426
+ } else if (route.path === "*") {
427
+ parts.push(`path: "*"`);
428
+ } else if (parentPath && route.path.startsWith(parentPath) && parentPath !== "/") {
429
+ const rel = route.path.slice(parentPath.length + 1);
430
+ parts.push(`path: "${rel || ""}"`);
431
+ } else {
432
+ parts.push(`path: "${route.path}"`);
433
+ }
434
+ if (route.isLayout && route.layoutFile) {
435
+ const loadingProp = route.loadingFile ? ` Loading={${name}Loading}` : "";
436
+ parts.push(`element: <LazyRoute Component={${name}Layout}${loadingProp} />`);
437
+ } else if (route.file) {
438
+ parts.push(`element: <LazyRoute Component={${name}Page} />`);
439
+ }
440
+ if (route.errorFile) {
441
+ parts.push(`errorElement: <LazyRoute Component={${name}Error} />`);
442
+ }
443
+ if (route.children && route.children.length > 0) {
444
+ const childParent = route.pathless ? parentPath : route.path;
445
+ parts.push(`children: ${this.emitRouteArray(route.children, childParent)}`);
446
+ }
447
+ return `{
448
+ ${parts.join(",\n ")}
449
+ }`;
450
+ }
451
+ /**
452
+ * 写入路由文件
453
+ */
454
+ write() {
455
+ const { outputDir } = this.options;
456
+ const code = this.generateCode();
457
+ if (!existsSync2(outputDir)) {
458
+ mkdirSync(outputDir, { recursive: true });
459
+ }
460
+ writeFileSync(join(outputDir, "routes.tsx"), code, "utf-8");
461
+ console.log(`[ywkf] \u7EA6\u5B9A\u5F0F\u8DEF\u7531\u5DF2\u751F\u6210`);
462
+ }
463
+ };
464
+
465
+ // src/generator/templates/entry.ts
466
+ function generateEntry(config, injections = {}) {
467
+ const imports = [
468
+ `import "@/index.css";`,
469
+ `import { runApp } from "./bootstrap";`,
470
+ ...injections.imports || []
471
+ ];
472
+ const topLevel = injections.topLevel || [];
473
+ const exports = injections.exports || [];
474
+ const hasPluginExports = exports.length > 0;
475
+ const startupCode = hasPluginExports ? `// \u72EC\u7ACB\u8FD0\u884C\u6A21\u5F0F
476
+ if (shouldRunIndependently !== false) {
477
+ runApp();
478
+ }` : `// \u542F\u52A8\u5E94\u7528
479
+ runApp();`;
480
+ return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/core \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u4FEE\u6539
481
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
482
+
483
+ ${imports.join("\n")}
484
+
485
+ ${topLevel.length > 0 ? topLevel.join("\n") + "\n" : ""}${exports.length > 0 ? exports.join("\n") + "\n\n" : ""}${startupCode}
486
+ `;
487
+ }
488
+
489
+ // src/generator/templates/bootstrap.ts
490
+ function generateBootstrap(config, injections = {}) {
491
+ const { appName, router } = config;
492
+ const routerImport = router.conventional ? `import { createRouter } from "./routes";` : `import { createRouter } from "@/routes";`;
493
+ const imports = [
494
+ `import { bootstrap, type AppConfig } from "@4399ywkf/core/runtime";`,
495
+ routerImport,
496
+ ...injections.imports || []
497
+ ];
498
+ const topLevel = injections.topLevel || [];
499
+ const exports = injections.exports || [];
500
+ return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/core \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u4FEE\u6539
501
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
502
+
503
+ ${imports.join("\n")}
504
+
505
+ /**
506
+ * \u5E94\u7528\u540D\u79F0
507
+ */
508
+ export const APP_NAME = "${appName}";
509
+
510
+ /**
511
+ * \u8DEF\u7531 basename
512
+ */
513
+ export const BASENAME = "${router.basename || "/"}";
514
+
515
+ /**
516
+ * \u83B7\u53D6\u7528\u6237\u81EA\u5B9A\u4E49\u914D\u7F6E\uFF08\u5982\u679C\u5B58\u5728\uFF09
517
+ * \u7528\u6237\u53EF\u5728 src/app.config.ts \u4E2D\u5BFC\u51FA\u914D\u7F6E\u6765\u8986\u76D6\u9ED8\u8BA4\u503C
518
+ */
519
+ async function getUserConfig(): Promise<Partial<AppConfig>> {
520
+ try {
521
+ // webpackIgnore \u6CE8\u91CA\u8BA9\u6253\u5305\u5668\u8DF3\u8FC7\u6A21\u5757\u5B58\u5728\u6027\u68C0\u67E5
522
+ const userConfigModule = await import(/* webpackIgnore: true */ "@/app.config");
523
+ return userConfigModule.default || {};
524
+ } catch {
525
+ // \u6587\u4EF6\u4E0D\u5B58\u5728\u65F6\u8FD4\u56DE\u7A7A\u914D\u7F6E
526
+ return {};
527
+ }
528
+ }
529
+
530
+ /**
531
+ * \u521B\u5EFA\u5E94\u7528\u914D\u7F6E
532
+ */
533
+ export function createAppConfig(userConfig: Partial<AppConfig> = {}): AppConfig {
534
+ const router = createRouter(BASENAME);
535
+
536
+ const defaultConfig: AppConfig = {
537
+ appName: APP_NAME,
538
+ router,
539
+ basename: BASENAME,
540
+ rootId: APP_NAME,
541
+ strictMode: true,
542
+ antd: {
543
+ enabled: true,
544
+ },
545
+ providers: [],
546
+ lifecycle: {
547
+ onMounted() {
548
+ console.log(\`[\${APP_NAME}] \u5E94\u7528\u5DF2\u6302\u8F7D\`);
549
+ },
550
+ onUnmount() {
551
+ console.log(\`[\${APP_NAME}] \u5E94\u7528\u5DF2\u5378\u8F7D\`);
552
+ },
553
+ onError(error) {
554
+ console.error(\`[\${APP_NAME}] \u5168\u5C40\u9519\u8BEF:\`, error);
555
+ },
556
+ },
557
+ };
558
+
559
+ // \u6DF1\u5EA6\u5408\u5E76\u7528\u6237\u914D\u7F6E
560
+ return {
561
+ ...defaultConfig,
562
+ ...userConfig,
563
+ antd: { ...defaultConfig.antd, ...userConfig.antd },
564
+ providers: [...(defaultConfig.providers || []), ...(userConfig.providers || [])],
565
+ lifecycle: { ...defaultConfig.lifecycle, ...userConfig.lifecycle },
566
+ };
567
+ }
568
+
569
+ /**
570
+ * \u542F\u52A8\u5E94\u7528
571
+ */
572
+ export async function runApp(): Promise<void> {
573
+ const userConfig = await getUserConfig();
574
+ await bootstrap(createAppConfig(userConfig));
575
+ }
576
+ ${topLevel.length > 0 ? "\n" + topLevel.join("\n") : ""}
577
+ ${exports.length > 0 ? "\n" + exports.join("\n") : ""}
578
+ `;
579
+ }
580
+
581
+ // src/generator/templates/env-types.ts
582
+ import { existsSync as existsSync3, readFileSync } from "fs";
583
+ import { join as join2 } from "path";
584
+ function generateEnvTypes(cwd, config) {
585
+ const envVars = collectEnvVars(cwd, config);
586
+ const envInterface = envVars.map((key) => ` readonly ${key}: string;`).join("\n");
587
+ return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/core \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u4FEE\u6539
588
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
589
+
590
+ /// <reference types="react" />
591
+ /// <reference types="react-dom" />
592
+
593
+ declare namespace NodeJS {
594
+ interface ProcessEnv {
595
+ ${envInterface}
596
+ readonly NODE_ENV: "development" | "production" | "test";
597
+ }
598
+ }
599
+
600
+ declare module "*.svg" {
601
+ import * as React from "react";
602
+ const ReactComponent: React.FunctionComponent<
603
+ React.SVGProps<SVGSVGElement> & { title?: string }
604
+ >;
605
+ export default ReactComponent;
606
+ }
607
+
608
+ declare module "*.png" {
609
+ const src: string;
610
+ export default src;
611
+ }
612
+
613
+ declare module "*.jpg" {
614
+ const src: string;
615
+ export default src;
616
+ }
617
+
618
+ declare module "*.jpeg" {
619
+ const src: string;
620
+ export default src;
621
+ }
622
+
623
+ declare module "*.gif" {
624
+ const src: string;
625
+ export default src;
626
+ }
627
+
628
+ declare module "*.webp" {
629
+ const src: string;
630
+ export default src;
631
+ }
632
+
633
+ declare module "*.css" {
634
+ const classes: { readonly [key: string]: string };
635
+ export default classes;
636
+ }
637
+
638
+ declare module "*.less" {
639
+ const classes: { readonly [key: string]: string };
640
+ export default classes;
641
+ }
642
+
643
+ declare module "*.scss" {
644
+ const classes: { readonly [key: string]: string };
645
+ export default classes;
646
+ }
647
+
648
+ declare module "*.md" {
649
+ const content: string;
650
+ export default content;
651
+ }
652
+ `;
653
+ }
654
+ function collectEnvVars(cwd, config) {
655
+ const envVars = /* @__PURE__ */ new Set();
656
+ const publicEnvPath = join2(cwd, config.env.publicEnvFile || "config/env/.env.public");
657
+ if (existsSync3(publicEnvPath)) {
658
+ const content = readFileSync(publicEnvPath, "utf-8");
659
+ parseEnvFile(content, envVars);
660
+ }
661
+ const devEnvPath = join2(cwd, config.env.envDir || "config/env", ".env.development");
662
+ if (existsSync3(devEnvPath)) {
663
+ const content = readFileSync(devEnvPath, "utf-8");
664
+ parseEnvFile(content, envVars);
665
+ }
666
+ const prodEnvPath = join2(cwd, config.env.envDir || "config/env", ".env.production");
667
+ if (existsSync3(prodEnvPath)) {
668
+ const content = readFileSync(prodEnvPath, "utf-8");
669
+ parseEnvFile(content, envVars);
670
+ }
671
+ return Array.from(envVars).sort();
672
+ }
673
+ function parseEnvFile(content, envVars) {
674
+ const lines = content.split("\n");
675
+ for (const line of lines) {
676
+ const trimmed = line.trim();
677
+ if (!trimmed || trimmed.startsWith("#")) {
678
+ continue;
679
+ }
680
+ const match = trimmed.match(/^([A-Z_][A-Z0-9_]*)\s*=/);
681
+ if (match) {
682
+ envVars.add(match[1]);
683
+ }
684
+ }
685
+ }
686
+
687
+ // src/generator/templates/route-types.ts
688
+ function generateRouteTypes() {
689
+ return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/core \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u4FEE\u6539
690
+ // Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
691
+
692
+ import type { RouteObject } from "react-router";
693
+
694
+ declare module "@ywkf/routes" {
695
+ export const routes: RouteObject[];
696
+ export function createRouter(basename?: string): ReturnType<typeof import("react-router").createBrowserRouter>;
697
+ export default routes;
698
+ }
699
+ `;
700
+ }
701
+
702
+ // src/generator/generator.ts
703
+ var YwkfGenerator = class {
704
+ context;
705
+ pluginHooks;
706
+ lastGeneratedContent = /* @__PURE__ */ new Map();
707
+ constructor(context, pluginHooks = []) {
708
+ this.context = {
709
+ ...context,
710
+ outputDir: context.outputDir || join3(context.cwd, ".ywkf")
711
+ };
712
+ this.pluginHooks = pluginHooks;
713
+ }
714
+ /**
715
+ * 生成所有文件
716
+ */
717
+ async generate() {
718
+ const { outputDir } = this.context;
719
+ this.ensureDir(outputDir);
720
+ this.ensureDir(join3(outputDir, "types"));
721
+ this.generateConfigSnapshot();
722
+ this.generateRoutes();
723
+ this.generateBootstrapFile();
724
+ this.generateEntryFile();
725
+ this.generateTypes();
726
+ await this.generatePluginFiles();
727
+ for (const hooks of this.pluginHooks) {
728
+ if (hooks.afterGenerate) {
729
+ await hooks.afterGenerate(this.context);
730
+ }
731
+ }
732
+ console.log(`[ywkf] \u5DF2\u751F\u6210 .ywkf \u76EE\u5F55`);
733
+ }
734
+ /**
735
+ * 生成配置快照
736
+ */
737
+ generateConfigSnapshot() {
738
+ const { outputDir, config } = this.context;
739
+ const configPath = join3(outputDir, "config.json");
740
+ const serializableConfig = JSON.parse(
741
+ JSON.stringify(config, (key, value) => {
742
+ if (typeof value === "function") {
743
+ return "[Function]";
744
+ }
745
+ return value;
746
+ })
747
+ );
748
+ this.writeFileIfChanged(
749
+ configPath,
750
+ JSON.stringify(serializableConfig, null, 2)
751
+ );
752
+ }
753
+ /**
754
+ * 生成约定式路由
755
+ */
756
+ generateRoutes() {
757
+ const { cwd, outputDir, config } = this.context;
758
+ if (!config.router.conventional) {
759
+ return;
760
+ }
761
+ const pagesDir = join3(cwd, config.router.pagesDir || "src/pages");
762
+ const generator = new ConventionalRouteGenerator({
763
+ pagesDir,
764
+ outputDir,
765
+ basename: config.router.basename
766
+ });
767
+ const content = generator.generateCode();
768
+ const routesPath = join3(outputDir, "routes.tsx");
769
+ this.writeFileIfChanged(routesPath, content);
770
+ }
771
+ /**
772
+ * 生成启动文件
773
+ */
774
+ generateBootstrapFile() {
775
+ const { outputDir, config } = this.context;
776
+ const injections = this.collectInjections("bootstrap");
777
+ let content = generateBootstrap(config, injections);
778
+ for (const hooks of this.pluginHooks) {
779
+ if (hooks.modifyBootstrapCode) {
780
+ const result = hooks.modifyBootstrapCode(content, this.context);
781
+ if (result) {
782
+ content = result;
783
+ }
784
+ }
785
+ }
786
+ const bootstrapPath = join3(outputDir, "bootstrap.tsx");
787
+ this.writeFileIfChanged(bootstrapPath, content);
788
+ }
789
+ /**
790
+ * 生成入口文件
791
+ */
792
+ generateEntryFile() {
793
+ const { outputDir, config } = this.context;
794
+ const injections = this.collectInjections("entry");
795
+ let content = generateEntry(config, injections);
796
+ for (const hooks of this.pluginHooks) {
797
+ if (hooks.modifyEntryCode) {
798
+ const result = hooks.modifyEntryCode(content, this.context);
799
+ if (result) {
800
+ content = result;
801
+ }
802
+ }
803
+ }
804
+ const entryPath = join3(outputDir, "index.tsx");
805
+ this.writeFileIfChanged(entryPath, content);
806
+ }
807
+ /**
808
+ * 收集插件代码注入
809
+ */
810
+ collectInjections(type) {
811
+ const result = {
812
+ imports: [],
813
+ topLevel: [],
814
+ exports: []
815
+ };
816
+ const hookName = type === "entry" ? "injectEntry" : "injectBootstrap";
817
+ for (const hooks of this.pluginHooks) {
818
+ const hook = hooks[hookName];
819
+ if (hook) {
820
+ const injection = hook(this.context);
821
+ if (injection) {
822
+ result.imports?.push(...injection.imports || []);
823
+ result.topLevel?.push(...injection.topLevel || []);
824
+ result.exports?.push(...injection.exports || []);
825
+ }
826
+ }
827
+ }
828
+ return result;
829
+ }
830
+ /**
831
+ * 生成插件附加文件
832
+ */
833
+ async generatePluginFiles() {
834
+ const { outputDir } = this.context;
835
+ for (const hooks of this.pluginHooks) {
836
+ if (hooks.generateFiles) {
837
+ const files = hooks.generateFiles(this.context);
838
+ if (files) {
839
+ for (const file of files) {
840
+ const filePath = join3(outputDir, file.path);
841
+ const dir = join3(filePath, "..");
842
+ this.ensureDir(dir);
843
+ this.writeFileIfChanged(filePath, file.content);
844
+ }
845
+ }
846
+ }
847
+ }
848
+ }
849
+ /**
850
+ * 生成类型定义
851
+ */
852
+ generateTypes() {
853
+ const { outputDir, config, cwd } = this.context;
854
+ const envTypesContent = generateEnvTypes(cwd, config);
855
+ this.writeFileIfChanged(
856
+ join3(outputDir, "types", "env.d.ts"),
857
+ envTypesContent
858
+ );
859
+ if (config.router.conventional) {
860
+ const routeTypesContent = generateRouteTypes();
861
+ this.writeFileIfChanged(
862
+ join3(outputDir, "types", "routes.d.ts"),
863
+ routeTypesContent
864
+ );
865
+ }
866
+ }
867
+ /**
868
+ * 确保目录存在
869
+ */
870
+ ensureDir(dir) {
871
+ if (!existsSync4(dir)) {
872
+ mkdirSync2(dir, { recursive: true });
873
+ }
874
+ }
875
+ /**
876
+ * 只在内容变化时写入文件(避免触发不必要的热更新)
877
+ */
878
+ writeFileIfChanged(filePath, content) {
879
+ const normalizedContent = this.normalizeContent(content);
880
+ const lastContent = this.lastGeneratedContent.get(filePath);
881
+ if (lastContent === normalizedContent) {
882
+ return;
883
+ }
884
+ if (existsSync4(filePath)) {
885
+ const existingContent = readFileSync2(filePath, "utf-8");
886
+ if (this.normalizeContent(existingContent) === normalizedContent) {
887
+ this.lastGeneratedContent.set(filePath, normalizedContent);
888
+ return;
889
+ }
890
+ }
891
+ writeFileSync2(filePath, content, "utf-8");
892
+ this.lastGeneratedContent.set(filePath, normalizedContent);
893
+ }
894
+ /**
895
+ * 标准化内容(去掉时间戳等动态部分)
896
+ */
897
+ normalizeContent(content) {
898
+ return content.replace(/\/\/ Generated at: .+/g, "").replace(/\/\*\* Generated at: .+ \*\//g, "");
899
+ }
900
+ };
901
+
902
+ // src/generator/plugin.ts
903
+ var YwkfGeneratorPlugin = class {
904
+ options;
905
+ hasGenerated = false;
906
+ isWatching = false;
907
+ generator = null;
908
+ pluginHooks = [];
909
+ initialized = false;
910
+ constructor(options) {
911
+ this.options = options;
912
+ }
913
+ /**
914
+ * 解析插件配置为插件实例
915
+ */
916
+ async resolvePlugin(pluginConfig) {
917
+ try {
918
+ let plugin;
919
+ let options = {};
920
+ if (typeof pluginConfig === "string") {
921
+ const module = await import(pluginConfig);
922
+ plugin = module.default || module;
923
+ } else if (Array.isArray(pluginConfig)) {
924
+ const [pluginOrName, pluginOptions] = pluginConfig;
925
+ options = pluginOptions;
926
+ if (typeof pluginOrName === "string") {
927
+ const module = await import(pluginOrName);
928
+ plugin = module.default || module;
929
+ } else {
930
+ plugin = pluginOrName;
931
+ }
932
+ } else {
933
+ plugin = pluginConfig;
934
+ }
935
+ if (typeof plugin === "function") {
936
+ plugin = plugin(options);
937
+ }
938
+ return plugin;
939
+ } catch (error) {
940
+ console.error(`[ywkf] \u89E3\u6790\u63D2\u4EF6\u5931\u8D25: ${error}`);
941
+ return null;
942
+ }
943
+ }
944
+ /**
945
+ * 初始化插件钩子
946
+ */
947
+ async initialize() {
948
+ if (this.initialized) {
949
+ return;
950
+ }
951
+ const { cwd, config, isDev, pluginConfigs = [] } = this.options;
952
+ const context = {
953
+ cwd,
954
+ isDev,
955
+ isProd: !isDev,
956
+ config,
957
+ logger: {
958
+ info: (msg) => console.log(`[ywkf] ${msg}`),
959
+ warn: (msg) => console.warn(`[ywkf] ${msg}`),
960
+ error: (msg) => console.error(`[ywkf] ${msg}`),
961
+ debug: (msg) => {
962
+ if (process.env.DEBUG) {
963
+ console.log(`[ywkf:debug] ${msg}`);
964
+ }
965
+ }
966
+ }
967
+ };
968
+ for (const pluginConfig of pluginConfigs) {
969
+ const plugin = await this.resolvePlugin(pluginConfig);
970
+ if (plugin) {
971
+ const hooks = await plugin.setup(context);
972
+ this.pluginHooks.push(hooks);
973
+ console.log(`[ywkf] \u63D2\u4EF6\u5DF2\u52A0\u8F7D: ${plugin.name}`);
974
+ }
975
+ }
976
+ this.generator = new YwkfGenerator(
977
+ {
978
+ cwd: this.options.cwd,
979
+ config: this.options.config,
980
+ outputDir: this.options.outputDir,
981
+ isDev: this.options.isDev
982
+ },
983
+ this.pluginHooks
984
+ );
985
+ this.initialized = true;
986
+ }
987
+ apply(compiler) {
988
+ const pluginName = "YwkfGeneratorPlugin";
989
+ compiler.hooks.beforeCompile.tapAsync(pluginName, async (params, callback) => {
990
+ try {
991
+ await this.initialize();
992
+ if (!this.hasGenerated && this.generator) {
993
+ await this.generator.generate();
994
+ this.hasGenerated = true;
995
+ }
996
+ callback();
997
+ } catch (error) {
998
+ callback(error);
999
+ }
1000
+ });
1001
+ if (this.options.isDev && !this.isWatching) {
1002
+ this.watchPages();
1003
+ }
1004
+ }
1005
+ /**
1006
+ * 监听页面目录变化
1007
+ */
1008
+ watchPages() {
1009
+ const { config, cwd } = this.options;
1010
+ const pagesDir = join4(cwd, config.router.pagesDir || "src/pages");
1011
+ if (!existsSync5(pagesDir)) {
1012
+ return;
1013
+ }
1014
+ this.isWatching = true;
1015
+ let debounceTimer = null;
1016
+ const watcher = watch(
1017
+ pagesDir,
1018
+ { recursive: true },
1019
+ (eventType, filename) => {
1020
+ if (!filename?.match(/\.(tsx?|jsx?)$/)) {
1021
+ return;
1022
+ }
1023
+ if (debounceTimer) {
1024
+ clearTimeout(debounceTimer);
1025
+ }
1026
+ debounceTimer = setTimeout(async () => {
1027
+ console.log(`[ywkf] \u68C0\u6D4B\u5230\u9875\u9762\u53D8\u5316: ${filename}`);
1028
+ if (this.generator) {
1029
+ await this.generator.generate();
1030
+ }
1031
+ }, 500);
1032
+ }
1033
+ );
1034
+ process.on("exit", () => {
1035
+ watcher.close();
1036
+ });
1037
+ }
1038
+ };
1039
+
1040
+ // src/rspack/base.ts
1041
+ var require2 = createRequire(import.meta.url);
1042
+ var __dirname = dirname(fileURLToPath(import.meta.url));
1043
+ var coreNodeModules = join5(__dirname, "../../node_modules");
1044
+ function createBaseConfig(config, cwd) {
1045
+ const { resolveApp } = createPathResolver(cwd);
1046
+ const {
1047
+ appName,
1048
+ appCName,
1049
+ output,
1050
+ html,
1051
+ alias: userAlias,
1052
+ microFrontend,
1053
+ router
1054
+ } = config;
1055
+ const ywkfOutputDir = resolveApp(".ywkf");
1056
+ const defaultAlias = {
1057
+ "@": resolveApp("src"),
1058
+ "@config": resolveApp("config"),
1059
+ "@store": resolveApp("store"),
1060
+ "@locales": resolveApp("locales"),
1061
+ "@public": resolveApp("public"),
1062
+ // 约定式路由生成的文件
1063
+ "@ywkf/routes": join5(ywkfOutputDir, "routes.tsx"),
1064
+ // 请求层(由 reactQueryPlugin 生成)
1065
+ "@ywkf/request": join5(ywkfOutputDir, "request.ts"),
1066
+ // Store 工具(由 zustandPlugin 生成)
1067
+ "@ywkf/store": join5(ywkfOutputDir, "store.ts")
1068
+ };
1069
+ const alias = { ...defaultAlias, ...userAlias };
1070
+ return {
1071
+ // 使用 .ywkf/index.tsx 作为入口(由框架生成)
1072
+ entry: [join5(ywkfOutputDir, "index.tsx")],
1073
+ resolve: {
1074
+ extensions: [".ts", ".tsx", ".jsx", ".js", ".json", ".mjs"],
1075
+ symlinks: false,
1076
+ alias: {
1077
+ ...alias,
1078
+ "process/browser.js": require2.resolve("process/browser.js"),
1079
+ "process/browser": require2.resolve("process/browser.js"),
1080
+ // 确保 React 系列包从用户项目的 node_modules 解析
1081
+ react: resolveApp("node_modules/react"),
1082
+ "react-dom": resolveApp("node_modules/react-dom"),
1083
+ "react-router": resolveApp("node_modules/react-router")
1084
+ },
1085
+ fallback: {
1086
+ path: require2.resolve("path-browserify"),
1087
+ process: require2.resolve("process/browser.js"),
1088
+ buffer: require2.resolve("buffer/"),
1089
+ util: require2.resolve("util/"),
1090
+ stream: require2.resolve("stream-browserify"),
1091
+ crypto: require2.resolve("crypto-browserify"),
1092
+ zlib: require2.resolve("browserify-zlib"),
1093
+ querystring: require2.resolve("querystring-es3"),
1094
+ url: require2.resolve("url/"),
1095
+ assert: require2.resolve("assert/"),
1096
+ fs: false,
1097
+ net: false,
1098
+ tls: false,
1099
+ os: false,
1100
+ https: false,
1101
+ http: false
1102
+ }
1103
+ },
1104
+ resolveLoader: {
1105
+ modules: [coreNodeModules, "node_modules"]
1106
+ },
1107
+ module: {
1108
+ rules: [
1109
+ {
1110
+ test: /\.m?js$/,
1111
+ resolve: {
1112
+ fullySpecified: false
1113
+ }
1114
+ },
1115
+ {
1116
+ test: /\.[jt]sx?$/,
1117
+ include: [
1118
+ resolveApp("src"),
1119
+ resolveApp("config"),
1120
+ resolveApp("store"),
1121
+ resolveApp("packages"),
1122
+ ywkfOutputDir
1123
+ // 约定式路由生成的文件
1124
+ ],
1125
+ exclude: [resolveApp("node_modules")],
1126
+ use: [
1127
+ {
1128
+ loader: "builtin:swc-loader",
1129
+ options: {
1130
+ jsc: {
1131
+ parser: {
1132
+ syntax: "typescript",
1133
+ tsx: true,
1134
+ decorators: true
1135
+ },
1136
+ transform: {
1137
+ react: {
1138
+ runtime: "automatic"
1139
+ }
1140
+ },
1141
+ target: "es2015"
1142
+ },
1143
+ sourceMaps: true
1144
+ }
1145
+ }
1146
+ ]
1147
+ },
1148
+ {
1149
+ test: /\.(png|jpg|jpeg|gif|webp|m3u8|exr|hdr|json|woff2)$/,
1150
+ include: [resolveApp("public")],
1151
+ exclude: [resolveApp("src"), resolveApp("store")],
1152
+ type: "asset",
1153
+ parser: {
1154
+ dataUrlCondition: {
1155
+ maxSize: 10 * 1024
1156
+ }
1157
+ }
1158
+ },
1159
+ {
1160
+ test: /\.(md)$/,
1161
+ include: [resolveApp("src")],
1162
+ type: "asset/source"
1163
+ },
1164
+ {
1165
+ test: /\.svg$/,
1166
+ use: ["@svgr/webpack"]
1167
+ }
1168
+ ]
1169
+ },
1170
+ plugins: [
1171
+ new rspack.ProvidePlugin({
1172
+ process: "process/browser.js",
1173
+ Buffer: ["buffer", "Buffer"]
1174
+ }),
1175
+ new rspack.DefinePlugin({
1176
+ "process.env": JSON.stringify(process.env)
1177
+ }),
1178
+ new rspack.HtmlRspackPlugin({
1179
+ template: resolveApp(html.template ?? "public/index.html"),
1180
+ filename: "index.html",
1181
+ inject: "body",
1182
+ hash: true,
1183
+ minify: process.env.NODE_ENV === "production",
1184
+ favicon: resolveApp(html.favicon ?? "public/favicon.ico"),
1185
+ templateParameters: {
1186
+ title: html.title ?? appCName,
1187
+ mountRoot: html.mountRoot ?? appName
1188
+ }
1189
+ }),
1190
+ new rspack.CopyRspackPlugin({
1191
+ patterns: [
1192
+ {
1193
+ from: resolveApp("public/images"),
1194
+ to: "public/images",
1195
+ noErrorOnMissing: true
1196
+ }
1197
+ ]
1198
+ }),
1199
+ // .ywkf 目录生成插件(传递用户配置的插件列表)
1200
+ new YwkfGeneratorPlugin({
1201
+ cwd,
1202
+ config,
1203
+ outputDir: ywkfOutputDir,
1204
+ isDev: process.env.NODE_ENV !== "production",
1205
+ pluginConfigs: config.plugins
1206
+ })
1207
+ ].filter(Boolean),
1208
+ output: {
1209
+ assetModuleFilename: "images/[hash][ext]",
1210
+ library: microFrontend.enabled ? `${appCName}-[name]` : void 0,
1211
+ chunkFilename: "[name].[contenthash].js",
1212
+ libraryTarget: microFrontend.enabled ? "umd" : void 0,
1213
+ globalObject: microFrontend.enabled ? "window" : void 0,
1214
+ chunkLoadingGlobal: microFrontend.enabled ? `chunk_global_${appName}` : void 0,
1215
+ publicPath: output.publicPath ?? "/",
1216
+ path: resolveApp(output.path ?? "dist")
1217
+ },
1218
+ ignoreWarnings: [
1219
+ {
1220
+ module: /@testing-library\/react/,
1221
+ message: /export 'act'/
1222
+ }
1223
+ ],
1224
+ devtool: "source-map"
1225
+ };
1226
+ }
1227
+
1228
+ // src/rspack/dev.ts
1229
+ var require3 = createRequire2(import.meta.url);
1230
+ function convertProxyToArray(proxy) {
1231
+ if (!proxy || Object.keys(proxy).length === 0) {
1232
+ return void 0;
1233
+ }
1234
+ return Object.entries(proxy).map(([context, target]) => {
1235
+ if (typeof target === "string") {
1236
+ return {
1237
+ context: [context],
1238
+ target,
1239
+ changeOrigin: true,
1240
+ secure: false
1241
+ };
1242
+ }
1243
+ return {
1244
+ context: [context],
1245
+ changeOrigin: true,
1246
+ secure: false,
1247
+ ...target
1248
+ };
1249
+ });
1250
+ }
1251
+ function createDevConfig(config, cwd) {
1252
+ const baseConfig = createBaseConfig(config, cwd);
1253
+ const { resolveApp } = createPathResolver(cwd);
1254
+ const { dev: dev2, style } = config;
1255
+ const styleRules = {
1256
+ rules: [
1257
+ // Less - antd
1258
+ {
1259
+ test: /\.less$/,
1260
+ include: [
1261
+ /[\\/]node_modules[\\/].*antd/,
1262
+ /[\\/]node_modules[\\/]@4399ywkf[\\/]design/
1263
+ ],
1264
+ use: [
1265
+ { loader: "style-loader" },
1266
+ {
1267
+ loader: "css-loader",
1268
+ options: { sourceMap: true }
1269
+ },
1270
+ {
1271
+ loader: "less-loader",
1272
+ options: {
1273
+ sourceMap: true,
1274
+ lessOptions: {
1275
+ strictMath: false,
1276
+ math: "always",
1277
+ javascriptEnabled: true
1278
+ }
1279
+ }
1280
+ }
1281
+ ]
1282
+ },
1283
+ // Less - 项目文件
1284
+ ...style.less?.enabled !== false ? [
1285
+ {
1286
+ test: /\.less$/,
1287
+ include: [resolveApp("src")],
1288
+ exclude: [resolveApp("node_modules")],
1289
+ oneOf: [
1290
+ {
1291
+ test: /\.module\.less$/,
1292
+ use: [
1293
+ { loader: "style-loader" },
1294
+ {
1295
+ loader: "css-loader",
1296
+ options: {
1297
+ sourceMap: true,
1298
+ modules: {
1299
+ localIdentName: "[path][name]__[local]--[hash:base64:5]"
1300
+ },
1301
+ importLoaders: 2
1302
+ }
1303
+ },
1304
+ {
1305
+ loader: "less-loader",
1306
+ options: {
1307
+ sourceMap: true,
1308
+ lessOptions: {
1309
+ javascriptEnabled: true,
1310
+ math: "always",
1311
+ ...style.less?.lessOptions
1312
+ }
1313
+ }
1314
+ }
1315
+ ]
1316
+ },
1317
+ {
1318
+ use: [
1319
+ { loader: "style-loader" },
1320
+ {
1321
+ loader: "css-loader",
1322
+ options: { sourceMap: true }
1323
+ },
1324
+ {
1325
+ loader: "less-loader",
1326
+ options: {
1327
+ sourceMap: true,
1328
+ lessOptions: {
1329
+ javascriptEnabled: true,
1330
+ ...style.less?.lessOptions
1331
+ }
1332
+ }
1333
+ }
1334
+ ]
1335
+ }
1336
+ ]
1337
+ }
1338
+ ] : [],
1339
+ // Sass
1340
+ ...style.sass?.enabled !== false ? [
1341
+ {
1342
+ test: /\.s[ac]ss$/i,
1343
+ include: [resolveApp("src")],
1344
+ exclude: [resolveApp("node_modules")],
1345
+ oneOf: [
1346
+ {
1347
+ test: /\.module\.s[ac]ss$/,
1348
+ use: [
1349
+ { loader: "style-loader" },
1350
+ {
1351
+ loader: "css-loader",
1352
+ options: {
1353
+ sourceMap: true,
1354
+ modules: {
1355
+ localIdentName: "[path][name]__[local]--[hash:base64:5]"
1356
+ },
1357
+ importLoaders: 2
1358
+ }
1359
+ },
1360
+ {
1361
+ loader: "sass-loader",
1362
+ options: {
1363
+ sourceMap: true,
1364
+ sassOptions: {
1365
+ outputStyle: "compressed",
1366
+ ...style.sass?.sassOptions
1367
+ }
1368
+ }
1369
+ }
1370
+ ]
1371
+ },
1372
+ {
1373
+ use: [
1374
+ { loader: "style-loader" },
1375
+ {
1376
+ loader: "css-loader",
1377
+ options: { sourceMap: true }
1378
+ },
1379
+ {
1380
+ loader: "sass-loader",
1381
+ options: {
1382
+ sourceMap: true,
1383
+ sassOptions: {
1384
+ outputStyle: "compressed",
1385
+ ...style.sass?.sassOptions
1386
+ }
1387
+ }
1388
+ }
1389
+ ]
1390
+ }
1391
+ ]
1392
+ }
1393
+ ] : [],
1394
+ // TailwindCSS
1395
+ ...style.tailwindcss ? [
1396
+ {
1397
+ test: /\.css$/,
1398
+ include: [resolveApp("src/index.css")],
1399
+ use: [
1400
+ "style-loader",
1401
+ "css-loader",
1402
+ {
1403
+ loader: "postcss-loader",
1404
+ options: {
1405
+ postcssOptions: {
1406
+ config: resolveApp("postcss.config.js")
1407
+ }
1408
+ }
1409
+ }
1410
+ ]
1411
+ }
1412
+ ] : [],
1413
+ // 其他 CSS
1414
+ {
1415
+ test: /\.css$/,
1416
+ include: [resolveApp("src"), resolveApp("node_modules")],
1417
+ exclude: style.tailwindcss ? [resolveApp("src/index.css")] : [],
1418
+ use: [
1419
+ { loader: "style-loader" },
1420
+ {
1421
+ loader: "css-loader",
1422
+ options: { sourceMap: true }
1423
+ }
1424
+ ]
1425
+ }
1426
+ ]
1427
+ };
1428
+ const devConfig = {
1429
+ mode: "development",
1430
+ devtool: "inline-source-map",
1431
+ output: {
1432
+ filename: "[name].bundle.js",
1433
+ clean: false
1434
+ },
1435
+ stats: "errors-only",
1436
+ devServer: {
1437
+ host: dev2.host,
1438
+ port: dev2.port,
1439
+ compress: true,
1440
+ historyApiFallback: true,
1441
+ hot: true,
1442
+ allowedHosts: "all",
1443
+ client: {
1444
+ overlay: false,
1445
+ progress: true
1446
+ },
1447
+ proxy: convertProxyToArray(dev2.proxy)
1448
+ },
1449
+ module: styleRules
1450
+ };
1451
+ return merge(baseConfig, devConfig);
1452
+ }
1453
+
1454
+ // src/rspack/prod.ts
1455
+ import { rspack as rspack2 } from "@rspack/core";
1456
+ import { merge as merge2 } from "webpack-merge";
1457
+ function createProdConfig(config, cwd) {
1458
+ const baseConfig = createBaseConfig(config, cwd);
1459
+ const { resolveApp } = createPathResolver(cwd);
1460
+ const { style, performance, output } = config;
1461
+ const styleRules = {
1462
+ rules: [
1463
+ // Less - antd
1464
+ {
1465
+ test: /\.less$/,
1466
+ include: [
1467
+ /[\\/]node_modules[\\/].*antd/,
1468
+ /[\\/]node_modules[\\/]@4399ywkf[\\/]design/
1469
+ ],
1470
+ use: [
1471
+ { loader: rspack2.CssExtractRspackPlugin.loader },
1472
+ {
1473
+ loader: "css-loader",
1474
+ options: { sourceMap: true }
1475
+ },
1476
+ {
1477
+ loader: "less-loader",
1478
+ options: {
1479
+ sourceMap: true,
1480
+ lessOptions: {
1481
+ strictMath: false,
1482
+ math: "always",
1483
+ javascriptEnabled: true
1484
+ }
1485
+ }
1486
+ }
1487
+ ]
1488
+ },
1489
+ // Less - 项目文件
1490
+ ...style.less?.enabled !== false ? [
1491
+ {
1492
+ test: /\.less$/,
1493
+ include: [resolveApp("src")],
1494
+ exclude: [resolveApp("node_modules")],
1495
+ oneOf: [
1496
+ {
1497
+ test: /\.module\.less$/,
1498
+ use: [
1499
+ { loader: rspack2.CssExtractRspackPlugin.loader },
1500
+ {
1501
+ loader: "css-loader",
1502
+ options: {
1503
+ sourceMap: true,
1504
+ modules: {
1505
+ localIdentName: "[path][name]__[local]--[hash:base64:5]"
1506
+ },
1507
+ importLoaders: 2
1508
+ }
1509
+ },
1510
+ {
1511
+ loader: "less-loader",
1512
+ options: {
1513
+ sourceMap: true,
1514
+ lessOptions: {
1515
+ javascriptEnabled: true,
1516
+ ...style.less?.lessOptions
1517
+ }
1518
+ }
1519
+ }
1520
+ ]
1521
+ },
1522
+ {
1523
+ use: [
1524
+ { loader: rspack2.CssExtractRspackPlugin.loader },
1525
+ {
1526
+ loader: "css-loader",
1527
+ options: { sourceMap: true }
1528
+ },
1529
+ {
1530
+ loader: "less-loader",
1531
+ options: {
1532
+ sourceMap: true,
1533
+ lessOptions: {
1534
+ javascriptEnabled: true,
1535
+ ...style.less?.lessOptions
1536
+ }
1537
+ }
1538
+ }
1539
+ ]
1540
+ }
1541
+ ]
1542
+ }
1543
+ ] : [],
1544
+ // Sass
1545
+ ...style.sass?.enabled !== false ? [
1546
+ {
1547
+ test: /\.s[ac]ss$/i,
1548
+ include: [resolveApp("src")],
1549
+ exclude: [resolveApp("node_modules")],
1550
+ oneOf: [
1551
+ {
1552
+ test: /\.module\.s[ac]ss$/,
1553
+ use: [
1554
+ { loader: rspack2.CssExtractRspackPlugin.loader },
1555
+ {
1556
+ loader: "css-loader",
1557
+ options: {
1558
+ sourceMap: true,
1559
+ modules: {
1560
+ localIdentName: "[path][name]__[local]--[hash:base64:5]"
1561
+ },
1562
+ importLoaders: 2
1563
+ }
1564
+ },
1565
+ {
1566
+ loader: "sass-loader",
1567
+ options: {
1568
+ sourceMap: true,
1569
+ sassOptions: {
1570
+ outputStyle: "compressed",
1571
+ ...style.sass?.sassOptions
1572
+ }
1573
+ }
1574
+ }
1575
+ ]
1576
+ },
1577
+ {
1578
+ use: [
1579
+ { loader: rspack2.CssExtractRspackPlugin.loader },
1580
+ {
1581
+ loader: "css-loader",
1582
+ options: { sourceMap: true }
1583
+ },
1584
+ {
1585
+ loader: "sass-loader",
1586
+ options: {
1587
+ sourceMap: true,
1588
+ sassOptions: {
1589
+ outputStyle: "compressed",
1590
+ ...style.sass?.sassOptions
1591
+ }
1592
+ }
1593
+ }
1594
+ ]
1595
+ }
1596
+ ]
1597
+ }
1598
+ ] : [],
1599
+ // TailwindCSS
1600
+ ...style.tailwindcss ? [
1601
+ {
1602
+ test: /\.css$/,
1603
+ include: [resolveApp("src/index.css")],
1604
+ use: [
1605
+ { loader: rspack2.CssExtractRspackPlugin.loader },
1606
+ "css-loader",
1607
+ {
1608
+ loader: "postcss-loader",
1609
+ options: {
1610
+ postcssOptions: {
1611
+ config: resolveApp("postcss.config.js")
1612
+ }
1613
+ }
1614
+ }
1615
+ ]
1616
+ }
1617
+ ] : [],
1618
+ // src 目录中的其他 CSS
1619
+ {
1620
+ test: /\.css$/,
1621
+ include: [resolveApp("src")],
1622
+ exclude: style.tailwindcss ? [resolveApp("src/index.css")] : [],
1623
+ use: [
1624
+ { loader: rspack2.CssExtractRspackPlugin.loader },
1625
+ {
1626
+ loader: "css-loader",
1627
+ options: { sourceMap: true }
1628
+ }
1629
+ ]
1630
+ },
1631
+ // node_modules CSS
1632
+ {
1633
+ test: /\.css$/,
1634
+ include: [resolveApp("node_modules")],
1635
+ use: [
1636
+ { loader: "style-loader" },
1637
+ {
1638
+ loader: "css-loader",
1639
+ options: { sourceMap: true }
1640
+ }
1641
+ ]
1642
+ }
1643
+ ]
1644
+ };
1645
+ const prodConfig = {
1646
+ mode: "production",
1647
+ devtool: "hidden-source-map",
1648
+ output: {
1649
+ filename: "[name].[contenthash].bundle.js",
1650
+ chunkFilename: "[name].[contenthash].chunk.js",
1651
+ clean: output.clean
1652
+ },
1653
+ module: styleRules,
1654
+ plugins: [
1655
+ new rspack2.CssExtractRspackPlugin({
1656
+ filename: "[name].[contenthash:8].css",
1657
+ chunkFilename: "[name].[contenthash:8].chunk.css"
1658
+ })
1659
+ ],
1660
+ optimization: {
1661
+ minimize: true,
1662
+ minimizer: [
1663
+ new rspack2.SwcJsMinimizerRspackPlugin({
1664
+ exclude: [resolveApp("node_modules")],
1665
+ minimizerOptions: {
1666
+ compress: {
1667
+ pure_funcs: performance.dropConsole ? ["console.log", "console.info", "console.debug", "console.warn"] : ["console.info", "console.debug"],
1668
+ drop_console: false,
1669
+ drop_debugger: true
1670
+ }
1671
+ }
1672
+ }),
1673
+ new rspack2.LightningCssMinimizerRspackPlugin()
1674
+ ]
1675
+ }
1676
+ };
1677
+ return merge2(baseConfig, prodConfig);
1678
+ }
1679
+
1680
+ // src/rspack/index.ts
1681
+ function createRspackConfig(config, cwd, options = {}) {
1682
+ const isDev = options.isDev ?? process.env.NODE_ENV !== "production";
1683
+ let rspackConfig = isDev ? createDevConfig(config, cwd) : createProdConfig(config, cwd);
1684
+ if (config.performance.rsdoctor) {
1685
+ rspackConfig.plugins = [
1686
+ ...rspackConfig.plugins || [],
1687
+ new RsdoctorRspackPlugin({})
1688
+ ];
1689
+ }
1690
+ if (config.tools.rspack) {
1691
+ const userConfig = config.tools.rspack(rspackConfig, {
1692
+ isDev,
1693
+ isProd: !isDev
1694
+ });
1695
+ if (userConfig) {
1696
+ rspackConfig = userConfig;
1697
+ }
1698
+ }
1699
+ return rspackConfig;
1700
+ }
1701
+
1702
+ // src/cli/env.ts
1703
+ import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
1704
+ import { resolve as resolve2 } from "path";
1705
+ import dotenv from "dotenv";
1706
+ function preloadEnv(cwd, mode) {
1707
+ process.env.NODE_ENV = mode;
1708
+ const defaultPaths = [
1709
+ resolve2(cwd, "config/env/.env.public"),
1710
+ resolve2(cwd, ".env.public"),
1711
+ resolve2(cwd, ".env")
1712
+ ];
1713
+ for (const envPath of defaultPaths) {
1714
+ if (existsSync6(envPath)) {
1715
+ dotenv.config({ path: envPath });
1716
+ break;
1717
+ }
1718
+ }
1719
+ const modeEnvPaths = [
1720
+ resolve2(cwd, `config/env/.env.${mode}`),
1721
+ resolve2(cwd, `.env.${mode}`)
1722
+ ];
1723
+ for (const envPath of modeEnvPaths) {
1724
+ if (existsSync6(envPath)) {
1725
+ const envContent = readFileSync3(envPath, "utf-8");
1726
+ const parsed = dotenv.parse(envContent);
1727
+ for (const key in parsed) {
1728
+ process.env[key] = parsed[key];
1729
+ }
1730
+ break;
1731
+ }
1732
+ }
1733
+ }
1734
+ function loadEnv(config, cwd, mode) {
1735
+ const { env } = config;
1736
+ const publicEnvPath = resolve2(cwd, env.publicEnvFile ?? "config/env/.env.public");
1737
+ if (existsSync6(publicEnvPath)) {
1738
+ dotenv.config({ path: publicEnvPath });
1739
+ }
1740
+ const modeEnvPath = resolve2(cwd, env.envDir ?? "config/env", `.env.${mode}`);
1741
+ if (existsSync6(modeEnvPath)) {
1742
+ const envContent = readFileSync3(modeEnvPath, "utf-8");
1743
+ const parsed = dotenv.parse(envContent);
1744
+ for (const key in parsed) {
1745
+ process.env[key] = parsed[key];
1746
+ }
1747
+ }
1748
+ process.env.NODE_ENV = mode;
1749
+ process.env.APP_NAME = process.env.APP_NAME || config.appName;
1750
+ process.env.APP_CNAME = process.env.APP_CNAME || config.appCName;
1751
+ process.env.OUTPUT_PATH = process.env.OUTPUT_PATH || config.output.path;
1752
+ process.env.PUBLIC_PATH = process.env.PUBLIC_PATH || config.output.publicPath;
1753
+ process.env.APP_HOST = process.env.APP_HOST || config.dev.host;
1754
+ process.env.APP_PORT = process.env.APP_PORT || String(config.dev.port);
1755
+ }
1756
+
1757
+ // src/plugin/manager.ts
1758
+ function createLogger(pluginName) {
1759
+ const prefix = `[${pluginName}]`;
1760
+ return {
1761
+ info: (msg) => console.log(`${prefix} ${msg}`),
1762
+ warn: (msg) => console.warn(`${prefix} ${msg}`),
1763
+ error: (msg) => console.error(`${prefix} ${msg}`),
1764
+ debug: (msg) => {
1765
+ if (process.env.DEBUG) {
1766
+ console.debug(`${prefix} ${msg}`);
1767
+ }
1768
+ }
1769
+ };
1770
+ }
1771
+ async function resolvePlugin(pluginConfig, cwd) {
1772
+ let plugin;
1773
+ let options = {};
1774
+ if (typeof pluginConfig === "string") {
1775
+ const module = await import(pluginConfig);
1776
+ plugin = module.default || module;
1777
+ } else if (Array.isArray(pluginConfig)) {
1778
+ const [pluginOrName, pluginOptions] = pluginConfig;
1779
+ options = pluginOptions;
1780
+ if (typeof pluginOrName === "string") {
1781
+ const module = await import(pluginOrName);
1782
+ plugin = module.default || module;
1783
+ } else {
1784
+ plugin = pluginOrName;
1785
+ }
1786
+ } else {
1787
+ plugin = pluginConfig;
1788
+ }
1789
+ if (typeof plugin === "function") {
1790
+ plugin = plugin(options);
1791
+ }
1792
+ return { plugin, options };
1793
+ }
1794
+ var PluginManager = class {
1795
+ plugins = /* @__PURE__ */ new Map();
1796
+ context;
1797
+ constructor(config, cwd, isDev) {
1798
+ this.context = {
1799
+ cwd,
1800
+ isDev,
1801
+ isProd: !isDev,
1802
+ config,
1803
+ logger: createLogger("PluginManager")
1804
+ };
1805
+ }
1806
+ /**
1807
+ * 加载并初始化所有插件
1808
+ */
1809
+ async loadPlugins(pluginConfigs) {
1810
+ for (const pluginConfig of pluginConfigs) {
1811
+ try {
1812
+ const { plugin } = await resolvePlugin(pluginConfig, this.context.cwd);
1813
+ if (this.plugins.has(plugin.name)) {
1814
+ this.context.logger.warn(`\u63D2\u4EF6 ${plugin.name} \u5DF2\u52A0\u8F7D\uFF0C\u8DF3\u8FC7\u91CD\u590D\u52A0\u8F7D`);
1815
+ continue;
1816
+ }
1817
+ const pluginContext = {
1818
+ ...this.context,
1819
+ logger: createLogger(plugin.name)
1820
+ };
1821
+ const hooks = await plugin.setup(pluginContext);
1822
+ this.plugins.set(plugin.name, { plugin, hooks });
1823
+ this.context.logger.info(`\u5DF2\u52A0\u8F7D\u63D2\u4EF6: ${plugin.name}`);
1824
+ } catch (error) {
1825
+ this.context.logger.error(
1826
+ `\u52A0\u8F7D\u63D2\u4EF6\u5931\u8D25: ${String(pluginConfig)} - ${error}`
1827
+ );
1828
+ }
1829
+ }
1830
+ }
1831
+ /**
1832
+ * 执行 modifyRspackConfig 钩子
1833
+ */
1834
+ async applyRspackConfigHooks(config) {
1835
+ let result = config;
1836
+ for (const [name, { hooks }] of this.plugins) {
1837
+ if (hooks.modifyRspackConfig) {
1838
+ try {
1839
+ const modified = await hooks.modifyRspackConfig(result, this.context);
1840
+ if (modified) {
1841
+ result = modified;
1842
+ }
1843
+ } catch (error) {
1844
+ this.context.logger.error(
1845
+ `\u63D2\u4EF6 ${name} modifyRspackConfig \u6267\u884C\u5931\u8D25: ${error}`
1846
+ );
1847
+ }
1848
+ }
1849
+ }
1850
+ return result;
1851
+ }
1852
+ /**
1853
+ * 执行 modifyRoutes 钩子
1854
+ */
1855
+ async applyRoutesHooks(routes) {
1856
+ let result = routes;
1857
+ for (const [name, { hooks }] of this.plugins) {
1858
+ if (hooks.modifyRoutes) {
1859
+ try {
1860
+ const modified = await hooks.modifyRoutes(result, this.context);
1861
+ if (modified) {
1862
+ result = modified;
1863
+ }
1864
+ } catch (error) {
1865
+ this.context.logger.error(
1866
+ `\u63D2\u4EF6 ${name} modifyRoutes \u6267\u884C\u5931\u8D25: ${error}`
1867
+ );
1868
+ }
1869
+ }
1870
+ }
1871
+ return result;
1872
+ }
1873
+ /**
1874
+ * 执行 modifyAppConfig 钩子
1875
+ */
1876
+ applyAppConfigHooks(appConfig) {
1877
+ let result = appConfig;
1878
+ for (const [name, { hooks }] of this.plugins) {
1879
+ if (hooks.modifyAppConfig) {
1880
+ try {
1881
+ const modified = hooks.modifyAppConfig(result, this.context);
1882
+ if (modified) {
1883
+ result = modified;
1884
+ }
1885
+ } catch (error) {
1886
+ this.context.logger.error(
1887
+ `\u63D2\u4EF6 ${name} modifyAppConfig \u6267\u884C\u5931\u8D25: ${error}`
1888
+ );
1889
+ }
1890
+ }
1891
+ }
1892
+ return result;
1893
+ }
1894
+ /**
1895
+ * 收集所有插件的 Provider
1896
+ */
1897
+ collectProviders() {
1898
+ const providers = [];
1899
+ for (const [name, { hooks }] of this.plugins) {
1900
+ if (hooks.addProvider) {
1901
+ try {
1902
+ const result = hooks.addProvider(this.context);
1903
+ if (Array.isArray(result)) {
1904
+ providers.push(...result);
1905
+ } else if (result) {
1906
+ providers.push(result);
1907
+ }
1908
+ } catch (error) {
1909
+ this.context.logger.error(
1910
+ `\u63D2\u4EF6 ${name} addProvider \u6267\u884C\u5931\u8D25: ${error}`
1911
+ );
1912
+ }
1913
+ }
1914
+ }
1915
+ return providers;
1916
+ }
1917
+ /**
1918
+ * 执行 beforeBuild 钩子
1919
+ */
1920
+ async runBeforeBuild() {
1921
+ for (const [name, { hooks }] of this.plugins) {
1922
+ if (hooks.beforeBuild) {
1923
+ try {
1924
+ await hooks.beforeBuild(this.context);
1925
+ } catch (error) {
1926
+ this.context.logger.error(
1927
+ `\u63D2\u4EF6 ${name} beforeBuild \u6267\u884C\u5931\u8D25: ${error}`
1928
+ );
1929
+ }
1930
+ }
1931
+ }
1932
+ }
1933
+ /**
1934
+ * 执行 afterBuild 钩子
1935
+ */
1936
+ async runAfterBuild(stats) {
1937
+ for (const [name, { hooks }] of this.plugins) {
1938
+ if (hooks.afterBuild) {
1939
+ try {
1940
+ await hooks.afterBuild(this.context, stats);
1941
+ } catch (error) {
1942
+ this.context.logger.error(
1943
+ `\u63D2\u4EF6 ${name} afterBuild \u6267\u884C\u5931\u8D25: ${error}`
1944
+ );
1945
+ }
1946
+ }
1947
+ }
1948
+ }
1949
+ /**
1950
+ * 执行 beforeDevServer 钩子
1951
+ */
1952
+ async runBeforeDevServer() {
1953
+ for (const [name, { hooks }] of this.plugins) {
1954
+ if (hooks.beforeDevServer) {
1955
+ try {
1956
+ await hooks.beforeDevServer(this.context);
1957
+ } catch (error) {
1958
+ this.context.logger.error(
1959
+ `\u63D2\u4EF6 ${name} beforeDevServer \u6267\u884C\u5931\u8D25: ${error}`
1960
+ );
1961
+ }
1962
+ }
1963
+ }
1964
+ }
1965
+ /**
1966
+ * 执行 afterDevServer 钩子
1967
+ */
1968
+ async runAfterDevServer(server) {
1969
+ for (const [name, { hooks }] of this.plugins) {
1970
+ if (hooks.afterDevServer) {
1971
+ try {
1972
+ await hooks.afterDevServer(this.context, server);
1973
+ } catch (error) {
1974
+ this.context.logger.error(
1975
+ `\u63D2\u4EF6 ${name} afterDevServer \u6267\u884C\u5931\u8D25: ${error}`
1976
+ );
1977
+ }
1978
+ }
1979
+ }
1980
+ }
1981
+ /**
1982
+ * 获取所有已加载的插件名称
1983
+ */
1984
+ getPluginNames() {
1985
+ return Array.from(this.plugins.keys());
1986
+ }
1987
+ /**
1988
+ * 获取所有已加载的插件
1989
+ */
1990
+ getPlugins() {
1991
+ return Array.from(this.plugins.values()).map((p) => p.plugin);
1992
+ }
1993
+ /**
1994
+ * 获取所有已加载的插件钩子
1995
+ */
1996
+ getPluginHooks() {
1997
+ return Array.from(this.plugins.values()).map((p) => p.hooks);
1998
+ }
1999
+ };
2000
+
2001
+ // src/cli/dev.ts
2002
+ async function dev(options = {}) {
2003
+ const cwd = options.cwd || process.cwd();
2004
+ const spinner = ora("\u6B63\u5728\u542F\u52A8\u5F00\u53D1\u670D\u52A1\u5668...").start();
2005
+ try {
2006
+ preloadEnv(cwd, "development");
2007
+ const { config, configPath } = await resolveConfig(cwd);
2008
+ if (configPath) {
2009
+ spinner.text = `\u5DF2\u52A0\u8F7D\u914D\u7F6E: ${configPath}`;
2010
+ }
2011
+ loadEnv(config, cwd, "development");
2012
+ const pluginManager = new PluginManager(config, cwd, true);
2013
+ if (config.plugins && config.plugins.length > 0) {
2014
+ spinner.text = "\u52A0\u8F7D\u63D2\u4EF6...";
2015
+ await pluginManager.loadPlugins(config.plugins);
2016
+ }
2017
+ await pluginManager.runBeforeDevServer();
2018
+ let rspackConfig = createRspackConfig(config, cwd, { isDev: true });
2019
+ rspackConfig = await pluginManager.applyRspackConfigHooks(rspackConfig);
2020
+ const compiler = rspack3(rspackConfig);
2021
+ const devServerOptions = rspackConfig.devServer || {};
2022
+ const server = new RspackDevServer(devServerOptions, compiler);
2023
+ const host = config.dev.host || "localhost";
2024
+ const port = config.dev.port || 3e3;
2025
+ await server.start();
2026
+ await pluginManager.runAfterDevServer({ host, port });
2027
+ spinner.succeed(chalk.green("\u5F00\u53D1\u670D\u52A1\u5668\u542F\u52A8\u6210\u529F!"));
2028
+ console.log();
2029
+ console.log(
2030
+ ` ${chalk.bold("Local:")} ${chalk.cyan(`http://${host}:${port}`)}`
2031
+ );
2032
+ const pluginNames = pluginManager.getPluginNames();
2033
+ if (pluginNames.length > 0) {
2034
+ console.log(` ${chalk.bold("\u63D2\u4EF6:")} ${chalk.dim(pluginNames.join(", "))}`);
2035
+ }
2036
+ console.log();
2037
+ const signals = ["SIGINT", "SIGTERM"];
2038
+ for (const signal of signals) {
2039
+ process.on(signal, async () => {
2040
+ await server.stop();
2041
+ process.exit(0);
2042
+ });
2043
+ }
2044
+ } catch (error) {
2045
+ spinner.fail(chalk.red("\u5F00\u53D1\u670D\u52A1\u5668\u542F\u52A8\u5931\u8D25"));
2046
+ console.error(error);
2047
+ process.exit(1);
2048
+ }
2049
+ }
2050
+
2051
+ // src/cli/build.ts
2052
+ import { rspack as rspack4 } from "@rspack/core";
2053
+ import chalk2 from "chalk";
2054
+ import ora2 from "ora";
2055
+ function formatSize(bytes) {
2056
+ if (bytes < 1024) return `${bytes} B`;
2057
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`;
2058
+ return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
2059
+ }
2060
+ function printBuildResult(stats) {
2061
+ const info = stats.toJson({
2062
+ assets: true,
2063
+ errors: true,
2064
+ warnings: true
2065
+ });
2066
+ if (info.errors && info.errors.length > 0) {
2067
+ console.log(chalk2.red("\n\u6784\u5EFA\u9519\u8BEF:"));
2068
+ for (const error of info.errors) {
2069
+ console.log(chalk2.red(` ${error.message}`));
2070
+ }
2071
+ return;
2072
+ }
2073
+ if (info.warnings && info.warnings.length > 0) {
2074
+ console.log(chalk2.yellow("\n\u6784\u5EFA\u8B66\u544A:"));
2075
+ for (const warning of info.warnings) {
2076
+ console.log(chalk2.yellow(` ${warning.message}`));
2077
+ }
2078
+ }
2079
+ console.log(chalk2.bold("\n\u6784\u5EFA\u4EA7\u7269:"));
2080
+ console.log();
2081
+ const assets = info.assets || [];
2082
+ const sortedAssets = assets.filter((asset) => !asset.name.endsWith(".map")).sort((a, b) => b.size - a.size);
2083
+ for (const asset of sortedAssets.slice(0, 15)) {
2084
+ const sizeColor = asset.size > 500 * 1024 ? chalk2.yellow : chalk2.green;
2085
+ console.log(
2086
+ ` ${chalk2.dim(asset.name.padEnd(50))} ${sizeColor(formatSize(asset.size))}`
2087
+ );
2088
+ }
2089
+ if (sortedAssets.length > 15) {
2090
+ console.log(chalk2.dim(` ... \u8FD8\u6709 ${sortedAssets.length - 15} \u4E2A\u6587\u4EF6`));
2091
+ }
2092
+ console.log();
2093
+ console.log(
2094
+ chalk2.green(
2095
+ `\u2713 \u6784\u5EFA\u5B8C\u6210\uFF0C\u8017\u65F6 ${((info.time || 0) / 1e3).toFixed(2)}s`
2096
+ )
2097
+ );
2098
+ }
2099
+ async function build(options = {}) {
2100
+ const cwd = options.cwd || process.cwd();
2101
+ const spinner = ora2("\u6B63\u5728\u6784\u5EFA\u751F\u4EA7\u73AF\u5883...").start();
2102
+ try {
2103
+ preloadEnv(cwd, "production");
2104
+ const { config, configPath } = await resolveConfig(cwd);
2105
+ if (configPath) {
2106
+ spinner.text = `\u5DF2\u52A0\u8F7D\u914D\u7F6E: ${configPath}`;
2107
+ }
2108
+ loadEnv(config, cwd, "production");
2109
+ const pluginManager = new PluginManager(config, cwd, false);
2110
+ if (config.plugins && config.plugins.length > 0) {
2111
+ spinner.text = "\u52A0\u8F7D\u63D2\u4EF6...";
2112
+ await pluginManager.loadPlugins(config.plugins);
2113
+ }
2114
+ await pluginManager.runBeforeBuild();
2115
+ let rspackConfig = createRspackConfig(config, cwd, { isDev: false });
2116
+ rspackConfig = await pluginManager.applyRspackConfigHooks(rspackConfig);
2117
+ spinner.text = "\u6B63\u5728\u7F16\u8BD1...";
2118
+ const compiler = rspack4(rspackConfig);
2119
+ const stats = await new Promise((resolve3, reject) => {
2120
+ compiler.run((err, stats2) => {
2121
+ if (err) {
2122
+ reject(err);
2123
+ return;
2124
+ }
2125
+ if (!stats2) {
2126
+ reject(new Error("\u6784\u5EFA\u5931\u8D25: \u65E0\u6CD5\u83B7\u53D6\u7EDF\u8BA1\u4FE1\u606F"));
2127
+ return;
2128
+ }
2129
+ resolve3(stats2);
2130
+ });
2131
+ });
2132
+ await new Promise((resolve3, reject) => {
2133
+ compiler.close((err) => {
2134
+ if (err) reject(err);
2135
+ else resolve3();
2136
+ });
2137
+ });
2138
+ const hasErrors = stats.hasErrors();
2139
+ const statsInfo = stats.toJson({ errors: true });
2140
+ await pluginManager.runAfterBuild({
2141
+ success: !hasErrors,
2142
+ errors: statsInfo.errors?.map((e) => e.message)
2143
+ });
2144
+ if (hasErrors) {
2145
+ spinner.fail(chalk2.red("\u6784\u5EFA\u5931\u8D25"));
2146
+ printBuildResult(stats);
2147
+ process.exit(1);
2148
+ }
2149
+ spinner.succeed(chalk2.green("\u6784\u5EFA\u5B8C\u6210!"));
2150
+ printBuildResult(stats);
2151
+ } catch (error) {
2152
+ spinner.fail(chalk2.red("\u6784\u5EFA\u5931\u8D25"));
2153
+ console.error(error);
2154
+ process.exit(1);
2155
+ }
2156
+ }
2157
+
2158
+ // src/cli/index.ts
2159
+ var program = new Command();
2160
+ program.name("ywkf").description("4399\u8FD0\u7EF4\u5F00\u53D1\u524D\u7AEF\u6846\u67B6 CLI").version("1.0.0");
2161
+ program.command("dev").description("\u542F\u52A8\u5F00\u53D1\u670D\u52A1\u5668").option("-p, --port <port>", "\u6307\u5B9A\u7AEF\u53E3\u53F7").option("-h, --host <host>", "\u6307\u5B9A\u4E3B\u673A").action(async (options) => {
2162
+ if (options.port) {
2163
+ process.env.APP_PORT = options.port;
2164
+ }
2165
+ if (options.host) {
2166
+ process.env.APP_HOST = options.host;
2167
+ }
2168
+ await dev({ cwd: process.cwd() });
2169
+ });
2170
+ program.command("build").description("\u6784\u5EFA\u751F\u4EA7\u73AF\u5883").option("--analyze", "\u542F\u7528\u6784\u5EFA\u5206\u6790").action(async (options) => {
2171
+ if (options.analyze) {
2172
+ process.env.RSDOCTOR = "true";
2173
+ }
2174
+ await build({ cwd: process.cwd() });
2175
+ });
2176
+ program.command("start").description("\u542F\u52A8\u751F\u4EA7\u670D\u52A1\u5668\uFF08\u9700\u8981\u5148\u6267\u884C build\uFF09").action(() => {
2177
+ console.log(chalk3.yellow("\u26A0\uFE0F start \u547D\u4EE4\u6682\u672A\u5B9E\u73B0\uFF0C\u8BF7\u4F7F\u7528 nginx \u6216\u5176\u4ED6\u9759\u6001\u670D\u52A1\u5668"));
2178
+ });
2179
+ program.parse();
2180
+ //# sourceMappingURL=index.js.map