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