@4399ywkf/core 5.0.26 → 5.0.28

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.
@@ -7,11 +7,2068 @@ import ReactRefreshPlugin from "@rspack/plugin-react-refresh";
7
7
  import { merge } from "webpack-merge";
8
8
 
9
9
  // src/config/loader.ts
10
- import { existsSync } from "fs";
11
- import { extname, resolve } from "path";
10
+ import { existsSync as existsSync7 } from "fs";
11
+ import { extname, resolve as resolve2 } from "path";
12
12
  import { pathToFileURL } from "url";
13
13
  import deepmerge from "deepmerge";
14
14
 
15
+ // src/plugin/define.ts
16
+ function createPlugin(factory) {
17
+ return factory;
18
+ }
19
+
20
+ // src/plugin/builtin/analytics.ts
21
+ var analyticsPlugin = createPlugin((options = {}) => ({
22
+ name: "@4399ywkf/plugin-analytics",
23
+ version: "1.0.0",
24
+ description: "\u6784\u5EFA\u5206\u6790\u63D2\u4EF6",
25
+ setup(context) {
26
+ const {
27
+ buildAnalysis = true,
28
+ timing = true,
29
+ bundleSize = true,
30
+ sizeWarningThreshold = 500
31
+ } = options;
32
+ const { logger, isProd } = context;
33
+ let buildStartTime;
34
+ const hooks = {
35
+ beforeBuild() {
36
+ if (timing) {
37
+ buildStartTime = Date.now();
38
+ logger.info("\u5F00\u59CB\u6784\u5EFA...");
39
+ }
40
+ },
41
+ afterBuild(_context, stats) {
42
+ if (timing) {
43
+ const duration = Date.now() - buildStartTime;
44
+ logger.info(`\u6784\u5EFA\u5B8C\u6210\uFF0C\u8017\u65F6: ${(duration / 1e3).toFixed(2)}s`);
45
+ }
46
+ if (!stats.success && stats.errors) {
47
+ logger.error(`\u6784\u5EFA\u5931\u8D25\uFF0C\u9519\u8BEF\u6570: ${stats.errors.length}`);
48
+ }
49
+ },
50
+ modifyRspackConfig(config) {
51
+ if (buildAnalysis && isProd) {
52
+ logger.info("\u6784\u5EFA\u5206\u6790\u5DF2\u542F\u7528");
53
+ }
54
+ return config;
55
+ }
56
+ };
57
+ return hooks;
58
+ }
59
+ }));
60
+
61
+ // src/plugin/builtin/biome.ts
62
+ import { existsSync, writeFileSync } from "fs";
63
+ import { join } from "path";
64
+ var biomePlugin = createPlugin((options = {}) => ({
65
+ name: "@4399ywkf/plugin-biome",
66
+ version: "1.0.0",
67
+ description: "Biome \u4EE3\u7801\u89C4\u8303\u96C6\u6210",
68
+ setup(context) {
69
+ const {
70
+ scaffold = true,
71
+ ignore = [],
72
+ rules: customRules,
73
+ formatter: customFormatter,
74
+ javascript: customJavascript
75
+ } = options;
76
+ const { cwd, logger } = context;
77
+ if (scaffold) {
78
+ const biomeConfigPath = join(cwd, "biome.json");
79
+ if (!existsSync(biomeConfigPath)) {
80
+ const config = buildBiomeConfig({ ignore, customRules, customFormatter, customJavascript });
81
+ writeFileSync(biomeConfigPath, `${JSON.stringify(config, null, 2)}
82
+ `, "utf-8");
83
+ logger.info("\u5DF2\u751F\u6210 biome.json\uFF08\u7EE7\u627F @4399ywkf/core/biome\uFF09");
84
+ }
85
+ }
86
+ const hooks = {
87
+ modifyRspackConfig(rspackConfig) {
88
+ logger.info("Biome \u4EE3\u7801\u89C4\u8303\u5DF2\u542F\u7528");
89
+ return rspackConfig;
90
+ }
91
+ };
92
+ return hooks;
93
+ }
94
+ }));
95
+ function buildBiomeConfig(params) {
96
+ const { ignore, customRules, customFormatter, customJavascript } = params;
97
+ const config = {
98
+ $schema: "https://biomejs.dev/schemas/2.3.2/schema.json",
99
+ extends: ["@4399ywkf/core/biome"]
100
+ };
101
+ if (customFormatter) {
102
+ config.formatter = customFormatter;
103
+ }
104
+ if (customJavascript) {
105
+ config.javascript = customJavascript;
106
+ }
107
+ if (customRules && Object.keys(customRules).length > 0) {
108
+ config.linter = { rules: customRules };
109
+ }
110
+ if (ignore.length > 0) {
111
+ config.files = {
112
+ includes: ["**", ...ignore.map((p) => `!${p}`)]
113
+ };
114
+ }
115
+ return config;
116
+ }
117
+
118
+ // src/plugin/builtin/browser-check.ts
119
+ import { existsSync as existsSync2, readFileSync } from "fs";
120
+ import { join as join2 } from "path";
121
+ var DEFAULT_FEATURES = ["Object.hasOwn"];
122
+ var DEFAULT_MIN_VERSIONS = {
123
+ chrome: 93,
124
+ edge: 93,
125
+ firefox: 92,
126
+ safari: 15.4
127
+ };
128
+ var browserCheckPlugin = createPlugin((options = {}) => ({
129
+ name: "@4399ywkf/plugin-browser-check",
130
+ version: "1.0.0",
131
+ description: "\u6D4F\u89C8\u5668\u6700\u4F4E\u7248\u672C\u68C0\u67E5\u63D2\u4EF6",
132
+ setup(context) {
133
+ const {
134
+ features = DEFAULT_FEATURES,
135
+ minVersions: explicitVersions,
136
+ fromBrowserslist = false,
137
+ title = "\u6D4F\u89C8\u5668\u7248\u672C\u8FC7\u4F4E",
138
+ message = "\u60A8\u5F53\u524D\u7684\u6D4F\u89C8\u5668\u7248\u672C\u4E0D\u652F\u6301\u672C\u5E94\u7528\u6240\u9700\u7684\u529F\u80FD\u3002\u8BF7\u5347\u7EA7\u5230\u4EE5\u4E0B\u63A8\u8350\u6D4F\u89C8\u5668\u7684\u6700\u65B0\u7248\u672C\uFF1A",
139
+ showDownloadLinks = true,
140
+ renderHtml,
141
+ renderScript
142
+ } = options;
143
+ const { cwd, config, logger } = context;
144
+ const rootId = config.html.mountRoot || config.appName || "root";
145
+ let baseVersions = { ...DEFAULT_MIN_VERSIONS };
146
+ if (fromBrowserslist !== false) {
147
+ const parsed = parseBrowserslistConfig(cwd, fromBrowserslist, logger);
148
+ if (parsed) {
149
+ baseVersions = { ...baseVersions, ...parsed };
150
+ }
151
+ }
152
+ const minVersions = explicitVersions ? { ...baseVersions, ...explicitVersions } : baseVersions;
153
+ logger.info(
154
+ `\u6D4F\u89C8\u5668\u517C\u5BB9\u6027\u68C0\u67E5\u5DF2\u542F\u7528 (\u7279\u6027\u68C0\u6D4B: ${features.join(", ")})`
155
+ );
156
+ logger.info(
157
+ `\u6700\u4F4E\u7248\u672C\u8981\u6C42: Chrome ${minVersions.chrome ?? "\u2014"}, Edge ${minVersions.edge ?? "\u2014"}, Firefox ${minVersions.firefox ?? "\u2014"}, Safari ${minVersions.safari ?? "\u2014"}${fromBrowserslist !== false ? " (\u4ECE browserslist \u8BFB\u53D6)" : ""}`
158
+ );
159
+ const hooks = {
160
+ modifyEntryCode(code) {
161
+ const featureCheckLines = buildFeatureChecks(features);
162
+ const versionCheckLines = buildVersionChecks(minVersions);
163
+ const warningHtml = renderHtml ? renderHtml({ title, message, showDownloadLinks, minVersions }) : buildWarningHtml({ title, message, showDownloadLinks, minVersions });
164
+ const checkBlock = [
165
+ ``,
166
+ `// ======= \u6D4F\u89C8\u5668\u517C\u5BB9\u6027\u68C0\u67E5 (@4399ywkf/plugin-browser-check) =======`,
167
+ `const __ywkfBrowserOk = (() => {`,
168
+ ` try {`,
169
+ ...featureCheckLines.map((l) => ` ${l}`),
170
+ ...versionCheckLines.map((l) => ` ${l}`),
171
+ ` return true;`,
172
+ ` } catch {`,
173
+ ` return false;`,
174
+ ` }`,
175
+ `})();`,
176
+ ``,
177
+ `if (!__ywkfBrowserOk) {`,
178
+ ` const __root = document.getElementById(${JSON.stringify(rootId)}) || document.body;`,
179
+ ` __root.innerHTML = ${JSON.stringify(warningHtml)};`,
180
+ renderScript ? ` ${renderScript}` : ``,
181
+ `}`,
182
+ `// ======= End \u6D4F\u89C8\u5668\u517C\u5BB9\u6027\u68C0\u67E5 =======`,
183
+ ``
184
+ ].join("\n");
185
+ const lines = code.split("\n");
186
+ let lastImportIndex = -1;
187
+ for (let i = 0; i < lines.length; i++) {
188
+ if (lines[i].trimStart().startsWith("import ")) {
189
+ lastImportIndex = i;
190
+ }
191
+ }
192
+ const insertIndex = lastImportIndex >= 0 ? lastImportIndex + 1 : 0;
193
+ lines.splice(insertIndex, 0, checkBlock);
194
+ let result = lines.join("\n");
195
+ result = result.replace(
196
+ /(\s*)(await\s+)?runApp\(\);?/g,
197
+ (_, indent, awaitKw) => `${indent}if (__ywkfBrowserOk) { ${awaitKw || ""}runApp(); }`
198
+ );
199
+ return result;
200
+ }
201
+ };
202
+ return hooks;
203
+ }
204
+ }));
205
+ function buildFeatureChecks(features) {
206
+ if (features.length === 0) return [];
207
+ const lines = ["// \u7279\u6027\u68C0\u6D4B"];
208
+ for (const feature of features) {
209
+ const parts = feature.split(".");
210
+ if (parts.length === 1) {
211
+ lines.push(`if (typeof ${feature} === "undefined") return false;`);
212
+ } else {
213
+ lines.push(`if (typeof ${feature} !== "function") return false;`);
214
+ }
215
+ }
216
+ return lines;
217
+ }
218
+ function buildVersionChecks(minVersions) {
219
+ const entries = Object.entries(minVersions).filter(
220
+ ([, v]) => v != null
221
+ );
222
+ if (entries.length === 0) return [];
223
+ const lines = ["// UA \u7248\u672C\u68C0\u6D4B", "const ua = navigator.userAgent;"];
224
+ for (const [browser, minVer] of entries) {
225
+ switch (browser) {
226
+ case "chrome":
227
+ lines.push(
228
+ `const chromeMatch = /Chrome\\/(\\d+)/.exec(ua);`,
229
+ `if (chromeMatch && !/Edg\\//.test(ua) && Number(chromeMatch[1]) < ${minVer}) return false;`
230
+ );
231
+ break;
232
+ case "edge":
233
+ lines.push(
234
+ `const edgeMatch = /Edg\\/(\\d+)/.exec(ua);`,
235
+ `if (edgeMatch && Number(edgeMatch[1]) < ${minVer}) return false;`
236
+ );
237
+ break;
238
+ case "firefox":
239
+ lines.push(
240
+ `const firefoxMatch = /Firefox\\/(\\d+)/.exec(ua);`,
241
+ `if (firefoxMatch && Number(firefoxMatch[1]) < ${minVer}) return false;`
242
+ );
243
+ break;
244
+ case "safari":
245
+ lines.push(
246
+ `const safariMatch = /Version\\/(\\d+\\.?\\d*)/.exec(ua);`,
247
+ `if (safariMatch && /Safari/.test(ua) && !/Chrome/.test(ua) && Number(safariMatch[1]) < ${minVer}) return false;`
248
+ );
249
+ break;
250
+ }
251
+ }
252
+ return lines;
253
+ }
254
+ function buildWarningHtml(opts) {
255
+ const { title, message, showDownloadLinks, minVersions } = opts;
256
+ const versionHints = [
257
+ minVersions.chrome != null && `Chrome ${minVersions.chrome}+`,
258
+ minVersions.edge != null && `Edge ${minVersions.edge}+`,
259
+ minVersions.firefox != null && `Firefox ${minVersions.firefox}+`,
260
+ minVersions.safari != null && `Safari ${minVersions.safari}+`
261
+ ].filter(Boolean).join("\u3001");
262
+ const downloadLinksHtml = showDownloadLinks ? `<div style="display:flex;gap:12px;justify-content:center;flex-wrap:wrap;margin-top:32px">${browserLink("https://www.google.cn/chrome/", "Chrome \u6D4F\u89C8\u5668", "#4285f4", "#356ac3", "66,133,244")}${browserLink("https://www.microsoft.com/edge", "Edge \u6D4F\u89C8\u5668", "#0078d4", "#005a9e", "0,120,212")}${browserLink("https://www.firefox.com.cn/", "Firefox \u6D4F\u89C8\u5668", "#ff7139", "#e05a2b", "255,113,57")}</div>` : "";
263
+ const versionHintHtml = versionHints ? `<p style="margin:28px 0 0;font-size:13px;color:#aaa;letter-spacing:0.3px">\u63A8\u8350\u7248\u672C\uFF1A${versionHints}</p>` : "";
264
+ return [
265
+ `<div style="position:fixed;inset:0;z-index:99999;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#f5f7fa 0%,#c3cfe2 100%);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'PingFang SC','Microsoft YaHei',sans-serif;padding:24px;box-sizing:border-box">`,
266
+ `<div style="max-width:520px;width:100%;padding:48px 40px;background:#fff;border-radius:20px;box-shadow:0 8px 40px rgba(0,0,0,0.12);text-align:center">`,
267
+ // 警告图标
268
+ `<div style="width:80px;height:80px;margin:0 auto 28px;background:linear-gradient(135deg,#ff9a56,#ff6b6b);border-radius:50%;display:flex;align-items:center;justify-content:center;box-shadow:0 4px 20px rgba(255,107,107,0.3)">`,
269
+ `<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>`,
270
+ `</div>`,
271
+ // 标题 & 描述
272
+ `<h1 style="margin:0 0 16px;font-size:26px;font-weight:700;color:#1a1a2e;letter-spacing:-0.5px">${title}</h1>`,
273
+ `<p style="margin:0;font-size:15px;color:#666;line-height:1.8">${message}</p>`,
274
+ // 下载链接
275
+ downloadLinksHtml,
276
+ // 版本提示
277
+ versionHintHtml,
278
+ `</div></div>`
279
+ ].join("");
280
+ }
281
+ function browserLink(href, label, colorStart, colorEnd, rgbBase) {
282
+ const bg = `background:linear-gradient(135deg,${colorStart},${colorEnd})`;
283
+ const shadow = `box-shadow:0 4px 12px rgba(${rgbBase},0.3)`;
284
+ const hoverShadow = `0 6px 20px rgba(${rgbBase},0.4)`;
285
+ const base = `display:inline-flex;align-items:center;padding:14px 28px;${bg};color:#fff;border-radius:12px;text-decoration:none;font-size:15px;font-weight:500;${shadow};transition:transform .2s,box-shadow .2s`;
286
+ return `<a href="${href}" target="_blank" rel="noopener noreferrer" style="${base}" onmouseover="this.style.transform='translateY(-2px)';this.style.boxShadow='${hoverShadow}'" onmouseout="this.style.transform='';this.style.boxShadow='0 4px 12px rgba(${rgbBase},0.3)'">${label}</a>`;
287
+ }
288
+ var BROWSERSLIST_NAME_MAP = {
289
+ chrome: "chrome",
290
+ chromium: "chrome",
291
+ edge: "edge",
292
+ firefox: "firefox",
293
+ ff: "firefox",
294
+ safari: "safari",
295
+ ios_saf: "safari"
296
+ };
297
+ function parseBrowserslistConfig(cwd, source, logger) {
298
+ let queries = [];
299
+ if (typeof source === "string") {
300
+ const filePath = join2(cwd, source);
301
+ if (!existsSync2(filePath)) {
302
+ logger.warn(`browserslist \u6587\u4EF6\u672A\u627E\u5230: ${filePath}`);
303
+ return null;
304
+ }
305
+ queries = readBrowserslistFile(filePath);
306
+ logger.info(`\u4ECE ${source} \u8BFB\u53D6\u5230 ${queries.length} \u6761 browserslist \u67E5\u8BE2`);
307
+ } else {
308
+ const rcPath = join2(cwd, ".browserslistrc");
309
+ if (existsSync2(rcPath)) {
310
+ queries = readBrowserslistFile(rcPath);
311
+ logger.info(`\u4ECE .browserslistrc \u8BFB\u53D6\u5230 ${queries.length} \u6761 browserslist \u67E5\u8BE2`);
312
+ } else {
313
+ const pkgPath = join2(cwd, "package.json");
314
+ if (existsSync2(pkgPath)) {
315
+ try {
316
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
317
+ if (Array.isArray(pkg.browserslist)) {
318
+ queries = pkg.browserslist;
319
+ logger.info(`\u4ECE package.json#browserslist \u8BFB\u53D6\u5230 ${queries.length} \u6761\u67E5\u8BE2`);
320
+ } else if (typeof pkg.browserslist === "string") {
321
+ queries = [pkg.browserslist];
322
+ logger.info("\u4ECE package.json#browserslist \u8BFB\u53D6\u5230 1 \u6761\u67E5\u8BE2");
323
+ }
324
+ } catch {
325
+ }
326
+ }
327
+ }
328
+ if (queries.length === 0) {
329
+ logger.warn("\u672A\u627E\u5230 .browserslistrc \u6216 package.json#browserslist\uFF0C\u5C06\u4F7F\u7528\u5185\u7F6E\u9ED8\u8BA4\u503C");
330
+ return null;
331
+ }
332
+ }
333
+ return extractMinVersions(queries);
334
+ }
335
+ function readBrowserslistFile(filePath) {
336
+ const content = readFileSync(filePath, "utf-8");
337
+ return content.split("\n").map((line) => line.replace(/#.*$/, "").trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
338
+ }
339
+ function extractMinVersions(queries) {
340
+ const result = {};
341
+ const expanded = queries.flatMap((q) => q.split(",").map((s) => s.trim()));
342
+ const versionPattern = /^(\w+)\s*(?:(>=?)\s*)?(\d+(?:\.\d+)?)\s*$/i;
343
+ for (const query of expanded) {
344
+ if (/^(not|dead|last|defaults|>?\s*\d+%|unreleased|current)/i.test(query)) {
345
+ continue;
346
+ }
347
+ const match = versionPattern.exec(query);
348
+ if (!match) continue;
349
+ const [, browserName, operator, versionStr] = match;
350
+ const key = BROWSERSLIST_NAME_MAP[browserName.toLowerCase()];
351
+ if (!key) continue;
352
+ let version = Number.parseFloat(versionStr);
353
+ if (operator === ">") {
354
+ version = Math.ceil(version) + 1;
355
+ }
356
+ if (result[key] == null || version < result[key]) {
357
+ result[key] = version;
358
+ }
359
+ }
360
+ return result;
361
+ }
362
+
363
+ // src/plugin/builtin/garfish.ts
364
+ var garfishPlugin = createPlugin((options = {}) => ({
365
+ name: "@4399ywkf/plugin-garfish",
366
+ version: "1.0.0",
367
+ description: "Garfish \u5FAE\u524D\u7AEF\u63D2\u4EF6",
368
+ setup(context) {
369
+ const { appName, master = false, apps = [], sandbox = {} } = options;
370
+ const { config, logger, isDev } = context;
371
+ const finalAppName = appName || config.appName;
372
+ const hooks = {
373
+ // ===== 构建阶段钩子 =====
374
+ modifyRspackConfig(rspackConfig) {
375
+ if (!master) {
376
+ rspackConfig.output = {
377
+ ...rspackConfig.output,
378
+ library: finalAppName,
379
+ libraryTarget: "umd",
380
+ globalObject: "window",
381
+ chunkLoadingGlobal: `garfish_${finalAppName}`
382
+ };
383
+ rspackConfig.devServer = {
384
+ ...rspackConfig.devServer,
385
+ headers: {
386
+ ...rspackConfig.devServer?.headers || {},
387
+ "Access-Control-Allow-Origin": "*",
388
+ "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
389
+ "Access-Control-Allow-Headers": "*"
390
+ }
391
+ };
392
+ logger.info(`\u914D\u7F6E UMD \u8F93\u51FA: ${finalAppName}`);
393
+ }
394
+ return rspackConfig;
395
+ },
396
+ beforeDevServer() {
397
+ if (master) {
398
+ logger.info("Garfish \u4E3B\u5E94\u7528\u6A21\u5F0F");
399
+ logger.info(`\u5B50\u5E94\u7528\u5217\u8868: ${apps.map((a) => a.name).join(", ")}`);
400
+ } else {
401
+ logger.info(`Garfish \u5B50\u5E94\u7528\u6A21\u5F0F: ${finalAppName}`);
402
+ }
403
+ },
404
+ // ===== 代码生成阶段钩子 =====
405
+ injectEntry(_ctx) {
406
+ if (master && apps.length > 0) {
407
+ return {
408
+ imports: [`import { initGarfishMaster } from "./bootstrap";`],
409
+ topLevel: [`// Garfish \u4E3B\u5E94\u7528\uFF1A\u521D\u59CB\u5316\u5FAE\u524D\u7AEF\u5E76\u6CE8\u518C\u5B50\u5E94\u7528`, `await initGarfishMaster();`]
410
+ };
411
+ }
412
+ if (!master) {
413
+ return {
414
+ imports: [`import { garfishProvider, isMicroAppEnv } from "./bootstrap";`],
415
+ exports: [
416
+ `// Garfish \u5B50\u5E94\u7528\u751F\u547D\u5468\u671F\u5BFC\u51FA\uFF08provider \u51FD\u6570\u683C\u5F0F\uFF09`,
417
+ `export const provider = garfishProvider;`
418
+ ],
419
+ topLevel: [
420
+ `// Garfish \u5FAE\u524D\u7AEF\uFF1A\u72EC\u7ACB\u8FD0\u884C\u5224\u65AD`,
421
+ `const shouldRunIndependently = !isMicroAppEnv();`
422
+ ]
423
+ };
424
+ }
425
+ return {};
426
+ },
427
+ injectBootstrap(_ctx) {
428
+ const imports = [];
429
+ const exports = [];
430
+ const topLevel = [];
431
+ if (master) {
432
+ if (apps.length > 0) {
433
+ imports.push(`import Garfish from "garfish";`);
434
+ topLevel.push(generateMasterInitCode(apps, config.router.basename));
435
+ }
436
+ } else {
437
+ imports.push(
438
+ `import { createGarfishProvider, isMicroAppEnv } from "@4399ywkf/core/runtime";`
439
+ );
440
+ exports.push(
441
+ `/**`,
442
+ ` * Garfish \u5B50\u5E94\u7528 provider\uFF08render/destroy \u683C\u5F0F\uFF09`,
443
+ ` */`,
444
+ `export const garfishProvider = createGarfishProvider(createAppConfig);`,
445
+ ``,
446
+ `/**`,
447
+ ` * \u5224\u65AD\u662F\u5426\u5728\u5FAE\u524D\u7AEF\u73AF\u5883`,
448
+ ` */`,
449
+ `export { isMicroAppEnv };`
450
+ );
451
+ }
452
+ return { imports, exports, topLevel };
453
+ },
454
+ modifyBootstrapCode(code, _ctx) {
455
+ if (!master) {
456
+ return code.replace(/antd:\s*\{\s*enabled:\s*true/, "antd: { enabled: false");
457
+ }
458
+ return code;
459
+ },
460
+ // ===== 运行时阶段钩子 =====
461
+ modifyAppConfig(appConfig) {
462
+ return {
463
+ ...appConfig,
464
+ // 微前端模式下禁用 antd 样式注入(由主应用管理)
465
+ antd: {
466
+ ...appConfig.antd,
467
+ enabled: master
468
+ }
469
+ };
470
+ }
471
+ };
472
+ return hooks;
473
+ }
474
+ }));
475
+ function generateMasterInitCode(apps, basename) {
476
+ const appConfigs = apps.map(
477
+ (app) => `{
478
+ name: "${app.name}",
479
+ entry: "${app.entry}",
480
+ activeWhen: "${app.activeRule}",
481
+ ${app.basename ? `basename: "${app.basename}",` : ""}
482
+ }`
483
+ ).join(",\n ");
484
+ return `
485
+ /**
486
+ * Garfish \u4E3B\u5E94\u7528\u521D\u59CB\u5316
487
+ * \u5728 runApp \u4E4B\u524D\u8C03\u7528
488
+ */
489
+ export async function initGarfishMaster(): Promise<void> {
490
+ await Garfish.run({
491
+ basename: "${basename || "/"}",
492
+ domGetter: "#subapp-container",
493
+ apps: [
494
+ ${appConfigs}
495
+ ],
496
+ });
497
+ }
498
+ `;
499
+ }
500
+
501
+ // src/plugin/builtin/i18n.ts
502
+ import { existsSync as existsSync3, mkdirSync, writeFileSync as writeFileSync2 } from "fs";
503
+ import { join as join3 } from "path";
504
+ var i18nPlugin = createPlugin((options = {}) => ({
505
+ name: "@4399ywkf/plugin-i18n",
506
+ version: "1.0.0",
507
+ description: "\u56FD\u9645\u5316 (i18next) \u63D2\u4EF6 \u2014 TS-first \u5DE5\u4F5C\u6D41",
508
+ setup(context) {
509
+ const {
510
+ defaultLocale = "zh-CN",
511
+ locales = ["zh-CN", "en-US"],
512
+ localesDir = "locales",
513
+ sourceDir = "src/locales/default",
514
+ defaultNS = ["common"],
515
+ autoScaffold = true
516
+ } = options;
517
+ const { cwd, logger, isDev } = context;
518
+ if (autoScaffold) {
519
+ scaffoldSourceFiles(cwd, sourceDir, defaultNS, logger);
520
+ scaffoldResourcesFile(cwd, sourceDir, defaultNS, locales, defaultLocale, logger);
521
+ scaffoldConvertScript(cwd, sourceDir, localesDir, defaultLocale, logger);
522
+ scaffoldI18nRc(cwd, localesDir, defaultLocale, locales, logger);
523
+ scaffoldLocaleJsonDirs(cwd, localesDir, locales, defaultLocale, defaultNS, logger);
524
+ }
525
+ const hooks = {
526
+ modifyRspackConfig(rspackConfig) {
527
+ const aliases = rspackConfig.resolve?.alias || {};
528
+ if (!aliases["@locales"]) {
529
+ aliases["@locales"] = join3(cwd, localesDir);
530
+ }
531
+ rspackConfig.resolve = { ...rspackConfig.resolve, alias: aliases };
532
+ return rspackConfig;
533
+ },
534
+ generateFiles(_ctx) {
535
+ return [
536
+ {
537
+ path: "i18n.ts",
538
+ content: generateI18nCode({
539
+ defaultLocale,
540
+ locales,
541
+ localesDir,
542
+ sourceDir,
543
+ defaultNS,
544
+ isDev
545
+ })
546
+ }
547
+ ];
548
+ },
549
+ injectBootstrap(_ctx) {
550
+ return {
551
+ imports: [`import { initI18n } from "./i18n";`],
552
+ topLevel: [`await initI18n();`]
553
+ };
554
+ }
555
+ };
556
+ return hooks;
557
+ }
558
+ }));
559
+ function generateI18nCode(opts) {
560
+ const { defaultLocale, locales, defaultNS, sourceDir } = opts;
561
+ const defaultDirAlias = sourceDir.replace(/^src\//, "");
562
+ const parentDirAlias = defaultDirAlias.replace(/\/[^/]+$/, "");
563
+ return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/plugin-i18n \u81EA\u52A8\u751F\u6210
564
+ import i18n from "i18next";
565
+ import LanguageDetector from "i18next-browser-languagedetector";
566
+ import resourcesToBackend from "i18next-resources-to-backend";
567
+ import { initReactI18next } from "react-i18next";
568
+ import { normalizeLocale, SUPPORTED_LOCALES, DEFAULT_LOCALE } from "@/${parentDirAlias}/resources";
569
+
570
+ export { DEFAULT_LOCALE, SUPPORTED_LOCALES, normalizeLocale };
571
+ export type { SupportedLocale, NS } from "@/${parentDirAlias}/resources";
572
+
573
+ const isDev = process.env.NODE_ENV === "development";
574
+
575
+ const instance = i18n
576
+ .use(initReactI18next)
577
+ .use(LanguageDetector)
578
+ .use(
579
+ resourcesToBackend(async (lng: string, ns: string) => {
580
+ const normalizedLng = normalizeLocale(lng);
581
+
582
+ // \u5F00\u53D1\u6A21\u5F0F\u4E0B\uFF0C\u9ED8\u8BA4\u8BED\u8A00\u76F4\u63A5 import TS\uFF08\u5373\u65F6\u751F\u6548\uFF0C\u65E0\u9700\u5148\u751F\u6210 JSON\uFF09
583
+ if (isDev && normalizedLng === "${defaultLocale}") {
584
+ return import("@/${defaultDirAlias}/" + ns);
585
+ }
586
+
587
+ // \u5176\u4ED6\u8BED\u8A00 / \u751F\u4EA7\u6A21\u5F0F\uFF1A\u61D2\u52A0\u8F7D JSON
588
+ return import(
589
+ /* webpackInclude: /\\.json$/ */
590
+ /* webpackChunkName: "locales-[request]" */
591
+ \`@locales/\${normalizedLng}/\${ns}.json\`
592
+ );
593
+ })
594
+ );
595
+
596
+ let initialized = false;
597
+
598
+ export async function initI18n(lang?: string): Promise<typeof i18n> {
599
+ if (initialized) return instance;
600
+
601
+ await instance.init({
602
+ defaultNS: ${JSON.stringify(defaultNS)},
603
+ fallbackLng: DEFAULT_LOCALE,
604
+ interpolation: { escapeValue: false },
605
+ lng: lang,
606
+ });
607
+
608
+ initialized = true;
609
+ return instance;
610
+ }
611
+
612
+ export { instance as i18n };
613
+ export default instance;
614
+ `;
615
+ }
616
+ function scaffoldSourceFiles(cwd, sourceDir, namespaces, logger) {
617
+ const dir = join3(cwd, sourceDir);
618
+ if (!existsSync3(dir)) {
619
+ mkdirSync(dir, { recursive: true });
620
+ }
621
+ for (const ns of namespaces) {
622
+ const filePath = join3(dir, `${ns}.ts`);
623
+ if (!existsSync3(filePath)) {
624
+ writeFileSync2(filePath, buildDefaultNsFile(ns), "utf-8");
625
+ logger.info(`\u5DF2\u751F\u6210\u7FFB\u8BD1\u6E90\u6587\u4EF6: ${sourceDir}/${ns}.ts`);
626
+ }
627
+ }
628
+ const indexPath = join3(dir, "index.ts");
629
+ if (!existsSync3(indexPath)) {
630
+ const imports = namespaces.map((ns) => `import ${ns} from "./${ns}";`).join("\n");
631
+ const entries = namespaces.join(",\n ");
632
+ const content = `${imports}
633
+
634
+ const resources = {
635
+ ${entries},
636
+ } as const;
637
+
638
+ export default resources;
639
+ `;
640
+ writeFileSync2(indexPath, content, "utf-8");
641
+ logger.info(`\u5DF2\u751F\u6210\u7FFB\u8BD1\u7D22\u5F15: ${sourceDir}/index.ts`);
642
+ }
643
+ }
644
+ function buildDefaultNsFile(ns) {
645
+ if (ns === "common") {
646
+ return `export default {
647
+ welcome: "\u6B22\u8FCE\u4F7F\u7528",
648
+ loading: "\u52A0\u8F7D\u4E2D...",
649
+ confirm: "\u786E\u5B9A",
650
+ cancel: "\u53D6\u6D88",
651
+ save: "\u4FDD\u5B58",
652
+ delete: "\u5220\u9664",
653
+ edit: "\u7F16\u8F91",
654
+ back: "\u8FD4\u56DE",
655
+ } as const;
656
+ `;
657
+ }
658
+ return `export default {
659
+ title: "${ns}",
660
+ } as const;
661
+ `;
662
+ }
663
+ function scaffoldResourcesFile(cwd, sourceDir, _namespaces, locales, defaultLocale, logger) {
664
+ const parentDir = join3(cwd, sourceDir, "..");
665
+ const filePath = join3(parentDir, "resources.ts");
666
+ if (existsSync3(filePath)) return;
667
+ const content = `import resources from "./default";
668
+
669
+ export const DEFAULT_LOCALE = "${defaultLocale}";
670
+
671
+ export const SUPPORTED_LOCALES = ${JSON.stringify(locales)} as const;
672
+
673
+ export type SupportedLocale = (typeof SUPPORTED_LOCALES)[number];
674
+
675
+ export type DefaultResources = typeof resources;
676
+
677
+ export type NS = keyof DefaultResources;
678
+
679
+ export const normalizeLocale = (locale?: string): SupportedLocale => {
680
+ if (!locale) return DEFAULT_LOCALE as SupportedLocale;
681
+
682
+ for (const l of SUPPORTED_LOCALES) {
683
+ if (l.startsWith(locale) || locale.startsWith(l.split("-")[0])) {
684
+ return l;
685
+ }
686
+ }
687
+
688
+ return DEFAULT_LOCALE as SupportedLocale;
689
+ };
690
+
691
+ export const localeOptions = ${JSON.stringify(
692
+ locales.map((l) => ({ value: l, label: getLocaleLabel(l) })),
693
+ null,
694
+ 2
695
+ )} as const;
696
+ `;
697
+ writeFileSync2(filePath, content, "utf-8");
698
+ logger.info(`\u5DF2\u751F\u6210\u7C7B\u578B\u6587\u4EF6: ${sourceDir}/../resources.ts`);
699
+ }
700
+ function getLocaleLabel(locale) {
701
+ const map = {
702
+ "zh-CN": "\u7B80\u4F53\u4E2D\u6587",
703
+ "zh-TW": "\u7E41\u9AD4\u4E2D\u6587",
704
+ "en-US": "English",
705
+ "ja-JP": "\u65E5\u672C\u8A9E",
706
+ "ko-KR": "\uD55C\uAD6D\uC5B4",
707
+ "de-DE": "Deutsch",
708
+ "fr-FR": "Fran\xE7ais",
709
+ "es-ES": "Espa\xF1ol",
710
+ "pt-BR": "Portugu\xEAs",
711
+ "ru-RU": "\u0420\u0443\u0441\u0441\u043A\u0438\u0439",
712
+ "it-IT": "Italiano",
713
+ "vi-VN": "Ti\u1EBFng Vi\u1EC7t",
714
+ ar: "\u0627\u0644\u0639\u0631\u0628\u064A\u0629"
715
+ };
716
+ return map[locale] || locale;
717
+ }
718
+ function scaffoldConvertScript(cwd, sourceDir, localesDir, defaultLocale, logger) {
719
+ const scriptsDir = join3(cwd, "scripts");
720
+ const scriptPath = join3(scriptsDir, "i18n-gen.js");
721
+ if (existsSync3(scriptPath)) return;
722
+ if (!existsSync3(scriptsDir)) {
723
+ mkdirSync(scriptsDir, { recursive: true });
724
+ }
725
+ const content = `const fs = require("fs");
726
+ const path = require("path");
727
+
728
+ const sourceDir = path.join(__dirname, "../${sourceDir}");
729
+ const targetDir = path.join(__dirname, "../${localesDir}/${defaultLocale}");
730
+
731
+ const files = fs
732
+ .readdirSync(sourceDir)
733
+ .filter((file) => file.endsWith(".ts") && file !== "index.ts")
734
+ .map((file) => path.basename(file, ".ts"));
735
+
736
+ require("ts-node/register");
737
+
738
+ if (!fs.existsSync(targetDir)) {
739
+ fs.mkdirSync(targetDir, { recursive: true });
740
+ }
741
+
742
+ console.log("\u5F00\u59CB\u8F6C\u6362 TypeScript \u6587\u4EF6\u5230 JSON...\\n");
743
+
744
+ files.forEach((fileName) => {
745
+ try {
746
+ const modulePath = path.join(sourceDir, \`\${fileName}.ts\`);
747
+ delete require.cache[require.resolve(modulePath)];
748
+ const moduleContent = require(modulePath).default;
749
+ const jsonContent = JSON.stringify(moduleContent, null, 2);
750
+ const targetPath = path.join(targetDir, \`\${fileName}.json\`);
751
+ fs.writeFileSync(targetPath, jsonContent, "utf8");
752
+ console.log(\` \${fileName}.ts \u2192 \${fileName}.json\`);
753
+ } catch (error) {
754
+ console.error(\` \u8F6C\u6362 \${fileName}.ts \u5931\u8D25:\`, error.message);
755
+ }
756
+ });
757
+
758
+ console.log("\\n\u8F6C\u6362\u5B8C\u6210\uFF01");
759
+ `;
760
+ writeFileSync2(scriptPath, content, "utf-8");
761
+ logger.info("\u5DF2\u751F\u6210\u8F6C\u6362\u811A\u672C: scripts/i18n-gen.js");
762
+ }
763
+ function scaffoldI18nRc(cwd, localesDir, defaultLocale, locales, logger) {
764
+ const filePath = join3(cwd, ".i18nrc.js");
765
+ if (existsSync3(filePath)) return;
766
+ const outputLocales = locales.filter((l) => l !== defaultLocale).map((l) => `"${l}"`).join(", ");
767
+ const content = `const { defineConfig } = require("@lobehub/i18n-cli");
768
+
769
+ module.exports = defineConfig({
770
+ entry: "${localesDir}/${defaultLocale}",
771
+ entryLocale: "${defaultLocale}",
772
+ output: "${localesDir}",
773
+ outputLocales: [${outputLocales}],
774
+ });
775
+ `;
776
+ writeFileSync2(filePath, content, "utf-8");
777
+ logger.info("\u5DF2\u751F\u6210\u7FFB\u8BD1\u914D\u7F6E: .i18nrc.js\uFF08@lobehub/i18n-cli\uFF09");
778
+ }
779
+ function scaffoldLocaleJsonDirs(cwd, localesDir, locales, defaultLocale, namespaces, logger) {
780
+ const baseDir = join3(cwd, localesDir);
781
+ for (const locale of locales) {
782
+ const localeDir = join3(baseDir, locale);
783
+ if (!existsSync3(localeDir)) {
784
+ mkdirSync(localeDir, { recursive: true });
785
+ }
786
+ if (locale === defaultLocale) continue;
787
+ for (const ns of namespaces) {
788
+ const filePath = join3(localeDir, `${ns}.json`);
789
+ if (!existsSync3(filePath)) {
790
+ const placeholder = ns === "common" ? JSON.stringify(
791
+ {
792
+ welcome: "Welcome",
793
+ loading: "Loading...",
794
+ confirm: "OK",
795
+ cancel: "Cancel",
796
+ save: "Save",
797
+ delete: "Delete",
798
+ edit: "Edit",
799
+ back: "Back"
800
+ },
801
+ null,
802
+ 2
803
+ ) : JSON.stringify({ title: ns }, null, 2);
804
+ writeFileSync2(filePath, `${placeholder}
805
+ `, "utf-8");
806
+ logger.info(`\u5DF2\u751F\u6210\u7FFB\u8BD1\u6587\u4EF6: ${localesDir}/${locale}/${ns}.json`);
807
+ }
808
+ }
809
+ }
810
+ }
811
+
812
+ // src/plugin/builtin/mock.ts
813
+ import { existsSync as existsSync4, readdirSync, statSync } from "fs";
814
+ import { join as join4, resolve } from "path";
815
+ var mockPlugin = createPlugin((options = {}) => ({
816
+ name: "@4399ywkf/plugin-mock",
817
+ version: "1.0.0",
818
+ description: "Mock \u6570\u636E\u63D2\u4EF6",
819
+ setup(context) {
820
+ const { mockDir = "mock", enableInProd = false, delay = 0, prefix = "" } = options;
821
+ const { cwd, isDev, logger } = context;
822
+ if (!isDev && !enableInProd) {
823
+ logger.info("\u751F\u4EA7\u73AF\u5883\u5DF2\u7981\u7528 Mock");
824
+ return {};
825
+ }
826
+ const mockPath = resolve(cwd, mockDir);
827
+ if (!existsSync4(mockPath)) {
828
+ logger.warn(`Mock \u76EE\u5F55\u4E0D\u5B58\u5728: ${mockPath}`);
829
+ return {};
830
+ }
831
+ const hooks = {
832
+ modifyRspackConfig(rspackConfig) {
833
+ if (isDev && rspackConfig.devServer) {
834
+ const mockFiles = scanMockFiles(mockPath);
835
+ logger.info(`\u627E\u5230 ${mockFiles.length} \u4E2A Mock \u6587\u4EF6`);
836
+ rspackConfig.devServer.setupMiddlewares = (middlewares, _devServer) => {
837
+ middlewares.unshift({
838
+ name: "mock-middleware",
839
+ middleware: createMockMiddleware(mockPath, { delay, prefix, logger })
840
+ });
841
+ return middlewares;
842
+ };
843
+ }
844
+ return rspackConfig;
845
+ },
846
+ beforeDevServer() {
847
+ logger.info(`Mock \u5DF2\u542F\u7528\uFF0C\u76EE\u5F55: ${mockPath}`);
848
+ if (delay > 0) {
849
+ logger.info(`\u6A21\u62DF\u5EF6\u8FDF: ${delay}ms`);
850
+ }
851
+ }
852
+ };
853
+ return hooks;
854
+ }
855
+ }));
856
+ function scanMockFiles(dir) {
857
+ const files = [];
858
+ if (!existsSync4(dir)) {
859
+ return files;
860
+ }
861
+ const entries = readdirSync(dir);
862
+ for (const entry of entries) {
863
+ const fullPath = join4(dir, entry);
864
+ const stat = statSync(fullPath);
865
+ if (stat.isFile() && /\.(ts|js|mjs)$/.test(entry)) {
866
+ files.push(fullPath);
867
+ } else if (stat.isDirectory()) {
868
+ files.push(...scanMockFiles(fullPath));
869
+ }
870
+ }
871
+ return files;
872
+ }
873
+ function createMockMiddleware(mockPath, options) {
874
+ const { delay } = options;
875
+ scanMockFiles(mockPath);
876
+ return async (_req, _res, next) => {
877
+ if (delay > 0) {
878
+ await new Promise((resolve3) => setTimeout(resolve3, delay));
879
+ }
880
+ next();
881
+ };
882
+ }
883
+
884
+ // src/plugin/builtin/react-query.ts
885
+ var reactQueryPlugin = createPlugin((options = {}) => ({
886
+ name: "@4399ywkf/plugin-react-query",
887
+ version: "1.0.0",
888
+ description: "React Query + Axios \u8BF7\u6C42\u5C42\u96C6\u6210",
889
+ setup(context) {
890
+ const {
891
+ staleTime = 5 * 60 * 1e3,
892
+ gcTime = 10 * 60 * 1e3,
893
+ retry = 1,
894
+ devtools = true,
895
+ baseURL = "",
896
+ timeout = 15e3
897
+ } = options;
898
+ const { logger, isDev } = context;
899
+ const hooks = {
900
+ generateFiles(_ctx) {
901
+ logger.info("React Query \u5DF2\u542F\u7528");
902
+ const files = [
903
+ {
904
+ path: "query-client.ts",
905
+ content: buildQueryClientFile({ staleTime, gcTime, retry, devtools, isDev })
906
+ },
907
+ {
908
+ path: "request.ts",
909
+ content: buildRequestFile({ baseURL, timeout })
910
+ }
911
+ ];
912
+ return files;
913
+ },
914
+ injectBootstrap(_ctx) {
915
+ return {
916
+ imports: [
917
+ `import { QueryClientProvider } from "@tanstack/react-query";`,
918
+ `import { queryClient } from "./query-client";`
919
+ ],
920
+ topLevel: [`// React Query Provider\uFF08\u7531 @4399ywkf/plugin-react-query \u6CE8\u5165\uFF09`]
921
+ };
922
+ },
923
+ modifyBootstrapCode(code) {
924
+ const providerEntry = ` {
925
+ component: QueryClientProvider as React.ComponentType<{ children: React.ReactNode }>,
926
+ props: { client: queryClient },
927
+ order: 20,
928
+ }`;
929
+ if (code.includes("providers: []")) {
930
+ code = code.replace("providers: []", `providers: [
931
+ ${providerEntry},
932
+ ]`);
933
+ } else if (code.includes("providers: [")) {
934
+ code = code.replace("providers: [", `providers: [
935
+ ${providerEntry},`);
936
+ }
937
+ if (!code.includes("import React")) {
938
+ code = code.replace(
939
+ `import { bootstrap`,
940
+ `import React from "react";
941
+ import { bootstrap`
942
+ );
943
+ }
944
+ return code;
945
+ }
946
+ };
947
+ return hooks;
948
+ }
949
+ }));
950
+ function buildQueryClientFile(opts) {
951
+ const { staleTime, gcTime, retry, devtools, isDev } = opts;
952
+ return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/plugin-react-query \u81EA\u52A8\u751F\u6210
953
+ import { QueryClient } from "@tanstack/react-query";
954
+
955
+ /**
956
+ * \u5168\u5C40 QueryClient \u5B9E\u4F8B
957
+ *
958
+ * \u9ED8\u8BA4\u914D\u7F6E\u53EF\u5728 ywkf.config.ts \u7684 reactQueryPlugin \u9009\u9879\u4E2D\u4FEE\u6539\u3002
959
+ * \u8FD0\u884C\u65F6\u81EA\u5B9A\u4E49\u53EF\u901A\u8FC7 src/app.config.ts \u8986\u76D6\u3002
960
+ */
961
+ export const queryClient = new QueryClient({
962
+ defaultOptions: {
963
+ queries: {
964
+ staleTime: ${staleTime},
965
+ gcTime: ${gcTime},
966
+ retry: ${typeof retry === "boolean" ? retry : retry},
967
+ refetchOnWindowFocus: false,
968
+ },
969
+ mutations: {
970
+ retry: false,
971
+ },
972
+ },
973
+ });
974
+
975
+ export default queryClient;
976
+ `;
977
+ }
978
+ function buildRequestFile(opts) {
979
+ const { baseURL, timeout } = opts;
980
+ return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/plugin-react-query \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u4FEE\u6539
981
+ // \u5982\u9700\u5B9A\u5236\u62E6\u622A\u5668\uFF0C\u8BF7\u7F16\u8F91 src/request.ts
982
+ import axios from "axios";
983
+ import qs from "qs";
984
+ import type { AxiosInstance, AxiosRequestConfig, InternalAxiosRequestConfig, AxiosResponse, AxiosError } from "axios";
985
+ import type { RequestConfig, Result } from "@4399ywkf/core/runtime";
986
+
987
+ // ============ \u52A0\u8F7D\u7528\u6237\u914D\u7F6E ============
988
+
989
+ let userConfig: RequestConfig = {};
990
+
991
+ try {
992
+ const mod = require("@/request");
993
+ userConfig = mod.default || mod;
994
+ } catch {
995
+ // src/request.ts \u4E0D\u5B58\u5728\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E
996
+ }
997
+
998
+ // ============ \u53C2\u6570\u5E8F\u5217\u5316\uFF08GET \u8BF7\u6C42\u4E13\u7528\uFF09============
999
+
1000
+ /**
1001
+ * GET \u8BF7\u6C42\u53C2\u6570\u5E8F\u5217\u5316\u89C4\u5219\uFF1A
1002
+ * - \u6570\u7EC4\uFF1Arepeat \u6A21\u5F0F\uFF08key=1&key=2\uFF09
1003
+ * - \u5BF9\u8C61\uFF1AJSON.stringify \u540E\u4F5C\u4E3A\u5B57\u7B26\u4E32\u4F20\u9012
1004
+ * - null / undefined\uFF1AskipNulls \u8DF3\u8FC7
1005
+ * - \u57FA\u672C\u7C7B\u578B\uFF1A\u539F\u6837\u4F20\u9012
1006
+ */
1007
+ export function paramsSerializer(params: Record<string, any>): string {
1008
+ const normalized: Record<string, any> = {};
1009
+
1010
+ Object.keys(params).forEach((key) => {
1011
+ const value = params[key];
1012
+
1013
+ if (value === null || value === undefined) {
1014
+ normalized[key] = value;
1015
+ } else if (Array.isArray(value)) {
1016
+ normalized[key] = value;
1017
+ } else if (typeof value === "object") {
1018
+ normalized[key] = JSON.stringify(value);
1019
+ } else {
1020
+ normalized[key] = value;
1021
+ }
1022
+ });
1023
+
1024
+ return qs.stringify(normalized, { arrayFormat: "repeat", skipNulls: true });
1025
+ }
1026
+
1027
+ // ============ \u521B\u5EFA Axios \u5B9E\u4F8B ============
1028
+
1029
+ const instance: AxiosInstance = axios.create({
1030
+ baseURL: userConfig.baseURL ?? ("${baseURL}" || ""),
1031
+ timeout: userConfig.timeout ?? ${timeout},
1032
+ withCredentials: userConfig.withCredentials ?? false,
1033
+ headers: {
1034
+ "Content-Type": "application/json",
1035
+ ...(userConfig.headers || {}),
1036
+ },
1037
+ });
1038
+
1039
+ // ============ \u8BF7\u6C42\u62E6\u622A\u5668 ============
1040
+
1041
+ instance.interceptors.request.use(
1042
+ (config: InternalAxiosRequestConfig) => {
1043
+ // 1. Token \u6CE8\u5165
1044
+ if (userConfig.getToken) {
1045
+ const token = userConfig.getToken();
1046
+ if (token && config.headers) {
1047
+ const prefix = userConfig.tokenPrefix ?? "Bearer";
1048
+ config.headers.Authorization = prefix ? \`\${prefix} \${token}\` : token;
1049
+ }
1050
+ } else {
1051
+ // \u9ED8\u8BA4\uFF1A\u4ECE localStorage \u8BFB\u53D6
1052
+ const token = typeof localStorage !== "undefined"
1053
+ ? localStorage.getItem("token")
1054
+ : null;
1055
+ if (token && config.headers) {
1056
+ config.headers.Authorization = \`Bearer \${token}\`;
1057
+ }
1058
+ }
1059
+
1060
+ // 2. \u7528\u6237\u81EA\u5B9A\u4E49\u8BF7\u6C42\u62E6\u622A
1061
+ if (userConfig.requestInterceptor) {
1062
+ return userConfig.requestInterceptor(config as any) as any;
1063
+ }
1064
+
1065
+ return config;
1066
+ },
1067
+ (error: AxiosError) => Promise.reject(error)
1068
+ );
1069
+
1070
+ // ============ \u54CD\u5E94\u62E6\u622A\u5668 ============
1071
+
1072
+ instance.interceptors.response.use(
1073
+ (response: AxiosResponse) => {
1074
+ // \u7528\u6237\u81EA\u5B9A\u4E49\u54CD\u5E94\u62E6\u622A
1075
+ if (userConfig.responseInterceptor) {
1076
+ return userConfig.responseInterceptor(response as any);
1077
+ }
1078
+
1079
+ // \u9ED8\u8BA4\uFF1A\u76F4\u63A5\u8FD4\u56DE response.data
1080
+ return response.data;
1081
+ },
1082
+ (error: AxiosError) => {
1083
+ // \u7528\u6237\u81EA\u5B9A\u4E49\u9519\u8BEF\u5904\u7406
1084
+ if (userConfig.errorHandler) {
1085
+ return userConfig.errorHandler(error as any);
1086
+ }
1087
+
1088
+ // \u9ED8\u8BA4\u9519\u8BEF\u5904\u7406
1089
+ const status = error.response?.status;
1090
+
1091
+ switch (status) {
1092
+ case 401:
1093
+ console.warn("[request] \u672A\u6388\u6743\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55");
1094
+ break;
1095
+ case 403:
1096
+ console.warn("[request] \u65E0\u8BBF\u95EE\u6743\u9650");
1097
+ break;
1098
+ case 500:
1099
+ console.error("[request] \u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF");
1100
+ break;
1101
+ default:
1102
+ if (!error.response) {
1103
+ console.error("[request] \u7F51\u7EDC\u5F02\u5E38\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u8FDE\u63A5");
1104
+ }
1105
+ }
1106
+
1107
+ return Promise.reject(error);
1108
+ }
1109
+ );
1110
+
1111
+ // ============ \u8BF7\u6C42\u5C01\u88C5\u7C7B ============
1112
+
1113
+ type ExtraConfig = AxiosRequestConfig & { suppressErrorNotification?: boolean };
1114
+
1115
+ class Request {
1116
+ constructor(private readonly http: AxiosInstance) {}
1117
+
1118
+ get<T = any>(url: string, params?: Record<string, any>, config?: ExtraConfig): Promise<Result<T>> {
1119
+ return this.http.get(url, {
1120
+ ...config,
1121
+ params,
1122
+ paramsSerializer,
1123
+ });
1124
+ }
1125
+
1126
+ post<T = any>(url: string, data?: any, config?: ExtraConfig): Promise<Result<T>> {
1127
+ return this.http.post(url, data, config);
1128
+ }
1129
+
1130
+ put<T = any>(url: string, data?: any, config?: ExtraConfig): Promise<Result<T>> {
1131
+ return this.http.put(url, data, config);
1132
+ }
1133
+
1134
+ delete<T = any>(url: string, params?: Record<string, any>, config?: ExtraConfig): Promise<Result<T>> {
1135
+ return this.http.delete(url, {
1136
+ ...config,
1137
+ params,
1138
+ paramsSerializer,
1139
+ });
1140
+ }
1141
+
1142
+ patch<T = any>(url: string, data?: any, config?: ExtraConfig): Promise<Result<T>> {
1143
+ return this.http.patch(url, data, config);
1144
+ }
1145
+ }
1146
+
1147
+ // ============ \u5BFC\u51FA ============
1148
+
1149
+ export const request = new Request(instance);
1150
+ export type { Result };
1151
+ export default request;
1152
+ `;
1153
+ }
1154
+
1155
+ // src/plugin/builtin/tailwind.ts
1156
+ import { existsSync as existsSync5, writeFileSync as writeFileSync3 } from "fs";
1157
+ import { join as join5 } from "path";
1158
+ import { rspack } from "@rspack/core";
1159
+ var tailwindPlugin = createPlugin((options = {}) => ({
1160
+ name: "@4399ywkf/plugin-tailwind",
1161
+ version: "1.0.0",
1162
+ description: "Tailwind CSS \u96C6\u6210\u63D2\u4EF6",
1163
+ setup(context) {
1164
+ const {
1165
+ darkModeSelector = '&:where([data-theme="dark"], [data-theme="dark"] *)',
1166
+ autoConfig = true,
1167
+ extraCSS = ""
1168
+ } = options;
1169
+ const { cwd, logger } = context;
1170
+ if (autoConfig) {
1171
+ ensurePostcssConfig(cwd, logger);
1172
+ }
1173
+ const hooks = {
1174
+ modifyRspackConfig(rspackConfig) {
1175
+ logger.info("Tailwind CSS \u5DF2\u542F\u7528");
1176
+ const rules = rspackConfig.module?.rules || [];
1177
+ const postcssConfigPath = join5(cwd, "postcss.config.js");
1178
+ const isProd = rspackConfig.mode === "production";
1179
+ const tailwindRule = {
1180
+ test: /tailwind\.css$/,
1181
+ use: [
1182
+ isProd ? rspack.CssExtractRspackPlugin.loader : "style-loader",
1183
+ "css-loader",
1184
+ {
1185
+ loader: "postcss-loader",
1186
+ options: {
1187
+ postcssOptions: {
1188
+ config: postcssConfigPath
1189
+ }
1190
+ }
1191
+ }
1192
+ ]
1193
+ };
1194
+ rules.unshift(tailwindRule);
1195
+ rspackConfig.module = { ...rspackConfig.module, rules };
1196
+ return rspackConfig;
1197
+ },
1198
+ generateFiles(_ctx) {
1199
+ const cssContent = buildTailwindCSS(darkModeSelector, extraCSS);
1200
+ return [
1201
+ {
1202
+ path: "tailwind.css",
1203
+ content: cssContent
1204
+ }
1205
+ ];
1206
+ },
1207
+ injectEntry(_ctx) {
1208
+ return {
1209
+ imports: [`import "./tailwind.css";`]
1210
+ };
1211
+ }
1212
+ };
1213
+ return hooks;
1214
+ }
1215
+ }));
1216
+ function buildTailwindCSS(darkModeSelector, extraCSS) {
1217
+ return `/* \u6B64\u6587\u4EF6\u7531 @4399ywkf/plugin-tailwind \u81EA\u52A8\u751F\u6210 */
1218
+ @import "tailwindcss";
1219
+ @variant dark (${darkModeSelector});
1220
+ ${extraCSS ? `
1221
+ ${extraCSS}` : ""}
1222
+ `;
1223
+ }
1224
+ function ensurePostcssConfig(cwd, logger) {
1225
+ const configPath = join5(cwd, "postcss.config.js");
1226
+ if (!existsSync5(configPath)) {
1227
+ writeFileSync3(
1228
+ configPath,
1229
+ `module.exports = {
1230
+ plugins: {
1231
+ "@tailwindcss/postcss": {},
1232
+ },
1233
+ };
1234
+ `,
1235
+ "utf-8"
1236
+ );
1237
+ logger.info("\u5DF2\u81EA\u52A8\u751F\u6210 postcss.config.js");
1238
+ }
1239
+ }
1240
+
1241
+ // src/plugin/builtin/theme.ts
1242
+ import { join as join6 } from "path";
1243
+ var themePlugin = createPlugin((options = {}) => ({
1244
+ name: "@4399ywkf/plugin-theme",
1245
+ version: "3.0.0",
1246
+ description: "Lobe-UI \u98CE\u683C\u54CD\u5E94\u5F0F\u4E3B\u9898\u7CFB\u7EDF\u63D2\u4EF6",
1247
+ setup(context) {
1248
+ const {
1249
+ darkMode = true,
1250
+ defaultAppearance = "light",
1251
+ primaryColor,
1252
+ neutralColor,
1253
+ prefixCls = "ant",
1254
+ cssVar = true,
1255
+ globalReset = true,
1256
+ externalTheme = false,
1257
+ locale = "auto",
1258
+ scopePopupContainer = true
1259
+ } = options;
1260
+ const { logger } = context;
1261
+ logger.info(
1262
+ `\u4E3B\u9898\u6A21\u5F0F: ${defaultAppearance}, \u4E3B\u8272: ${primaryColor ?? "primary(\u9ED8\u8BA4\u9ED1)"}, \u54CD\u5E94\u5F0F: \u2713`
1263
+ );
1264
+ const hooks = {
1265
+ modifyRspackConfig(config, ctx) {
1266
+ const themePath = join6(ctx.cwd, ".ywkf", "theme.tsx");
1267
+ const currentAlias = config.resolve?.alias ?? {};
1268
+ config.resolve = {
1269
+ ...config.resolve,
1270
+ alias: {
1271
+ ...currentAlias,
1272
+ "@ywkf/theme": themePath
1273
+ }
1274
+ };
1275
+ return config;
1276
+ },
1277
+ generateFiles(_ctx) {
1278
+ return [
1279
+ {
1280
+ path: "theme.tsx",
1281
+ content: generateThemeProvider({
1282
+ darkMode,
1283
+ defaultAppearance,
1284
+ primaryColor,
1285
+ neutralColor,
1286
+ prefixCls,
1287
+ cssVar,
1288
+ globalReset,
1289
+ externalTheme,
1290
+ locale,
1291
+ scopePopupContainer
1292
+ })
1293
+ }
1294
+ ];
1295
+ },
1296
+ injectBootstrap(_ctx) {
1297
+ return {
1298
+ imports: [`import { ThemeWrapper } from "./theme";`]
1299
+ };
1300
+ },
1301
+ modifyBootstrapCode(code) {
1302
+ const providerEntry = ` {
1303
+ component: ThemeWrapper as React.ComponentType<{ children: React.ReactNode }>,
1304
+ props: {},
1305
+ order: 10,
1306
+ }`;
1307
+ if (code.includes("providers: []")) {
1308
+ code = code.replace(
1309
+ "providers: []",
1310
+ `providers: [
1311
+ ${providerEntry},
1312
+ ]`
1313
+ );
1314
+ } else if (code.includes("providers: [")) {
1315
+ code = code.replace(
1316
+ "providers: [",
1317
+ `providers: [
1318
+ ${providerEntry},`
1319
+ );
1320
+ }
1321
+ if (!code.includes("import React")) {
1322
+ code = code.replace(
1323
+ `import { bootstrap`,
1324
+ `import React from "react";
1325
+ import { bootstrap`
1326
+ );
1327
+ }
1328
+ return code;
1329
+ }
1330
+ };
1331
+ return hooks;
1332
+ }
1333
+ }));
1334
+ function generateThemeProvider(opts) {
1335
+ const {
1336
+ darkMode,
1337
+ defaultAppearance,
1338
+ primaryColor,
1339
+ neutralColor,
1340
+ prefixCls,
1341
+ cssVar,
1342
+ globalReset,
1343
+ externalTheme,
1344
+ locale,
1345
+ scopePopupContainer
1346
+ } = opts;
1347
+ const isAutoLocale = locale === "auto";
1348
+ const sections = [];
1349
+ sections.push(`// \u6B64\u6587\u4EF6\u7531 @4399ywkf/plugin-theme v3 \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u4FEE\u6539`);
1350
+ sections.push(buildImports({ globalReset, cssVar, scopePopupContainer, locale }));
1351
+ sections.push(TYPES_CODE);
1352
+ sections.push(
1353
+ buildStoreCode({ defaultAppearance, primaryColor, neutralColor, locale })
1354
+ );
1355
+ sections.push(HOOKS_CODE);
1356
+ if (globalReset) sections.push(GLOBAL_RESET_CODE);
1357
+ if (cssVar) sections.push(buildCssVarSyncCode());
1358
+ if (darkMode) sections.push(APPEARANCE_SYNC_CODE);
1359
+ if (externalTheme) sections.push(EXTERNAL_THEME_CODE);
1360
+ if (isAutoLocale) sections.push(LOCALE_AUTO_CODE);
1361
+ sections.push(
1362
+ buildWrapperCode({
1363
+ darkMode,
1364
+ cssVar,
1365
+ globalReset,
1366
+ externalTheme,
1367
+ locale,
1368
+ prefixCls,
1369
+ scopePopupContainer
1370
+ })
1371
+ );
1372
+ return sections.join("\n");
1373
+ }
1374
+ var ANTD_LOCALE_MAP = {
1375
+ zhCN: { importName: "zhCN", importPath: "antd/locale/zh_CN.js" },
1376
+ enUS: { importName: "enUS", importPath: "antd/locale/en_US.js" },
1377
+ zhTW: { importName: "zhTW", importPath: "antd/locale/zh_TW.js" },
1378
+ jaJP: { importName: "jaJP", importPath: "antd/locale/ja_JP.js" },
1379
+ koKR: { importName: "koKR", importPath: "antd/locale/ko_KR.js" }
1380
+ };
1381
+ var LOCALE_TO_BCP47 = {
1382
+ zhCN: "zh-CN",
1383
+ enUS: "en-US",
1384
+ zhTW: "zh-TW",
1385
+ jaJP: "ja-JP",
1386
+ koKR: "ko-KR"
1387
+ };
1388
+ function buildImports(opts) {
1389
+ const needsRef = opts.cssVar || opts.scopePopupContainer;
1390
+ const isAutoLocale = opts.locale === "auto";
1391
+ const reactImports = [
1392
+ "useCallback",
1393
+ "useEffect",
1394
+ "useMemo",
1395
+ "useState",
1396
+ ...opts.cssVar ? ["useLayoutEffect"] : [],
1397
+ ...needsRef ? ["useRef"] : [],
1398
+ "type ReactNode"
1399
+ ];
1400
+ const antdStyleImports = [
1401
+ "ThemeProvider as AntdThemeProvider",
1402
+ "StyleProvider",
1403
+ "type GetAntdTheme",
1404
+ ...opts.globalReset ? ["createGlobalStyle", "css"] : []
1405
+ ];
1406
+ const localeEntry = ANTD_LOCALE_MAP[opts.locale];
1407
+ const localeImports = isAutoLocale ? `import zhCN from "antd/locale/zh_CN.js";
1408
+ import type { Locale as AntdLocale } from "antd/es/locale";` : localeEntry ? `import ${localeEntry.importName} from "${localeEntry.importPath}";` : `import zhCN from "antd/locale/zh_CN.js";`;
1409
+ return `
1410
+ import React, { ${reactImports.join(", ")} } from "react";
1411
+ import { ConfigProvider } from "antd";
1412
+ ${localeImports}
1413
+ import { ${antdStyleImports.join(", ")} } from "antd-style";
1414
+ import { createWithEqualityFn } from "zustand/traditional";
1415
+ import { shallow } from "zustand/shallow";
1416
+ import { createThemeConfig, type PrimaryColors, type NeutralColors } from "@4399ywkf/theme-system";`;
1417
+ }
1418
+ var TYPES_CODE = `
1419
+ // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 Types \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1420
+
1421
+ export type ThemeAppearance = "light" | "dark" | "auto";
1422
+
1423
+ export interface ThemeState {
1424
+ appearance: ThemeAppearance;
1425
+ primaryColor?: PrimaryColors;
1426
+ neutralColor?: NeutralColors;
1427
+ /** BCP 47 language tag, e.g. "zh-CN", "en-US" */
1428
+ locale: string;
1429
+ }
1430
+
1431
+ export interface ThemeActions {
1432
+ setAppearance: (mode: ThemeAppearance) => void;
1433
+ setPrimaryColor: (color: PrimaryColors | undefined) => void;
1434
+ setNeutralColor: (color: NeutralColors | undefined) => void;
1435
+ setLocale: (locale: string) => void;
1436
+ setTheme: (partial: Partial<ThemeState>) => void;
1437
+ }
1438
+
1439
+ export type ThemeStore = ThemeState & ThemeActions;
1440
+
1441
+ export type { PrimaryColors, NeutralColors };`;
1442
+ function buildStoreCode(opts) {
1443
+ const isAutoLocale = opts.locale === "auto";
1444
+ const primaryLine = opts.primaryColor ? ` primaryColor: "${opts.primaryColor}" as PrimaryColors,` : ` primaryColor: undefined,`;
1445
+ const neutralLine = opts.neutralColor ? ` neutralColor: "${opts.neutralColor}" as NeutralColors,` : ` neutralColor: undefined,`;
1446
+ const localeLine = isAutoLocale ? ` locale: detectInitialLocale(),` : ` locale: "${LOCALE_TO_BCP47[opts.locale] ?? "zh-CN"}",`;
1447
+ const detectionFn = isAutoLocale ? `
1448
+ function detectInitialLocale(): string {
1449
+ if (typeof window !== "undefined") {
1450
+ const hostLocale = (window as any).__YWKF_LOCALE__;
1451
+ if (typeof hostLocale === "string" && hostLocale) return hostLocale;
1452
+ }
1453
+ if (typeof navigator !== "undefined") {
1454
+ return navigator.language || "zh-CN";
1455
+ }
1456
+ return "zh-CN";
1457
+ }
1458
+ ` : "";
1459
+ return `
1460
+ // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 Theme Store \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1461
+ ${detectionFn}
1462
+ const DEFAULT_THEME: ThemeState = {
1463
+ appearance: "${opts.defaultAppearance}",
1464
+ ${primaryLine}
1465
+ ${neutralLine}
1466
+ ${localeLine}
1467
+ };
1468
+
1469
+ export const useThemeStore = createWithEqualityFn<ThemeStore>()(
1470
+ (set) => ({
1471
+ ...DEFAULT_THEME,
1472
+ setAppearance: (mode) => set({ appearance: mode }),
1473
+ setPrimaryColor: (color) => set({ primaryColor: color }),
1474
+ setNeutralColor: (color) => set({ neutralColor: color }),
1475
+ setLocale: (locale) => set({ locale }),
1476
+ setTheme: (partial) => set(partial),
1477
+ }),
1478
+ shallow,
1479
+ );`;
1480
+ }
1481
+ var HOOKS_CODE = `
1482
+ // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 Convenience Hooks \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1483
+
1484
+ export const useTheme = () =>
1485
+ useThemeStore(
1486
+ (s) => ({
1487
+ appearance: s.appearance,
1488
+ primaryColor: s.primaryColor,
1489
+ neutralColor: s.neutralColor,
1490
+ }),
1491
+ shallow,
1492
+ );
1493
+
1494
+ export const useAppearance = () => useThemeStore((s) => s.appearance);
1495
+ export const usePrimaryColor = () => useThemeStore((s) => s.primaryColor);
1496
+ export const useLocale = () => useThemeStore((s) => s.locale);`;
1497
+ var GLOBAL_RESET_CODE = `
1498
+ // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 Global Style \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1499
+
1500
+ const GlobalReset = createGlobalStyle(({ theme }) => css\`
1501
+ :root {
1502
+ --font-settings: "cv01", "tnum", "kern";
1503
+ --font-variations: "opsz" auto, tabular-nums;
1504
+ }
1505
+
1506
+ *,
1507
+ *::before,
1508
+ *::after {
1509
+ box-sizing: border-box;
1510
+ vertical-align: baseline;
1511
+ }
1512
+
1513
+ * {
1514
+ scrollbar-color: \${theme.colorFill} transparent;
1515
+ scrollbar-width: thin;
1516
+ }
1517
+
1518
+ html {
1519
+ overscroll-behavior: none;
1520
+ color-scheme: \${theme.isDarkMode ? "dark" : "light"};
1521
+ }
1522
+
1523
+ html, body, #root, #app {
1524
+ height: 100%;
1525
+ margin: 0;
1526
+ padding: 0;
1527
+ }
1528
+
1529
+ body {
1530
+ overflow: hidden auto;
1531
+ min-height: 100vh;
1532
+ font-family: \${theme.fontFamily};
1533
+ font-size: \${theme.fontSize}px;
1534
+ font-feature-settings: var(--font-settings);
1535
+ font-variation-settings: var(--font-variations);
1536
+ line-height: 1;
1537
+ color: \${theme.colorTextBase};
1538
+ text-size-adjust: none;
1539
+ text-rendering: optimizelegibility;
1540
+ word-wrap: break-word;
1541
+ background-color: \${theme.colorBgLayout};
1542
+ -webkit-font-smoothing: antialiased;
1543
+ -moz-osx-font-smoothing: grayscale;
1544
+ -webkit-overflow-scrolling: touch;
1545
+ -webkit-tap-highlight-color: transparent;
1546
+ }
1547
+
1548
+ code {
1549
+ font-family: \${theme.fontFamilyCode} !important;
1550
+ }
1551
+
1552
+ ::selection {
1553
+ -webkit-text-fill-color: unset !important;
1554
+ }
1555
+ \`);`;
1556
+ function buildCssVarSyncCode() {
1557
+ return `
1558
+ // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 CSS Variable Sync \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1559
+
1560
+ const CSS_VAR_CLASS_RE = /(^|-)css-var(-|$)/;
1561
+
1562
+ function isCssVarClassName(className: string): boolean {
1563
+ return CSS_VAR_CLASS_RE.test(className);
1564
+ }
1565
+
1566
+ function useCssVarSync(ref: React.RefObject<HTMLDivElement | null>) {
1567
+ useLayoutEffect(() => {
1568
+ const node = ref.current;
1569
+ if (!node) return;
1570
+
1571
+ const htmlEl = document.documentElement;
1572
+ let currentClasses: string[] = [];
1573
+
1574
+ const sync = () => {
1575
+ for (const cls of currentClasses) {
1576
+ htmlEl.classList.remove(cls);
1577
+ }
1578
+
1579
+ const nextSet = new Set<string>();
1580
+ let el: HTMLElement | null = node;
1581
+
1582
+ while (el && el !== htmlEl) {
1583
+ for (const cls of el.classList) {
1584
+ if (isCssVarClassName(cls)) {
1585
+ nextSet.add(cls);
1586
+ }
1587
+ }
1588
+ el = el.parentElement;
1589
+ }
1590
+
1591
+ const next = Array.from(nextSet);
1592
+
1593
+ for (const cls of next) {
1594
+ htmlEl.classList.add(cls);
1595
+ }
1596
+
1597
+ currentClasses = next;
1598
+ };
1599
+
1600
+ sync();
1601
+
1602
+ const observer = new MutationObserver(sync);
1603
+ let el: HTMLElement | null = node;
1604
+ while (el && el !== htmlEl) {
1605
+ observer.observe(el, { attributeFilter: ["class"] });
1606
+ el = el.parentElement;
1607
+ }
1608
+
1609
+ return () => {
1610
+ observer.disconnect();
1611
+ for (const cls of currentClasses) {
1612
+ htmlEl.classList.remove(cls);
1613
+ }
1614
+ };
1615
+ }, []);
1616
+ }`;
1617
+ }
1618
+ var APPEARANCE_SYNC_CODE = `
1619
+ // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 Appearance Sync \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1620
+
1621
+ function useAppearanceSync(appearance: ThemeAppearance) {
1622
+ useEffect(() => {
1623
+ if (appearance !== "auto") {
1624
+ document.documentElement.dataset.theme = appearance;
1625
+ return;
1626
+ }
1627
+
1628
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
1629
+ document.documentElement.dataset.theme = mq.matches ? "dark" : "light";
1630
+
1631
+ function handleChange(e: MediaQueryListEvent) {
1632
+ document.documentElement.dataset.theme = e.matches ? "dark" : "light";
1633
+ }
1634
+
1635
+ mq.addEventListener("change", handleChange);
1636
+ return () => mq.removeEventListener("change", handleChange);
1637
+ }, [appearance]);
1638
+ }`;
1639
+ var EXTERNAL_THEME_CODE = `
1640
+ // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 External Theme Injection \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1641
+
1642
+ function useExternalTheme() {
1643
+ useEffect(() => {
1644
+ const hostTheme = (window as any).__YWKF_THEME__;
1645
+ if (hostTheme && typeof hostTheme === "object") {
1646
+ useThemeStore.getState().setTheme(hostTheme);
1647
+ }
1648
+
1649
+ const handler = (e: Event) => {
1650
+ const detail = (e as CustomEvent<Partial<ThemeState>>).detail;
1651
+ if (detail) useThemeStore.getState().setTheme(detail);
1652
+ };
1653
+
1654
+ window.addEventListener("ywkf:theme-change", handler);
1655
+ return () => window.removeEventListener("ywkf:theme-change", handler);
1656
+ }, []);
1657
+ }`;
1658
+ var LOCALE_AUTO_CODE = `
1659
+ // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 Locale Runtime Management \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1660
+
1661
+ const ANTD_LOCALE_LOADERS: Record<string, () => Promise<{ default: AntdLocale }>> = {
1662
+ en: () => import("antd/locale/en_US.js"),
1663
+ "zh-tw": () => import("antd/locale/zh_TW.js"),
1664
+ "zh-hk": () => import("antd/locale/zh_TW.js"),
1665
+ ja: () => import("antd/locale/ja_JP.js"),
1666
+ ko: () => import("antd/locale/ko_KR.js"),
1667
+ };
1668
+
1669
+ function useAntdLocale(): AntdLocale {
1670
+ const lang = useThemeStore((s) => s.locale);
1671
+ const [antdLocale, setAntdLocale] = useState<AntdLocale>(zhCN);
1672
+
1673
+ useEffect(() => {
1674
+ const normalized = (lang || "zh-CN").toLowerCase();
1675
+
1676
+ if (normalized === "zh" || normalized === "zh-cn") {
1677
+ setAntdLocale(zhCN);
1678
+ return;
1679
+ }
1680
+
1681
+ const loader =
1682
+ ANTD_LOCALE_LOADERS[normalized] ??
1683
+ ANTD_LOCALE_LOADERS[normalized.split("-")[0]];
1684
+
1685
+ if (loader) {
1686
+ let cancelled = false;
1687
+ loader().then((m) => {
1688
+ if (!cancelled) setAntdLocale(m.default);
1689
+ });
1690
+ return () => { cancelled = true; };
1691
+ }
1692
+
1693
+ setAntdLocale(zhCN);
1694
+ }, [lang]);
1695
+
1696
+ return antdLocale;
1697
+ }
1698
+
1699
+ function useExternalLocale() {
1700
+ useEffect(() => {
1701
+ const handler = (e: Event) => {
1702
+ const locale = (e as CustomEvent<string>).detail;
1703
+ if (typeof locale === "string" && locale) {
1704
+ useThemeStore.getState().setLocale(locale);
1705
+ }
1706
+ };
1707
+
1708
+ window.addEventListener("ywkf:locale-change", handler);
1709
+ return () => window.removeEventListener("ywkf:locale-change", handler);
1710
+ }, []);
1711
+ }`;
1712
+ function buildWrapperCode(opts) {
1713
+ const { darkMode, cssVar, globalReset, externalTheme, locale, prefixCls, scopePopupContainer } = opts;
1714
+ const cssVarRefLine = cssVar ? "\n const containerRef = useRef<HTMLDivElement>(null);" : "";
1715
+ const popupRefLine = scopePopupContainer ? "\n const popupContainerRef = useRef<HTMLDivElement>(null);" : "";
1716
+ const cssVarSyncLine = cssVar ? "\n useCssVarSync(containerRef);" : "";
1717
+ const appearanceSyncLine = darkMode ? "\n useAppearanceSync(appearance);" : "";
1718
+ const externalThemeLine = externalTheme ? "\n useExternalTheme();" : "";
1719
+ const isAutoLocale = locale === "auto";
1720
+ const localeEntry = ANTD_LOCALE_MAP[locale];
1721
+ const localeVarName = isAutoLocale ? "antdLocale" : localeEntry?.importName ?? "zhCN";
1722
+ const localeLine = isAutoLocale ? "\n const antdLocale = useAntdLocale();\n useExternalLocale();" : "";
1723
+ const getPopupContainerLine = scopePopupContainer ? `
1724
+ // \u5C06\u5F39\u5C42\u6302\u8F7D\u5230\u5BB9\u5668\u5185\u90E8\uFF0C\u786E\u4FDD\u80FD\u7EE7\u627F StyleProvider \u6CE8\u5165\u7684\u6837\u5F0F\u4E0E CSS \u53D8\u91CF\u3002
1725
+ // \u5FAE\u524D\u7AEF\u6A21\u5F0F\u4E0B\u4F18\u5148\u4F7F\u7528 styleContainer\uFF08\u5B50\u5E94\u7528\u6839\u5BB9\u5668\uFF09\uFF0C
1726
+ // \u666E\u901A\u6A21\u5F0F\u4E0B\u4F7F\u7528 popupContainerRef \u6240\u6307\u5411\u7684\u5305\u88C5 div\u3002
1727
+ const getPopupContainer = useCallback(
1728
+ (): HTMLElement =>
1729
+ (IS_MICRO_APP ? styleContainer : popupContainerRef.current) ?? document.body,
1730
+ [styleContainer],
1731
+ );` : "";
1732
+ const childrenSlot = cssVar ? `<div ref={containerRef} style={{ display: "contents" }}>
1733
+ {children}
1734
+ </div>` : "{children}";
1735
+ const innerContent = scopePopupContainer ? `<ConfigProvider locale={${localeVarName}} getPopupContainer={getPopupContainer}>
1736
+ ${globalReset ? "<GlobalReset />" : ""}
1737
+ ${childrenSlot}
1738
+ </ConfigProvider>` : `<ConfigProvider locale={${localeVarName}}>
1739
+ ${globalReset ? "<GlobalReset />" : ""}
1740
+ ${childrenSlot}
1741
+ </ConfigProvider>`;
1742
+ const antdProvider = `<AntdThemeProvider
1743
+ prefixCls={RUNTIME_PREFIX_CLS}
1744
+ appearance={resolvedAppearance}
1745
+ themeMode={appearance}
1746
+ theme={theme}${cssVar ? "\n customToken={{ cssVar: true }}" : ""}
1747
+ >
1748
+ ${innerContent}
1749
+ </AntdThemeProvider>`;
1750
+ const wrapperOpen = scopePopupContainer ? `<div
1751
+ ref={popupContainerRef}
1752
+ data-ywkf-root
1753
+ style={{ position: "relative", height: "100%" }}
1754
+ >` : "";
1755
+ const wrapperClose = scopePopupContainer ? `</div>` : "";
1756
+ const innerReturn = `IS_MICRO_APP && styleContainer ? (
1757
+ <StyleProvider container={styleContainer}>{provider}</StyleProvider>
1758
+ ) : provider`;
1759
+ const returnBody = scopePopupContainer ? `(
1760
+ ${wrapperOpen}
1761
+ {IS_MICRO_APP && styleContainer ? (
1762
+ <StyleProvider container={styleContainer}>{provider}</StyleProvider>
1763
+ ) : provider}
1764
+ ${wrapperClose}
1765
+ )` : `(${innerReturn})`;
1766
+ return `
1767
+ // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 ThemeWrapper \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1768
+
1769
+ // \u8FD0\u884C\u65F6\u5224\u65AD\u662F\u5426\u5904\u4E8E Garfish \u5B50\u5E94\u7528\u73AF\u5883\uFF0C\u540C\u4E00 bundle \u652F\u6301\u72EC\u7ACB\u90E8\u7F72\u4E0E\u5FAE\u524D\u7AEF\u4E24\u79CD\u6A21\u5F0F\u3002
1770
+ const IS_MICRO_APP = typeof window !== "undefined" && !!(window as any).__GARFISH__;
1771
+
1772
+ const RUNTIME_PREFIX_CLS = typeof process !== "undefined"
1773
+ && process.env?.YWKF_PREFIX_CLS
1774
+ || "${prefixCls}";
1775
+
1776
+ interface ThemeWrapperProps {
1777
+ children: ReactNode;
1778
+ }
1779
+
1780
+ export function ThemeWrapper({ children }: ThemeWrapperProps) {${cssVarRefLine}${popupRefLine}
1781
+ const { appearance, primaryColor, neutralColor } = useThemeStore(
1782
+ (s) => ({ appearance: s.appearance, primaryColor: s.primaryColor, neutralColor: s.neutralColor }),
1783
+ shallow,
1784
+ );${cssVarSyncLine}${appearanceSyncLine}${externalThemeLine}${localeLine}
1785
+
1786
+ // \u5FAE\u524D\u7AEF\u6A21\u5F0F\u4E0B\uFF0C\u5C06 CSS-in-JS \u6837\u5F0F\u6CE8\u5165\u5230 Garfish \u5B50\u5E94\u7528\u5BB9\u5668\u800C\u975E document.head\uFF0C
1787
+ // \u907F\u514D\u4E0E\u4E3B\u5E94\u7528\u5171\u4EAB\u7684 @ant-design/cssinjs StyleContext \u4EA7\u751F\u51B2\u7A81\u3002
1788
+ // useState lazy initializer \u540C\u6B65\u8BFB\u53D6\uFF08Garfish \u6302\u8F7D\u65F6\u5BB9\u5668\u5DF2\u63D0\u524D\u521B\u5EFA\uFF09\uFF0C
1789
+ // useEffect \u515C\u5E95\u5904\u7406\u6781\u7AEF\u7ADE\u6001\uFF08\u5982 SSR hydration\uFF09\u3002
1790
+ const [styleContainer, setStyleContainer] = useState<HTMLElement | undefined>(() => {
1791
+ if (!IS_MICRO_APP || typeof document === "undefined") return undefined;
1792
+ return document.getElementById(RUNTIME_PREFIX_CLS)?.parentElement ?? undefined;
1793
+ });
1794
+
1795
+ useEffect(() => {
1796
+ if (!IS_MICRO_APP) return;
1797
+ const el = document.getElementById(RUNTIME_PREFIX_CLS)?.parentElement;
1798
+ if (el) setStyleContainer(el);
1799
+ }, []);
1800
+
1801
+ const resolvedAppearance = useMemo(() => {
1802
+ if (appearance !== "auto") return appearance;
1803
+ if (typeof window === "undefined") return "light";
1804
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
1805
+ }, [appearance]);
1806
+
1807
+ const theme = useCallback<GetAntdTheme>(
1808
+ (ap) => createThemeConfig({
1809
+ appearance: ap as "light" | "dark",
1810
+ primaryColor,
1811
+ neutralColor,
1812
+ }),
1813
+ [primaryColor, neutralColor],
1814
+ );
1815
+ ${getPopupContainerLine}
1816
+ const provider = (
1817
+ ${antdProvider}
1818
+ );
1819
+
1820
+ return ${returnBody};
1821
+ }
1822
+
1823
+ export default ThemeWrapper;
1824
+ `;
1825
+ }
1826
+
1827
+ // src/plugin/builtin/zustand.ts
1828
+ import { existsSync as existsSync6, mkdirSync as mkdirSync2, writeFileSync as writeFileSync4 } from "fs";
1829
+ import { join as join7 } from "path";
1830
+ var zustandPlugin = createPlugin((options = {}) => ({
1831
+ name: "@4399ywkf/plugin-zustand",
1832
+ version: "1.0.0",
1833
+ description: "Zustand \u72B6\u6001\u7BA1\u7406\u96C6\u6210",
1834
+ setup(context) {
1835
+ const { scaffold = true, storeDir = "store" } = options;
1836
+ const { cwd, logger } = context;
1837
+ const storePath = join7(cwd, storeDir);
1838
+ if (scaffold && !existsSync6(storePath)) {
1839
+ scaffoldStore(storePath, logger);
1840
+ }
1841
+ const hooks = {
1842
+ modifyRspackConfig(rspackConfig) {
1843
+ logger.info("Zustand \u5DF2\u542F\u7528");
1844
+ const resolve3 = rspackConfig.resolve || {};
1845
+ const alias = resolve3.alias || {};
1846
+ alias["@store"] = storePath;
1847
+ resolve3.alias = alias;
1848
+ rspackConfig.resolve = resolve3;
1849
+ return rspackConfig;
1850
+ },
1851
+ generateFiles(_ctx) {
1852
+ return [
1853
+ {
1854
+ path: "store.ts",
1855
+ content: buildStoreHelperFile()
1856
+ }
1857
+ ];
1858
+ }
1859
+ };
1860
+ return hooks;
1861
+ }
1862
+ }));
1863
+ function scaffoldStore(storePath, logger) {
1864
+ const dirs = [
1865
+ storePath,
1866
+ join7(storePath, "middleware"),
1867
+ join7(storePath, "app"),
1868
+ join7(storePath, "app", "slices"),
1869
+ join7(storePath, "app", "slices", "counter")
1870
+ ];
1871
+ for (const dir of dirs) {
1872
+ if (!existsSync6(dir)) {
1873
+ mkdirSync2(dir, { recursive: true });
1874
+ }
1875
+ }
1876
+ writeFileSync4(join7(storePath, "middleware", "createDevtools.ts"), TPL_CREATE_DEVTOOLS, "utf-8");
1877
+ writeFileSync4(
1878
+ join7(storePath, "app", "slices", "counter", "initialState.ts"),
1879
+ TPL_COUNTER_INITIAL_STATE,
1880
+ "utf-8"
1881
+ );
1882
+ writeFileSync4(
1883
+ join7(storePath, "app", "slices", "counter", "actions.ts"),
1884
+ TPL_COUNTER_ACTIONS,
1885
+ "utf-8"
1886
+ );
1887
+ writeFileSync4(join7(storePath, "app", "initialState.ts"), TPL_APP_INITIAL_STATE, "utf-8");
1888
+ writeFileSync4(join7(storePath, "app", "store.ts"), TPL_APP_STORE, "utf-8");
1889
+ writeFileSync4(join7(storePath, "app", "index.tsx"), TPL_APP_INDEX, "utf-8");
1890
+ writeFileSync4(join7(storePath, "index.ts"), TPL_STORE_INDEX, "utf-8");
1891
+ logger.info("\u5DF2\u751F\u6210 store/ \u811A\u624B\u67B6\uFF08Agent/Slice \u67B6\u6784\uFF09");
1892
+ }
1893
+ var TPL_CREATE_DEVTOOLS = `import type { DevtoolsOptions } from "zustand/middleware";
1894
+ import { devtools as devtoolsMiddleware } from "zustand/middleware";
1895
+ import type { StateCreator, StoreMutatorIdentifier } from "zustand/vanilla";
1896
+
1897
+ /**
1898
+ * \u5C01\u88C5 zustand devtools \u4E2D\u95F4\u4EF6
1899
+ * \u751F\u4EA7\u73AF\u5883\u81EA\u52A8\u7981\u7528\uFF0C\u5F00\u53D1\u73AF\u5883\u542F\u7528 trace
1900
+ */
1901
+ export const createDevtools =
1902
+ (name: string, options?: DevtoolsOptions) =>
1903
+ <
1904
+ T,
1905
+ Mps extends [StoreMutatorIdentifier, unknown][] = [],
1906
+ Mcs extends [StoreMutatorIdentifier, unknown][] = [],
1907
+ >(
1908
+ initializer: StateCreator<T, [...Mps, ["zustand/devtools", never]], Mcs>,
1909
+ ) =>
1910
+ devtoolsMiddleware(initializer, {
1911
+ name,
1912
+ enabled: process.env.NODE_ENV === "development",
1913
+ trace: process.env.NODE_ENV === "development",
1914
+ ...options,
1915
+ });
1916
+ `;
1917
+ var TPL_COUNTER_INITIAL_STATE = `export interface CounterState {
1918
+ count: number;
1919
+ loading: boolean;
1920
+ error: string | null;
1921
+ }
1922
+
1923
+ export const initialCounterState: CounterState = {
1924
+ count: 0,
1925
+ loading: false,
1926
+ error: null,
1927
+ };
1928
+ `;
1929
+ var TPL_COUNTER_ACTIONS = `import type { StateCreator } from "zustand/vanilla";
1930
+ import type { AppStore } from "../../store";
1931
+
1932
+ export interface CounterAction {
1933
+ increment: () => void;
1934
+ decrement: () => void;
1935
+ reset: () => void;
1936
+ setLoading: (loading: boolean) => void;
1937
+ setError: (error: string | null) => void;
1938
+ fetchCount: () => Promise<void>;
1939
+ }
1940
+
1941
+ export const createCounterSlice: StateCreator<
1942
+ AppStore,
1943
+ [["zustand/devtools", never]],
1944
+ [],
1945
+ CounterAction
1946
+ > = (set, get) => ({
1947
+ increment: () => set((s) => ({ count: s.count + 1 })),
1948
+ decrement: () => set((s) => ({ count: s.count - 1 })),
1949
+ reset: () => set({ count: 0, error: null }),
1950
+ setLoading: (loading) => set({ loading }),
1951
+ setError: (error) => set({ error }),
1952
+
1953
+ fetchCount: async () => {
1954
+ set({ loading: true, error: null });
1955
+ try {
1956
+ // \u66FF\u6362\u4E3A\u5B9E\u9645 API \u8C03\u7528
1957
+ // import request from "@ywkf/request";
1958
+ // const res = await request.get("/api/count");
1959
+ // set({ count: res.data, loading: false });
1960
+ await new Promise((r) => setTimeout(r, 500));
1961
+ set({ count: 42, loading: false });
1962
+ } catch (err) {
1963
+ set({
1964
+ error: err instanceof Error ? err.message : "\u672A\u77E5\u9519\u8BEF",
1965
+ loading: false,
1966
+ });
1967
+ }
1968
+ },
1969
+ });
1970
+ `;
1971
+ var TPL_APP_INITIAL_STATE = `import {
1972
+ type CounterState,
1973
+ initialCounterState,
1974
+ } from "./slices/counter/initialState";
1975
+
1976
+ // ============== \u805A\u5408\u6240\u6709 Slice \u72B6\u6001 ============== //
1977
+
1978
+ export type AppStoreState = CounterState;
1979
+
1980
+ export const initialState: AppStoreState = {
1981
+ ...initialCounterState,
1982
+ };
1983
+ `;
1984
+ var TPL_APP_STORE = `import { subscribeWithSelector } from "zustand/middleware";
1985
+ import { shallow } from "zustand/shallow";
1986
+ import { createWithEqualityFn } from "zustand/traditional";
1987
+ import type { StateCreator } from "zustand/vanilla";
1988
+
1989
+ import { createDevtools } from "../middleware/createDevtools";
1990
+ import { type AppStoreState, initialState } from "./initialState";
1991
+
1992
+ import {
1993
+ type CounterAction,
1994
+ createCounterSlice,
1995
+ } from "./slices/counter/actions";
1996
+
1997
+ // ============== \u805A\u5408 Store \u7C7B\u578B ============== //
1998
+
1999
+ export type AppStore = AppStoreState & CounterAction;
2000
+
2001
+ // ============== \u521B\u5EFA Store ============== //
2002
+
2003
+ const createStore: StateCreator<AppStore, [["zustand/devtools", never]]> = (
2004
+ ...parameters
2005
+ ) => ({
2006
+ ...initialState,
2007
+ ...createCounterSlice(...parameters),
2008
+ });
2009
+
2010
+ // ============== \u5B9E\u88C5 useStore ============== //
2011
+
2012
+ const devtools = createDevtools("app");
2013
+
2014
+ export const useAppStore = createWithEqualityFn<AppStore>()(
2015
+ subscribeWithSelector(devtools(createStore)),
2016
+ shallow,
2017
+ );
2018
+
2019
+ export const getAppStoreState = () => useAppStore.getState();
2020
+ `;
2021
+ var TPL_APP_INDEX = `export { useAppStore, getAppStoreState } from "./store";
2022
+ export type { AppStore } from "./store";
2023
+ export { initialState } from "./initialState";
2024
+ export type { AppStoreState } from "./initialState";
2025
+ `;
2026
+ var TPL_STORE_INDEX = `/**
2027
+ * Store \u805A\u5408\u5165\u53E3
2028
+ *
2029
+ * \u67B6\u6784\uFF1A
2030
+ * - store/middleware/ \u2014 \u4E2D\u95F4\u4EF6\uFF08devtools \u7B49\uFF09
2031
+ * - store/{domain}/ \u2014 \u6BCF\u4E2A\u57DF\u4EE3\u8868\u4E00\u4E2A\u4E1A\u52A1\u5B50\u7CFB\u7EDF
2032
+ * - store/{domain}/slices \u2014 \u6BCF\u4E2A slice \u62C6\u4E3A initialState + actions
2033
+ *
2034
+ * \u7EA6\u675F\uFF1A
2035
+ * - initialState\uFF1A\u7EAF\u6570\u636E\u5B9A\u4E49\uFF0C\u4E0D\u542B\u903B\u8F91
2036
+ * - actions\uFF1A\u540C\u6B65\u72B6\u6001\u53D8\u66F4 + \u5F02\u6B65\u526F\u4F5C\u7528
2037
+ * - slice \u4E4B\u95F4\u7981\u6B62\u76F4\u63A5\u4FEE\u6539\u5F7C\u6B64\u72B6\u6001
2038
+ * - \u57DF\u5C42\u8D1F\u8D23\u8DE8 slice \u534F\u8C03
2039
+ */
2040
+ export { useAppStore, getAppStoreState } from "./app";
2041
+ export type { AppStore, AppStoreState } from "./app";
2042
+ `;
2043
+ function buildStoreHelperFile() {
2044
+ return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/plugin-zustand \u81EA\u52A8\u751F\u6210
2045
+ // \u63D0\u4F9B Zustand \u5DE5\u5177\u51FD\u6570\u7684 re-export\uFF0C\u65B9\u4FBF\u4E1A\u52A1\u4EE3\u7801\u5F15\u7528
2046
+
2047
+ export { useShallow } from "zustand/react/shallow";
2048
+ export type { StateCreator, StoreApi } from "zustand";
2049
+ `;
2050
+ }
2051
+
2052
+ // src/config/normalize.ts
2053
+ var BUILTIN_PLUGINS = [
2054
+ { key: "reactQuery", factory: reactQueryPlugin, pluginName: "@4399ywkf/plugin-react-query" },
2055
+ { key: "zustand", factory: zustandPlugin, pluginName: "@4399ywkf/plugin-zustand" },
2056
+ { key: "tailwind", factory: tailwindPlugin, pluginName: "@4399ywkf/plugin-tailwind" }
2057
+ ];
2058
+ var OPTIONAL_PLUGINS = [
2059
+ { key: "biome", factory: biomePlugin, pluginName: "@4399ywkf/plugin-biome" },
2060
+ { key: "analytics", factory: analyticsPlugin, pluginName: "@4399ywkf/plugin-analytics" },
2061
+ { key: "mock", factory: mockPlugin, pluginName: "@4399ywkf/plugin-mock" },
2062
+ { key: "theme", factory: themePlugin, pluginName: "@4399ywkf/plugin-theme" },
2063
+ { key: "i18n", factory: i18nPlugin, pluginName: "@4399ywkf/plugin-i18n" },
2064
+ { key: "garfish", factory: garfishPlugin, pluginName: "@4399ywkf/plugin-garfish" },
2065
+ { key: "browserCheck", factory: browserCheckPlugin, pluginName: "@4399ywkf/plugin-browser-check" }
2066
+ ];
2067
+ var ALL_SHORTHAND_KEYS = /* @__PURE__ */ new Set([
2068
+ ...BUILTIN_PLUGINS.map((p) => p.key),
2069
+ ...OPTIONAL_PLUGINS.map((p) => p.key)
2070
+ ]);
2071
+
15
2072
  // src/config/schema.ts
16
2073
  var defaultConfig = {
17
2074
  appName: "app",
@@ -75,8 +2132,8 @@ var defaultConfig = {
75
2132
  var CONFIG_FILES = ["ywkf.config.ts", "ywkf.config.mts", "ywkf.config.js", "ywkf.config.mjs"];
76
2133
  function findConfigFile(cwd) {
77
2134
  for (const file of CONFIG_FILES) {
78
- const configPath = resolve(cwd, file);
79
- if (existsSync(configPath)) {
2135
+ const configPath = resolve2(cwd, file);
2136
+ if (existsSync7(configPath)) {
80
2137
  return configPath;
81
2138
  }
82
2139
  }
@@ -109,28 +2166,28 @@ function mergeConfig(userConfig, baseConfig = defaultConfig) {
109
2166
  }
110
2167
  function createPathResolver(cwd) {
111
2168
  return {
112
- resolveApp: (relativePath) => resolve(cwd, relativePath),
2169
+ resolveApp: (relativePath) => resolve2(cwd, relativePath),
113
2170
  cwd
114
2171
  };
115
2172
  }
116
2173
 
117
2174
  // src/rspack/base.ts
118
2175
  import { createRequire } from "module";
119
- import { dirname, join as join5 } from "path";
2176
+ import { dirname, join as join12 } from "path";
120
2177
  import { fileURLToPath } from "url";
121
- import { rspack } from "@rspack/core";
2178
+ import { rspack as rspack2 } from "@rspack/core";
122
2179
 
123
2180
  // src/generator/plugin.ts
124
- import { existsSync as existsSync5, watch } from "fs";
125
- import { join as join4 } from "path";
2181
+ import { existsSync as existsSync11, watch } from "fs";
2182
+ import { join as join11 } from "path";
126
2183
 
127
2184
  // src/generator/generator.ts
128
- import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
129
- import { join as join3 } from "path";
2185
+ import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
2186
+ import { join as join10 } from "path";
130
2187
 
131
2188
  // src/router/generator.ts
132
- import { existsSync as existsSync2, mkdirSync, readdirSync, statSync, writeFileSync } from "fs";
133
- import { join, relative } from "path";
2189
+ import { existsSync as existsSync8, mkdirSync as mkdirSync3, readdirSync as readdirSync2, statSync as statSync2, writeFileSync as writeFileSync5 } from "fs";
2190
+ import { join as join8, relative } from "path";
134
2191
  function winPath(path) {
135
2192
  const isExtendedLengthPath = /^\\\\\?\\/.test(path);
136
2193
  if (isExtendedLengthPath) {
@@ -163,7 +2220,7 @@ var ConventionalRouteGenerator = class {
163
2220
  }
164
2221
  generate() {
165
2222
  const { pagesDir } = this.options;
166
- if (!existsSync2(pagesDir)) {
2223
+ if (!existsSync8(pagesDir)) {
167
2224
  console.warn(`[ywkf] \u9875\u9762\u76EE\u5F55\u4E0D\u5B58\u5728: ${pagesDir}`);
168
2225
  return [];
169
2226
  }
@@ -173,7 +2230,7 @@ var ConventionalRouteGenerator = class {
173
2230
  * 扫描目录,生成路由树
174
2231
  */
175
2232
  scanDirectory(dir, routePath) {
176
- const entries = readdirSync(dir);
2233
+ const entries = readdirSync2(dir);
177
2234
  const layoutFile = entries.find((e) => CONVENTION_FILES.layout.test(e));
178
2235
  const pageFile = entries.find((e) => CONVENTION_FILES.page.test(e));
179
2236
  const errorFile = entries.find((e) => CONVENTION_FILES.error.test(e));
@@ -182,18 +2239,18 @@ var ConventionalRouteGenerator = class {
182
2239
  const subDirs = entries.filter((e) => {
183
2240
  if (e.startsWith(".")) return false;
184
2241
  if (EXCLUDED_DIRS.has(e)) return false;
185
- return statSync(join(dir, e)).isDirectory();
2242
+ return statSync2(join8(dir, e)).isDirectory();
186
2243
  });
187
2244
  const childRoutes = [];
188
2245
  for (const subDir of subDirs) {
189
- const subDirPath = join(dir, subDir);
2246
+ const subDirPath = join8(dir, subDir);
190
2247
  if (subDir.startsWith("__")) {
191
2248
  const pathlessRoutes = this.scanDirectory(subDirPath, routePath);
192
- const pathlessLayout = readdirSync(subDirPath).find((e) => CONVENTION_FILES.layout.test(e));
2249
+ const pathlessLayout = readdirSync2(subDirPath).find((e) => CONVENTION_FILES.layout.test(e));
193
2250
  if (pathlessLayout) {
194
2251
  const pathlessChildren = this.scanDirectory(subDirPath, routePath);
195
2252
  const pathlessLayoutRel = winPath(
196
- relative(this.options.pagesDir, join(subDirPath, pathlessLayout))
2253
+ relative(this.options.pagesDir, join8(subDirPath, pathlessLayout))
197
2254
  );
198
2255
  childRoutes.push({
199
2256
  path: routePath,
@@ -212,7 +2269,7 @@ var ConventionalRouteGenerator = class {
212
2269
  childRoutes.push(...this.scanDirectory(subDirPath, subRoutePath));
213
2270
  }
214
2271
  const routeName = this.generateRouteName(routePath);
215
- const relPath = (file) => winPath(relative(this.options.pagesDir, join(dir, file)));
2272
+ const relPath = (file) => winPath(relative(this.options.pagesDir, join8(dir, file)));
216
2273
  if (layoutFile) {
217
2274
  const layoutChildren = [];
218
2275
  if (pageFile) {
@@ -470,10 +2527,10 @@ export default routes;
470
2527
  write() {
471
2528
  const { outputDir } = this.options;
472
2529
  const code = this.generateCode();
473
- if (!existsSync2(outputDir)) {
474
- mkdirSync(outputDir, { recursive: true });
2530
+ if (!existsSync8(outputDir)) {
2531
+ mkdirSync3(outputDir, { recursive: true });
475
2532
  }
476
- writeFileSync(join(outputDir, "routes.tsx"), code, "utf-8");
2533
+ writeFileSync5(join8(outputDir, "routes.tsx"), code, "utf-8");
477
2534
  if (process.env.DEBUG) console.log(`[ywkf] \u7EA6\u5B9A\u5F0F\u8DEF\u7531\u5DF2\u751F\u6210`);
478
2535
  }
479
2536
  };
@@ -614,8 +2671,8 @@ ${startupBody}
614
2671
  }
615
2672
 
616
2673
  // src/generator/templates/env-types.ts
617
- import { existsSync as existsSync3, readFileSync } from "fs";
618
- import { join as join2 } from "path";
2674
+ import { existsSync as existsSync9, readFileSync as readFileSync2 } from "fs";
2675
+ import { join as join9 } from "path";
619
2676
  function generateEnvTypes(cwd, config) {
620
2677
  const envVars = collectEnvVars(cwd, config);
621
2678
  const envInterface = envVars.map((key) => ` readonly ${key}: string;`).join("\n");
@@ -688,19 +2745,19 @@ declare module "*.md" {
688
2745
  }
689
2746
  function collectEnvVars(cwd, config) {
690
2747
  const envVars = /* @__PURE__ */ new Set();
691
- const publicEnvPath = join2(cwd, config.env.publicEnvFile || "config/env/.env.public");
692
- if (existsSync3(publicEnvPath)) {
693
- const content = readFileSync(publicEnvPath, "utf-8");
2748
+ const publicEnvPath = join9(cwd, config.env.publicEnvFile || "config/env/.env.public");
2749
+ if (existsSync9(publicEnvPath)) {
2750
+ const content = readFileSync2(publicEnvPath, "utf-8");
694
2751
  parseEnvFile(content, envVars);
695
2752
  }
696
- const devEnvPath = join2(cwd, config.env.envDir || "config/env", ".env.development");
697
- if (existsSync3(devEnvPath)) {
698
- const content = readFileSync(devEnvPath, "utf-8");
2753
+ const devEnvPath = join9(cwd, config.env.envDir || "config/env", ".env.development");
2754
+ if (existsSync9(devEnvPath)) {
2755
+ const content = readFileSync2(devEnvPath, "utf-8");
699
2756
  parseEnvFile(content, envVars);
700
2757
  }
701
- const prodEnvPath = join2(cwd, config.env.envDir || "config/env", ".env.production");
702
- if (existsSync3(prodEnvPath)) {
703
- const content = readFileSync(prodEnvPath, "utf-8");
2758
+ const prodEnvPath = join9(cwd, config.env.envDir || "config/env", ".env.production");
2759
+ if (existsSync9(prodEnvPath)) {
2760
+ const content = readFileSync2(prodEnvPath, "utf-8");
704
2761
  parseEnvFile(content, envVars);
705
2762
  }
706
2763
  return Array.from(envVars).sort();
@@ -742,7 +2799,7 @@ var YwkfGenerator = class {
742
2799
  constructor(context, pluginHooks = []) {
743
2800
  this.context = {
744
2801
  ...context,
745
- outputDir: context.outputDir || join3(context.cwd, ".ywkf")
2802
+ outputDir: context.outputDir || join10(context.cwd, ".ywkf")
746
2803
  };
747
2804
  this.pluginHooks = pluginHooks;
748
2805
  }
@@ -752,7 +2809,7 @@ var YwkfGenerator = class {
752
2809
  async generate() {
753
2810
  const { outputDir } = this.context;
754
2811
  this.ensureDir(outputDir);
755
- this.ensureDir(join3(outputDir, "types"));
2812
+ this.ensureDir(join10(outputDir, "types"));
756
2813
  this.generateConfigSnapshot();
757
2814
  this.generateRoutes();
758
2815
  this.generateBootstrapFile();
@@ -771,7 +2828,7 @@ var YwkfGenerator = class {
771
2828
  */
772
2829
  generateConfigSnapshot() {
773
2830
  const { outputDir, config } = this.context;
774
- const configPath = join3(outputDir, "config.json");
2831
+ const configPath = join10(outputDir, "config.json");
775
2832
  const serializableConfig = JSON.parse(
776
2833
  JSON.stringify(config, (_key, value) => {
777
2834
  if (typeof value === "function") {
@@ -790,7 +2847,7 @@ var YwkfGenerator = class {
790
2847
  if (!config.router.conventional) {
791
2848
  return;
792
2849
  }
793
- const pagesDir = join3(cwd, config.router.pagesDir || "src/pages");
2850
+ const pagesDir = join10(cwd, config.router.pagesDir || "src/pages");
794
2851
  const generator = new ConventionalRouteGenerator({
795
2852
  pagesDir,
796
2853
  outputDir,
@@ -798,7 +2855,7 @@ var YwkfGenerator = class {
798
2855
  loading: config.router.loading
799
2856
  });
800
2857
  const content = generator.generateCode();
801
- const routesPath = join3(outputDir, "routes.tsx");
2858
+ const routesPath = join10(outputDir, "routes.tsx");
802
2859
  this.writeFileIfChanged(routesPath, content);
803
2860
  }
804
2861
  /**
@@ -816,7 +2873,7 @@ var YwkfGenerator = class {
816
2873
  }
817
2874
  }
818
2875
  }
819
- const bootstrapPath = join3(outputDir, "bootstrap.tsx");
2876
+ const bootstrapPath = join10(outputDir, "bootstrap.tsx");
820
2877
  this.writeFileIfChanged(bootstrapPath, content);
821
2878
  }
822
2879
  /**
@@ -834,7 +2891,7 @@ var YwkfGenerator = class {
834
2891
  }
835
2892
  }
836
2893
  }
837
- const entryPath = join3(outputDir, "index.tsx");
2894
+ const entryPath = join10(outputDir, "index.tsx");
838
2895
  this.writeFileIfChanged(entryPath, content);
839
2896
  }
840
2897
  /**
@@ -870,8 +2927,8 @@ var YwkfGenerator = class {
870
2927
  const files = hooks.generateFiles(this.context);
871
2928
  if (files) {
872
2929
  for (const file of files) {
873
- const filePath = join3(outputDir, file.path);
874
- const dir = join3(filePath, "..");
2930
+ const filePath = join10(outputDir, file.path);
2931
+ const dir = join10(filePath, "..");
875
2932
  this.ensureDir(dir);
876
2933
  this.writeFileIfChanged(filePath, file.content);
877
2934
  }
@@ -885,18 +2942,18 @@ var YwkfGenerator = class {
885
2942
  generateTypes() {
886
2943
  const { outputDir, config, cwd } = this.context;
887
2944
  const envTypesContent = generateEnvTypes(cwd, config);
888
- this.writeFileIfChanged(join3(outputDir, "types", "env.d.ts"), envTypesContent);
2945
+ this.writeFileIfChanged(join10(outputDir, "types", "env.d.ts"), envTypesContent);
889
2946
  if (config.router.conventional) {
890
2947
  const routeTypesContent = generateRouteTypes();
891
- this.writeFileIfChanged(join3(outputDir, "types", "routes.d.ts"), routeTypesContent);
2948
+ this.writeFileIfChanged(join10(outputDir, "types", "routes.d.ts"), routeTypesContent);
892
2949
  }
893
2950
  }
894
2951
  /**
895
2952
  * 确保目录存在
896
2953
  */
897
2954
  ensureDir(dir) {
898
- if (!existsSync4(dir)) {
899
- mkdirSync2(dir, { recursive: true });
2955
+ if (!existsSync10(dir)) {
2956
+ mkdirSync4(dir, { recursive: true });
900
2957
  }
901
2958
  }
902
2959
  /**
@@ -908,14 +2965,14 @@ var YwkfGenerator = class {
908
2965
  if (lastContent === normalizedContent) {
909
2966
  return;
910
2967
  }
911
- if (existsSync4(filePath)) {
912
- const existingContent = readFileSync2(filePath, "utf-8");
2968
+ if (existsSync10(filePath)) {
2969
+ const existingContent = readFileSync3(filePath, "utf-8");
913
2970
  if (this.normalizeContent(existingContent) === normalizedContent) {
914
2971
  this.lastGeneratedContent.set(filePath, normalizedContent);
915
2972
  return;
916
2973
  }
917
2974
  }
918
- writeFileSync2(filePath, content, "utf-8");
2975
+ writeFileSync6(filePath, content, "utf-8");
919
2976
  this.lastGeneratedContent.set(filePath, normalizedContent);
920
2977
  }
921
2978
  /**
@@ -1045,8 +3102,8 @@ var YwkfGeneratorPlugin = class {
1045
3102
  */
1046
3103
  watchPages() {
1047
3104
  const { config, cwd } = this.options;
1048
- const pagesDir = join4(cwd, config.router.pagesDir || "src/pages");
1049
- if (!existsSync5(pagesDir)) {
3105
+ const pagesDir = join11(cwd, config.router.pagesDir || "src/pages");
3106
+ if (!existsSync11(pagesDir)) {
1050
3107
  return;
1051
3108
  }
1052
3109
  this.isWatching = true;
@@ -1113,7 +3170,7 @@ var YwkfGeneratorPlugin = class {
1113
3170
  // src/rspack/base.ts
1114
3171
  var require2 = createRequire(import.meta.url);
1115
3172
  var __dirname = dirname(fileURLToPath(import.meta.url));
1116
- var coreNodeModules = join5(__dirname, "../../node_modules");
3173
+ var coreNodeModules = join12(__dirname, "../../node_modules");
1117
3174
  function createBaseConfig(config, cwd, options = {}) {
1118
3175
  const isDev = options.isDev ?? process.env.NODE_ENV !== "production";
1119
3176
  const { resolveApp } = createPathResolver(cwd);
@@ -1126,16 +3183,16 @@ function createBaseConfig(config, cwd, options = {}) {
1126
3183
  "@locales": resolveApp("locales"),
1127
3184
  "@public": resolveApp("public"),
1128
3185
  // 约定式路由生成的文件
1129
- "@ywkf/routes": join5(ywkfOutputDir, "routes.tsx"),
3186
+ "@ywkf/routes": join12(ywkfOutputDir, "routes.tsx"),
1130
3187
  // 请求层(由 reactQueryPlugin 生成)
1131
- "@ywkf/request": join5(ywkfOutputDir, "request.ts"),
3188
+ "@ywkf/request": join12(ywkfOutputDir, "request.ts"),
1132
3189
  // Store 工具(由 zustandPlugin 生成)
1133
- "@ywkf/store": join5(ywkfOutputDir, "store.ts")
3190
+ "@ywkf/store": join12(ywkfOutputDir, "store.ts")
1134
3191
  };
1135
3192
  const alias = { ...defaultAlias, ...userAlias };
1136
3193
  return {
1137
3194
  // 使用 .ywkf/index.tsx 作为入口(由框架生成)
1138
- entry: [join5(ywkfOutputDir, "index.tsx")],
3195
+ entry: [join12(ywkfOutputDir, "index.tsx")],
1139
3196
  resolve: {
1140
3197
  extensions: [".ts", ".tsx", ".jsx", ".js", ".json", ".mjs"],
1141
3198
  symlinks: true,
@@ -1235,14 +3292,14 @@ function createBaseConfig(config, cwd, options = {}) {
1235
3292
  ]
1236
3293
  },
1237
3294
  plugins: [
1238
- new rspack.ProvidePlugin({
3295
+ new rspack2.ProvidePlugin({
1239
3296
  process: "process/browser.js",
1240
3297
  Buffer: ["buffer", "Buffer"]
1241
3298
  }),
1242
- new rspack.DefinePlugin({
3299
+ new rspack2.DefinePlugin({
1243
3300
  "process.env": JSON.stringify(process.env)
1244
3301
  }),
1245
- new rspack.HtmlRspackPlugin({
3302
+ new rspack2.HtmlRspackPlugin({
1246
3303
  template: resolveApp(html.template ?? "public/index.html"),
1247
3304
  filename: "index.html",
1248
3305
  inject: "body",
@@ -1254,7 +3311,7 @@ function createBaseConfig(config, cwd, options = {}) {
1254
3311
  mountRoot: html.mountRoot ?? appName
1255
3312
  }
1256
3313
  }),
1257
- new rspack.CopyRspackPlugin({
3314
+ new rspack2.CopyRspackPlugin({
1258
3315
  patterns: [
1259
3316
  {
1260
3317
  from: resolveApp("public/images"),
@@ -1517,7 +3574,7 @@ function createDevConfig(config, cwd) {
1517
3574
  }
1518
3575
 
1519
3576
  // src/rspack/prod.ts
1520
- import { rspack as rspack2 } from "@rspack/core";
3577
+ import { rspack as rspack3 } from "@rspack/core";
1521
3578
  import { merge as merge2 } from "webpack-merge";
1522
3579
  function createProdConfig(config, cwd) {
1523
3580
  const baseConfig = createBaseConfig(config, cwd, { isDev: false });
@@ -1530,7 +3587,7 @@ function createProdConfig(config, cwd) {
1530
3587
  test: /\.less$/,
1531
3588
  include: [/[\\/]node_modules[\\/].*antd/, /[\\/]node_modules[\\/]@4399ywkf[\\/]design/],
1532
3589
  use: [
1533
- { loader: rspack2.CssExtractRspackPlugin.loader },
3590
+ { loader: rspack3.CssExtractRspackPlugin.loader },
1534
3591
  {
1535
3592
  loader: "css-loader",
1536
3593
  options: { sourceMap: true }
@@ -1558,7 +3615,7 @@ function createProdConfig(config, cwd) {
1558
3615
  {
1559
3616
  test: /\.module\.less$/,
1560
3617
  use: [
1561
- { loader: rspack2.CssExtractRspackPlugin.loader },
3618
+ { loader: rspack3.CssExtractRspackPlugin.loader },
1562
3619
  {
1563
3620
  loader: "css-loader",
1564
3621
  options: {
@@ -1583,7 +3640,7 @@ function createProdConfig(config, cwd) {
1583
3640
  },
1584
3641
  {
1585
3642
  use: [
1586
- { loader: rspack2.CssExtractRspackPlugin.loader },
3643
+ { loader: rspack3.CssExtractRspackPlugin.loader },
1587
3644
  {
1588
3645
  loader: "css-loader",
1589
3646
  options: { sourceMap: true }
@@ -1613,7 +3670,7 @@ function createProdConfig(config, cwd) {
1613
3670
  {
1614
3671
  test: /\.module\.s[ac]ss$/,
1615
3672
  use: [
1616
- { loader: rspack2.CssExtractRspackPlugin.loader },
3673
+ { loader: rspack3.CssExtractRspackPlugin.loader },
1617
3674
  {
1618
3675
  loader: "css-loader",
1619
3676
  options: {
@@ -1638,7 +3695,7 @@ function createProdConfig(config, cwd) {
1638
3695
  },
1639
3696
  {
1640
3697
  use: [
1641
- { loader: rspack2.CssExtractRspackPlugin.loader },
3698
+ { loader: rspack3.CssExtractRspackPlugin.loader },
1642
3699
  {
1643
3700
  loader: "css-loader",
1644
3701
  options: { sourceMap: true }
@@ -1664,7 +3721,7 @@ function createProdConfig(config, cwd) {
1664
3721
  test: /\.css$/,
1665
3722
  include: [resolveApp("src/index.css")],
1666
3723
  use: [
1667
- { loader: rspack2.CssExtractRspackPlugin.loader },
3724
+ { loader: rspack3.CssExtractRspackPlugin.loader },
1668
3725
  "css-loader",
1669
3726
  {
1670
3727
  loader: "postcss-loader",
@@ -1683,7 +3740,7 @@ function createProdConfig(config, cwd) {
1683
3740
  include: [resolveApp("src")],
1684
3741
  exclude: style.tailwindcss ? [resolveApp("src/index.css")] : [],
1685
3742
  use: [
1686
- { loader: rspack2.CssExtractRspackPlugin.loader },
3743
+ { loader: rspack3.CssExtractRspackPlugin.loader },
1687
3744
  {
1688
3745
  loader: "css-loader",
1689
3746
  options: { sourceMap: true }
@@ -1714,7 +3771,7 @@ function createProdConfig(config, cwd) {
1714
3771
  },
1715
3772
  module: styleRules,
1716
3773
  plugins: [
1717
- new rspack2.CssExtractRspackPlugin({
3774
+ new rspack3.CssExtractRspackPlugin({
1718
3775
  filename: "[name].[contenthash:8].css",
1719
3776
  chunkFilename: "[name].[contenthash:8].chunk.css"
1720
3777
  })
@@ -1722,7 +3779,7 @@ function createProdConfig(config, cwd) {
1722
3779
  optimization: {
1723
3780
  minimize: true,
1724
3781
  minimizer: [
1725
- new rspack2.SwcJsMinimizerRspackPlugin({
3782
+ new rspack3.SwcJsMinimizerRspackPlugin({
1726
3783
  exclude: [resolveApp("node_modules")],
1727
3784
  minimizerOptions: {
1728
3785
  compress: {
@@ -1732,7 +3789,7 @@ function createProdConfig(config, cwd) {
1732
3789
  }
1733
3790
  }
1734
3791
  }),
1735
- new rspack2.LightningCssMinimizerRspackPlugin()
3792
+ new rspack3.LightningCssMinimizerRspackPlugin()
1736
3793
  ]
1737
3794
  }
1738
3795
  };