@4399ywkf/core 5.0.27 → 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.
- package/dist/cli/index.js +2225 -108
- package/dist/cli/index.js.map +1 -1
- package/dist/config/index.d.ts +24 -4
- package/dist/config/index.js +2124 -6
- package/dist/config/index.js.map +1 -1
- package/dist/index.d.ts +5 -475
- package/dist/index.js +4291 -3860
- package/dist/index.js.map +1 -1
- package/dist/plugin/index.d.ts +3 -3
- package/dist/rspack/index.d.ts +2 -2
- package/dist/rspack/index.js +2129 -72
- package/dist/rspack/index.js.map +1 -1
- package/dist/runtime/index.d.ts +2 -2
- package/dist/schema-CMyZyL6E.d.ts +1153 -0
- package/dist/{types-DbUq-VY8.d.ts → types-BYqzyGAY.d.ts} +1 -1
- package/package.json +1 -1
- package/dist/schema-runIYOB7.d.ts +0 -407
package/dist/cli/index.js
CHANGED
|
@@ -2,20 +2,2136 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli/index.ts
|
|
4
4
|
import { createRequire as createRequire4 } from "module";
|
|
5
|
-
import { dirname as dirname3, join as
|
|
5
|
+
import { dirname as dirname3, join as join14 } from "path";
|
|
6
6
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
7
7
|
import chalk4 from "chalk";
|
|
8
8
|
import { Command } from "commander";
|
|
9
9
|
|
|
10
10
|
// src/cli/build.ts
|
|
11
|
-
import { rspack as
|
|
11
|
+
import { rspack as rspack4 } from "@rspack/core";
|
|
12
12
|
import chalk2 from "chalk";
|
|
13
13
|
|
|
14
|
-
// src/config/loader.ts
|
|
15
|
-
import { existsSync } from "fs";
|
|
16
|
-
import { extname, resolve } from "path";
|
|
17
|
-
import { pathToFileURL } from "url";
|
|
18
|
-
import deepmerge from "deepmerge";
|
|
14
|
+
// src/config/loader.ts
|
|
15
|
+
import { existsSync as existsSync7 } from "fs";
|
|
16
|
+
import { extname, resolve as resolve2 } from "path";
|
|
17
|
+
import { pathToFileURL } from "url";
|
|
18
|
+
import deepmerge from "deepmerge";
|
|
19
|
+
|
|
20
|
+
// src/plugin/define.ts
|
|
21
|
+
function createPlugin(factory) {
|
|
22
|
+
return factory;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/plugin/builtin/analytics.ts
|
|
26
|
+
var analyticsPlugin = createPlugin((options = {}) => ({
|
|
27
|
+
name: "@4399ywkf/plugin-analytics",
|
|
28
|
+
version: "1.0.0",
|
|
29
|
+
description: "\u6784\u5EFA\u5206\u6790\u63D2\u4EF6",
|
|
30
|
+
setup(context) {
|
|
31
|
+
const {
|
|
32
|
+
buildAnalysis = true,
|
|
33
|
+
timing = true,
|
|
34
|
+
bundleSize = true,
|
|
35
|
+
sizeWarningThreshold = 500
|
|
36
|
+
} = options;
|
|
37
|
+
const { logger, isProd } = context;
|
|
38
|
+
let buildStartTime;
|
|
39
|
+
const hooks = {
|
|
40
|
+
beforeBuild() {
|
|
41
|
+
if (timing) {
|
|
42
|
+
buildStartTime = Date.now();
|
|
43
|
+
logger.info("\u5F00\u59CB\u6784\u5EFA...");
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
afterBuild(_context, stats) {
|
|
47
|
+
if (timing) {
|
|
48
|
+
const duration = Date.now() - buildStartTime;
|
|
49
|
+
logger.info(`\u6784\u5EFA\u5B8C\u6210\uFF0C\u8017\u65F6: ${(duration / 1e3).toFixed(2)}s`);
|
|
50
|
+
}
|
|
51
|
+
if (!stats.success && stats.errors) {
|
|
52
|
+
logger.error(`\u6784\u5EFA\u5931\u8D25\uFF0C\u9519\u8BEF\u6570: ${stats.errors.length}`);
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
modifyRspackConfig(config) {
|
|
56
|
+
if (buildAnalysis && isProd) {
|
|
57
|
+
logger.info("\u6784\u5EFA\u5206\u6790\u5DF2\u542F\u7528");
|
|
58
|
+
}
|
|
59
|
+
return config;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
return hooks;
|
|
63
|
+
}
|
|
64
|
+
}));
|
|
65
|
+
|
|
66
|
+
// src/plugin/builtin/biome.ts
|
|
67
|
+
import { existsSync, writeFileSync } from "fs";
|
|
68
|
+
import { join } from "path";
|
|
69
|
+
var biomePlugin = createPlugin((options = {}) => ({
|
|
70
|
+
name: "@4399ywkf/plugin-biome",
|
|
71
|
+
version: "1.0.0",
|
|
72
|
+
description: "Biome \u4EE3\u7801\u89C4\u8303\u96C6\u6210",
|
|
73
|
+
setup(context) {
|
|
74
|
+
const {
|
|
75
|
+
scaffold = true,
|
|
76
|
+
ignore = [],
|
|
77
|
+
rules: customRules,
|
|
78
|
+
formatter: customFormatter,
|
|
79
|
+
javascript: customJavascript
|
|
80
|
+
} = options;
|
|
81
|
+
const { cwd, logger } = context;
|
|
82
|
+
if (scaffold) {
|
|
83
|
+
const biomeConfigPath = join(cwd, "biome.json");
|
|
84
|
+
if (!existsSync(biomeConfigPath)) {
|
|
85
|
+
const config = buildBiomeConfig({ ignore, customRules, customFormatter, customJavascript });
|
|
86
|
+
writeFileSync(biomeConfigPath, `${JSON.stringify(config, null, 2)}
|
|
87
|
+
`, "utf-8");
|
|
88
|
+
logger.info("\u5DF2\u751F\u6210 biome.json\uFF08\u7EE7\u627F @4399ywkf/core/biome\uFF09");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const hooks = {
|
|
92
|
+
modifyRspackConfig(rspackConfig) {
|
|
93
|
+
logger.info("Biome \u4EE3\u7801\u89C4\u8303\u5DF2\u542F\u7528");
|
|
94
|
+
return rspackConfig;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
return hooks;
|
|
98
|
+
}
|
|
99
|
+
}));
|
|
100
|
+
function buildBiomeConfig(params) {
|
|
101
|
+
const { ignore, customRules, customFormatter, customJavascript } = params;
|
|
102
|
+
const config = {
|
|
103
|
+
$schema: "https://biomejs.dev/schemas/2.3.2/schema.json",
|
|
104
|
+
extends: ["@4399ywkf/core/biome"]
|
|
105
|
+
};
|
|
106
|
+
if (customFormatter) {
|
|
107
|
+
config.formatter = customFormatter;
|
|
108
|
+
}
|
|
109
|
+
if (customJavascript) {
|
|
110
|
+
config.javascript = customJavascript;
|
|
111
|
+
}
|
|
112
|
+
if (customRules && Object.keys(customRules).length > 0) {
|
|
113
|
+
config.linter = { rules: customRules };
|
|
114
|
+
}
|
|
115
|
+
if (ignore.length > 0) {
|
|
116
|
+
config.files = {
|
|
117
|
+
includes: ["**", ...ignore.map((p) => `!${p}`)]
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return config;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/plugin/builtin/browser-check.ts
|
|
124
|
+
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
125
|
+
import { join as join2 } from "path";
|
|
126
|
+
var DEFAULT_FEATURES = ["Object.hasOwn"];
|
|
127
|
+
var DEFAULT_MIN_VERSIONS = {
|
|
128
|
+
chrome: 93,
|
|
129
|
+
edge: 93,
|
|
130
|
+
firefox: 92,
|
|
131
|
+
safari: 15.4
|
|
132
|
+
};
|
|
133
|
+
var browserCheckPlugin = createPlugin((options = {}) => ({
|
|
134
|
+
name: "@4399ywkf/plugin-browser-check",
|
|
135
|
+
version: "1.0.0",
|
|
136
|
+
description: "\u6D4F\u89C8\u5668\u6700\u4F4E\u7248\u672C\u68C0\u67E5\u63D2\u4EF6",
|
|
137
|
+
setup(context) {
|
|
138
|
+
const {
|
|
139
|
+
features = DEFAULT_FEATURES,
|
|
140
|
+
minVersions: explicitVersions,
|
|
141
|
+
fromBrowserslist = false,
|
|
142
|
+
title = "\u6D4F\u89C8\u5668\u7248\u672C\u8FC7\u4F4E",
|
|
143
|
+
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",
|
|
144
|
+
showDownloadLinks = true,
|
|
145
|
+
renderHtml,
|
|
146
|
+
renderScript
|
|
147
|
+
} = options;
|
|
148
|
+
const { cwd, config, logger } = context;
|
|
149
|
+
const rootId = config.html.mountRoot || config.appName || "root";
|
|
150
|
+
let baseVersions = { ...DEFAULT_MIN_VERSIONS };
|
|
151
|
+
if (fromBrowserslist !== false) {
|
|
152
|
+
const parsed = parseBrowserslistConfig(cwd, fromBrowserslist, logger);
|
|
153
|
+
if (parsed) {
|
|
154
|
+
baseVersions = { ...baseVersions, ...parsed };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const minVersions = explicitVersions ? { ...baseVersions, ...explicitVersions } : baseVersions;
|
|
158
|
+
logger.info(
|
|
159
|
+
`\u6D4F\u89C8\u5668\u517C\u5BB9\u6027\u68C0\u67E5\u5DF2\u542F\u7528 (\u7279\u6027\u68C0\u6D4B: ${features.join(", ")})`
|
|
160
|
+
);
|
|
161
|
+
logger.info(
|
|
162
|
+
`\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)" : ""}`
|
|
163
|
+
);
|
|
164
|
+
const hooks = {
|
|
165
|
+
modifyEntryCode(code) {
|
|
166
|
+
const featureCheckLines = buildFeatureChecks(features);
|
|
167
|
+
const versionCheckLines = buildVersionChecks(minVersions);
|
|
168
|
+
const warningHtml = renderHtml ? renderHtml({ title, message, showDownloadLinks, minVersions }) : buildWarningHtml({ title, message, showDownloadLinks, minVersions });
|
|
169
|
+
const checkBlock = [
|
|
170
|
+
``,
|
|
171
|
+
`// ======= \u6D4F\u89C8\u5668\u517C\u5BB9\u6027\u68C0\u67E5 (@4399ywkf/plugin-browser-check) =======`,
|
|
172
|
+
`const __ywkfBrowserOk = (() => {`,
|
|
173
|
+
` try {`,
|
|
174
|
+
...featureCheckLines.map((l) => ` ${l}`),
|
|
175
|
+
...versionCheckLines.map((l) => ` ${l}`),
|
|
176
|
+
` return true;`,
|
|
177
|
+
` } catch {`,
|
|
178
|
+
` return false;`,
|
|
179
|
+
` }`,
|
|
180
|
+
`})();`,
|
|
181
|
+
``,
|
|
182
|
+
`if (!__ywkfBrowserOk) {`,
|
|
183
|
+
` const __root = document.getElementById(${JSON.stringify(rootId)}) || document.body;`,
|
|
184
|
+
` __root.innerHTML = ${JSON.stringify(warningHtml)};`,
|
|
185
|
+
renderScript ? ` ${renderScript}` : ``,
|
|
186
|
+
`}`,
|
|
187
|
+
`// ======= End \u6D4F\u89C8\u5668\u517C\u5BB9\u6027\u68C0\u67E5 =======`,
|
|
188
|
+
``
|
|
189
|
+
].join("\n");
|
|
190
|
+
const lines = code.split("\n");
|
|
191
|
+
let lastImportIndex = -1;
|
|
192
|
+
for (let i = 0; i < lines.length; i++) {
|
|
193
|
+
if (lines[i].trimStart().startsWith("import ")) {
|
|
194
|
+
lastImportIndex = i;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const insertIndex = lastImportIndex >= 0 ? lastImportIndex + 1 : 0;
|
|
198
|
+
lines.splice(insertIndex, 0, checkBlock);
|
|
199
|
+
let result = lines.join("\n");
|
|
200
|
+
result = result.replace(
|
|
201
|
+
/(\s*)(await\s+)?runApp\(\);?/g,
|
|
202
|
+
(_, indent, awaitKw) => `${indent}if (__ywkfBrowserOk) { ${awaitKw || ""}runApp(); }`
|
|
203
|
+
);
|
|
204
|
+
return result;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
return hooks;
|
|
208
|
+
}
|
|
209
|
+
}));
|
|
210
|
+
function buildFeatureChecks(features) {
|
|
211
|
+
if (features.length === 0) return [];
|
|
212
|
+
const lines = ["// \u7279\u6027\u68C0\u6D4B"];
|
|
213
|
+
for (const feature of features) {
|
|
214
|
+
const parts = feature.split(".");
|
|
215
|
+
if (parts.length === 1) {
|
|
216
|
+
lines.push(`if (typeof ${feature} === "undefined") return false;`);
|
|
217
|
+
} else {
|
|
218
|
+
lines.push(`if (typeof ${feature} !== "function") return false;`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return lines;
|
|
222
|
+
}
|
|
223
|
+
function buildVersionChecks(minVersions) {
|
|
224
|
+
const entries = Object.entries(minVersions).filter(
|
|
225
|
+
([, v]) => v != null
|
|
226
|
+
);
|
|
227
|
+
if (entries.length === 0) return [];
|
|
228
|
+
const lines = ["// UA \u7248\u672C\u68C0\u6D4B", "const ua = navigator.userAgent;"];
|
|
229
|
+
for (const [browser, minVer] of entries) {
|
|
230
|
+
switch (browser) {
|
|
231
|
+
case "chrome":
|
|
232
|
+
lines.push(
|
|
233
|
+
`const chromeMatch = /Chrome\\/(\\d+)/.exec(ua);`,
|
|
234
|
+
`if (chromeMatch && !/Edg\\//.test(ua) && Number(chromeMatch[1]) < ${minVer}) return false;`
|
|
235
|
+
);
|
|
236
|
+
break;
|
|
237
|
+
case "edge":
|
|
238
|
+
lines.push(
|
|
239
|
+
`const edgeMatch = /Edg\\/(\\d+)/.exec(ua);`,
|
|
240
|
+
`if (edgeMatch && Number(edgeMatch[1]) < ${minVer}) return false;`
|
|
241
|
+
);
|
|
242
|
+
break;
|
|
243
|
+
case "firefox":
|
|
244
|
+
lines.push(
|
|
245
|
+
`const firefoxMatch = /Firefox\\/(\\d+)/.exec(ua);`,
|
|
246
|
+
`if (firefoxMatch && Number(firefoxMatch[1]) < ${minVer}) return false;`
|
|
247
|
+
);
|
|
248
|
+
break;
|
|
249
|
+
case "safari":
|
|
250
|
+
lines.push(
|
|
251
|
+
`const safariMatch = /Version\\/(\\d+\\.?\\d*)/.exec(ua);`,
|
|
252
|
+
`if (safariMatch && /Safari/.test(ua) && !/Chrome/.test(ua) && Number(safariMatch[1]) < ${minVer}) return false;`
|
|
253
|
+
);
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return lines;
|
|
258
|
+
}
|
|
259
|
+
function buildWarningHtml(opts) {
|
|
260
|
+
const { title, message, showDownloadLinks, minVersions } = opts;
|
|
261
|
+
const versionHints = [
|
|
262
|
+
minVersions.chrome != null && `Chrome ${minVersions.chrome}+`,
|
|
263
|
+
minVersions.edge != null && `Edge ${minVersions.edge}+`,
|
|
264
|
+
minVersions.firefox != null && `Firefox ${minVersions.firefox}+`,
|
|
265
|
+
minVersions.safari != null && `Safari ${minVersions.safari}+`
|
|
266
|
+
].filter(Boolean).join("\u3001");
|
|
267
|
+
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>` : "";
|
|
268
|
+
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>` : "";
|
|
269
|
+
return [
|
|
270
|
+
`<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">`,
|
|
271
|
+
`<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">`,
|
|
272
|
+
// 警告图标
|
|
273
|
+
`<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)">`,
|
|
274
|
+
`<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>`,
|
|
275
|
+
`</div>`,
|
|
276
|
+
// 标题 & 描述
|
|
277
|
+
`<h1 style="margin:0 0 16px;font-size:26px;font-weight:700;color:#1a1a2e;letter-spacing:-0.5px">${title}</h1>`,
|
|
278
|
+
`<p style="margin:0;font-size:15px;color:#666;line-height:1.8">${message}</p>`,
|
|
279
|
+
// 下载链接
|
|
280
|
+
downloadLinksHtml,
|
|
281
|
+
// 版本提示
|
|
282
|
+
versionHintHtml,
|
|
283
|
+
`</div></div>`
|
|
284
|
+
].join("");
|
|
285
|
+
}
|
|
286
|
+
function browserLink(href, label, colorStart, colorEnd, rgbBase) {
|
|
287
|
+
const bg = `background:linear-gradient(135deg,${colorStart},${colorEnd})`;
|
|
288
|
+
const shadow = `box-shadow:0 4px 12px rgba(${rgbBase},0.3)`;
|
|
289
|
+
const hoverShadow = `0 6px 20px rgba(${rgbBase},0.4)`;
|
|
290
|
+
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`;
|
|
291
|
+
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>`;
|
|
292
|
+
}
|
|
293
|
+
var BROWSERSLIST_NAME_MAP = {
|
|
294
|
+
chrome: "chrome",
|
|
295
|
+
chromium: "chrome",
|
|
296
|
+
edge: "edge",
|
|
297
|
+
firefox: "firefox",
|
|
298
|
+
ff: "firefox",
|
|
299
|
+
safari: "safari",
|
|
300
|
+
ios_saf: "safari"
|
|
301
|
+
};
|
|
302
|
+
function parseBrowserslistConfig(cwd, source, logger) {
|
|
303
|
+
let queries = [];
|
|
304
|
+
if (typeof source === "string") {
|
|
305
|
+
const filePath = join2(cwd, source);
|
|
306
|
+
if (!existsSync2(filePath)) {
|
|
307
|
+
logger.warn(`browserslist \u6587\u4EF6\u672A\u627E\u5230: ${filePath}`);
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
queries = readBrowserslistFile(filePath);
|
|
311
|
+
logger.info(`\u4ECE ${source} \u8BFB\u53D6\u5230 ${queries.length} \u6761 browserslist \u67E5\u8BE2`);
|
|
312
|
+
} else {
|
|
313
|
+
const rcPath = join2(cwd, ".browserslistrc");
|
|
314
|
+
if (existsSync2(rcPath)) {
|
|
315
|
+
queries = readBrowserslistFile(rcPath);
|
|
316
|
+
logger.info(`\u4ECE .browserslistrc \u8BFB\u53D6\u5230 ${queries.length} \u6761 browserslist \u67E5\u8BE2`);
|
|
317
|
+
} else {
|
|
318
|
+
const pkgPath = join2(cwd, "package.json");
|
|
319
|
+
if (existsSync2(pkgPath)) {
|
|
320
|
+
try {
|
|
321
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
322
|
+
if (Array.isArray(pkg.browserslist)) {
|
|
323
|
+
queries = pkg.browserslist;
|
|
324
|
+
logger.info(`\u4ECE package.json#browserslist \u8BFB\u53D6\u5230 ${queries.length} \u6761\u67E5\u8BE2`);
|
|
325
|
+
} else if (typeof pkg.browserslist === "string") {
|
|
326
|
+
queries = [pkg.browserslist];
|
|
327
|
+
logger.info("\u4ECE package.json#browserslist \u8BFB\u53D6\u5230 1 \u6761\u67E5\u8BE2");
|
|
328
|
+
}
|
|
329
|
+
} catch {
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (queries.length === 0) {
|
|
334
|
+
logger.warn("\u672A\u627E\u5230 .browserslistrc \u6216 package.json#browserslist\uFF0C\u5C06\u4F7F\u7528\u5185\u7F6E\u9ED8\u8BA4\u503C");
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return extractMinVersions(queries);
|
|
339
|
+
}
|
|
340
|
+
function readBrowserslistFile(filePath) {
|
|
341
|
+
const content = readFileSync(filePath, "utf-8");
|
|
342
|
+
return content.split("\n").map((line) => line.replace(/#.*$/, "").trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
343
|
+
}
|
|
344
|
+
function extractMinVersions(queries) {
|
|
345
|
+
const result = {};
|
|
346
|
+
const expanded = queries.flatMap((q) => q.split(",").map((s) => s.trim()));
|
|
347
|
+
const versionPattern = /^(\w+)\s*(?:(>=?)\s*)?(\d+(?:\.\d+)?)\s*$/i;
|
|
348
|
+
for (const query of expanded) {
|
|
349
|
+
if (/^(not|dead|last|defaults|>?\s*\d+%|unreleased|current)/i.test(query)) {
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
const match = versionPattern.exec(query);
|
|
353
|
+
if (!match) continue;
|
|
354
|
+
const [, browserName, operator, versionStr] = match;
|
|
355
|
+
const key = BROWSERSLIST_NAME_MAP[browserName.toLowerCase()];
|
|
356
|
+
if (!key) continue;
|
|
357
|
+
let version2 = Number.parseFloat(versionStr);
|
|
358
|
+
if (operator === ">") {
|
|
359
|
+
version2 = Math.ceil(version2) + 1;
|
|
360
|
+
}
|
|
361
|
+
if (result[key] == null || version2 < result[key]) {
|
|
362
|
+
result[key] = version2;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return result;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// src/plugin/builtin/garfish.ts
|
|
369
|
+
var garfishPlugin = createPlugin((options = {}) => ({
|
|
370
|
+
name: "@4399ywkf/plugin-garfish",
|
|
371
|
+
version: "1.0.0",
|
|
372
|
+
description: "Garfish \u5FAE\u524D\u7AEF\u63D2\u4EF6",
|
|
373
|
+
setup(context) {
|
|
374
|
+
const { appName, master = false, apps = [], sandbox = {} } = options;
|
|
375
|
+
const { config, logger, isDev } = context;
|
|
376
|
+
const finalAppName = appName || config.appName;
|
|
377
|
+
const hooks = {
|
|
378
|
+
// ===== 构建阶段钩子 =====
|
|
379
|
+
modifyRspackConfig(rspackConfig) {
|
|
380
|
+
if (!master) {
|
|
381
|
+
rspackConfig.output = {
|
|
382
|
+
...rspackConfig.output,
|
|
383
|
+
library: finalAppName,
|
|
384
|
+
libraryTarget: "umd",
|
|
385
|
+
globalObject: "window",
|
|
386
|
+
chunkLoadingGlobal: `garfish_${finalAppName}`
|
|
387
|
+
};
|
|
388
|
+
rspackConfig.devServer = {
|
|
389
|
+
...rspackConfig.devServer,
|
|
390
|
+
headers: {
|
|
391
|
+
...rspackConfig.devServer?.headers || {},
|
|
392
|
+
"Access-Control-Allow-Origin": "*",
|
|
393
|
+
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
|
394
|
+
"Access-Control-Allow-Headers": "*"
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
logger.info(`\u914D\u7F6E UMD \u8F93\u51FA: ${finalAppName}`);
|
|
398
|
+
}
|
|
399
|
+
return rspackConfig;
|
|
400
|
+
},
|
|
401
|
+
beforeDevServer() {
|
|
402
|
+
if (master) {
|
|
403
|
+
logger.info("Garfish \u4E3B\u5E94\u7528\u6A21\u5F0F");
|
|
404
|
+
logger.info(`\u5B50\u5E94\u7528\u5217\u8868: ${apps.map((a) => a.name).join(", ")}`);
|
|
405
|
+
} else {
|
|
406
|
+
logger.info(`Garfish \u5B50\u5E94\u7528\u6A21\u5F0F: ${finalAppName}`);
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
// ===== 代码生成阶段钩子 =====
|
|
410
|
+
injectEntry(_ctx) {
|
|
411
|
+
if (master && apps.length > 0) {
|
|
412
|
+
return {
|
|
413
|
+
imports: [`import { initGarfishMaster } from "./bootstrap";`],
|
|
414
|
+
topLevel: [`// Garfish \u4E3B\u5E94\u7528\uFF1A\u521D\u59CB\u5316\u5FAE\u524D\u7AEF\u5E76\u6CE8\u518C\u5B50\u5E94\u7528`, `await initGarfishMaster();`]
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
if (!master) {
|
|
418
|
+
return {
|
|
419
|
+
imports: [`import { garfishProvider, isMicroAppEnv } from "./bootstrap";`],
|
|
420
|
+
exports: [
|
|
421
|
+
`// Garfish \u5B50\u5E94\u7528\u751F\u547D\u5468\u671F\u5BFC\u51FA\uFF08provider \u51FD\u6570\u683C\u5F0F\uFF09`,
|
|
422
|
+
`export const provider = garfishProvider;`
|
|
423
|
+
],
|
|
424
|
+
topLevel: [
|
|
425
|
+
`// Garfish \u5FAE\u524D\u7AEF\uFF1A\u72EC\u7ACB\u8FD0\u884C\u5224\u65AD`,
|
|
426
|
+
`const shouldRunIndependently = !isMicroAppEnv();`
|
|
427
|
+
]
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
return {};
|
|
431
|
+
},
|
|
432
|
+
injectBootstrap(_ctx) {
|
|
433
|
+
const imports = [];
|
|
434
|
+
const exports = [];
|
|
435
|
+
const topLevel = [];
|
|
436
|
+
if (master) {
|
|
437
|
+
if (apps.length > 0) {
|
|
438
|
+
imports.push(`import Garfish from "garfish";`);
|
|
439
|
+
topLevel.push(generateMasterInitCode(apps, config.router.basename));
|
|
440
|
+
}
|
|
441
|
+
} else {
|
|
442
|
+
imports.push(
|
|
443
|
+
`import { createGarfishProvider, isMicroAppEnv } from "@4399ywkf/core/runtime";`
|
|
444
|
+
);
|
|
445
|
+
exports.push(
|
|
446
|
+
`/**`,
|
|
447
|
+
` * Garfish \u5B50\u5E94\u7528 provider\uFF08render/destroy \u683C\u5F0F\uFF09`,
|
|
448
|
+
` */`,
|
|
449
|
+
`export const garfishProvider = createGarfishProvider(createAppConfig);`,
|
|
450
|
+
``,
|
|
451
|
+
`/**`,
|
|
452
|
+
` * \u5224\u65AD\u662F\u5426\u5728\u5FAE\u524D\u7AEF\u73AF\u5883`,
|
|
453
|
+
` */`,
|
|
454
|
+
`export { isMicroAppEnv };`
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
return { imports, exports, topLevel };
|
|
458
|
+
},
|
|
459
|
+
modifyBootstrapCode(code, _ctx) {
|
|
460
|
+
if (!master) {
|
|
461
|
+
return code.replace(/antd:\s*\{\s*enabled:\s*true/, "antd: { enabled: false");
|
|
462
|
+
}
|
|
463
|
+
return code;
|
|
464
|
+
},
|
|
465
|
+
// ===== 运行时阶段钩子 =====
|
|
466
|
+
modifyAppConfig(appConfig) {
|
|
467
|
+
return {
|
|
468
|
+
...appConfig,
|
|
469
|
+
// 微前端模式下禁用 antd 样式注入(由主应用管理)
|
|
470
|
+
antd: {
|
|
471
|
+
...appConfig.antd,
|
|
472
|
+
enabled: master
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
return hooks;
|
|
478
|
+
}
|
|
479
|
+
}));
|
|
480
|
+
function generateMasterInitCode(apps, basename) {
|
|
481
|
+
const appConfigs = apps.map(
|
|
482
|
+
(app) => `{
|
|
483
|
+
name: "${app.name}",
|
|
484
|
+
entry: "${app.entry}",
|
|
485
|
+
activeWhen: "${app.activeRule}",
|
|
486
|
+
${app.basename ? `basename: "${app.basename}",` : ""}
|
|
487
|
+
}`
|
|
488
|
+
).join(",\n ");
|
|
489
|
+
return `
|
|
490
|
+
/**
|
|
491
|
+
* Garfish \u4E3B\u5E94\u7528\u521D\u59CB\u5316
|
|
492
|
+
* \u5728 runApp \u4E4B\u524D\u8C03\u7528
|
|
493
|
+
*/
|
|
494
|
+
export async function initGarfishMaster(): Promise<void> {
|
|
495
|
+
await Garfish.run({
|
|
496
|
+
basename: "${basename || "/"}",
|
|
497
|
+
domGetter: "#subapp-container",
|
|
498
|
+
apps: [
|
|
499
|
+
${appConfigs}
|
|
500
|
+
],
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
`;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// src/plugin/builtin/i18n.ts
|
|
507
|
+
import { existsSync as existsSync3, mkdirSync, writeFileSync as writeFileSync2 } from "fs";
|
|
508
|
+
import { join as join3 } from "path";
|
|
509
|
+
var i18nPlugin = createPlugin((options = {}) => ({
|
|
510
|
+
name: "@4399ywkf/plugin-i18n",
|
|
511
|
+
version: "1.0.0",
|
|
512
|
+
description: "\u56FD\u9645\u5316 (i18next) \u63D2\u4EF6 \u2014 TS-first \u5DE5\u4F5C\u6D41",
|
|
513
|
+
setup(context) {
|
|
514
|
+
const {
|
|
515
|
+
defaultLocale = "zh-CN",
|
|
516
|
+
locales = ["zh-CN", "en-US"],
|
|
517
|
+
localesDir = "locales",
|
|
518
|
+
sourceDir = "src/locales/default",
|
|
519
|
+
defaultNS = ["common"],
|
|
520
|
+
autoScaffold = true
|
|
521
|
+
} = options;
|
|
522
|
+
const { cwd, logger, isDev } = context;
|
|
523
|
+
if (autoScaffold) {
|
|
524
|
+
scaffoldSourceFiles(cwd, sourceDir, defaultNS, logger);
|
|
525
|
+
scaffoldResourcesFile(cwd, sourceDir, defaultNS, locales, defaultLocale, logger);
|
|
526
|
+
scaffoldConvertScript(cwd, sourceDir, localesDir, defaultLocale, logger);
|
|
527
|
+
scaffoldI18nRc(cwd, localesDir, defaultLocale, locales, logger);
|
|
528
|
+
scaffoldLocaleJsonDirs(cwd, localesDir, locales, defaultLocale, defaultNS, logger);
|
|
529
|
+
}
|
|
530
|
+
const hooks = {
|
|
531
|
+
modifyRspackConfig(rspackConfig) {
|
|
532
|
+
const aliases = rspackConfig.resolve?.alias || {};
|
|
533
|
+
if (!aliases["@locales"]) {
|
|
534
|
+
aliases["@locales"] = join3(cwd, localesDir);
|
|
535
|
+
}
|
|
536
|
+
rspackConfig.resolve = { ...rspackConfig.resolve, alias: aliases };
|
|
537
|
+
return rspackConfig;
|
|
538
|
+
},
|
|
539
|
+
generateFiles(_ctx) {
|
|
540
|
+
return [
|
|
541
|
+
{
|
|
542
|
+
path: "i18n.ts",
|
|
543
|
+
content: generateI18nCode({
|
|
544
|
+
defaultLocale,
|
|
545
|
+
locales,
|
|
546
|
+
localesDir,
|
|
547
|
+
sourceDir,
|
|
548
|
+
defaultNS,
|
|
549
|
+
isDev
|
|
550
|
+
})
|
|
551
|
+
}
|
|
552
|
+
];
|
|
553
|
+
},
|
|
554
|
+
injectBootstrap(_ctx) {
|
|
555
|
+
return {
|
|
556
|
+
imports: [`import { initI18n } from "./i18n";`],
|
|
557
|
+
topLevel: [`await initI18n();`]
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
return hooks;
|
|
562
|
+
}
|
|
563
|
+
}));
|
|
564
|
+
function generateI18nCode(opts) {
|
|
565
|
+
const { defaultLocale, locales, defaultNS, sourceDir } = opts;
|
|
566
|
+
const defaultDirAlias = sourceDir.replace(/^src\//, "");
|
|
567
|
+
const parentDirAlias = defaultDirAlias.replace(/\/[^/]+$/, "");
|
|
568
|
+
return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/plugin-i18n \u81EA\u52A8\u751F\u6210
|
|
569
|
+
import i18n from "i18next";
|
|
570
|
+
import LanguageDetector from "i18next-browser-languagedetector";
|
|
571
|
+
import resourcesToBackend from "i18next-resources-to-backend";
|
|
572
|
+
import { initReactI18next } from "react-i18next";
|
|
573
|
+
import { normalizeLocale, SUPPORTED_LOCALES, DEFAULT_LOCALE } from "@/${parentDirAlias}/resources";
|
|
574
|
+
|
|
575
|
+
export { DEFAULT_LOCALE, SUPPORTED_LOCALES, normalizeLocale };
|
|
576
|
+
export type { SupportedLocale, NS } from "@/${parentDirAlias}/resources";
|
|
577
|
+
|
|
578
|
+
const isDev = process.env.NODE_ENV === "development";
|
|
579
|
+
|
|
580
|
+
const instance = i18n
|
|
581
|
+
.use(initReactI18next)
|
|
582
|
+
.use(LanguageDetector)
|
|
583
|
+
.use(
|
|
584
|
+
resourcesToBackend(async (lng: string, ns: string) => {
|
|
585
|
+
const normalizedLng = normalizeLocale(lng);
|
|
586
|
+
|
|
587
|
+
// \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
|
|
588
|
+
if (isDev && normalizedLng === "${defaultLocale}") {
|
|
589
|
+
return import("@/${defaultDirAlias}/" + ns);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// \u5176\u4ED6\u8BED\u8A00 / \u751F\u4EA7\u6A21\u5F0F\uFF1A\u61D2\u52A0\u8F7D JSON
|
|
593
|
+
return import(
|
|
594
|
+
/* webpackInclude: /\\.json$/ */
|
|
595
|
+
/* webpackChunkName: "locales-[request]" */
|
|
596
|
+
\`@locales/\${normalizedLng}/\${ns}.json\`
|
|
597
|
+
);
|
|
598
|
+
})
|
|
599
|
+
);
|
|
600
|
+
|
|
601
|
+
let initialized = false;
|
|
602
|
+
|
|
603
|
+
export async function initI18n(lang?: string): Promise<typeof i18n> {
|
|
604
|
+
if (initialized) return instance;
|
|
605
|
+
|
|
606
|
+
await instance.init({
|
|
607
|
+
defaultNS: ${JSON.stringify(defaultNS)},
|
|
608
|
+
fallbackLng: DEFAULT_LOCALE,
|
|
609
|
+
interpolation: { escapeValue: false },
|
|
610
|
+
lng: lang,
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
initialized = true;
|
|
614
|
+
return instance;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
export { instance as i18n };
|
|
618
|
+
export default instance;
|
|
619
|
+
`;
|
|
620
|
+
}
|
|
621
|
+
function scaffoldSourceFiles(cwd, sourceDir, namespaces, logger) {
|
|
622
|
+
const dir = join3(cwd, sourceDir);
|
|
623
|
+
if (!existsSync3(dir)) {
|
|
624
|
+
mkdirSync(dir, { recursive: true });
|
|
625
|
+
}
|
|
626
|
+
for (const ns of namespaces) {
|
|
627
|
+
const filePath = join3(dir, `${ns}.ts`);
|
|
628
|
+
if (!existsSync3(filePath)) {
|
|
629
|
+
writeFileSync2(filePath, buildDefaultNsFile(ns), "utf-8");
|
|
630
|
+
logger.info(`\u5DF2\u751F\u6210\u7FFB\u8BD1\u6E90\u6587\u4EF6: ${sourceDir}/${ns}.ts`);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
const indexPath = join3(dir, "index.ts");
|
|
634
|
+
if (!existsSync3(indexPath)) {
|
|
635
|
+
const imports = namespaces.map((ns) => `import ${ns} from "./${ns}";`).join("\n");
|
|
636
|
+
const entries = namespaces.join(",\n ");
|
|
637
|
+
const content = `${imports}
|
|
638
|
+
|
|
639
|
+
const resources = {
|
|
640
|
+
${entries},
|
|
641
|
+
} as const;
|
|
642
|
+
|
|
643
|
+
export default resources;
|
|
644
|
+
`;
|
|
645
|
+
writeFileSync2(indexPath, content, "utf-8");
|
|
646
|
+
logger.info(`\u5DF2\u751F\u6210\u7FFB\u8BD1\u7D22\u5F15: ${sourceDir}/index.ts`);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
function buildDefaultNsFile(ns) {
|
|
650
|
+
if (ns === "common") {
|
|
651
|
+
return `export default {
|
|
652
|
+
welcome: "\u6B22\u8FCE\u4F7F\u7528",
|
|
653
|
+
loading: "\u52A0\u8F7D\u4E2D...",
|
|
654
|
+
confirm: "\u786E\u5B9A",
|
|
655
|
+
cancel: "\u53D6\u6D88",
|
|
656
|
+
save: "\u4FDD\u5B58",
|
|
657
|
+
delete: "\u5220\u9664",
|
|
658
|
+
edit: "\u7F16\u8F91",
|
|
659
|
+
back: "\u8FD4\u56DE",
|
|
660
|
+
} as const;
|
|
661
|
+
`;
|
|
662
|
+
}
|
|
663
|
+
return `export default {
|
|
664
|
+
title: "${ns}",
|
|
665
|
+
} as const;
|
|
666
|
+
`;
|
|
667
|
+
}
|
|
668
|
+
function scaffoldResourcesFile(cwd, sourceDir, _namespaces, locales, defaultLocale, logger) {
|
|
669
|
+
const parentDir = join3(cwd, sourceDir, "..");
|
|
670
|
+
const filePath = join3(parentDir, "resources.ts");
|
|
671
|
+
if (existsSync3(filePath)) return;
|
|
672
|
+
const content = `import resources from "./default";
|
|
673
|
+
|
|
674
|
+
export const DEFAULT_LOCALE = "${defaultLocale}";
|
|
675
|
+
|
|
676
|
+
export const SUPPORTED_LOCALES = ${JSON.stringify(locales)} as const;
|
|
677
|
+
|
|
678
|
+
export type SupportedLocale = (typeof SUPPORTED_LOCALES)[number];
|
|
679
|
+
|
|
680
|
+
export type DefaultResources = typeof resources;
|
|
681
|
+
|
|
682
|
+
export type NS = keyof DefaultResources;
|
|
683
|
+
|
|
684
|
+
export const normalizeLocale = (locale?: string): SupportedLocale => {
|
|
685
|
+
if (!locale) return DEFAULT_LOCALE as SupportedLocale;
|
|
686
|
+
|
|
687
|
+
for (const l of SUPPORTED_LOCALES) {
|
|
688
|
+
if (l.startsWith(locale) || locale.startsWith(l.split("-")[0])) {
|
|
689
|
+
return l;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
return DEFAULT_LOCALE as SupportedLocale;
|
|
694
|
+
};
|
|
695
|
+
|
|
696
|
+
export const localeOptions = ${JSON.stringify(
|
|
697
|
+
locales.map((l) => ({ value: l, label: getLocaleLabel(l) })),
|
|
698
|
+
null,
|
|
699
|
+
2
|
|
700
|
+
)} as const;
|
|
701
|
+
`;
|
|
702
|
+
writeFileSync2(filePath, content, "utf-8");
|
|
703
|
+
logger.info(`\u5DF2\u751F\u6210\u7C7B\u578B\u6587\u4EF6: ${sourceDir}/../resources.ts`);
|
|
704
|
+
}
|
|
705
|
+
function getLocaleLabel(locale) {
|
|
706
|
+
const map = {
|
|
707
|
+
"zh-CN": "\u7B80\u4F53\u4E2D\u6587",
|
|
708
|
+
"zh-TW": "\u7E41\u9AD4\u4E2D\u6587",
|
|
709
|
+
"en-US": "English",
|
|
710
|
+
"ja-JP": "\u65E5\u672C\u8A9E",
|
|
711
|
+
"ko-KR": "\uD55C\uAD6D\uC5B4",
|
|
712
|
+
"de-DE": "Deutsch",
|
|
713
|
+
"fr-FR": "Fran\xE7ais",
|
|
714
|
+
"es-ES": "Espa\xF1ol",
|
|
715
|
+
"pt-BR": "Portugu\xEAs",
|
|
716
|
+
"ru-RU": "\u0420\u0443\u0441\u0441\u043A\u0438\u0439",
|
|
717
|
+
"it-IT": "Italiano",
|
|
718
|
+
"vi-VN": "Ti\u1EBFng Vi\u1EC7t",
|
|
719
|
+
ar: "\u0627\u0644\u0639\u0631\u0628\u064A\u0629"
|
|
720
|
+
};
|
|
721
|
+
return map[locale] || locale;
|
|
722
|
+
}
|
|
723
|
+
function scaffoldConvertScript(cwd, sourceDir, localesDir, defaultLocale, logger) {
|
|
724
|
+
const scriptsDir = join3(cwd, "scripts");
|
|
725
|
+
const scriptPath = join3(scriptsDir, "i18n-gen.js");
|
|
726
|
+
if (existsSync3(scriptPath)) return;
|
|
727
|
+
if (!existsSync3(scriptsDir)) {
|
|
728
|
+
mkdirSync(scriptsDir, { recursive: true });
|
|
729
|
+
}
|
|
730
|
+
const content = `const fs = require("fs");
|
|
731
|
+
const path = require("path");
|
|
732
|
+
|
|
733
|
+
const sourceDir = path.join(__dirname, "../${sourceDir}");
|
|
734
|
+
const targetDir = path.join(__dirname, "../${localesDir}/${defaultLocale}");
|
|
735
|
+
|
|
736
|
+
const files = fs
|
|
737
|
+
.readdirSync(sourceDir)
|
|
738
|
+
.filter((file) => file.endsWith(".ts") && file !== "index.ts")
|
|
739
|
+
.map((file) => path.basename(file, ".ts"));
|
|
740
|
+
|
|
741
|
+
require("ts-node/register");
|
|
742
|
+
|
|
743
|
+
if (!fs.existsSync(targetDir)) {
|
|
744
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
console.log("\u5F00\u59CB\u8F6C\u6362 TypeScript \u6587\u4EF6\u5230 JSON...\\n");
|
|
748
|
+
|
|
749
|
+
files.forEach((fileName) => {
|
|
750
|
+
try {
|
|
751
|
+
const modulePath = path.join(sourceDir, \`\${fileName}.ts\`);
|
|
752
|
+
delete require.cache[require.resolve(modulePath)];
|
|
753
|
+
const moduleContent = require(modulePath).default;
|
|
754
|
+
const jsonContent = JSON.stringify(moduleContent, null, 2);
|
|
755
|
+
const targetPath = path.join(targetDir, \`\${fileName}.json\`);
|
|
756
|
+
fs.writeFileSync(targetPath, jsonContent, "utf8");
|
|
757
|
+
console.log(\` \${fileName}.ts \u2192 \${fileName}.json\`);
|
|
758
|
+
} catch (error) {
|
|
759
|
+
console.error(\` \u8F6C\u6362 \${fileName}.ts \u5931\u8D25:\`, error.message);
|
|
760
|
+
}
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
console.log("\\n\u8F6C\u6362\u5B8C\u6210\uFF01");
|
|
764
|
+
`;
|
|
765
|
+
writeFileSync2(scriptPath, content, "utf-8");
|
|
766
|
+
logger.info("\u5DF2\u751F\u6210\u8F6C\u6362\u811A\u672C: scripts/i18n-gen.js");
|
|
767
|
+
}
|
|
768
|
+
function scaffoldI18nRc(cwd, localesDir, defaultLocale, locales, logger) {
|
|
769
|
+
const filePath = join3(cwd, ".i18nrc.js");
|
|
770
|
+
if (existsSync3(filePath)) return;
|
|
771
|
+
const outputLocales = locales.filter((l) => l !== defaultLocale).map((l) => `"${l}"`).join(", ");
|
|
772
|
+
const content = `const { defineConfig } = require("@lobehub/i18n-cli");
|
|
773
|
+
|
|
774
|
+
module.exports = defineConfig({
|
|
775
|
+
entry: "${localesDir}/${defaultLocale}",
|
|
776
|
+
entryLocale: "${defaultLocale}",
|
|
777
|
+
output: "${localesDir}",
|
|
778
|
+
outputLocales: [${outputLocales}],
|
|
779
|
+
});
|
|
780
|
+
`;
|
|
781
|
+
writeFileSync2(filePath, content, "utf-8");
|
|
782
|
+
logger.info("\u5DF2\u751F\u6210\u7FFB\u8BD1\u914D\u7F6E: .i18nrc.js\uFF08@lobehub/i18n-cli\uFF09");
|
|
783
|
+
}
|
|
784
|
+
function scaffoldLocaleJsonDirs(cwd, localesDir, locales, defaultLocale, namespaces, logger) {
|
|
785
|
+
const baseDir = join3(cwd, localesDir);
|
|
786
|
+
for (const locale of locales) {
|
|
787
|
+
const localeDir = join3(baseDir, locale);
|
|
788
|
+
if (!existsSync3(localeDir)) {
|
|
789
|
+
mkdirSync(localeDir, { recursive: true });
|
|
790
|
+
}
|
|
791
|
+
if (locale === defaultLocale) continue;
|
|
792
|
+
for (const ns of namespaces) {
|
|
793
|
+
const filePath = join3(localeDir, `${ns}.json`);
|
|
794
|
+
if (!existsSync3(filePath)) {
|
|
795
|
+
const placeholder = ns === "common" ? JSON.stringify(
|
|
796
|
+
{
|
|
797
|
+
welcome: "Welcome",
|
|
798
|
+
loading: "Loading...",
|
|
799
|
+
confirm: "OK",
|
|
800
|
+
cancel: "Cancel",
|
|
801
|
+
save: "Save",
|
|
802
|
+
delete: "Delete",
|
|
803
|
+
edit: "Edit",
|
|
804
|
+
back: "Back"
|
|
805
|
+
},
|
|
806
|
+
null,
|
|
807
|
+
2
|
|
808
|
+
) : JSON.stringify({ title: ns }, null, 2);
|
|
809
|
+
writeFileSync2(filePath, `${placeholder}
|
|
810
|
+
`, "utf-8");
|
|
811
|
+
logger.info(`\u5DF2\u751F\u6210\u7FFB\u8BD1\u6587\u4EF6: ${localesDir}/${locale}/${ns}.json`);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// src/plugin/builtin/mock.ts
|
|
818
|
+
import { existsSync as existsSync4, readdirSync, statSync } from "fs";
|
|
819
|
+
import { join as join4, resolve } from "path";
|
|
820
|
+
var mockPlugin = createPlugin((options = {}) => ({
|
|
821
|
+
name: "@4399ywkf/plugin-mock",
|
|
822
|
+
version: "1.0.0",
|
|
823
|
+
description: "Mock \u6570\u636E\u63D2\u4EF6",
|
|
824
|
+
setup(context) {
|
|
825
|
+
const { mockDir = "mock", enableInProd = false, delay = 0, prefix = "" } = options;
|
|
826
|
+
const { cwd, isDev, logger } = context;
|
|
827
|
+
if (!isDev && !enableInProd) {
|
|
828
|
+
logger.info("\u751F\u4EA7\u73AF\u5883\u5DF2\u7981\u7528 Mock");
|
|
829
|
+
return {};
|
|
830
|
+
}
|
|
831
|
+
const mockPath = resolve(cwd, mockDir);
|
|
832
|
+
if (!existsSync4(mockPath)) {
|
|
833
|
+
logger.warn(`Mock \u76EE\u5F55\u4E0D\u5B58\u5728: ${mockPath}`);
|
|
834
|
+
return {};
|
|
835
|
+
}
|
|
836
|
+
const hooks = {
|
|
837
|
+
modifyRspackConfig(rspackConfig) {
|
|
838
|
+
if (isDev && rspackConfig.devServer) {
|
|
839
|
+
const mockFiles = scanMockFiles(mockPath);
|
|
840
|
+
logger.info(`\u627E\u5230 ${mockFiles.length} \u4E2A Mock \u6587\u4EF6`);
|
|
841
|
+
rspackConfig.devServer.setupMiddlewares = (middlewares, _devServer) => {
|
|
842
|
+
middlewares.unshift({
|
|
843
|
+
name: "mock-middleware",
|
|
844
|
+
middleware: createMockMiddleware(mockPath, { delay, prefix, logger })
|
|
845
|
+
});
|
|
846
|
+
return middlewares;
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
return rspackConfig;
|
|
850
|
+
},
|
|
851
|
+
beforeDevServer() {
|
|
852
|
+
logger.info(`Mock \u5DF2\u542F\u7528\uFF0C\u76EE\u5F55: ${mockPath}`);
|
|
853
|
+
if (delay > 0) {
|
|
854
|
+
logger.info(`\u6A21\u62DF\u5EF6\u8FDF: ${delay}ms`);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
};
|
|
858
|
+
return hooks;
|
|
859
|
+
}
|
|
860
|
+
}));
|
|
861
|
+
function scanMockFiles(dir) {
|
|
862
|
+
const files = [];
|
|
863
|
+
if (!existsSync4(dir)) {
|
|
864
|
+
return files;
|
|
865
|
+
}
|
|
866
|
+
const entries = readdirSync(dir);
|
|
867
|
+
for (const entry of entries) {
|
|
868
|
+
const fullPath = join4(dir, entry);
|
|
869
|
+
const stat = statSync(fullPath);
|
|
870
|
+
if (stat.isFile() && /\.(ts|js|mjs)$/.test(entry)) {
|
|
871
|
+
files.push(fullPath);
|
|
872
|
+
} else if (stat.isDirectory()) {
|
|
873
|
+
files.push(...scanMockFiles(fullPath));
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
return files;
|
|
877
|
+
}
|
|
878
|
+
function createMockMiddleware(mockPath, options) {
|
|
879
|
+
const { delay } = options;
|
|
880
|
+
scanMockFiles(mockPath);
|
|
881
|
+
return async (_req, _res, next) => {
|
|
882
|
+
if (delay > 0) {
|
|
883
|
+
await new Promise((resolve4) => setTimeout(resolve4, delay));
|
|
884
|
+
}
|
|
885
|
+
next();
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// src/plugin/builtin/react-query.ts
|
|
890
|
+
var reactQueryPlugin = createPlugin((options = {}) => ({
|
|
891
|
+
name: "@4399ywkf/plugin-react-query",
|
|
892
|
+
version: "1.0.0",
|
|
893
|
+
description: "React Query + Axios \u8BF7\u6C42\u5C42\u96C6\u6210",
|
|
894
|
+
setup(context) {
|
|
895
|
+
const {
|
|
896
|
+
staleTime = 5 * 60 * 1e3,
|
|
897
|
+
gcTime = 10 * 60 * 1e3,
|
|
898
|
+
retry = 1,
|
|
899
|
+
devtools = true,
|
|
900
|
+
baseURL = "",
|
|
901
|
+
timeout = 15e3
|
|
902
|
+
} = options;
|
|
903
|
+
const { logger, isDev } = context;
|
|
904
|
+
const hooks = {
|
|
905
|
+
generateFiles(_ctx) {
|
|
906
|
+
logger.info("React Query \u5DF2\u542F\u7528");
|
|
907
|
+
const files = [
|
|
908
|
+
{
|
|
909
|
+
path: "query-client.ts",
|
|
910
|
+
content: buildQueryClientFile({ staleTime, gcTime, retry, devtools, isDev })
|
|
911
|
+
},
|
|
912
|
+
{
|
|
913
|
+
path: "request.ts",
|
|
914
|
+
content: buildRequestFile({ baseURL, timeout })
|
|
915
|
+
}
|
|
916
|
+
];
|
|
917
|
+
return files;
|
|
918
|
+
},
|
|
919
|
+
injectBootstrap(_ctx) {
|
|
920
|
+
return {
|
|
921
|
+
imports: [
|
|
922
|
+
`import { QueryClientProvider } from "@tanstack/react-query";`,
|
|
923
|
+
`import { queryClient } from "./query-client";`
|
|
924
|
+
],
|
|
925
|
+
topLevel: [`// React Query Provider\uFF08\u7531 @4399ywkf/plugin-react-query \u6CE8\u5165\uFF09`]
|
|
926
|
+
};
|
|
927
|
+
},
|
|
928
|
+
modifyBootstrapCode(code) {
|
|
929
|
+
const providerEntry = ` {
|
|
930
|
+
component: QueryClientProvider as React.ComponentType<{ children: React.ReactNode }>,
|
|
931
|
+
props: { client: queryClient },
|
|
932
|
+
order: 20,
|
|
933
|
+
}`;
|
|
934
|
+
if (code.includes("providers: []")) {
|
|
935
|
+
code = code.replace("providers: []", `providers: [
|
|
936
|
+
${providerEntry},
|
|
937
|
+
]`);
|
|
938
|
+
} else if (code.includes("providers: [")) {
|
|
939
|
+
code = code.replace("providers: [", `providers: [
|
|
940
|
+
${providerEntry},`);
|
|
941
|
+
}
|
|
942
|
+
if (!code.includes("import React")) {
|
|
943
|
+
code = code.replace(
|
|
944
|
+
`import { bootstrap`,
|
|
945
|
+
`import React from "react";
|
|
946
|
+
import { bootstrap`
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
return code;
|
|
950
|
+
}
|
|
951
|
+
};
|
|
952
|
+
return hooks;
|
|
953
|
+
}
|
|
954
|
+
}));
|
|
955
|
+
function buildQueryClientFile(opts) {
|
|
956
|
+
const { staleTime, gcTime, retry, devtools, isDev } = opts;
|
|
957
|
+
return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/plugin-react-query \u81EA\u52A8\u751F\u6210
|
|
958
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
959
|
+
|
|
960
|
+
/**
|
|
961
|
+
* \u5168\u5C40 QueryClient \u5B9E\u4F8B
|
|
962
|
+
*
|
|
963
|
+
* \u9ED8\u8BA4\u914D\u7F6E\u53EF\u5728 ywkf.config.ts \u7684 reactQueryPlugin \u9009\u9879\u4E2D\u4FEE\u6539\u3002
|
|
964
|
+
* \u8FD0\u884C\u65F6\u81EA\u5B9A\u4E49\u53EF\u901A\u8FC7 src/app.config.ts \u8986\u76D6\u3002
|
|
965
|
+
*/
|
|
966
|
+
export const queryClient = new QueryClient({
|
|
967
|
+
defaultOptions: {
|
|
968
|
+
queries: {
|
|
969
|
+
staleTime: ${staleTime},
|
|
970
|
+
gcTime: ${gcTime},
|
|
971
|
+
retry: ${typeof retry === "boolean" ? retry : retry},
|
|
972
|
+
refetchOnWindowFocus: false,
|
|
973
|
+
},
|
|
974
|
+
mutations: {
|
|
975
|
+
retry: false,
|
|
976
|
+
},
|
|
977
|
+
},
|
|
978
|
+
});
|
|
979
|
+
|
|
980
|
+
export default queryClient;
|
|
981
|
+
`;
|
|
982
|
+
}
|
|
983
|
+
function buildRequestFile(opts) {
|
|
984
|
+
const { baseURL, timeout } = opts;
|
|
985
|
+
return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/plugin-react-query \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u4FEE\u6539
|
|
986
|
+
// \u5982\u9700\u5B9A\u5236\u62E6\u622A\u5668\uFF0C\u8BF7\u7F16\u8F91 src/request.ts
|
|
987
|
+
import axios from "axios";
|
|
988
|
+
import qs from "qs";
|
|
989
|
+
import type { AxiosInstance, AxiosRequestConfig, InternalAxiosRequestConfig, AxiosResponse, AxiosError } from "axios";
|
|
990
|
+
import type { RequestConfig, Result } from "@4399ywkf/core/runtime";
|
|
991
|
+
|
|
992
|
+
// ============ \u52A0\u8F7D\u7528\u6237\u914D\u7F6E ============
|
|
993
|
+
|
|
994
|
+
let userConfig: RequestConfig = {};
|
|
995
|
+
|
|
996
|
+
try {
|
|
997
|
+
const mod = require("@/request");
|
|
998
|
+
userConfig = mod.default || mod;
|
|
999
|
+
} catch {
|
|
1000
|
+
// src/request.ts \u4E0D\u5B58\u5728\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
// ============ \u53C2\u6570\u5E8F\u5217\u5316\uFF08GET \u8BF7\u6C42\u4E13\u7528\uFF09============
|
|
1004
|
+
|
|
1005
|
+
/**
|
|
1006
|
+
* GET \u8BF7\u6C42\u53C2\u6570\u5E8F\u5217\u5316\u89C4\u5219\uFF1A
|
|
1007
|
+
* - \u6570\u7EC4\uFF1Arepeat \u6A21\u5F0F\uFF08key=1&key=2\uFF09
|
|
1008
|
+
* - \u5BF9\u8C61\uFF1AJSON.stringify \u540E\u4F5C\u4E3A\u5B57\u7B26\u4E32\u4F20\u9012
|
|
1009
|
+
* - null / undefined\uFF1AskipNulls \u8DF3\u8FC7
|
|
1010
|
+
* - \u57FA\u672C\u7C7B\u578B\uFF1A\u539F\u6837\u4F20\u9012
|
|
1011
|
+
*/
|
|
1012
|
+
export function paramsSerializer(params: Record<string, any>): string {
|
|
1013
|
+
const normalized: Record<string, any> = {};
|
|
1014
|
+
|
|
1015
|
+
Object.keys(params).forEach((key) => {
|
|
1016
|
+
const value = params[key];
|
|
1017
|
+
|
|
1018
|
+
if (value === null || value === undefined) {
|
|
1019
|
+
normalized[key] = value;
|
|
1020
|
+
} else if (Array.isArray(value)) {
|
|
1021
|
+
normalized[key] = value;
|
|
1022
|
+
} else if (typeof value === "object") {
|
|
1023
|
+
normalized[key] = JSON.stringify(value);
|
|
1024
|
+
} else {
|
|
1025
|
+
normalized[key] = value;
|
|
1026
|
+
}
|
|
1027
|
+
});
|
|
1028
|
+
|
|
1029
|
+
return qs.stringify(normalized, { arrayFormat: "repeat", skipNulls: true });
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
// ============ \u521B\u5EFA Axios \u5B9E\u4F8B ============
|
|
1033
|
+
|
|
1034
|
+
const instance: AxiosInstance = axios.create({
|
|
1035
|
+
baseURL: userConfig.baseURL ?? ("${baseURL}" || ""),
|
|
1036
|
+
timeout: userConfig.timeout ?? ${timeout},
|
|
1037
|
+
withCredentials: userConfig.withCredentials ?? false,
|
|
1038
|
+
headers: {
|
|
1039
|
+
"Content-Type": "application/json",
|
|
1040
|
+
...(userConfig.headers || {}),
|
|
1041
|
+
},
|
|
1042
|
+
});
|
|
1043
|
+
|
|
1044
|
+
// ============ \u8BF7\u6C42\u62E6\u622A\u5668 ============
|
|
1045
|
+
|
|
1046
|
+
instance.interceptors.request.use(
|
|
1047
|
+
(config: InternalAxiosRequestConfig) => {
|
|
1048
|
+
// 1. Token \u6CE8\u5165
|
|
1049
|
+
if (userConfig.getToken) {
|
|
1050
|
+
const token = userConfig.getToken();
|
|
1051
|
+
if (token && config.headers) {
|
|
1052
|
+
const prefix = userConfig.tokenPrefix ?? "Bearer";
|
|
1053
|
+
config.headers.Authorization = prefix ? \`\${prefix} \${token}\` : token;
|
|
1054
|
+
}
|
|
1055
|
+
} else {
|
|
1056
|
+
// \u9ED8\u8BA4\uFF1A\u4ECE localStorage \u8BFB\u53D6
|
|
1057
|
+
const token = typeof localStorage !== "undefined"
|
|
1058
|
+
? localStorage.getItem("token")
|
|
1059
|
+
: null;
|
|
1060
|
+
if (token && config.headers) {
|
|
1061
|
+
config.headers.Authorization = \`Bearer \${token}\`;
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
// 2. \u7528\u6237\u81EA\u5B9A\u4E49\u8BF7\u6C42\u62E6\u622A
|
|
1066
|
+
if (userConfig.requestInterceptor) {
|
|
1067
|
+
return userConfig.requestInterceptor(config as any) as any;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
return config;
|
|
1071
|
+
},
|
|
1072
|
+
(error: AxiosError) => Promise.reject(error)
|
|
1073
|
+
);
|
|
1074
|
+
|
|
1075
|
+
// ============ \u54CD\u5E94\u62E6\u622A\u5668 ============
|
|
1076
|
+
|
|
1077
|
+
instance.interceptors.response.use(
|
|
1078
|
+
(response: AxiosResponse) => {
|
|
1079
|
+
// \u7528\u6237\u81EA\u5B9A\u4E49\u54CD\u5E94\u62E6\u622A
|
|
1080
|
+
if (userConfig.responseInterceptor) {
|
|
1081
|
+
return userConfig.responseInterceptor(response as any);
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
// \u9ED8\u8BA4\uFF1A\u76F4\u63A5\u8FD4\u56DE response.data
|
|
1085
|
+
return response.data;
|
|
1086
|
+
},
|
|
1087
|
+
(error: AxiosError) => {
|
|
1088
|
+
// \u7528\u6237\u81EA\u5B9A\u4E49\u9519\u8BEF\u5904\u7406
|
|
1089
|
+
if (userConfig.errorHandler) {
|
|
1090
|
+
return userConfig.errorHandler(error as any);
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
// \u9ED8\u8BA4\u9519\u8BEF\u5904\u7406
|
|
1094
|
+
const status = error.response?.status;
|
|
1095
|
+
|
|
1096
|
+
switch (status) {
|
|
1097
|
+
case 401:
|
|
1098
|
+
console.warn("[request] \u672A\u6388\u6743\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55");
|
|
1099
|
+
break;
|
|
1100
|
+
case 403:
|
|
1101
|
+
console.warn("[request] \u65E0\u8BBF\u95EE\u6743\u9650");
|
|
1102
|
+
break;
|
|
1103
|
+
case 500:
|
|
1104
|
+
console.error("[request] \u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF");
|
|
1105
|
+
break;
|
|
1106
|
+
default:
|
|
1107
|
+
if (!error.response) {
|
|
1108
|
+
console.error("[request] \u7F51\u7EDC\u5F02\u5E38\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u8FDE\u63A5");
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
return Promise.reject(error);
|
|
1113
|
+
}
|
|
1114
|
+
);
|
|
1115
|
+
|
|
1116
|
+
// ============ \u8BF7\u6C42\u5C01\u88C5\u7C7B ============
|
|
1117
|
+
|
|
1118
|
+
type ExtraConfig = AxiosRequestConfig & { suppressErrorNotification?: boolean };
|
|
1119
|
+
|
|
1120
|
+
class Request {
|
|
1121
|
+
constructor(private readonly http: AxiosInstance) {}
|
|
1122
|
+
|
|
1123
|
+
get<T = any>(url: string, params?: Record<string, any>, config?: ExtraConfig): Promise<Result<T>> {
|
|
1124
|
+
return this.http.get(url, {
|
|
1125
|
+
...config,
|
|
1126
|
+
params,
|
|
1127
|
+
paramsSerializer,
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
post<T = any>(url: string, data?: any, config?: ExtraConfig): Promise<Result<T>> {
|
|
1132
|
+
return this.http.post(url, data, config);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
put<T = any>(url: string, data?: any, config?: ExtraConfig): Promise<Result<T>> {
|
|
1136
|
+
return this.http.put(url, data, config);
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
delete<T = any>(url: string, params?: Record<string, any>, config?: ExtraConfig): Promise<Result<T>> {
|
|
1140
|
+
return this.http.delete(url, {
|
|
1141
|
+
...config,
|
|
1142
|
+
params,
|
|
1143
|
+
paramsSerializer,
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
patch<T = any>(url: string, data?: any, config?: ExtraConfig): Promise<Result<T>> {
|
|
1148
|
+
return this.http.patch(url, data, config);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
// ============ \u5BFC\u51FA ============
|
|
1153
|
+
|
|
1154
|
+
export const request = new Request(instance);
|
|
1155
|
+
export type { Result };
|
|
1156
|
+
export default request;
|
|
1157
|
+
`;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
// src/plugin/builtin/tailwind.ts
|
|
1161
|
+
import { existsSync as existsSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
1162
|
+
import { join as join5 } from "path";
|
|
1163
|
+
import { rspack } from "@rspack/core";
|
|
1164
|
+
var tailwindPlugin = createPlugin((options = {}) => ({
|
|
1165
|
+
name: "@4399ywkf/plugin-tailwind",
|
|
1166
|
+
version: "1.0.0",
|
|
1167
|
+
description: "Tailwind CSS \u96C6\u6210\u63D2\u4EF6",
|
|
1168
|
+
setup(context) {
|
|
1169
|
+
const {
|
|
1170
|
+
darkModeSelector = '&:where([data-theme="dark"], [data-theme="dark"] *)',
|
|
1171
|
+
autoConfig = true,
|
|
1172
|
+
extraCSS = ""
|
|
1173
|
+
} = options;
|
|
1174
|
+
const { cwd, logger } = context;
|
|
1175
|
+
if (autoConfig) {
|
|
1176
|
+
ensurePostcssConfig(cwd, logger);
|
|
1177
|
+
}
|
|
1178
|
+
const hooks = {
|
|
1179
|
+
modifyRspackConfig(rspackConfig) {
|
|
1180
|
+
logger.info("Tailwind CSS \u5DF2\u542F\u7528");
|
|
1181
|
+
const rules = rspackConfig.module?.rules || [];
|
|
1182
|
+
const postcssConfigPath = join5(cwd, "postcss.config.js");
|
|
1183
|
+
const isProd = rspackConfig.mode === "production";
|
|
1184
|
+
const tailwindRule = {
|
|
1185
|
+
test: /tailwind\.css$/,
|
|
1186
|
+
use: [
|
|
1187
|
+
isProd ? rspack.CssExtractRspackPlugin.loader : "style-loader",
|
|
1188
|
+
"css-loader",
|
|
1189
|
+
{
|
|
1190
|
+
loader: "postcss-loader",
|
|
1191
|
+
options: {
|
|
1192
|
+
postcssOptions: {
|
|
1193
|
+
config: postcssConfigPath
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
]
|
|
1198
|
+
};
|
|
1199
|
+
rules.unshift(tailwindRule);
|
|
1200
|
+
rspackConfig.module = { ...rspackConfig.module, rules };
|
|
1201
|
+
return rspackConfig;
|
|
1202
|
+
},
|
|
1203
|
+
generateFiles(_ctx) {
|
|
1204
|
+
const cssContent = buildTailwindCSS(darkModeSelector, extraCSS);
|
|
1205
|
+
return [
|
|
1206
|
+
{
|
|
1207
|
+
path: "tailwind.css",
|
|
1208
|
+
content: cssContent
|
|
1209
|
+
}
|
|
1210
|
+
];
|
|
1211
|
+
},
|
|
1212
|
+
injectEntry(_ctx) {
|
|
1213
|
+
return {
|
|
1214
|
+
imports: [`import "./tailwind.css";`]
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
1217
|
+
};
|
|
1218
|
+
return hooks;
|
|
1219
|
+
}
|
|
1220
|
+
}));
|
|
1221
|
+
function buildTailwindCSS(darkModeSelector, extraCSS) {
|
|
1222
|
+
return `/* \u6B64\u6587\u4EF6\u7531 @4399ywkf/plugin-tailwind \u81EA\u52A8\u751F\u6210 */
|
|
1223
|
+
@import "tailwindcss";
|
|
1224
|
+
@variant dark (${darkModeSelector});
|
|
1225
|
+
${extraCSS ? `
|
|
1226
|
+
${extraCSS}` : ""}
|
|
1227
|
+
`;
|
|
1228
|
+
}
|
|
1229
|
+
function ensurePostcssConfig(cwd, logger) {
|
|
1230
|
+
const configPath = join5(cwd, "postcss.config.js");
|
|
1231
|
+
if (!existsSync5(configPath)) {
|
|
1232
|
+
writeFileSync3(
|
|
1233
|
+
configPath,
|
|
1234
|
+
`module.exports = {
|
|
1235
|
+
plugins: {
|
|
1236
|
+
"@tailwindcss/postcss": {},
|
|
1237
|
+
},
|
|
1238
|
+
};
|
|
1239
|
+
`,
|
|
1240
|
+
"utf-8"
|
|
1241
|
+
);
|
|
1242
|
+
logger.info("\u5DF2\u81EA\u52A8\u751F\u6210 postcss.config.js");
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
// src/plugin/builtin/theme.ts
|
|
1247
|
+
import { join as join6 } from "path";
|
|
1248
|
+
var themePlugin = createPlugin((options = {}) => ({
|
|
1249
|
+
name: "@4399ywkf/plugin-theme",
|
|
1250
|
+
version: "3.0.0",
|
|
1251
|
+
description: "Lobe-UI \u98CE\u683C\u54CD\u5E94\u5F0F\u4E3B\u9898\u7CFB\u7EDF\u63D2\u4EF6",
|
|
1252
|
+
setup(context) {
|
|
1253
|
+
const {
|
|
1254
|
+
darkMode = true,
|
|
1255
|
+
defaultAppearance = "light",
|
|
1256
|
+
primaryColor,
|
|
1257
|
+
neutralColor,
|
|
1258
|
+
prefixCls = "ant",
|
|
1259
|
+
cssVar = true,
|
|
1260
|
+
globalReset = true,
|
|
1261
|
+
externalTheme = false,
|
|
1262
|
+
locale = "auto",
|
|
1263
|
+
scopePopupContainer = true
|
|
1264
|
+
} = options;
|
|
1265
|
+
const { logger } = context;
|
|
1266
|
+
logger.info(
|
|
1267
|
+
`\u4E3B\u9898\u6A21\u5F0F: ${defaultAppearance}, \u4E3B\u8272: ${primaryColor ?? "primary(\u9ED8\u8BA4\u9ED1)"}, \u54CD\u5E94\u5F0F: \u2713`
|
|
1268
|
+
);
|
|
1269
|
+
const hooks = {
|
|
1270
|
+
modifyRspackConfig(config, ctx) {
|
|
1271
|
+
const themePath = join6(ctx.cwd, ".ywkf", "theme.tsx");
|
|
1272
|
+
const currentAlias = config.resolve?.alias ?? {};
|
|
1273
|
+
config.resolve = {
|
|
1274
|
+
...config.resolve,
|
|
1275
|
+
alias: {
|
|
1276
|
+
...currentAlias,
|
|
1277
|
+
"@ywkf/theme": themePath
|
|
1278
|
+
}
|
|
1279
|
+
};
|
|
1280
|
+
return config;
|
|
1281
|
+
},
|
|
1282
|
+
generateFiles(_ctx) {
|
|
1283
|
+
return [
|
|
1284
|
+
{
|
|
1285
|
+
path: "theme.tsx",
|
|
1286
|
+
content: generateThemeProvider({
|
|
1287
|
+
darkMode,
|
|
1288
|
+
defaultAppearance,
|
|
1289
|
+
primaryColor,
|
|
1290
|
+
neutralColor,
|
|
1291
|
+
prefixCls,
|
|
1292
|
+
cssVar,
|
|
1293
|
+
globalReset,
|
|
1294
|
+
externalTheme,
|
|
1295
|
+
locale,
|
|
1296
|
+
scopePopupContainer
|
|
1297
|
+
})
|
|
1298
|
+
}
|
|
1299
|
+
];
|
|
1300
|
+
},
|
|
1301
|
+
injectBootstrap(_ctx) {
|
|
1302
|
+
return {
|
|
1303
|
+
imports: [`import { ThemeWrapper } from "./theme";`]
|
|
1304
|
+
};
|
|
1305
|
+
},
|
|
1306
|
+
modifyBootstrapCode(code) {
|
|
1307
|
+
const providerEntry = ` {
|
|
1308
|
+
component: ThemeWrapper as React.ComponentType<{ children: React.ReactNode }>,
|
|
1309
|
+
props: {},
|
|
1310
|
+
order: 10,
|
|
1311
|
+
}`;
|
|
1312
|
+
if (code.includes("providers: []")) {
|
|
1313
|
+
code = code.replace(
|
|
1314
|
+
"providers: []",
|
|
1315
|
+
`providers: [
|
|
1316
|
+
${providerEntry},
|
|
1317
|
+
]`
|
|
1318
|
+
);
|
|
1319
|
+
} else if (code.includes("providers: [")) {
|
|
1320
|
+
code = code.replace(
|
|
1321
|
+
"providers: [",
|
|
1322
|
+
`providers: [
|
|
1323
|
+
${providerEntry},`
|
|
1324
|
+
);
|
|
1325
|
+
}
|
|
1326
|
+
if (!code.includes("import React")) {
|
|
1327
|
+
code = code.replace(
|
|
1328
|
+
`import { bootstrap`,
|
|
1329
|
+
`import React from "react";
|
|
1330
|
+
import { bootstrap`
|
|
1331
|
+
);
|
|
1332
|
+
}
|
|
1333
|
+
return code;
|
|
1334
|
+
}
|
|
1335
|
+
};
|
|
1336
|
+
return hooks;
|
|
1337
|
+
}
|
|
1338
|
+
}));
|
|
1339
|
+
function generateThemeProvider(opts) {
|
|
1340
|
+
const {
|
|
1341
|
+
darkMode,
|
|
1342
|
+
defaultAppearance,
|
|
1343
|
+
primaryColor,
|
|
1344
|
+
neutralColor,
|
|
1345
|
+
prefixCls,
|
|
1346
|
+
cssVar,
|
|
1347
|
+
globalReset,
|
|
1348
|
+
externalTheme,
|
|
1349
|
+
locale,
|
|
1350
|
+
scopePopupContainer
|
|
1351
|
+
} = opts;
|
|
1352
|
+
const isAutoLocale = locale === "auto";
|
|
1353
|
+
const sections = [];
|
|
1354
|
+
sections.push(`// \u6B64\u6587\u4EF6\u7531 @4399ywkf/plugin-theme v3 \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u4FEE\u6539`);
|
|
1355
|
+
sections.push(buildImports({ globalReset, cssVar, scopePopupContainer, locale }));
|
|
1356
|
+
sections.push(TYPES_CODE);
|
|
1357
|
+
sections.push(
|
|
1358
|
+
buildStoreCode({ defaultAppearance, primaryColor, neutralColor, locale })
|
|
1359
|
+
);
|
|
1360
|
+
sections.push(HOOKS_CODE);
|
|
1361
|
+
if (globalReset) sections.push(GLOBAL_RESET_CODE);
|
|
1362
|
+
if (cssVar) sections.push(buildCssVarSyncCode());
|
|
1363
|
+
if (darkMode) sections.push(APPEARANCE_SYNC_CODE);
|
|
1364
|
+
if (externalTheme) sections.push(EXTERNAL_THEME_CODE);
|
|
1365
|
+
if (isAutoLocale) sections.push(LOCALE_AUTO_CODE);
|
|
1366
|
+
sections.push(
|
|
1367
|
+
buildWrapperCode({
|
|
1368
|
+
darkMode,
|
|
1369
|
+
cssVar,
|
|
1370
|
+
globalReset,
|
|
1371
|
+
externalTheme,
|
|
1372
|
+
locale,
|
|
1373
|
+
prefixCls,
|
|
1374
|
+
scopePopupContainer
|
|
1375
|
+
})
|
|
1376
|
+
);
|
|
1377
|
+
return sections.join("\n");
|
|
1378
|
+
}
|
|
1379
|
+
var ANTD_LOCALE_MAP = {
|
|
1380
|
+
zhCN: { importName: "zhCN", importPath: "antd/locale/zh_CN.js" },
|
|
1381
|
+
enUS: { importName: "enUS", importPath: "antd/locale/en_US.js" },
|
|
1382
|
+
zhTW: { importName: "zhTW", importPath: "antd/locale/zh_TW.js" },
|
|
1383
|
+
jaJP: { importName: "jaJP", importPath: "antd/locale/ja_JP.js" },
|
|
1384
|
+
koKR: { importName: "koKR", importPath: "antd/locale/ko_KR.js" }
|
|
1385
|
+
};
|
|
1386
|
+
var LOCALE_TO_BCP47 = {
|
|
1387
|
+
zhCN: "zh-CN",
|
|
1388
|
+
enUS: "en-US",
|
|
1389
|
+
zhTW: "zh-TW",
|
|
1390
|
+
jaJP: "ja-JP",
|
|
1391
|
+
koKR: "ko-KR"
|
|
1392
|
+
};
|
|
1393
|
+
function buildImports(opts) {
|
|
1394
|
+
const needsRef = opts.cssVar || opts.scopePopupContainer;
|
|
1395
|
+
const isAutoLocale = opts.locale === "auto";
|
|
1396
|
+
const reactImports = [
|
|
1397
|
+
"useCallback",
|
|
1398
|
+
"useEffect",
|
|
1399
|
+
"useMemo",
|
|
1400
|
+
"useState",
|
|
1401
|
+
...opts.cssVar ? ["useLayoutEffect"] : [],
|
|
1402
|
+
...needsRef ? ["useRef"] : [],
|
|
1403
|
+
"type ReactNode"
|
|
1404
|
+
];
|
|
1405
|
+
const antdStyleImports = [
|
|
1406
|
+
"ThemeProvider as AntdThemeProvider",
|
|
1407
|
+
"StyleProvider",
|
|
1408
|
+
"type GetAntdTheme",
|
|
1409
|
+
...opts.globalReset ? ["createGlobalStyle", "css"] : []
|
|
1410
|
+
];
|
|
1411
|
+
const localeEntry = ANTD_LOCALE_MAP[opts.locale];
|
|
1412
|
+
const localeImports = isAutoLocale ? `import zhCN from "antd/locale/zh_CN.js";
|
|
1413
|
+
import type { Locale as AntdLocale } from "antd/es/locale";` : localeEntry ? `import ${localeEntry.importName} from "${localeEntry.importPath}";` : `import zhCN from "antd/locale/zh_CN.js";`;
|
|
1414
|
+
return `
|
|
1415
|
+
import React, { ${reactImports.join(", ")} } from "react";
|
|
1416
|
+
import { ConfigProvider } from "antd";
|
|
1417
|
+
${localeImports}
|
|
1418
|
+
import { ${antdStyleImports.join(", ")} } from "antd-style";
|
|
1419
|
+
import { createWithEqualityFn } from "zustand/traditional";
|
|
1420
|
+
import { shallow } from "zustand/shallow";
|
|
1421
|
+
import { createThemeConfig, type PrimaryColors, type NeutralColors } from "@4399ywkf/theme-system";`;
|
|
1422
|
+
}
|
|
1423
|
+
var TYPES_CODE = `
|
|
1424
|
+
// \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
|
|
1425
|
+
|
|
1426
|
+
export type ThemeAppearance = "light" | "dark" | "auto";
|
|
1427
|
+
|
|
1428
|
+
export interface ThemeState {
|
|
1429
|
+
appearance: ThemeAppearance;
|
|
1430
|
+
primaryColor?: PrimaryColors;
|
|
1431
|
+
neutralColor?: NeutralColors;
|
|
1432
|
+
/** BCP 47 language tag, e.g. "zh-CN", "en-US" */
|
|
1433
|
+
locale: string;
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
export interface ThemeActions {
|
|
1437
|
+
setAppearance: (mode: ThemeAppearance) => void;
|
|
1438
|
+
setPrimaryColor: (color: PrimaryColors | undefined) => void;
|
|
1439
|
+
setNeutralColor: (color: NeutralColors | undefined) => void;
|
|
1440
|
+
setLocale: (locale: string) => void;
|
|
1441
|
+
setTheme: (partial: Partial<ThemeState>) => void;
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
export type ThemeStore = ThemeState & ThemeActions;
|
|
1445
|
+
|
|
1446
|
+
export type { PrimaryColors, NeutralColors };`;
|
|
1447
|
+
function buildStoreCode(opts) {
|
|
1448
|
+
const isAutoLocale = opts.locale === "auto";
|
|
1449
|
+
const primaryLine = opts.primaryColor ? ` primaryColor: "${opts.primaryColor}" as PrimaryColors,` : ` primaryColor: undefined,`;
|
|
1450
|
+
const neutralLine = opts.neutralColor ? ` neutralColor: "${opts.neutralColor}" as NeutralColors,` : ` neutralColor: undefined,`;
|
|
1451
|
+
const localeLine = isAutoLocale ? ` locale: detectInitialLocale(),` : ` locale: "${LOCALE_TO_BCP47[opts.locale] ?? "zh-CN"}",`;
|
|
1452
|
+
const detectionFn = isAutoLocale ? `
|
|
1453
|
+
function detectInitialLocale(): string {
|
|
1454
|
+
if (typeof window !== "undefined") {
|
|
1455
|
+
const hostLocale = (window as any).__YWKF_LOCALE__;
|
|
1456
|
+
if (typeof hostLocale === "string" && hostLocale) return hostLocale;
|
|
1457
|
+
}
|
|
1458
|
+
if (typeof navigator !== "undefined") {
|
|
1459
|
+
return navigator.language || "zh-CN";
|
|
1460
|
+
}
|
|
1461
|
+
return "zh-CN";
|
|
1462
|
+
}
|
|
1463
|
+
` : "";
|
|
1464
|
+
return `
|
|
1465
|
+
// \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
|
|
1466
|
+
${detectionFn}
|
|
1467
|
+
const DEFAULT_THEME: ThemeState = {
|
|
1468
|
+
appearance: "${opts.defaultAppearance}",
|
|
1469
|
+
${primaryLine}
|
|
1470
|
+
${neutralLine}
|
|
1471
|
+
${localeLine}
|
|
1472
|
+
};
|
|
1473
|
+
|
|
1474
|
+
export const useThemeStore = createWithEqualityFn<ThemeStore>()(
|
|
1475
|
+
(set) => ({
|
|
1476
|
+
...DEFAULT_THEME,
|
|
1477
|
+
setAppearance: (mode) => set({ appearance: mode }),
|
|
1478
|
+
setPrimaryColor: (color) => set({ primaryColor: color }),
|
|
1479
|
+
setNeutralColor: (color) => set({ neutralColor: color }),
|
|
1480
|
+
setLocale: (locale) => set({ locale }),
|
|
1481
|
+
setTheme: (partial) => set(partial),
|
|
1482
|
+
}),
|
|
1483
|
+
shallow,
|
|
1484
|
+
);`;
|
|
1485
|
+
}
|
|
1486
|
+
var HOOKS_CODE = `
|
|
1487
|
+
// \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
|
|
1488
|
+
|
|
1489
|
+
export const useTheme = () =>
|
|
1490
|
+
useThemeStore(
|
|
1491
|
+
(s) => ({
|
|
1492
|
+
appearance: s.appearance,
|
|
1493
|
+
primaryColor: s.primaryColor,
|
|
1494
|
+
neutralColor: s.neutralColor,
|
|
1495
|
+
}),
|
|
1496
|
+
shallow,
|
|
1497
|
+
);
|
|
1498
|
+
|
|
1499
|
+
export const useAppearance = () => useThemeStore((s) => s.appearance);
|
|
1500
|
+
export const usePrimaryColor = () => useThemeStore((s) => s.primaryColor);
|
|
1501
|
+
export const useLocale = () => useThemeStore((s) => s.locale);`;
|
|
1502
|
+
var GLOBAL_RESET_CODE = `
|
|
1503
|
+
// \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
|
|
1504
|
+
|
|
1505
|
+
const GlobalReset = createGlobalStyle(({ theme }) => css\`
|
|
1506
|
+
:root {
|
|
1507
|
+
--font-settings: "cv01", "tnum", "kern";
|
|
1508
|
+
--font-variations: "opsz" auto, tabular-nums;
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
*,
|
|
1512
|
+
*::before,
|
|
1513
|
+
*::after {
|
|
1514
|
+
box-sizing: border-box;
|
|
1515
|
+
vertical-align: baseline;
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
* {
|
|
1519
|
+
scrollbar-color: \${theme.colorFill} transparent;
|
|
1520
|
+
scrollbar-width: thin;
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
html {
|
|
1524
|
+
overscroll-behavior: none;
|
|
1525
|
+
color-scheme: \${theme.isDarkMode ? "dark" : "light"};
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
html, body, #root, #app {
|
|
1529
|
+
height: 100%;
|
|
1530
|
+
margin: 0;
|
|
1531
|
+
padding: 0;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
body {
|
|
1535
|
+
overflow: hidden auto;
|
|
1536
|
+
min-height: 100vh;
|
|
1537
|
+
font-family: \${theme.fontFamily};
|
|
1538
|
+
font-size: \${theme.fontSize}px;
|
|
1539
|
+
font-feature-settings: var(--font-settings);
|
|
1540
|
+
font-variation-settings: var(--font-variations);
|
|
1541
|
+
line-height: 1;
|
|
1542
|
+
color: \${theme.colorTextBase};
|
|
1543
|
+
text-size-adjust: none;
|
|
1544
|
+
text-rendering: optimizelegibility;
|
|
1545
|
+
word-wrap: break-word;
|
|
1546
|
+
background-color: \${theme.colorBgLayout};
|
|
1547
|
+
-webkit-font-smoothing: antialiased;
|
|
1548
|
+
-moz-osx-font-smoothing: grayscale;
|
|
1549
|
+
-webkit-overflow-scrolling: touch;
|
|
1550
|
+
-webkit-tap-highlight-color: transparent;
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
code {
|
|
1554
|
+
font-family: \${theme.fontFamilyCode} !important;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
::selection {
|
|
1558
|
+
-webkit-text-fill-color: unset !important;
|
|
1559
|
+
}
|
|
1560
|
+
\`);`;
|
|
1561
|
+
function buildCssVarSyncCode() {
|
|
1562
|
+
return `
|
|
1563
|
+
// \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
|
|
1564
|
+
|
|
1565
|
+
const CSS_VAR_CLASS_RE = /(^|-)css-var(-|$)/;
|
|
1566
|
+
|
|
1567
|
+
function isCssVarClassName(className: string): boolean {
|
|
1568
|
+
return CSS_VAR_CLASS_RE.test(className);
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
function useCssVarSync(ref: React.RefObject<HTMLDivElement | null>) {
|
|
1572
|
+
useLayoutEffect(() => {
|
|
1573
|
+
const node = ref.current;
|
|
1574
|
+
if (!node) return;
|
|
1575
|
+
|
|
1576
|
+
const htmlEl = document.documentElement;
|
|
1577
|
+
let currentClasses: string[] = [];
|
|
1578
|
+
|
|
1579
|
+
const sync = () => {
|
|
1580
|
+
for (const cls of currentClasses) {
|
|
1581
|
+
htmlEl.classList.remove(cls);
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
const nextSet = new Set<string>();
|
|
1585
|
+
let el: HTMLElement | null = node;
|
|
1586
|
+
|
|
1587
|
+
while (el && el !== htmlEl) {
|
|
1588
|
+
for (const cls of el.classList) {
|
|
1589
|
+
if (isCssVarClassName(cls)) {
|
|
1590
|
+
nextSet.add(cls);
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
el = el.parentElement;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
const next = Array.from(nextSet);
|
|
1597
|
+
|
|
1598
|
+
for (const cls of next) {
|
|
1599
|
+
htmlEl.classList.add(cls);
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
currentClasses = next;
|
|
1603
|
+
};
|
|
1604
|
+
|
|
1605
|
+
sync();
|
|
1606
|
+
|
|
1607
|
+
const observer = new MutationObserver(sync);
|
|
1608
|
+
let el: HTMLElement | null = node;
|
|
1609
|
+
while (el && el !== htmlEl) {
|
|
1610
|
+
observer.observe(el, { attributeFilter: ["class"] });
|
|
1611
|
+
el = el.parentElement;
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
return () => {
|
|
1615
|
+
observer.disconnect();
|
|
1616
|
+
for (const cls of currentClasses) {
|
|
1617
|
+
htmlEl.classList.remove(cls);
|
|
1618
|
+
}
|
|
1619
|
+
};
|
|
1620
|
+
}, []);
|
|
1621
|
+
}`;
|
|
1622
|
+
}
|
|
1623
|
+
var APPEARANCE_SYNC_CODE = `
|
|
1624
|
+
// \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
|
|
1625
|
+
|
|
1626
|
+
function useAppearanceSync(appearance: ThemeAppearance) {
|
|
1627
|
+
useEffect(() => {
|
|
1628
|
+
if (appearance !== "auto") {
|
|
1629
|
+
document.documentElement.dataset.theme = appearance;
|
|
1630
|
+
return;
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
1634
|
+
document.documentElement.dataset.theme = mq.matches ? "dark" : "light";
|
|
1635
|
+
|
|
1636
|
+
function handleChange(e: MediaQueryListEvent) {
|
|
1637
|
+
document.documentElement.dataset.theme = e.matches ? "dark" : "light";
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
mq.addEventListener("change", handleChange);
|
|
1641
|
+
return () => mq.removeEventListener("change", handleChange);
|
|
1642
|
+
}, [appearance]);
|
|
1643
|
+
}`;
|
|
1644
|
+
var EXTERNAL_THEME_CODE = `
|
|
1645
|
+
// \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
|
|
1646
|
+
|
|
1647
|
+
function useExternalTheme() {
|
|
1648
|
+
useEffect(() => {
|
|
1649
|
+
const hostTheme = (window as any).__YWKF_THEME__;
|
|
1650
|
+
if (hostTheme && typeof hostTheme === "object") {
|
|
1651
|
+
useThemeStore.getState().setTheme(hostTheme);
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
const handler = (e: Event) => {
|
|
1655
|
+
const detail = (e as CustomEvent<Partial<ThemeState>>).detail;
|
|
1656
|
+
if (detail) useThemeStore.getState().setTheme(detail);
|
|
1657
|
+
};
|
|
1658
|
+
|
|
1659
|
+
window.addEventListener("ywkf:theme-change", handler);
|
|
1660
|
+
return () => window.removeEventListener("ywkf:theme-change", handler);
|
|
1661
|
+
}, []);
|
|
1662
|
+
}`;
|
|
1663
|
+
var LOCALE_AUTO_CODE = `
|
|
1664
|
+
// \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
|
|
1665
|
+
|
|
1666
|
+
const ANTD_LOCALE_LOADERS: Record<string, () => Promise<{ default: AntdLocale }>> = {
|
|
1667
|
+
en: () => import("antd/locale/en_US.js"),
|
|
1668
|
+
"zh-tw": () => import("antd/locale/zh_TW.js"),
|
|
1669
|
+
"zh-hk": () => import("antd/locale/zh_TW.js"),
|
|
1670
|
+
ja: () => import("antd/locale/ja_JP.js"),
|
|
1671
|
+
ko: () => import("antd/locale/ko_KR.js"),
|
|
1672
|
+
};
|
|
1673
|
+
|
|
1674
|
+
function useAntdLocale(): AntdLocale {
|
|
1675
|
+
const lang = useThemeStore((s) => s.locale);
|
|
1676
|
+
const [antdLocale, setAntdLocale] = useState<AntdLocale>(zhCN);
|
|
1677
|
+
|
|
1678
|
+
useEffect(() => {
|
|
1679
|
+
const normalized = (lang || "zh-CN").toLowerCase();
|
|
1680
|
+
|
|
1681
|
+
if (normalized === "zh" || normalized === "zh-cn") {
|
|
1682
|
+
setAntdLocale(zhCN);
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
const loader =
|
|
1687
|
+
ANTD_LOCALE_LOADERS[normalized] ??
|
|
1688
|
+
ANTD_LOCALE_LOADERS[normalized.split("-")[0]];
|
|
1689
|
+
|
|
1690
|
+
if (loader) {
|
|
1691
|
+
let cancelled = false;
|
|
1692
|
+
loader().then((m) => {
|
|
1693
|
+
if (!cancelled) setAntdLocale(m.default);
|
|
1694
|
+
});
|
|
1695
|
+
return () => { cancelled = true; };
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
setAntdLocale(zhCN);
|
|
1699
|
+
}, [lang]);
|
|
1700
|
+
|
|
1701
|
+
return antdLocale;
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
function useExternalLocale() {
|
|
1705
|
+
useEffect(() => {
|
|
1706
|
+
const handler = (e: Event) => {
|
|
1707
|
+
const locale = (e as CustomEvent<string>).detail;
|
|
1708
|
+
if (typeof locale === "string" && locale) {
|
|
1709
|
+
useThemeStore.getState().setLocale(locale);
|
|
1710
|
+
}
|
|
1711
|
+
};
|
|
1712
|
+
|
|
1713
|
+
window.addEventListener("ywkf:locale-change", handler);
|
|
1714
|
+
return () => window.removeEventListener("ywkf:locale-change", handler);
|
|
1715
|
+
}, []);
|
|
1716
|
+
}`;
|
|
1717
|
+
function buildWrapperCode(opts) {
|
|
1718
|
+
const { darkMode, cssVar, globalReset, externalTheme, locale, prefixCls, scopePopupContainer } = opts;
|
|
1719
|
+
const cssVarRefLine = cssVar ? "\n const containerRef = useRef<HTMLDivElement>(null);" : "";
|
|
1720
|
+
const popupRefLine = scopePopupContainer ? "\n const popupContainerRef = useRef<HTMLDivElement>(null);" : "";
|
|
1721
|
+
const cssVarSyncLine = cssVar ? "\n useCssVarSync(containerRef);" : "";
|
|
1722
|
+
const appearanceSyncLine = darkMode ? "\n useAppearanceSync(appearance);" : "";
|
|
1723
|
+
const externalThemeLine = externalTheme ? "\n useExternalTheme();" : "";
|
|
1724
|
+
const isAutoLocale = locale === "auto";
|
|
1725
|
+
const localeEntry = ANTD_LOCALE_MAP[locale];
|
|
1726
|
+
const localeVarName = isAutoLocale ? "antdLocale" : localeEntry?.importName ?? "zhCN";
|
|
1727
|
+
const localeLine = isAutoLocale ? "\n const antdLocale = useAntdLocale();\n useExternalLocale();" : "";
|
|
1728
|
+
const getPopupContainerLine = scopePopupContainer ? `
|
|
1729
|
+
// \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
|
|
1730
|
+
// \u5FAE\u524D\u7AEF\u6A21\u5F0F\u4E0B\u4F18\u5148\u4F7F\u7528 styleContainer\uFF08\u5B50\u5E94\u7528\u6839\u5BB9\u5668\uFF09\uFF0C
|
|
1731
|
+
// \u666E\u901A\u6A21\u5F0F\u4E0B\u4F7F\u7528 popupContainerRef \u6240\u6307\u5411\u7684\u5305\u88C5 div\u3002
|
|
1732
|
+
const getPopupContainer = useCallback(
|
|
1733
|
+
(): HTMLElement =>
|
|
1734
|
+
(IS_MICRO_APP ? styleContainer : popupContainerRef.current) ?? document.body,
|
|
1735
|
+
[styleContainer],
|
|
1736
|
+
);` : "";
|
|
1737
|
+
const childrenSlot = cssVar ? `<div ref={containerRef} style={{ display: "contents" }}>
|
|
1738
|
+
{children}
|
|
1739
|
+
</div>` : "{children}";
|
|
1740
|
+
const innerContent = scopePopupContainer ? `<ConfigProvider locale={${localeVarName}} getPopupContainer={getPopupContainer}>
|
|
1741
|
+
${globalReset ? "<GlobalReset />" : ""}
|
|
1742
|
+
${childrenSlot}
|
|
1743
|
+
</ConfigProvider>` : `<ConfigProvider locale={${localeVarName}}>
|
|
1744
|
+
${globalReset ? "<GlobalReset />" : ""}
|
|
1745
|
+
${childrenSlot}
|
|
1746
|
+
</ConfigProvider>`;
|
|
1747
|
+
const antdProvider = `<AntdThemeProvider
|
|
1748
|
+
prefixCls={RUNTIME_PREFIX_CLS}
|
|
1749
|
+
appearance={resolvedAppearance}
|
|
1750
|
+
themeMode={appearance}
|
|
1751
|
+
theme={theme}${cssVar ? "\n customToken={{ cssVar: true }}" : ""}
|
|
1752
|
+
>
|
|
1753
|
+
${innerContent}
|
|
1754
|
+
</AntdThemeProvider>`;
|
|
1755
|
+
const wrapperOpen = scopePopupContainer ? `<div
|
|
1756
|
+
ref={popupContainerRef}
|
|
1757
|
+
data-ywkf-root
|
|
1758
|
+
style={{ position: "relative", height: "100%" }}
|
|
1759
|
+
>` : "";
|
|
1760
|
+
const wrapperClose = scopePopupContainer ? `</div>` : "";
|
|
1761
|
+
const innerReturn = `IS_MICRO_APP && styleContainer ? (
|
|
1762
|
+
<StyleProvider container={styleContainer}>{provider}</StyleProvider>
|
|
1763
|
+
) : provider`;
|
|
1764
|
+
const returnBody = scopePopupContainer ? `(
|
|
1765
|
+
${wrapperOpen}
|
|
1766
|
+
{IS_MICRO_APP && styleContainer ? (
|
|
1767
|
+
<StyleProvider container={styleContainer}>{provider}</StyleProvider>
|
|
1768
|
+
) : provider}
|
|
1769
|
+
${wrapperClose}
|
|
1770
|
+
)` : `(${innerReturn})`;
|
|
1771
|
+
return `
|
|
1772
|
+
// \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
|
|
1773
|
+
|
|
1774
|
+
// \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
|
|
1775
|
+
const IS_MICRO_APP = typeof window !== "undefined" && !!(window as any).__GARFISH__;
|
|
1776
|
+
|
|
1777
|
+
const RUNTIME_PREFIX_CLS = typeof process !== "undefined"
|
|
1778
|
+
&& process.env?.YWKF_PREFIX_CLS
|
|
1779
|
+
|| "${prefixCls}";
|
|
1780
|
+
|
|
1781
|
+
interface ThemeWrapperProps {
|
|
1782
|
+
children: ReactNode;
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1785
|
+
export function ThemeWrapper({ children }: ThemeWrapperProps) {${cssVarRefLine}${popupRefLine}
|
|
1786
|
+
const { appearance, primaryColor, neutralColor } = useThemeStore(
|
|
1787
|
+
(s) => ({ appearance: s.appearance, primaryColor: s.primaryColor, neutralColor: s.neutralColor }),
|
|
1788
|
+
shallow,
|
|
1789
|
+
);${cssVarSyncLine}${appearanceSyncLine}${externalThemeLine}${localeLine}
|
|
1790
|
+
|
|
1791
|
+
// \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
|
|
1792
|
+
// \u907F\u514D\u4E0E\u4E3B\u5E94\u7528\u5171\u4EAB\u7684 @ant-design/cssinjs StyleContext \u4EA7\u751F\u51B2\u7A81\u3002
|
|
1793
|
+
// useState lazy initializer \u540C\u6B65\u8BFB\u53D6\uFF08Garfish \u6302\u8F7D\u65F6\u5BB9\u5668\u5DF2\u63D0\u524D\u521B\u5EFA\uFF09\uFF0C
|
|
1794
|
+
// useEffect \u515C\u5E95\u5904\u7406\u6781\u7AEF\u7ADE\u6001\uFF08\u5982 SSR hydration\uFF09\u3002
|
|
1795
|
+
const [styleContainer, setStyleContainer] = useState<HTMLElement | undefined>(() => {
|
|
1796
|
+
if (!IS_MICRO_APP || typeof document === "undefined") return undefined;
|
|
1797
|
+
return document.getElementById(RUNTIME_PREFIX_CLS)?.parentElement ?? undefined;
|
|
1798
|
+
});
|
|
1799
|
+
|
|
1800
|
+
useEffect(() => {
|
|
1801
|
+
if (!IS_MICRO_APP) return;
|
|
1802
|
+
const el = document.getElementById(RUNTIME_PREFIX_CLS)?.parentElement;
|
|
1803
|
+
if (el) setStyleContainer(el);
|
|
1804
|
+
}, []);
|
|
1805
|
+
|
|
1806
|
+
const resolvedAppearance = useMemo(() => {
|
|
1807
|
+
if (appearance !== "auto") return appearance;
|
|
1808
|
+
if (typeof window === "undefined") return "light";
|
|
1809
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
1810
|
+
}, [appearance]);
|
|
1811
|
+
|
|
1812
|
+
const theme = useCallback<GetAntdTheme>(
|
|
1813
|
+
(ap) => createThemeConfig({
|
|
1814
|
+
appearance: ap as "light" | "dark",
|
|
1815
|
+
primaryColor,
|
|
1816
|
+
neutralColor,
|
|
1817
|
+
}),
|
|
1818
|
+
[primaryColor, neutralColor],
|
|
1819
|
+
);
|
|
1820
|
+
${getPopupContainerLine}
|
|
1821
|
+
const provider = (
|
|
1822
|
+
${antdProvider}
|
|
1823
|
+
);
|
|
1824
|
+
|
|
1825
|
+
return ${returnBody};
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
export default ThemeWrapper;
|
|
1829
|
+
`;
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
// src/plugin/builtin/zustand.ts
|
|
1833
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
1834
|
+
import { join as join7 } from "path";
|
|
1835
|
+
var zustandPlugin = createPlugin((options = {}) => ({
|
|
1836
|
+
name: "@4399ywkf/plugin-zustand",
|
|
1837
|
+
version: "1.0.0",
|
|
1838
|
+
description: "Zustand \u72B6\u6001\u7BA1\u7406\u96C6\u6210",
|
|
1839
|
+
setup(context) {
|
|
1840
|
+
const { scaffold = true, storeDir = "store" } = options;
|
|
1841
|
+
const { cwd, logger } = context;
|
|
1842
|
+
const storePath = join7(cwd, storeDir);
|
|
1843
|
+
if (scaffold && !existsSync6(storePath)) {
|
|
1844
|
+
scaffoldStore(storePath, logger);
|
|
1845
|
+
}
|
|
1846
|
+
const hooks = {
|
|
1847
|
+
modifyRspackConfig(rspackConfig) {
|
|
1848
|
+
logger.info("Zustand \u5DF2\u542F\u7528");
|
|
1849
|
+
const resolve4 = rspackConfig.resolve || {};
|
|
1850
|
+
const alias = resolve4.alias || {};
|
|
1851
|
+
alias["@store"] = storePath;
|
|
1852
|
+
resolve4.alias = alias;
|
|
1853
|
+
rspackConfig.resolve = resolve4;
|
|
1854
|
+
return rspackConfig;
|
|
1855
|
+
},
|
|
1856
|
+
generateFiles(_ctx) {
|
|
1857
|
+
return [
|
|
1858
|
+
{
|
|
1859
|
+
path: "store.ts",
|
|
1860
|
+
content: buildStoreHelperFile()
|
|
1861
|
+
}
|
|
1862
|
+
];
|
|
1863
|
+
}
|
|
1864
|
+
};
|
|
1865
|
+
return hooks;
|
|
1866
|
+
}
|
|
1867
|
+
}));
|
|
1868
|
+
function scaffoldStore(storePath, logger) {
|
|
1869
|
+
const dirs = [
|
|
1870
|
+
storePath,
|
|
1871
|
+
join7(storePath, "middleware"),
|
|
1872
|
+
join7(storePath, "app"),
|
|
1873
|
+
join7(storePath, "app", "slices"),
|
|
1874
|
+
join7(storePath, "app", "slices", "counter")
|
|
1875
|
+
];
|
|
1876
|
+
for (const dir of dirs) {
|
|
1877
|
+
if (!existsSync6(dir)) {
|
|
1878
|
+
mkdirSync2(dir, { recursive: true });
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
writeFileSync4(join7(storePath, "middleware", "createDevtools.ts"), TPL_CREATE_DEVTOOLS, "utf-8");
|
|
1882
|
+
writeFileSync4(
|
|
1883
|
+
join7(storePath, "app", "slices", "counter", "initialState.ts"),
|
|
1884
|
+
TPL_COUNTER_INITIAL_STATE,
|
|
1885
|
+
"utf-8"
|
|
1886
|
+
);
|
|
1887
|
+
writeFileSync4(
|
|
1888
|
+
join7(storePath, "app", "slices", "counter", "actions.ts"),
|
|
1889
|
+
TPL_COUNTER_ACTIONS,
|
|
1890
|
+
"utf-8"
|
|
1891
|
+
);
|
|
1892
|
+
writeFileSync4(join7(storePath, "app", "initialState.ts"), TPL_APP_INITIAL_STATE, "utf-8");
|
|
1893
|
+
writeFileSync4(join7(storePath, "app", "store.ts"), TPL_APP_STORE, "utf-8");
|
|
1894
|
+
writeFileSync4(join7(storePath, "app", "index.tsx"), TPL_APP_INDEX, "utf-8");
|
|
1895
|
+
writeFileSync4(join7(storePath, "index.ts"), TPL_STORE_INDEX, "utf-8");
|
|
1896
|
+
logger.info("\u5DF2\u751F\u6210 store/ \u811A\u624B\u67B6\uFF08Agent/Slice \u67B6\u6784\uFF09");
|
|
1897
|
+
}
|
|
1898
|
+
var TPL_CREATE_DEVTOOLS = `import type { DevtoolsOptions } from "zustand/middleware";
|
|
1899
|
+
import { devtools as devtoolsMiddleware } from "zustand/middleware";
|
|
1900
|
+
import type { StateCreator, StoreMutatorIdentifier } from "zustand/vanilla";
|
|
1901
|
+
|
|
1902
|
+
/**
|
|
1903
|
+
* \u5C01\u88C5 zustand devtools \u4E2D\u95F4\u4EF6
|
|
1904
|
+
* \u751F\u4EA7\u73AF\u5883\u81EA\u52A8\u7981\u7528\uFF0C\u5F00\u53D1\u73AF\u5883\u542F\u7528 trace
|
|
1905
|
+
*/
|
|
1906
|
+
export const createDevtools =
|
|
1907
|
+
(name: string, options?: DevtoolsOptions) =>
|
|
1908
|
+
<
|
|
1909
|
+
T,
|
|
1910
|
+
Mps extends [StoreMutatorIdentifier, unknown][] = [],
|
|
1911
|
+
Mcs extends [StoreMutatorIdentifier, unknown][] = [],
|
|
1912
|
+
>(
|
|
1913
|
+
initializer: StateCreator<T, [...Mps, ["zustand/devtools", never]], Mcs>,
|
|
1914
|
+
) =>
|
|
1915
|
+
devtoolsMiddleware(initializer, {
|
|
1916
|
+
name,
|
|
1917
|
+
enabled: process.env.NODE_ENV === "development",
|
|
1918
|
+
trace: process.env.NODE_ENV === "development",
|
|
1919
|
+
...options,
|
|
1920
|
+
});
|
|
1921
|
+
`;
|
|
1922
|
+
var TPL_COUNTER_INITIAL_STATE = `export interface CounterState {
|
|
1923
|
+
count: number;
|
|
1924
|
+
loading: boolean;
|
|
1925
|
+
error: string | null;
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
export const initialCounterState: CounterState = {
|
|
1929
|
+
count: 0,
|
|
1930
|
+
loading: false,
|
|
1931
|
+
error: null,
|
|
1932
|
+
};
|
|
1933
|
+
`;
|
|
1934
|
+
var TPL_COUNTER_ACTIONS = `import type { StateCreator } from "zustand/vanilla";
|
|
1935
|
+
import type { AppStore } from "../../store";
|
|
1936
|
+
|
|
1937
|
+
export interface CounterAction {
|
|
1938
|
+
increment: () => void;
|
|
1939
|
+
decrement: () => void;
|
|
1940
|
+
reset: () => void;
|
|
1941
|
+
setLoading: (loading: boolean) => void;
|
|
1942
|
+
setError: (error: string | null) => void;
|
|
1943
|
+
fetchCount: () => Promise<void>;
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
export const createCounterSlice: StateCreator<
|
|
1947
|
+
AppStore,
|
|
1948
|
+
[["zustand/devtools", never]],
|
|
1949
|
+
[],
|
|
1950
|
+
CounterAction
|
|
1951
|
+
> = (set, get) => ({
|
|
1952
|
+
increment: () => set((s) => ({ count: s.count + 1 })),
|
|
1953
|
+
decrement: () => set((s) => ({ count: s.count - 1 })),
|
|
1954
|
+
reset: () => set({ count: 0, error: null }),
|
|
1955
|
+
setLoading: (loading) => set({ loading }),
|
|
1956
|
+
setError: (error) => set({ error }),
|
|
1957
|
+
|
|
1958
|
+
fetchCount: async () => {
|
|
1959
|
+
set({ loading: true, error: null });
|
|
1960
|
+
try {
|
|
1961
|
+
// \u66FF\u6362\u4E3A\u5B9E\u9645 API \u8C03\u7528
|
|
1962
|
+
// import request from "@ywkf/request";
|
|
1963
|
+
// const res = await request.get("/api/count");
|
|
1964
|
+
// set({ count: res.data, loading: false });
|
|
1965
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
1966
|
+
set({ count: 42, loading: false });
|
|
1967
|
+
} catch (err) {
|
|
1968
|
+
set({
|
|
1969
|
+
error: err instanceof Error ? err.message : "\u672A\u77E5\u9519\u8BEF",
|
|
1970
|
+
loading: false,
|
|
1971
|
+
});
|
|
1972
|
+
}
|
|
1973
|
+
},
|
|
1974
|
+
});
|
|
1975
|
+
`;
|
|
1976
|
+
var TPL_APP_INITIAL_STATE = `import {
|
|
1977
|
+
type CounterState,
|
|
1978
|
+
initialCounterState,
|
|
1979
|
+
} from "./slices/counter/initialState";
|
|
1980
|
+
|
|
1981
|
+
// ============== \u805A\u5408\u6240\u6709 Slice \u72B6\u6001 ============== //
|
|
1982
|
+
|
|
1983
|
+
export type AppStoreState = CounterState;
|
|
1984
|
+
|
|
1985
|
+
export const initialState: AppStoreState = {
|
|
1986
|
+
...initialCounterState,
|
|
1987
|
+
};
|
|
1988
|
+
`;
|
|
1989
|
+
var TPL_APP_STORE = `import { subscribeWithSelector } from "zustand/middleware";
|
|
1990
|
+
import { shallow } from "zustand/shallow";
|
|
1991
|
+
import { createWithEqualityFn } from "zustand/traditional";
|
|
1992
|
+
import type { StateCreator } from "zustand/vanilla";
|
|
1993
|
+
|
|
1994
|
+
import { createDevtools } from "../middleware/createDevtools";
|
|
1995
|
+
import { type AppStoreState, initialState } from "./initialState";
|
|
1996
|
+
|
|
1997
|
+
import {
|
|
1998
|
+
type CounterAction,
|
|
1999
|
+
createCounterSlice,
|
|
2000
|
+
} from "./slices/counter/actions";
|
|
2001
|
+
|
|
2002
|
+
// ============== \u805A\u5408 Store \u7C7B\u578B ============== //
|
|
2003
|
+
|
|
2004
|
+
export type AppStore = AppStoreState & CounterAction;
|
|
2005
|
+
|
|
2006
|
+
// ============== \u521B\u5EFA Store ============== //
|
|
2007
|
+
|
|
2008
|
+
const createStore: StateCreator<AppStore, [["zustand/devtools", never]]> = (
|
|
2009
|
+
...parameters
|
|
2010
|
+
) => ({
|
|
2011
|
+
...initialState,
|
|
2012
|
+
...createCounterSlice(...parameters),
|
|
2013
|
+
});
|
|
2014
|
+
|
|
2015
|
+
// ============== \u5B9E\u88C5 useStore ============== //
|
|
2016
|
+
|
|
2017
|
+
const devtools = createDevtools("app");
|
|
2018
|
+
|
|
2019
|
+
export const useAppStore = createWithEqualityFn<AppStore>()(
|
|
2020
|
+
subscribeWithSelector(devtools(createStore)),
|
|
2021
|
+
shallow,
|
|
2022
|
+
);
|
|
2023
|
+
|
|
2024
|
+
export const getAppStoreState = () => useAppStore.getState();
|
|
2025
|
+
`;
|
|
2026
|
+
var TPL_APP_INDEX = `export { useAppStore, getAppStoreState } from "./store";
|
|
2027
|
+
export type { AppStore } from "./store";
|
|
2028
|
+
export { initialState } from "./initialState";
|
|
2029
|
+
export type { AppStoreState } from "./initialState";
|
|
2030
|
+
`;
|
|
2031
|
+
var TPL_STORE_INDEX = `/**
|
|
2032
|
+
* Store \u805A\u5408\u5165\u53E3
|
|
2033
|
+
*
|
|
2034
|
+
* \u67B6\u6784\uFF1A
|
|
2035
|
+
* - store/middleware/ \u2014 \u4E2D\u95F4\u4EF6\uFF08devtools \u7B49\uFF09
|
|
2036
|
+
* - store/{domain}/ \u2014 \u6BCF\u4E2A\u57DF\u4EE3\u8868\u4E00\u4E2A\u4E1A\u52A1\u5B50\u7CFB\u7EDF
|
|
2037
|
+
* - store/{domain}/slices \u2014 \u6BCF\u4E2A slice \u62C6\u4E3A initialState + actions
|
|
2038
|
+
*
|
|
2039
|
+
* \u7EA6\u675F\uFF1A
|
|
2040
|
+
* - initialState\uFF1A\u7EAF\u6570\u636E\u5B9A\u4E49\uFF0C\u4E0D\u542B\u903B\u8F91
|
|
2041
|
+
* - actions\uFF1A\u540C\u6B65\u72B6\u6001\u53D8\u66F4 + \u5F02\u6B65\u526F\u4F5C\u7528
|
|
2042
|
+
* - slice \u4E4B\u95F4\u7981\u6B62\u76F4\u63A5\u4FEE\u6539\u5F7C\u6B64\u72B6\u6001
|
|
2043
|
+
* - \u57DF\u5C42\u8D1F\u8D23\u8DE8 slice \u534F\u8C03
|
|
2044
|
+
*/
|
|
2045
|
+
export { useAppStore, getAppStoreState } from "./app";
|
|
2046
|
+
export type { AppStore, AppStoreState } from "./app";
|
|
2047
|
+
`;
|
|
2048
|
+
function buildStoreHelperFile() {
|
|
2049
|
+
return `// \u6B64\u6587\u4EF6\u7531 @4399ywkf/plugin-zustand \u81EA\u52A8\u751F\u6210
|
|
2050
|
+
// \u63D0\u4F9B Zustand \u5DE5\u5177\u51FD\u6570\u7684 re-export\uFF0C\u65B9\u4FBF\u4E1A\u52A1\u4EE3\u7801\u5F15\u7528
|
|
2051
|
+
|
|
2052
|
+
export { useShallow } from "zustand/react/shallow";
|
|
2053
|
+
export type { StateCreator, StoreApi } from "zustand";
|
|
2054
|
+
`;
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
// src/config/normalize.ts
|
|
2058
|
+
var BUILTIN_PLUGINS = [
|
|
2059
|
+
{ key: "reactQuery", factory: reactQueryPlugin, pluginName: "@4399ywkf/plugin-react-query" },
|
|
2060
|
+
{ key: "zustand", factory: zustandPlugin, pluginName: "@4399ywkf/plugin-zustand" },
|
|
2061
|
+
{ key: "tailwind", factory: tailwindPlugin, pluginName: "@4399ywkf/plugin-tailwind" }
|
|
2062
|
+
];
|
|
2063
|
+
var OPTIONAL_PLUGINS = [
|
|
2064
|
+
{ key: "biome", factory: biomePlugin, pluginName: "@4399ywkf/plugin-biome" },
|
|
2065
|
+
{ key: "analytics", factory: analyticsPlugin, pluginName: "@4399ywkf/plugin-analytics" },
|
|
2066
|
+
{ key: "mock", factory: mockPlugin, pluginName: "@4399ywkf/plugin-mock" },
|
|
2067
|
+
{ key: "theme", factory: themePlugin, pluginName: "@4399ywkf/plugin-theme" },
|
|
2068
|
+
{ key: "i18n", factory: i18nPlugin, pluginName: "@4399ywkf/plugin-i18n" },
|
|
2069
|
+
{ key: "garfish", factory: garfishPlugin, pluginName: "@4399ywkf/plugin-garfish" },
|
|
2070
|
+
{ key: "browserCheck", factory: browserCheckPlugin, pluginName: "@4399ywkf/plugin-browser-check" }
|
|
2071
|
+
];
|
|
2072
|
+
var ALL_SHORTHAND_KEYS = /* @__PURE__ */ new Set([
|
|
2073
|
+
...BUILTIN_PLUGINS.map((p) => p.key),
|
|
2074
|
+
...OPTIONAL_PLUGINS.map((p) => p.key)
|
|
2075
|
+
]);
|
|
2076
|
+
function normalizeConfig(input) {
|
|
2077
|
+
const raw = input;
|
|
2078
|
+
const result = { ...raw };
|
|
2079
|
+
const hasShorthands = hasAnyShorthand(result);
|
|
2080
|
+
if (!hasShorthands) {
|
|
2081
|
+
return input;
|
|
2082
|
+
}
|
|
2083
|
+
inferFields(result);
|
|
2084
|
+
const plugins = result.plugins;
|
|
2085
|
+
const explicitPlugins = plugins ?? [];
|
|
2086
|
+
const explicitNames = collectPluginNames(explicitPlugins);
|
|
2087
|
+
const shorthandPlugins = [];
|
|
2088
|
+
for (const { key, factory, pluginName } of BUILTIN_PLUGINS) {
|
|
2089
|
+
const value = result[key];
|
|
2090
|
+
if (explicitNames.has(pluginName)) continue;
|
|
2091
|
+
if (value === false) continue;
|
|
2092
|
+
if (value === void 0 && !hasShorthands) continue;
|
|
2093
|
+
const opts = typeof value === "object" ? value : void 0;
|
|
2094
|
+
shorthandPlugins.push(factory(opts));
|
|
2095
|
+
}
|
|
2096
|
+
for (const { key, factory, pluginName } of OPTIONAL_PLUGINS) {
|
|
2097
|
+
const value = result[key];
|
|
2098
|
+
if (value === void 0 || explicitNames.has(pluginName)) continue;
|
|
2099
|
+
const opts = value === true ? void 0 : value;
|
|
2100
|
+
shorthandPlugins.push(factory(opts));
|
|
2101
|
+
}
|
|
2102
|
+
result.plugins = [...shorthandPlugins, ...explicitPlugins];
|
|
2103
|
+
for (const key of ALL_SHORTHAND_KEYS) {
|
|
2104
|
+
delete result[key];
|
|
2105
|
+
}
|
|
2106
|
+
return result;
|
|
2107
|
+
}
|
|
2108
|
+
function inferFields(config) {
|
|
2109
|
+
const html = config.html ?? {};
|
|
2110
|
+
config.html = html;
|
|
2111
|
+
if (html.title === void 0 && config.appCName) {
|
|
2112
|
+
html.title = config.appCName;
|
|
2113
|
+
}
|
|
2114
|
+
if (html.mountRoot === void 0 && config.appName) {
|
|
2115
|
+
html.mountRoot = config.appName;
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
function hasAnyShorthand(config) {
|
|
2119
|
+
for (const key of ALL_SHORTHAND_KEYS) {
|
|
2120
|
+
if (config[key] !== void 0) {
|
|
2121
|
+
return true;
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
return false;
|
|
2125
|
+
}
|
|
2126
|
+
function collectPluginNames(plugins) {
|
|
2127
|
+
const names = /* @__PURE__ */ new Set();
|
|
2128
|
+
for (const p of plugins) {
|
|
2129
|
+
if (typeof p === "object" && p !== null && "name" in p) {
|
|
2130
|
+
names.add(p.name);
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
return names;
|
|
2134
|
+
}
|
|
19
2135
|
|
|
20
2136
|
// src/config/schema.ts
|
|
21
2137
|
var defaultConfig = {
|
|
@@ -80,8 +2196,8 @@ var defaultConfig = {
|
|
|
80
2196
|
var CONFIG_FILES = ["ywkf.config.ts", "ywkf.config.mts", "ywkf.config.js", "ywkf.config.mjs"];
|
|
81
2197
|
function findConfigFile(cwd) {
|
|
82
2198
|
for (const file of CONFIG_FILES) {
|
|
83
|
-
const configPath =
|
|
84
|
-
if (
|
|
2199
|
+
const configPath = resolve2(cwd, file);
|
|
2200
|
+
if (existsSync7(configPath)) {
|
|
85
2201
|
return configPath;
|
|
86
2202
|
}
|
|
87
2203
|
}
|
|
@@ -123,7 +2239,8 @@ async function resolveConfig(cwd) {
|
|
|
123
2239
|
}
|
|
124
2240
|
try {
|
|
125
2241
|
const userConfig = await loadConfigFile(configPath);
|
|
126
|
-
const
|
|
2242
|
+
const normalized = normalizeConfig(userConfig);
|
|
2243
|
+
const config = mergeConfig(normalized);
|
|
127
2244
|
return { config, configPath };
|
|
128
2245
|
} catch (error) {
|
|
129
2246
|
console.error("\u274C \u914D\u7F6E\u6587\u4EF6\u52A0\u8F7D\u5931\u8D25:", error);
|
|
@@ -132,7 +2249,7 @@ async function resolveConfig(cwd) {
|
|
|
132
2249
|
}
|
|
133
2250
|
function createPathResolver(cwd) {
|
|
134
2251
|
return {
|
|
135
|
-
resolveApp: (relativePath) =>
|
|
2252
|
+
resolveApp: (relativePath) => resolve2(cwd, relativePath),
|
|
136
2253
|
cwd
|
|
137
2254
|
};
|
|
138
2255
|
}
|
|
@@ -374,21 +2491,21 @@ import { merge } from "webpack-merge";
|
|
|
374
2491
|
|
|
375
2492
|
// src/rspack/base.ts
|
|
376
2493
|
import { createRequire } from "module";
|
|
377
|
-
import { dirname, join as
|
|
2494
|
+
import { dirname, join as join12 } from "path";
|
|
378
2495
|
import { fileURLToPath } from "url";
|
|
379
|
-
import { rspack } from "@rspack/core";
|
|
2496
|
+
import { rspack as rspack2 } from "@rspack/core";
|
|
380
2497
|
|
|
381
2498
|
// src/generator/plugin.ts
|
|
382
|
-
import { existsSync as
|
|
383
|
-
import { join as
|
|
2499
|
+
import { existsSync as existsSync11, watch } from "fs";
|
|
2500
|
+
import { join as join11 } from "path";
|
|
384
2501
|
|
|
385
2502
|
// src/generator/generator.ts
|
|
386
|
-
import { existsSync as
|
|
387
|
-
import { join as
|
|
2503
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
2504
|
+
import { join as join10 } from "path";
|
|
388
2505
|
|
|
389
2506
|
// src/router/generator.ts
|
|
390
|
-
import { existsSync as
|
|
391
|
-
import { join, relative } from "path";
|
|
2507
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync3, readdirSync as readdirSync2, statSync as statSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
2508
|
+
import { join as join8, relative } from "path";
|
|
392
2509
|
function winPath(path) {
|
|
393
2510
|
const isExtendedLengthPath = /^\\\\\?\\/.test(path);
|
|
394
2511
|
if (isExtendedLengthPath) {
|
|
@@ -421,7 +2538,7 @@ var ConventionalRouteGenerator = class {
|
|
|
421
2538
|
}
|
|
422
2539
|
generate() {
|
|
423
2540
|
const { pagesDir } = this.options;
|
|
424
|
-
if (!
|
|
2541
|
+
if (!existsSync8(pagesDir)) {
|
|
425
2542
|
console.warn(`[ywkf] \u9875\u9762\u76EE\u5F55\u4E0D\u5B58\u5728: ${pagesDir}`);
|
|
426
2543
|
return [];
|
|
427
2544
|
}
|
|
@@ -431,7 +2548,7 @@ var ConventionalRouteGenerator = class {
|
|
|
431
2548
|
* 扫描目录,生成路由树
|
|
432
2549
|
*/
|
|
433
2550
|
scanDirectory(dir, routePath) {
|
|
434
|
-
const entries =
|
|
2551
|
+
const entries = readdirSync2(dir);
|
|
435
2552
|
const layoutFile = entries.find((e) => CONVENTION_FILES.layout.test(e));
|
|
436
2553
|
const pageFile = entries.find((e) => CONVENTION_FILES.page.test(e));
|
|
437
2554
|
const errorFile = entries.find((e) => CONVENTION_FILES.error.test(e));
|
|
@@ -440,18 +2557,18 @@ var ConventionalRouteGenerator = class {
|
|
|
440
2557
|
const subDirs = entries.filter((e) => {
|
|
441
2558
|
if (e.startsWith(".")) return false;
|
|
442
2559
|
if (EXCLUDED_DIRS.has(e)) return false;
|
|
443
|
-
return
|
|
2560
|
+
return statSync2(join8(dir, e)).isDirectory();
|
|
444
2561
|
});
|
|
445
2562
|
const childRoutes = [];
|
|
446
2563
|
for (const subDir of subDirs) {
|
|
447
|
-
const subDirPath =
|
|
2564
|
+
const subDirPath = join8(dir, subDir);
|
|
448
2565
|
if (subDir.startsWith("__")) {
|
|
449
2566
|
const pathlessRoutes = this.scanDirectory(subDirPath, routePath);
|
|
450
|
-
const pathlessLayout =
|
|
2567
|
+
const pathlessLayout = readdirSync2(subDirPath).find((e) => CONVENTION_FILES.layout.test(e));
|
|
451
2568
|
if (pathlessLayout) {
|
|
452
2569
|
const pathlessChildren = this.scanDirectory(subDirPath, routePath);
|
|
453
2570
|
const pathlessLayoutRel = winPath(
|
|
454
|
-
relative(this.options.pagesDir,
|
|
2571
|
+
relative(this.options.pagesDir, join8(subDirPath, pathlessLayout))
|
|
455
2572
|
);
|
|
456
2573
|
childRoutes.push({
|
|
457
2574
|
path: routePath,
|
|
@@ -470,7 +2587,7 @@ var ConventionalRouteGenerator = class {
|
|
|
470
2587
|
childRoutes.push(...this.scanDirectory(subDirPath, subRoutePath));
|
|
471
2588
|
}
|
|
472
2589
|
const routeName = this.generateRouteName(routePath);
|
|
473
|
-
const relPath = (file) => winPath(relative(this.options.pagesDir,
|
|
2590
|
+
const relPath = (file) => winPath(relative(this.options.pagesDir, join8(dir, file)));
|
|
474
2591
|
if (layoutFile) {
|
|
475
2592
|
const layoutChildren = [];
|
|
476
2593
|
if (pageFile) {
|
|
@@ -728,10 +2845,10 @@ export default routes;
|
|
|
728
2845
|
write() {
|
|
729
2846
|
const { outputDir } = this.options;
|
|
730
2847
|
const code = this.generateCode();
|
|
731
|
-
if (!
|
|
732
|
-
|
|
2848
|
+
if (!existsSync8(outputDir)) {
|
|
2849
|
+
mkdirSync3(outputDir, { recursive: true });
|
|
733
2850
|
}
|
|
734
|
-
|
|
2851
|
+
writeFileSync5(join8(outputDir, "routes.tsx"), code, "utf-8");
|
|
735
2852
|
if (process.env.DEBUG) console.log(`[ywkf] \u7EA6\u5B9A\u5F0F\u8DEF\u7531\u5DF2\u751F\u6210`);
|
|
736
2853
|
}
|
|
737
2854
|
};
|
|
@@ -872,8 +2989,8 @@ ${startupBody}
|
|
|
872
2989
|
}
|
|
873
2990
|
|
|
874
2991
|
// src/generator/templates/env-types.ts
|
|
875
|
-
import { existsSync as
|
|
876
|
-
import { join as
|
|
2992
|
+
import { existsSync as existsSync9, readFileSync as readFileSync2 } from "fs";
|
|
2993
|
+
import { join as join9 } from "path";
|
|
877
2994
|
function generateEnvTypes(cwd, config) {
|
|
878
2995
|
const envVars = collectEnvVars(cwd, config);
|
|
879
2996
|
const envInterface = envVars.map((key) => ` readonly ${key}: string;`).join("\n");
|
|
@@ -946,19 +3063,19 @@ declare module "*.md" {
|
|
|
946
3063
|
}
|
|
947
3064
|
function collectEnvVars(cwd, config) {
|
|
948
3065
|
const envVars = /* @__PURE__ */ new Set();
|
|
949
|
-
const publicEnvPath =
|
|
950
|
-
if (
|
|
951
|
-
const content =
|
|
3066
|
+
const publicEnvPath = join9(cwd, config.env.publicEnvFile || "config/env/.env.public");
|
|
3067
|
+
if (existsSync9(publicEnvPath)) {
|
|
3068
|
+
const content = readFileSync2(publicEnvPath, "utf-8");
|
|
952
3069
|
parseEnvFile(content, envVars);
|
|
953
3070
|
}
|
|
954
|
-
const devEnvPath =
|
|
955
|
-
if (
|
|
956
|
-
const content =
|
|
3071
|
+
const devEnvPath = join9(cwd, config.env.envDir || "config/env", ".env.development");
|
|
3072
|
+
if (existsSync9(devEnvPath)) {
|
|
3073
|
+
const content = readFileSync2(devEnvPath, "utf-8");
|
|
957
3074
|
parseEnvFile(content, envVars);
|
|
958
3075
|
}
|
|
959
|
-
const prodEnvPath =
|
|
960
|
-
if (
|
|
961
|
-
const content =
|
|
3076
|
+
const prodEnvPath = join9(cwd, config.env.envDir || "config/env", ".env.production");
|
|
3077
|
+
if (existsSync9(prodEnvPath)) {
|
|
3078
|
+
const content = readFileSync2(prodEnvPath, "utf-8");
|
|
962
3079
|
parseEnvFile(content, envVars);
|
|
963
3080
|
}
|
|
964
3081
|
return Array.from(envVars).sort();
|
|
@@ -1000,7 +3117,7 @@ var YwkfGenerator = class {
|
|
|
1000
3117
|
constructor(context, pluginHooks = []) {
|
|
1001
3118
|
this.context = {
|
|
1002
3119
|
...context,
|
|
1003
|
-
outputDir: context.outputDir ||
|
|
3120
|
+
outputDir: context.outputDir || join10(context.cwd, ".ywkf")
|
|
1004
3121
|
};
|
|
1005
3122
|
this.pluginHooks = pluginHooks;
|
|
1006
3123
|
}
|
|
@@ -1010,7 +3127,7 @@ var YwkfGenerator = class {
|
|
|
1010
3127
|
async generate() {
|
|
1011
3128
|
const { outputDir } = this.context;
|
|
1012
3129
|
this.ensureDir(outputDir);
|
|
1013
|
-
this.ensureDir(
|
|
3130
|
+
this.ensureDir(join10(outputDir, "types"));
|
|
1014
3131
|
this.generateConfigSnapshot();
|
|
1015
3132
|
this.generateRoutes();
|
|
1016
3133
|
this.generateBootstrapFile();
|
|
@@ -1029,7 +3146,7 @@ var YwkfGenerator = class {
|
|
|
1029
3146
|
*/
|
|
1030
3147
|
generateConfigSnapshot() {
|
|
1031
3148
|
const { outputDir, config } = this.context;
|
|
1032
|
-
const configPath =
|
|
3149
|
+
const configPath = join10(outputDir, "config.json");
|
|
1033
3150
|
const serializableConfig = JSON.parse(
|
|
1034
3151
|
JSON.stringify(config, (_key, value) => {
|
|
1035
3152
|
if (typeof value === "function") {
|
|
@@ -1048,7 +3165,7 @@ var YwkfGenerator = class {
|
|
|
1048
3165
|
if (!config.router.conventional) {
|
|
1049
3166
|
return;
|
|
1050
3167
|
}
|
|
1051
|
-
const pagesDir =
|
|
3168
|
+
const pagesDir = join10(cwd, config.router.pagesDir || "src/pages");
|
|
1052
3169
|
const generator = new ConventionalRouteGenerator({
|
|
1053
3170
|
pagesDir,
|
|
1054
3171
|
outputDir,
|
|
@@ -1056,7 +3173,7 @@ var YwkfGenerator = class {
|
|
|
1056
3173
|
loading: config.router.loading
|
|
1057
3174
|
});
|
|
1058
3175
|
const content = generator.generateCode();
|
|
1059
|
-
const routesPath =
|
|
3176
|
+
const routesPath = join10(outputDir, "routes.tsx");
|
|
1060
3177
|
this.writeFileIfChanged(routesPath, content);
|
|
1061
3178
|
}
|
|
1062
3179
|
/**
|
|
@@ -1074,7 +3191,7 @@ var YwkfGenerator = class {
|
|
|
1074
3191
|
}
|
|
1075
3192
|
}
|
|
1076
3193
|
}
|
|
1077
|
-
const bootstrapPath =
|
|
3194
|
+
const bootstrapPath = join10(outputDir, "bootstrap.tsx");
|
|
1078
3195
|
this.writeFileIfChanged(bootstrapPath, content);
|
|
1079
3196
|
}
|
|
1080
3197
|
/**
|
|
@@ -1092,7 +3209,7 @@ var YwkfGenerator = class {
|
|
|
1092
3209
|
}
|
|
1093
3210
|
}
|
|
1094
3211
|
}
|
|
1095
|
-
const entryPath =
|
|
3212
|
+
const entryPath = join10(outputDir, "index.tsx");
|
|
1096
3213
|
this.writeFileIfChanged(entryPath, content);
|
|
1097
3214
|
}
|
|
1098
3215
|
/**
|
|
@@ -1128,8 +3245,8 @@ var YwkfGenerator = class {
|
|
|
1128
3245
|
const files = hooks.generateFiles(this.context);
|
|
1129
3246
|
if (files) {
|
|
1130
3247
|
for (const file of files) {
|
|
1131
|
-
const filePath =
|
|
1132
|
-
const dir =
|
|
3248
|
+
const filePath = join10(outputDir, file.path);
|
|
3249
|
+
const dir = join10(filePath, "..");
|
|
1133
3250
|
this.ensureDir(dir);
|
|
1134
3251
|
this.writeFileIfChanged(filePath, file.content);
|
|
1135
3252
|
}
|
|
@@ -1143,18 +3260,18 @@ var YwkfGenerator = class {
|
|
|
1143
3260
|
generateTypes() {
|
|
1144
3261
|
const { outputDir, config, cwd } = this.context;
|
|
1145
3262
|
const envTypesContent = generateEnvTypes(cwd, config);
|
|
1146
|
-
this.writeFileIfChanged(
|
|
3263
|
+
this.writeFileIfChanged(join10(outputDir, "types", "env.d.ts"), envTypesContent);
|
|
1147
3264
|
if (config.router.conventional) {
|
|
1148
3265
|
const routeTypesContent = generateRouteTypes();
|
|
1149
|
-
this.writeFileIfChanged(
|
|
3266
|
+
this.writeFileIfChanged(join10(outputDir, "types", "routes.d.ts"), routeTypesContent);
|
|
1150
3267
|
}
|
|
1151
3268
|
}
|
|
1152
3269
|
/**
|
|
1153
3270
|
* 确保目录存在
|
|
1154
3271
|
*/
|
|
1155
3272
|
ensureDir(dir) {
|
|
1156
|
-
if (!
|
|
1157
|
-
|
|
3273
|
+
if (!existsSync10(dir)) {
|
|
3274
|
+
mkdirSync4(dir, { recursive: true });
|
|
1158
3275
|
}
|
|
1159
3276
|
}
|
|
1160
3277
|
/**
|
|
@@ -1166,14 +3283,14 @@ var YwkfGenerator = class {
|
|
|
1166
3283
|
if (lastContent === normalizedContent) {
|
|
1167
3284
|
return;
|
|
1168
3285
|
}
|
|
1169
|
-
if (
|
|
1170
|
-
const existingContent =
|
|
3286
|
+
if (existsSync10(filePath)) {
|
|
3287
|
+
const existingContent = readFileSync3(filePath, "utf-8");
|
|
1171
3288
|
if (this.normalizeContent(existingContent) === normalizedContent) {
|
|
1172
3289
|
this.lastGeneratedContent.set(filePath, normalizedContent);
|
|
1173
3290
|
return;
|
|
1174
3291
|
}
|
|
1175
3292
|
}
|
|
1176
|
-
|
|
3293
|
+
writeFileSync6(filePath, content, "utf-8");
|
|
1177
3294
|
this.lastGeneratedContent.set(filePath, normalizedContent);
|
|
1178
3295
|
}
|
|
1179
3296
|
/**
|
|
@@ -1303,8 +3420,8 @@ var YwkfGeneratorPlugin = class {
|
|
|
1303
3420
|
*/
|
|
1304
3421
|
watchPages() {
|
|
1305
3422
|
const { config, cwd } = this.options;
|
|
1306
|
-
const pagesDir =
|
|
1307
|
-
if (!
|
|
3423
|
+
const pagesDir = join11(cwd, config.router.pagesDir || "src/pages");
|
|
3424
|
+
if (!existsSync11(pagesDir)) {
|
|
1308
3425
|
return;
|
|
1309
3426
|
}
|
|
1310
3427
|
this.isWatching = true;
|
|
@@ -1371,7 +3488,7 @@ var YwkfGeneratorPlugin = class {
|
|
|
1371
3488
|
// src/rspack/base.ts
|
|
1372
3489
|
var require2 = createRequire(import.meta.url);
|
|
1373
3490
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
1374
|
-
var coreNodeModules =
|
|
3491
|
+
var coreNodeModules = join12(__dirname, "../../node_modules");
|
|
1375
3492
|
function createBaseConfig(config, cwd, options = {}) {
|
|
1376
3493
|
const isDev = options.isDev ?? process.env.NODE_ENV !== "production";
|
|
1377
3494
|
const { resolveApp } = createPathResolver(cwd);
|
|
@@ -1384,16 +3501,16 @@ function createBaseConfig(config, cwd, options = {}) {
|
|
|
1384
3501
|
"@locales": resolveApp("locales"),
|
|
1385
3502
|
"@public": resolveApp("public"),
|
|
1386
3503
|
// 约定式路由生成的文件
|
|
1387
|
-
"@ywkf/routes":
|
|
3504
|
+
"@ywkf/routes": join12(ywkfOutputDir, "routes.tsx"),
|
|
1388
3505
|
// 请求层(由 reactQueryPlugin 生成)
|
|
1389
|
-
"@ywkf/request":
|
|
3506
|
+
"@ywkf/request": join12(ywkfOutputDir, "request.ts"),
|
|
1390
3507
|
// Store 工具(由 zustandPlugin 生成)
|
|
1391
|
-
"@ywkf/store":
|
|
3508
|
+
"@ywkf/store": join12(ywkfOutputDir, "store.ts")
|
|
1392
3509
|
};
|
|
1393
3510
|
const alias = { ...defaultAlias, ...userAlias };
|
|
1394
3511
|
return {
|
|
1395
3512
|
// 使用 .ywkf/index.tsx 作为入口(由框架生成)
|
|
1396
|
-
entry: [
|
|
3513
|
+
entry: [join12(ywkfOutputDir, "index.tsx")],
|
|
1397
3514
|
resolve: {
|
|
1398
3515
|
extensions: [".ts", ".tsx", ".jsx", ".js", ".json", ".mjs"],
|
|
1399
3516
|
symlinks: true,
|
|
@@ -1493,14 +3610,14 @@ function createBaseConfig(config, cwd, options = {}) {
|
|
|
1493
3610
|
]
|
|
1494
3611
|
},
|
|
1495
3612
|
plugins: [
|
|
1496
|
-
new
|
|
3613
|
+
new rspack2.ProvidePlugin({
|
|
1497
3614
|
process: "process/browser.js",
|
|
1498
3615
|
Buffer: ["buffer", "Buffer"]
|
|
1499
3616
|
}),
|
|
1500
|
-
new
|
|
3617
|
+
new rspack2.DefinePlugin({
|
|
1501
3618
|
"process.env": JSON.stringify(process.env)
|
|
1502
3619
|
}),
|
|
1503
|
-
new
|
|
3620
|
+
new rspack2.HtmlRspackPlugin({
|
|
1504
3621
|
template: resolveApp(html.template ?? "public/index.html"),
|
|
1505
3622
|
filename: "index.html",
|
|
1506
3623
|
inject: "body",
|
|
@@ -1512,7 +3629,7 @@ function createBaseConfig(config, cwd, options = {}) {
|
|
|
1512
3629
|
mountRoot: html.mountRoot ?? appName
|
|
1513
3630
|
}
|
|
1514
3631
|
}),
|
|
1515
|
-
new
|
|
3632
|
+
new rspack2.CopyRspackPlugin({
|
|
1516
3633
|
patterns: [
|
|
1517
3634
|
{
|
|
1518
3635
|
from: resolveApp("public/images"),
|
|
@@ -1775,7 +3892,7 @@ function createDevConfig(config, cwd) {
|
|
|
1775
3892
|
}
|
|
1776
3893
|
|
|
1777
3894
|
// src/rspack/prod.ts
|
|
1778
|
-
import { rspack as
|
|
3895
|
+
import { rspack as rspack3 } from "@rspack/core";
|
|
1779
3896
|
import { merge as merge2 } from "webpack-merge";
|
|
1780
3897
|
function createProdConfig(config, cwd) {
|
|
1781
3898
|
const baseConfig = createBaseConfig(config, cwd, { isDev: false });
|
|
@@ -1788,7 +3905,7 @@ function createProdConfig(config, cwd) {
|
|
|
1788
3905
|
test: /\.less$/,
|
|
1789
3906
|
include: [/[\\/]node_modules[\\/].*antd/, /[\\/]node_modules[\\/]@4399ywkf[\\/]design/],
|
|
1790
3907
|
use: [
|
|
1791
|
-
{ loader:
|
|
3908
|
+
{ loader: rspack3.CssExtractRspackPlugin.loader },
|
|
1792
3909
|
{
|
|
1793
3910
|
loader: "css-loader",
|
|
1794
3911
|
options: { sourceMap: true }
|
|
@@ -1816,7 +3933,7 @@ function createProdConfig(config, cwd) {
|
|
|
1816
3933
|
{
|
|
1817
3934
|
test: /\.module\.less$/,
|
|
1818
3935
|
use: [
|
|
1819
|
-
{ loader:
|
|
3936
|
+
{ loader: rspack3.CssExtractRspackPlugin.loader },
|
|
1820
3937
|
{
|
|
1821
3938
|
loader: "css-loader",
|
|
1822
3939
|
options: {
|
|
@@ -1841,7 +3958,7 @@ function createProdConfig(config, cwd) {
|
|
|
1841
3958
|
},
|
|
1842
3959
|
{
|
|
1843
3960
|
use: [
|
|
1844
|
-
{ loader:
|
|
3961
|
+
{ loader: rspack3.CssExtractRspackPlugin.loader },
|
|
1845
3962
|
{
|
|
1846
3963
|
loader: "css-loader",
|
|
1847
3964
|
options: { sourceMap: true }
|
|
@@ -1871,7 +3988,7 @@ function createProdConfig(config, cwd) {
|
|
|
1871
3988
|
{
|
|
1872
3989
|
test: /\.module\.s[ac]ss$/,
|
|
1873
3990
|
use: [
|
|
1874
|
-
{ loader:
|
|
3991
|
+
{ loader: rspack3.CssExtractRspackPlugin.loader },
|
|
1875
3992
|
{
|
|
1876
3993
|
loader: "css-loader",
|
|
1877
3994
|
options: {
|
|
@@ -1896,7 +4013,7 @@ function createProdConfig(config, cwd) {
|
|
|
1896
4013
|
},
|
|
1897
4014
|
{
|
|
1898
4015
|
use: [
|
|
1899
|
-
{ loader:
|
|
4016
|
+
{ loader: rspack3.CssExtractRspackPlugin.loader },
|
|
1900
4017
|
{
|
|
1901
4018
|
loader: "css-loader",
|
|
1902
4019
|
options: { sourceMap: true }
|
|
@@ -1922,7 +4039,7 @@ function createProdConfig(config, cwd) {
|
|
|
1922
4039
|
test: /\.css$/,
|
|
1923
4040
|
include: [resolveApp("src/index.css")],
|
|
1924
4041
|
use: [
|
|
1925
|
-
{ loader:
|
|
4042
|
+
{ loader: rspack3.CssExtractRspackPlugin.loader },
|
|
1926
4043
|
"css-loader",
|
|
1927
4044
|
{
|
|
1928
4045
|
loader: "postcss-loader",
|
|
@@ -1941,7 +4058,7 @@ function createProdConfig(config, cwd) {
|
|
|
1941
4058
|
include: [resolveApp("src")],
|
|
1942
4059
|
exclude: style.tailwindcss ? [resolveApp("src/index.css")] : [],
|
|
1943
4060
|
use: [
|
|
1944
|
-
{ loader:
|
|
4061
|
+
{ loader: rspack3.CssExtractRspackPlugin.loader },
|
|
1945
4062
|
{
|
|
1946
4063
|
loader: "css-loader",
|
|
1947
4064
|
options: { sourceMap: true }
|
|
@@ -1972,7 +4089,7 @@ function createProdConfig(config, cwd) {
|
|
|
1972
4089
|
},
|
|
1973
4090
|
module: styleRules,
|
|
1974
4091
|
plugins: [
|
|
1975
|
-
new
|
|
4092
|
+
new rspack3.CssExtractRspackPlugin({
|
|
1976
4093
|
filename: "[name].[contenthash:8].css",
|
|
1977
4094
|
chunkFilename: "[name].[contenthash:8].chunk.css"
|
|
1978
4095
|
})
|
|
@@ -1980,7 +4097,7 @@ function createProdConfig(config, cwd) {
|
|
|
1980
4097
|
optimization: {
|
|
1981
4098
|
minimize: true,
|
|
1982
4099
|
minimizer: [
|
|
1983
|
-
new
|
|
4100
|
+
new rspack3.SwcJsMinimizerRspackPlugin({
|
|
1984
4101
|
exclude: [resolveApp("node_modules")],
|
|
1985
4102
|
minimizerOptions: {
|
|
1986
4103
|
compress: {
|
|
@@ -1990,7 +4107,7 @@ function createProdConfig(config, cwd) {
|
|
|
1990
4107
|
}
|
|
1991
4108
|
}
|
|
1992
4109
|
}),
|
|
1993
|
-
new
|
|
4110
|
+
new rspack3.LightningCssMinimizerRspackPlugin()
|
|
1994
4111
|
]
|
|
1995
4112
|
}
|
|
1996
4113
|
};
|
|
@@ -2017,27 +4134,27 @@ function createRspackConfig(config, cwd, options = {}) {
|
|
|
2017
4134
|
}
|
|
2018
4135
|
|
|
2019
4136
|
// src/cli/env.ts
|
|
2020
|
-
import { existsSync as
|
|
2021
|
-
import { resolve as
|
|
4137
|
+
import { existsSync as existsSync12, readFileSync as readFileSync4 } from "fs";
|
|
4138
|
+
import { resolve as resolve3 } from "path";
|
|
2022
4139
|
import dotenv from "dotenv";
|
|
2023
4140
|
function preloadEnv(cwd, mode, nodeEnv) {
|
|
2024
4141
|
process.env.NODE_ENV = nodeEnv;
|
|
2025
4142
|
process.env.APP_MODE = mode;
|
|
2026
4143
|
const defaultPaths = [
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
4144
|
+
resolve3(cwd, "config/env/.env.public"),
|
|
4145
|
+
resolve3(cwd, ".env.public"),
|
|
4146
|
+
resolve3(cwd, ".env")
|
|
2030
4147
|
];
|
|
2031
4148
|
for (const envPath of defaultPaths) {
|
|
2032
|
-
if (
|
|
4149
|
+
if (existsSync12(envPath)) {
|
|
2033
4150
|
dotenv.config({ path: envPath });
|
|
2034
4151
|
break;
|
|
2035
4152
|
}
|
|
2036
4153
|
}
|
|
2037
|
-
const modeEnvPaths = [
|
|
4154
|
+
const modeEnvPaths = [resolve3(cwd, `config/env/.env.${mode}`), resolve3(cwd, `.env.${mode}`)];
|
|
2038
4155
|
for (const envPath of modeEnvPaths) {
|
|
2039
|
-
if (
|
|
2040
|
-
const envContent =
|
|
4156
|
+
if (existsSync12(envPath)) {
|
|
4157
|
+
const envContent = readFileSync4(envPath, "utf-8");
|
|
2041
4158
|
const parsed = dotenv.parse(envContent);
|
|
2042
4159
|
for (const key in parsed) {
|
|
2043
4160
|
process.env[key] = parsed[key];
|
|
@@ -2050,13 +4167,13 @@ function preloadEnv(cwd, mode, nodeEnv) {
|
|
|
2050
4167
|
}
|
|
2051
4168
|
function loadEnv(config, cwd, mode, nodeEnv) {
|
|
2052
4169
|
const { env } = config;
|
|
2053
|
-
const publicEnvPath =
|
|
2054
|
-
if (
|
|
4170
|
+
const publicEnvPath = resolve3(cwd, env.publicEnvFile ?? "config/env/.env.public");
|
|
4171
|
+
if (existsSync12(publicEnvPath)) {
|
|
2055
4172
|
dotenv.config({ path: publicEnvPath });
|
|
2056
4173
|
}
|
|
2057
|
-
const modeEnvPath =
|
|
2058
|
-
if (
|
|
2059
|
-
const envContent =
|
|
4174
|
+
const modeEnvPath = resolve3(cwd, env.envDir ?? "config/env", `.env.${mode}`);
|
|
4175
|
+
if (existsSync12(modeEnvPath)) {
|
|
4176
|
+
const envContent = readFileSync4(modeEnvPath, "utf-8");
|
|
2060
4177
|
const parsed = dotenv.parse(envContent);
|
|
2061
4178
|
for (const key in parsed) {
|
|
2062
4179
|
process.env[key] = parsed[key];
|
|
@@ -2075,7 +4192,7 @@ function loadEnv(config, cwd, mode, nodeEnv) {
|
|
|
2075
4192
|
// src/cli/printer.ts
|
|
2076
4193
|
import { createRequire as createRequire3 } from "module";
|
|
2077
4194
|
import os from "os";
|
|
2078
|
-
import { dirname as dirname2, join as
|
|
4195
|
+
import { dirname as dirname2, join as join13 } from "path";
|
|
2079
4196
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2080
4197
|
import chalk from "chalk";
|
|
2081
4198
|
var __filename = fileURLToPath2(import.meta.url);
|
|
@@ -2085,7 +4202,7 @@ var _version = null;
|
|
|
2085
4202
|
function getFrameworkVersion() {
|
|
2086
4203
|
if (_version) return _version;
|
|
2087
4204
|
try {
|
|
2088
|
-
const pkgPath =
|
|
4205
|
+
const pkgPath = join13(__dirname2, "../../package.json");
|
|
2089
4206
|
const pkg = require3(pkgPath);
|
|
2090
4207
|
_version = pkg.version || "0.0.0";
|
|
2091
4208
|
} catch {
|
|
@@ -2328,11 +4445,11 @@ async function build(options = {}) {
|
|
|
2328
4445
|
printer.printBuildStart();
|
|
2329
4446
|
printer.updateProgress(0, "preparing");
|
|
2330
4447
|
rspackConfig.plugins = rspackConfig.plugins || [];
|
|
2331
|
-
rspackConfig.plugins.push(new
|
|
4448
|
+
rspackConfig.plugins.push(new rspack4.ProgressPlugin(createProgressHandler(printer)));
|
|
2332
4449
|
rspackConfig.stats = "none";
|
|
2333
4450
|
rspackConfig.infrastructureLogging = { level: "none" };
|
|
2334
|
-
const compiler =
|
|
2335
|
-
const stats = await new Promise((
|
|
4451
|
+
const compiler = rspack4(rspackConfig);
|
|
4452
|
+
const stats = await new Promise((resolve4, reject) => {
|
|
2336
4453
|
compiler.run((err, stats2) => {
|
|
2337
4454
|
if (err) {
|
|
2338
4455
|
reject(err);
|
|
@@ -2342,13 +4459,13 @@ async function build(options = {}) {
|
|
|
2342
4459
|
reject(new Error("Build failed: no stats available"));
|
|
2343
4460
|
return;
|
|
2344
4461
|
}
|
|
2345
|
-
|
|
4462
|
+
resolve4(stats2);
|
|
2346
4463
|
});
|
|
2347
4464
|
});
|
|
2348
|
-
await new Promise((
|
|
4465
|
+
await new Promise((resolve4, reject) => {
|
|
2349
4466
|
compiler.close((err) => {
|
|
2350
4467
|
if (err) reject(err);
|
|
2351
|
-
else
|
|
4468
|
+
else resolve4();
|
|
2352
4469
|
});
|
|
2353
4470
|
});
|
|
2354
4471
|
const hasErrors = stats.hasErrors();
|
|
@@ -2372,24 +4489,24 @@ async function build(options = {}) {
|
|
|
2372
4489
|
}
|
|
2373
4490
|
|
|
2374
4491
|
// src/cli/dev.ts
|
|
2375
|
-
import { rspack as
|
|
4492
|
+
import { rspack as rspack5 } from "@rspack/core";
|
|
2376
4493
|
import { RspackDevServer } from "@rspack/dev-server";
|
|
2377
4494
|
import chalk3 from "chalk";
|
|
2378
4495
|
|
|
2379
4496
|
// src/cli/port.ts
|
|
2380
4497
|
import net from "net";
|
|
2381
4498
|
function isPortAvailable(port, host) {
|
|
2382
|
-
return new Promise((
|
|
4499
|
+
return new Promise((resolve4) => {
|
|
2383
4500
|
const server = net.createServer();
|
|
2384
4501
|
server.once("error", (err) => {
|
|
2385
4502
|
if (err.code === "EADDRINUSE") {
|
|
2386
|
-
|
|
4503
|
+
resolve4(false);
|
|
2387
4504
|
} else {
|
|
2388
|
-
|
|
4505
|
+
resolve4(false);
|
|
2389
4506
|
}
|
|
2390
4507
|
});
|
|
2391
4508
|
server.once("listening", () => {
|
|
2392
|
-
server.close(() =>
|
|
4509
|
+
server.close(() => resolve4(true));
|
|
2393
4510
|
});
|
|
2394
4511
|
server.listen(port, host);
|
|
2395
4512
|
});
|
|
@@ -2434,7 +4551,7 @@ async function dev(options = {}) {
|
|
|
2434
4551
|
printer.printBuildStart();
|
|
2435
4552
|
printer.updateProgress(0, "preparing");
|
|
2436
4553
|
rspackConfig.plugins = rspackConfig.plugins || [];
|
|
2437
|
-
rspackConfig.plugins.push(new
|
|
4554
|
+
rspackConfig.plugins.push(new rspack5.ProgressPlugin(createProgressHandler(printer)));
|
|
2438
4555
|
rspackConfig.stats = "none";
|
|
2439
4556
|
rspackConfig.infrastructureLogging = { level: "none" };
|
|
2440
4557
|
if (rspackConfig.devServer) {
|
|
@@ -2447,7 +4564,7 @@ async function dev(options = {}) {
|
|
|
2447
4564
|
progress: false
|
|
2448
4565
|
};
|
|
2449
4566
|
}
|
|
2450
|
-
const compiler =
|
|
4567
|
+
const compiler = rspack5(rspackConfig);
|
|
2451
4568
|
compiler.hooks.done.tap("ywkf-dev-printer", (stats) => {
|
|
2452
4569
|
const hasErrors = stats.hasErrors();
|
|
2453
4570
|
if (hasErrors) {
|
|
@@ -2502,7 +4619,7 @@ var __dirname3 = dirname3(__filename2);
|
|
|
2502
4619
|
var require4 = createRequire4(import.meta.url);
|
|
2503
4620
|
var version = "0.0.0";
|
|
2504
4621
|
try {
|
|
2505
|
-
const pkg = require4(
|
|
4622
|
+
const pkg = require4(join14(__dirname3, "../../package.json"));
|
|
2506
4623
|
version = pkg.version;
|
|
2507
4624
|
} catch {
|
|
2508
4625
|
}
|