@faapi/faapi 0.0.0-canary.2a7b6a3 → 0.0.0-canary.4e89b9b
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 +465 -209
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +54 -10
- package/dist/index.js +396 -338
- package/dist/index.js.map +1 -1
- package/package.json +7 -4
package/dist/cli/index.js
CHANGED
|
@@ -78,17 +78,47 @@ function toProdImportPath(sourceFile, importer) {
|
|
|
78
78
|
if (!rel.startsWith(".")) rel = "./" + rel;
|
|
79
79
|
return toProdExtension(rel);
|
|
80
80
|
}
|
|
81
|
-
function
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
81
|
+
function toRealPath(p) {
|
|
82
|
+
try {
|
|
83
|
+
return fs2.realpathSync(p);
|
|
84
|
+
} catch {
|
|
85
|
+
return p;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function isInsideDir(filePath, dir) {
|
|
89
|
+
const rel = path2.relative(dir, filePath);
|
|
90
|
+
return rel !== "" && !rel.startsWith("..") && !path2.isAbsolute(rel);
|
|
91
|
+
}
|
|
92
|
+
function toStrippedProdImportPath(sourceFile, rootDir) {
|
|
93
|
+
const appDirAbs = toRealPath(path2.resolve(rootDir, APP_DIR));
|
|
94
|
+
const sourceReal = toRealPath(sourceFile);
|
|
95
|
+
let rel = path2.relative(appDirAbs, sourceReal);
|
|
96
|
+
rel = rel.split(path2.sep).join("/");
|
|
97
|
+
if (!rel.startsWith(".")) rel = "./" + rel;
|
|
98
|
+
return toProdExtension(rel);
|
|
99
|
+
}
|
|
100
|
+
function resolveRelativeSpecifier(importer, specifier) {
|
|
101
|
+
const importerDir = path2.dirname(importer);
|
|
102
|
+
const base = path2.resolve(importerDir, specifier);
|
|
103
|
+
if (PROD_EXTS.some((ext) => specifier.endsWith(ext))) {
|
|
104
|
+
return fs2.existsSync(base) ? base : null;
|
|
105
|
+
}
|
|
106
|
+
if (/\.(ts|tsx|jsx)$/.test(specifier)) {
|
|
107
|
+
return fs2.existsSync(base) ? base : null;
|
|
108
|
+
}
|
|
109
|
+
for (const ext of SOURCE_EXTS) {
|
|
110
|
+
const file = base + ext;
|
|
111
|
+
if (fs2.existsSync(file)) return file;
|
|
112
|
+
}
|
|
113
|
+
for (const indexExt of INDEX_EXTS) {
|
|
114
|
+
const file = base + indexExt;
|
|
115
|
+
if (fs2.existsSync(file)) return file;
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
function createAliasPlugin(config, options) {
|
|
120
|
+
const SPEC_RE2 = /(\bfrom\s*|import\s*\(\s*)(['"])([^'"]+)\2/g;
|
|
121
|
+
const appDirAbs = options?.rootDir ? toRealPath(path2.resolve(options.rootDir, APP_DIR)) : null;
|
|
92
122
|
return {
|
|
93
123
|
name: "faapi-alias",
|
|
94
124
|
setup(build) {
|
|
@@ -100,17 +130,42 @@ function createAliasPlugin(config) {
|
|
|
100
130
|
return void 0;
|
|
101
131
|
}
|
|
102
132
|
const importer = args.path;
|
|
133
|
+
const importerOutsideAppDir = appDirAbs ? !isInsideDir(importer, appDirAbs) : false;
|
|
103
134
|
let modified = false;
|
|
104
|
-
const newSource = source.replace(
|
|
105
|
-
if (specifier.startsWith("
|
|
135
|
+
const newSource = source.replace(SPEC_RE2, (full, prefix, quote, specifier) => {
|
|
136
|
+
if (specifier.startsWith("/") || specifier.startsWith("file:") || specifier.startsWith("node:")) {
|
|
137
|
+
return full;
|
|
138
|
+
}
|
|
139
|
+
if (specifier.startsWith("./") || specifier.startsWith("../")) {
|
|
140
|
+
const resolved = resolveRelativeSpecifier(importer, specifier);
|
|
141
|
+
if (resolved) {
|
|
142
|
+
if (PROD_EXTS.some((ext) => specifier.endsWith(ext))) {
|
|
143
|
+
return full;
|
|
144
|
+
}
|
|
145
|
+
if (appDirAbs && importerOutsideAppDir && isInsideDir(resolved, appDirAbs)) {
|
|
146
|
+
modified = true;
|
|
147
|
+
return `${prefix}${quote}${toStrippedProdImportPath(
|
|
148
|
+
resolved,
|
|
149
|
+
options.rootDir
|
|
150
|
+
)}${quote}`;
|
|
151
|
+
}
|
|
152
|
+
modified = true;
|
|
153
|
+
return `${prefix}${quote}${toProdImportPath(resolved, importer)}${quote}`;
|
|
154
|
+
}
|
|
106
155
|
return full;
|
|
107
156
|
}
|
|
108
157
|
const candidates = resolveAlias(specifier, config);
|
|
109
158
|
for (const candidate of candidates) {
|
|
110
|
-
for (const ext of
|
|
159
|
+
for (const ext of SOURCE_EXTS) {
|
|
111
160
|
const file = candidate + ext;
|
|
112
161
|
if (fs2.existsSync(file)) {
|
|
113
162
|
modified = true;
|
|
163
|
+
if (appDirAbs && importerOutsideAppDir && isInsideDir(file, appDirAbs)) {
|
|
164
|
+
return `${prefix}${quote}${toStrippedProdImportPath(
|
|
165
|
+
file,
|
|
166
|
+
options.rootDir
|
|
167
|
+
)}${quote}`;
|
|
168
|
+
}
|
|
114
169
|
return `${prefix}${quote}${toProdImportPath(file, importer)}${quote}`;
|
|
115
170
|
}
|
|
116
171
|
}
|
|
@@ -118,6 +173,12 @@ function createAliasPlugin(config) {
|
|
|
118
173
|
const file = candidate + indexExt;
|
|
119
174
|
if (fs2.existsSync(file)) {
|
|
120
175
|
modified = true;
|
|
176
|
+
if (appDirAbs && importerOutsideAppDir && isInsideDir(file, appDirAbs)) {
|
|
177
|
+
return `${prefix}${quote}${toStrippedProdImportPath(
|
|
178
|
+
file,
|
|
179
|
+
options.rootDir
|
|
180
|
+
)}${quote}`;
|
|
181
|
+
}
|
|
121
182
|
return `${prefix}${quote}${toProdImportPath(file, importer)}${quote}`;
|
|
122
183
|
}
|
|
123
184
|
}
|
|
@@ -132,13 +193,25 @@ function createAliasPlugin(config) {
|
|
|
132
193
|
}
|
|
133
194
|
function buildAliasPlugins(rootDir) {
|
|
134
195
|
const tsconfig = readTsconfig(rootDir);
|
|
135
|
-
return
|
|
196
|
+
return [createAliasPlugin(tsconfig ?? { baseUrl: ".", paths: {} }, { rootDir })];
|
|
136
197
|
}
|
|
198
|
+
var APP_DIR, PROD_EXTS, SOURCE_EXTS, INDEX_EXTS;
|
|
137
199
|
var init_aliasPlugin = __esm({
|
|
138
200
|
"src/cli/aliasPlugin.ts"() {
|
|
139
201
|
"use strict";
|
|
140
202
|
init_resolveAlias();
|
|
141
203
|
init_readTsconfig();
|
|
204
|
+
APP_DIR = "src";
|
|
205
|
+
PROD_EXTS = [".js", ".mjs", ".cjs"];
|
|
206
|
+
SOURCE_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
207
|
+
INDEX_EXTS = [
|
|
208
|
+
"/index.ts",
|
|
209
|
+
"/index.tsx",
|
|
210
|
+
"/index.js",
|
|
211
|
+
"/index.jsx",
|
|
212
|
+
"/index.mjs",
|
|
213
|
+
"/index.cjs"
|
|
214
|
+
];
|
|
142
215
|
}
|
|
143
216
|
});
|
|
144
217
|
|
|
@@ -147,8 +220,8 @@ import path3 from "path";
|
|
|
147
220
|
import fs3 from "fs";
|
|
148
221
|
import fg from "fast-glob";
|
|
149
222
|
async function compileDevRoutes(options) {
|
|
150
|
-
const { rootDir,
|
|
151
|
-
const entryPoints = files ?? await fg([`${
|
|
223
|
+
const { rootDir, dist, files, logLevel = "silent" } = options;
|
|
224
|
+
const entryPoints = files ?? await fg([`${APP_DIR2}/**/*.ts`], {
|
|
152
225
|
cwd: rootDir,
|
|
153
226
|
onlyFiles: true,
|
|
154
227
|
absolute: true,
|
|
@@ -157,14 +230,14 @@ async function compileDevRoutes(options) {
|
|
|
157
230
|
if (entryPoints.length === 0) {
|
|
158
231
|
return { compiledFiles: [] };
|
|
159
232
|
}
|
|
160
|
-
const
|
|
161
|
-
await fs3.promises.mkdir(
|
|
233
|
+
const absDist = path3.resolve(rootDir, dist);
|
|
234
|
+
await fs3.promises.mkdir(absDist, { recursive: true });
|
|
162
235
|
const plugins = buildAliasPlugins(rootDir);
|
|
163
236
|
const esbuild = await import("esbuild");
|
|
164
|
-
const outbase =
|
|
237
|
+
const outbase = path3.resolve(rootDir, APP_DIR2);
|
|
165
238
|
await esbuild.build({
|
|
166
239
|
entryPoints,
|
|
167
|
-
outdir:
|
|
240
|
+
outdir: absDist,
|
|
168
241
|
outbase,
|
|
169
242
|
bundle: false,
|
|
170
243
|
platform: "node",
|
|
@@ -176,10 +249,12 @@ async function compileDevRoutes(options) {
|
|
|
176
249
|
});
|
|
177
250
|
return { compiledFiles: entryPoints };
|
|
178
251
|
}
|
|
252
|
+
var APP_DIR2;
|
|
179
253
|
var init_compileDevRoutes = __esm({
|
|
180
254
|
"src/cli/compileDevRoutes.ts"() {
|
|
181
255
|
"use strict";
|
|
182
256
|
init_aliasPlugin();
|
|
257
|
+
APP_DIR2 = "src";
|
|
183
258
|
}
|
|
184
259
|
});
|
|
185
260
|
|
|
@@ -215,13 +290,24 @@ var init_deepMerge = __esm({
|
|
|
215
290
|
// src/cli/compileConfig.ts
|
|
216
291
|
import path4 from "path";
|
|
217
292
|
import fs4 from "fs";
|
|
293
|
+
function toRealPath2(p) {
|
|
294
|
+
try {
|
|
295
|
+
return fs4.realpathSync(p);
|
|
296
|
+
} catch {
|
|
297
|
+
return p;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
function isInsideDir2(filePath, dir) {
|
|
301
|
+
const rel = path4.relative(dir, filePath);
|
|
302
|
+
return rel !== "" && !rel.startsWith("..") && !path4.isAbsolute(rel);
|
|
303
|
+
}
|
|
218
304
|
function getEnv() {
|
|
219
305
|
return process.env.FAAPI_ENV || process.env.NODE_ENV || "development";
|
|
220
306
|
}
|
|
221
307
|
function findBaseConfig(rootDir) {
|
|
222
308
|
for (const f of BASE_CONFIG_FILES) {
|
|
223
309
|
if (fs4.existsSync(path4.join(rootDir, f))) {
|
|
224
|
-
return f
|
|
310
|
+
return f;
|
|
225
311
|
}
|
|
226
312
|
}
|
|
227
313
|
return null;
|
|
@@ -230,33 +316,137 @@ function findEnvConfig(rootDir, env) {
|
|
|
230
316
|
for (const ext of ENV_CONFIG_EXTS) {
|
|
231
317
|
const f = `faapi.config.${env}${ext}`;
|
|
232
318
|
if (fs4.existsSync(path4.join(rootDir, f))) {
|
|
233
|
-
return
|
|
319
|
+
return f;
|
|
234
320
|
}
|
|
235
321
|
}
|
|
236
322
|
return null;
|
|
237
323
|
}
|
|
324
|
+
function toProdExtension2(filePath) {
|
|
325
|
+
if (filePath.endsWith(".ts")) return filePath.slice(0, -3) + ".js";
|
|
326
|
+
if (filePath.endsWith(".tsx")) return filePath.slice(0, -4) + ".js";
|
|
327
|
+
if (filePath.endsWith(".jsx")) return filePath.slice(0, -4) + ".js";
|
|
328
|
+
return filePath;
|
|
329
|
+
}
|
|
330
|
+
function toProdImport(filename) {
|
|
331
|
+
return toProdExtension2(filename);
|
|
332
|
+
}
|
|
333
|
+
function extractRelativeSpecifiers(source) {
|
|
334
|
+
const specifiers = [];
|
|
335
|
+
let match;
|
|
336
|
+
SPEC_RE.lastIndex = 0;
|
|
337
|
+
while ((match = SPEC_RE.exec(source)) !== null) {
|
|
338
|
+
const specifier = match[3];
|
|
339
|
+
if (specifier.startsWith("./") || specifier.startsWith("../")) {
|
|
340
|
+
specifiers.push(specifier);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return specifiers;
|
|
344
|
+
}
|
|
345
|
+
async function collectRelativeImports(entryFiles, rootDir) {
|
|
346
|
+
const appDirAbs = toRealPath2(path4.resolve(rootDir, "src"));
|
|
347
|
+
const visited = /* @__PURE__ */ new Set();
|
|
348
|
+
const appDirFiles = /* @__PURE__ */ new Set();
|
|
349
|
+
const nonAppDirFiles = /* @__PURE__ */ new Set();
|
|
350
|
+
async function collect(filePath) {
|
|
351
|
+
if (visited.has(filePath)) return;
|
|
352
|
+
visited.add(filePath);
|
|
353
|
+
let source;
|
|
354
|
+
try {
|
|
355
|
+
source = await fs4.promises.readFile(filePath, "utf8");
|
|
356
|
+
} catch {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const specifiers = extractRelativeSpecifiers(source);
|
|
360
|
+
for (const specifier of specifiers) {
|
|
361
|
+
const resolved = resolveRelativeSpecifier(filePath, specifier);
|
|
362
|
+
if (!resolved) continue;
|
|
363
|
+
if (/\.(js|mjs|cjs)$/.test(specifier)) continue;
|
|
364
|
+
if (isInsideDir2(resolved, appDirAbs)) {
|
|
365
|
+
appDirFiles.add(resolved);
|
|
366
|
+
} else {
|
|
367
|
+
nonAppDirFiles.add(resolved);
|
|
368
|
+
}
|
|
369
|
+
await collect(resolved);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
for (const entry of entryFiles) {
|
|
373
|
+
await collect(entry);
|
|
374
|
+
}
|
|
375
|
+
return {
|
|
376
|
+
appDirFiles: Array.from(appDirFiles),
|
|
377
|
+
nonAppDirFiles: Array.from(nonAppDirFiles)
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
function createExternalRelativePlugin() {
|
|
381
|
+
return {
|
|
382
|
+
name: "faapi-external-relative",
|
|
383
|
+
setup(build) {
|
|
384
|
+
build.onResolve({ filter: /^\.{1,2}\// }, (args) => ({
|
|
385
|
+
path: args.path,
|
|
386
|
+
external: true
|
|
387
|
+
}));
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
}
|
|
238
391
|
async function compileConfig(options) {
|
|
239
|
-
const { rootDir,
|
|
240
|
-
const
|
|
241
|
-
if (!
|
|
392
|
+
const { rootDir, dist } = options;
|
|
393
|
+
const baseConfigName = findBaseConfig(rootDir);
|
|
394
|
+
if (!baseConfigName) {
|
|
242
395
|
return { generated: false, outputFile: "" };
|
|
243
396
|
}
|
|
244
397
|
const env = getEnv();
|
|
245
|
-
const
|
|
246
|
-
const
|
|
398
|
+
const envConfigName = findEnvConfig(rootDir, env);
|
|
399
|
+
const absDist = path4.resolve(rootDir, dist);
|
|
400
|
+
await fs4.promises.mkdir(absDist, { recursive: true });
|
|
401
|
+
const configEntryPoints = [path4.resolve(rootDir, baseConfigName)];
|
|
402
|
+
if (envConfigName) {
|
|
403
|
+
configEntryPoints.push(path4.resolve(rootDir, envConfigName));
|
|
404
|
+
}
|
|
405
|
+
const { appDirFiles, nonAppDirFiles } = await collectRelativeImports(configEntryPoints, rootDir);
|
|
406
|
+
const esbuild = await import("esbuild");
|
|
407
|
+
const aliasPlugins = buildAliasPlugins(rootDir);
|
|
408
|
+
const step1aEntries = [...configEntryPoints, ...nonAppDirFiles];
|
|
409
|
+
await esbuild.build({
|
|
410
|
+
entryPoints: step1aEntries,
|
|
411
|
+
outdir: absDist,
|
|
412
|
+
outbase: rootDir,
|
|
413
|
+
bundle: false,
|
|
414
|
+
platform: "node",
|
|
415
|
+
format: "esm",
|
|
416
|
+
sourcemap: true,
|
|
417
|
+
packages: "external",
|
|
418
|
+
plugins: aliasPlugins,
|
|
419
|
+
logLevel: "silent"
|
|
420
|
+
});
|
|
421
|
+
if (appDirFiles.length > 0) {
|
|
422
|
+
const appOutbase = path4.resolve(rootDir, "src");
|
|
423
|
+
await esbuild.build({
|
|
424
|
+
entryPoints: appDirFiles,
|
|
425
|
+
outdir: absDist,
|
|
426
|
+
outbase: appOutbase,
|
|
427
|
+
bundle: false,
|
|
428
|
+
platform: "node",
|
|
429
|
+
format: "esm",
|
|
430
|
+
sourcemap: true,
|
|
431
|
+
packages: "external",
|
|
432
|
+
plugins: aliasPlugins,
|
|
433
|
+
logLevel: "silent"
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
const baseImport = `import base from './${toProdImport(baseConfigName)}';`;
|
|
437
|
+
const imports = [baseImport];
|
|
247
438
|
let exportDefault;
|
|
248
|
-
if (
|
|
249
|
-
|
|
439
|
+
if (envConfigName) {
|
|
440
|
+
const envImport = `import env from './${toProdImport(envConfigName)}';`;
|
|
441
|
+
imports.push(envImport);
|
|
250
442
|
exportDefault = "export default deepMerge(base, env);";
|
|
251
443
|
} else {
|
|
252
444
|
exportDefault = "export default base;";
|
|
253
445
|
}
|
|
254
446
|
const entryCode = [...imports, DEEP_MERGE_SOURCE, exportDefault].join("\n");
|
|
255
|
-
const outputFile = path4.resolve(
|
|
256
|
-
await fs4.promises.mkdir(path4.dirname(outputFile), { recursive: true });
|
|
257
|
-
const esbuild = await import("esbuild");
|
|
447
|
+
const outputFile = path4.resolve(absDist, "faapi-config.js");
|
|
258
448
|
await esbuild.build({
|
|
259
|
-
stdin: { contents: entryCode, resolveDir:
|
|
449
|
+
stdin: { contents: entryCode, resolveDir: absDist, loader: "ts" },
|
|
260
450
|
outfile: outputFile,
|
|
261
451
|
bundle: true,
|
|
262
452
|
format: "esm",
|
|
@@ -264,17 +454,20 @@ async function compileConfig(options) {
|
|
|
264
454
|
target: "node20",
|
|
265
455
|
sourcemap: true,
|
|
266
456
|
packages: "external",
|
|
457
|
+
plugins: [createExternalRelativePlugin()],
|
|
267
458
|
logLevel: "silent"
|
|
268
459
|
});
|
|
269
460
|
return { generated: true, outputFile };
|
|
270
461
|
}
|
|
271
|
-
var BASE_CONFIG_FILES, ENV_CONFIG_EXTS;
|
|
462
|
+
var BASE_CONFIG_FILES, ENV_CONFIG_EXTS, SPEC_RE;
|
|
272
463
|
var init_compileConfig = __esm({
|
|
273
464
|
"src/cli/compileConfig.ts"() {
|
|
274
465
|
"use strict";
|
|
275
466
|
init_deepMerge();
|
|
467
|
+
init_aliasPlugin();
|
|
276
468
|
BASE_CONFIG_FILES = ["faapi.config.ts", "faapi.config.js"];
|
|
277
469
|
ENV_CONFIG_EXTS = [".ts", ".js"];
|
|
470
|
+
SPEC_RE = /(\bfrom\s*|import\s*\(\s*)(['"])([^'"]+)\2/g;
|
|
278
471
|
}
|
|
279
472
|
});
|
|
280
473
|
|
|
@@ -563,10 +756,35 @@ function resolveTypeReference(typeNode, checker, visited = /* @__PURE__ */ new S
|
|
|
563
756
|
const properties = typeName === "Pick" ? innerType.properties.filter((p) => keySet.has(p.name)) : innerType.properties.filter((p) => !keySet.has(p.name));
|
|
564
757
|
return { kind: "object", properties };
|
|
565
758
|
}
|
|
566
|
-
if (typeName === "Map"
|
|
759
|
+
if (typeName === "Map") {
|
|
760
|
+
if (!typeNode.typeArguments || typeNode.typeArguments.length !== 2) {
|
|
761
|
+
throw new SchemaExtractionError(
|
|
762
|
+
typeNode.getText(),
|
|
763
|
+
"Map \u5FC5\u987B\u5E26 2 \u4E2A\u7C7B\u578B\u53C2\u6570\uFF0C\u5982 Map<K, V>\uFF0C\u88F8 Map \u4E0D\u652F\u6301"
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
return {
|
|
767
|
+
kind: "map",
|
|
768
|
+
key: resolveTypeNode(typeNode.typeArguments[0], checker, visited),
|
|
769
|
+
value: resolveTypeNode(typeNode.typeArguments[1], checker, visited)
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
if (typeName === "Set") {
|
|
773
|
+
if (!typeNode.typeArguments || typeNode.typeArguments.length !== 1) {
|
|
774
|
+
throw new SchemaExtractionError(
|
|
775
|
+
typeNode.getText(),
|
|
776
|
+
"Set \u5FC5\u987B\u5E26 1 \u4E2A\u7C7B\u578B\u53C2\u6570\uFF0C\u5982 Set<T>\uFF0C\u88F8 Set \u4E0D\u652F\u6301"
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
return {
|
|
780
|
+
kind: "set",
|
|
781
|
+
element: resolveTypeNode(typeNode.typeArguments[0], checker, visited)
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
if (typeName === "WeakMap" || typeName === "WeakSet") {
|
|
567
785
|
throw new SchemaExtractionError(
|
|
568
786
|
typeNode.getText(),
|
|
569
|
-
`${typeName} \u8FD0\u884C\u65F6\u65E0\u6CD5\u6821\u9A8C\uFF0C\u8BF7\u6539\u7528\u5BF9\u8C61
|
|
787
|
+
`${typeName} \u8FD0\u884C\u65F6\u65E0\u6CD5\u679A\u4E3E\u6821\u9A8C\uFF0C\u8BF7\u6539\u7528 Map / Set \u6216\u5BF9\u8C61`
|
|
570
788
|
);
|
|
571
789
|
}
|
|
572
790
|
if (typeName === "Promise") {
|
|
@@ -1025,6 +1243,7 @@ var init_resolveInjection = __esm({
|
|
|
1025
1243
|
PARAM_TYPE_MAP = {
|
|
1026
1244
|
query: "query",
|
|
1027
1245
|
body: "body",
|
|
1246
|
+
form: "form",
|
|
1028
1247
|
headers: "headers",
|
|
1029
1248
|
params: "params",
|
|
1030
1249
|
context: "context",
|
|
@@ -1123,9 +1342,16 @@ function collectRouteSchemaSources(routes, rootDir) {
|
|
|
1123
1342
|
const inputType = getInputTypeForMethod(method);
|
|
1124
1343
|
const schemaName = getSchemaName(method, inputType);
|
|
1125
1344
|
const meta = analyzeInjection(code, method);
|
|
1126
|
-
const param = meta.params.find((p) => p.type === inputType);
|
|
1345
|
+
const param = meta.params.find((p) => p.type === inputType) ?? (inputType === "body" ? meta.params.find((p) => p.type === "form") : void 0);
|
|
1346
|
+
const isForm = param?.type === "form";
|
|
1127
1347
|
const typeInfo = param?.typeName ? extractTypeInfo(program, filePath, param.typeName) : null;
|
|
1128
|
-
sources.push({
|
|
1348
|
+
sources.push({
|
|
1349
|
+
urlPath: entry.urlPath,
|
|
1350
|
+
filePath,
|
|
1351
|
+
schemaName,
|
|
1352
|
+
typeInfo,
|
|
1353
|
+
coerce: isForm || void 0
|
|
1354
|
+
});
|
|
1129
1355
|
}
|
|
1130
1356
|
}
|
|
1131
1357
|
return { sources, allTypesByFile, mergedAllTypes };
|
|
@@ -1177,6 +1403,13 @@ function collectNamedTypes(type, ctx) {
|
|
|
1177
1403
|
collectNamedTypes(type.key, ctx);
|
|
1178
1404
|
collectNamedTypes(type.value, ctx);
|
|
1179
1405
|
return;
|
|
1406
|
+
case "map":
|
|
1407
|
+
collectNamedTypes(type.key, ctx);
|
|
1408
|
+
collectNamedTypes(type.value, ctx);
|
|
1409
|
+
return;
|
|
1410
|
+
case "set":
|
|
1411
|
+
collectNamedTypes(type.element, ctx);
|
|
1412
|
+
return;
|
|
1180
1413
|
case "ref": {
|
|
1181
1414
|
if (ctx.namedTypes.has(type.name)) return;
|
|
1182
1415
|
ctx.namedTypes.set(type.name, { kind: "any" });
|
|
@@ -1266,6 +1499,10 @@ function baseExpression(type, ctx) {
|
|
|
1266
1499
|
return 'z.preprocess((v) => (typeof v === "string" ? new Date(v) : v), z.date())';
|
|
1267
1500
|
case "record":
|
|
1268
1501
|
return `z.record(${runtimeTypeToZodExpression(type.key, ctx)}, ${runtimeTypeToZodExpression(type.value, ctx)})`;
|
|
1502
|
+
case "map":
|
|
1503
|
+
return `z.preprocess(coerceMap, z.map(${runtimeTypeToZodExpression(type.key, ctx)}, ${runtimeTypeToZodExpression(type.value, ctx)}))`;
|
|
1504
|
+
case "set":
|
|
1505
|
+
return `z.preprocess(coerceSet, z.set(${runtimeTypeToZodExpression(type.element, ctx)}))`;
|
|
1269
1506
|
case "ref":
|
|
1270
1507
|
if (type.name === ctx.entryTypeName) {
|
|
1271
1508
|
return `${ctx.entryExportName}Schema`;
|
|
@@ -1278,11 +1515,13 @@ function generateHelpersFileSource() {
|
|
|
1278
1515
|
"// faapi-helpers.js \u2014 faapi \u81EA\u52A8\u751F\u6210\u7684\u516C\u7528\u51FD\u6570\uFF08\u8BF7\u52FF\u624B\u52A8\u7F16\u8F91\uFF09",
|
|
1279
1516
|
COERCE_NUMBER_HELPER,
|
|
1280
1517
|
COERCE_BOOLEAN_HELPER,
|
|
1518
|
+
COERCE_MAP_HELPER,
|
|
1519
|
+
COERCE_SET_HELPER,
|
|
1281
1520
|
""
|
|
1282
1521
|
].join("\n");
|
|
1283
1522
|
}
|
|
1284
1523
|
function usesCoerceHelpers(code) {
|
|
1285
|
-
return code.includes("coerceNumber") || code.includes("coerceBoolean");
|
|
1524
|
+
return code.includes("coerceNumber") || code.includes("coerceBoolean") || code.includes("coerceMap") || code.includes("coerceSet");
|
|
1286
1525
|
}
|
|
1287
1526
|
function wrapCoercePreprocess(kind, inner) {
|
|
1288
1527
|
if (kind === "number") {
|
|
@@ -1369,6 +1608,10 @@ function containsRef(type, visited) {
|
|
|
1369
1608
|
return type.members.some((m) => containsRef(m, visited));
|
|
1370
1609
|
case "record":
|
|
1371
1610
|
return containsRef(type.key, visited) || containsRef(type.value, visited);
|
|
1611
|
+
case "map":
|
|
1612
|
+
return containsRef(type.key, visited) || containsRef(type.value, visited);
|
|
1613
|
+
case "set":
|
|
1614
|
+
return containsRef(type.element, visited);
|
|
1372
1615
|
default:
|
|
1373
1616
|
return false;
|
|
1374
1617
|
}
|
|
@@ -1397,7 +1640,7 @@ function generateZodSchemaSource(typeInfo, resolveType, exportName, coerce = fal
|
|
|
1397
1640
|
}
|
|
1398
1641
|
return lines.join("\n");
|
|
1399
1642
|
}
|
|
1400
|
-
var CodeGenContext, COERCE_NUMBER_HELPER, COERCE_BOOLEAN_HELPER, HELPERS_FILENAME;
|
|
1643
|
+
var CodeGenContext, COERCE_NUMBER_HELPER, COERCE_BOOLEAN_HELPER, COERCE_MAP_HELPER, COERCE_SET_HELPER, HELPERS_FILENAME;
|
|
1401
1644
|
var init_generateZodSchema = __esm({
|
|
1402
1645
|
"src/ast/generateZodSchema.ts"() {
|
|
1403
1646
|
"use strict";
|
|
@@ -1423,6 +1666,8 @@ var init_generateZodSchema = __esm({
|
|
|
1423
1666
|
};
|
|
1424
1667
|
COERCE_NUMBER_HELPER = 'export const coerceNumber = (v) => typeof v === "string" && v.trim() !== "" && !isNaN(Number(v)) ? Number(v) : v;';
|
|
1425
1668
|
COERCE_BOOLEAN_HELPER = 'export const coerceBoolean = (v) => v === "true" || v === "1" ? true : v === "false" || v === "0" ? false : v;';
|
|
1669
|
+
COERCE_MAP_HELPER = 'export const coerceMap = (v) => Array.isArray(v) ? new Map(v) : v instanceof Map ? v : (v && typeof v === "object" ? new Map(Object.entries(v)) : v);';
|
|
1670
|
+
COERCE_SET_HELPER = "export const coerceSet = (v) => v instanceof Set ? v : (Array.isArray(v) ? new Set(v) : v);";
|
|
1426
1671
|
HELPERS_FILENAME = "faapi-helpers.js";
|
|
1427
1672
|
}
|
|
1428
1673
|
});
|
|
@@ -1430,25 +1675,25 @@ var init_generateZodSchema = __esm({
|
|
|
1430
1675
|
// src/cli/generateSchemaFiles.ts
|
|
1431
1676
|
import path6 from "path";
|
|
1432
1677
|
import fs5 from "fs/promises";
|
|
1433
|
-
function getSchemaOutputPath(sourceFile,
|
|
1678
|
+
function getSchemaOutputPath(sourceFile, dist, rootDir) {
|
|
1434
1679
|
let rel = sourceFile.replace(/\\/g, "/");
|
|
1435
|
-
if (
|
|
1436
|
-
rel = rel.slice(
|
|
1680
|
+
if (rel.startsWith("src/")) {
|
|
1681
|
+
rel = rel.slice(4);
|
|
1437
1682
|
}
|
|
1438
1683
|
const idx = rel.lastIndexOf("/");
|
|
1439
1684
|
const relDir = idx >= 0 ? rel.slice(0, idx) : "";
|
|
1440
|
-
return path6.resolve(rootDir,
|
|
1685
|
+
return path6.resolve(rootDir, dist, relDir, "zod.js");
|
|
1441
1686
|
}
|
|
1442
|
-
function getRuntimeSchemaPath(filePath,
|
|
1687
|
+
function getRuntimeSchemaPath(filePath, dist, rootDir) {
|
|
1443
1688
|
let rel = filePath.replace(/\\/g, "/");
|
|
1444
|
-
if (
|
|
1445
|
-
rel = rel.slice(
|
|
1446
|
-
} else if (rel.startsWith(`${
|
|
1447
|
-
rel = rel.slice(
|
|
1689
|
+
if (rel.startsWith("src/")) {
|
|
1690
|
+
rel = rel.slice(4);
|
|
1691
|
+
} else if (rel.startsWith(`${dist}/`)) {
|
|
1692
|
+
rel = rel.slice(dist.length + 1);
|
|
1448
1693
|
}
|
|
1449
1694
|
const idx = rel.lastIndexOf("/");
|
|
1450
1695
|
const relDir = idx >= 0 ? rel.slice(0, idx) : "";
|
|
1451
|
-
return path6.resolve(rootDir,
|
|
1696
|
+
return path6.resolve(rootDir, dist, relDir, "zod.js");
|
|
1452
1697
|
}
|
|
1453
1698
|
function getHelpersImportPath(relDir) {
|
|
1454
1699
|
if (!relDir) return `./${HELPERS_FILENAME}`;
|
|
@@ -1464,7 +1709,7 @@ function generateSchemaFileSource(sources, allTypes, helpersImportPath) {
|
|
|
1464
1709
|
if (!typeInfo) {
|
|
1465
1710
|
continue;
|
|
1466
1711
|
}
|
|
1467
|
-
const coerce = /(?:Query|Params)$/.test(schemaName);
|
|
1712
|
+
const coerce = source.coerce ?? /(?:Query|Params)$/.test(schemaName);
|
|
1468
1713
|
const block = [`// ${schemaName}`];
|
|
1469
1714
|
const schemaCode = generateZodSchemaSource(typeInfo, resolveType, schemaName, coerce).replace(
|
|
1470
1715
|
/^import \{ z \} from 'zod';\s*\n\s*\n/,
|
|
@@ -1476,13 +1721,15 @@ function generateSchemaFileSource(sources, allTypes, helpersImportPath) {
|
|
|
1476
1721
|
}
|
|
1477
1722
|
const allSchemaCode = schemaBlocks.join("\n");
|
|
1478
1723
|
if (helpersImportPath && usesCoerceHelpers(allSchemaCode)) {
|
|
1479
|
-
lines.push(
|
|
1724
|
+
lines.push(
|
|
1725
|
+
`import { coerceNumber, coerceBoolean, coerceMap, coerceSet } from '${helpersImportPath}';`
|
|
1726
|
+
);
|
|
1480
1727
|
}
|
|
1481
1728
|
lines.push("");
|
|
1482
1729
|
lines.push(...schemaBlocks);
|
|
1483
1730
|
return lines.join("\n").replace(/\n+$/, "\n");
|
|
1484
1731
|
}
|
|
1485
|
-
async function generateSchemaFiles(routes, rootDir,
|
|
1732
|
+
async function generateSchemaFiles(routes, rootDir, dist) {
|
|
1486
1733
|
if (routes.length === 0) return;
|
|
1487
1734
|
const { sources, allTypesByFile } = collectRouteSchemaSources(routes, rootDir);
|
|
1488
1735
|
const sourcesByFile = /* @__PURE__ */ new Map();
|
|
@@ -1497,11 +1744,11 @@ async function generateSchemaFiles(routes, rootDir, appDir, outDir) {
|
|
|
1497
1744
|
const fileEntries = [];
|
|
1498
1745
|
for (const [filePath, fileSources] of sourcesByFile) {
|
|
1499
1746
|
const relFile = path6.relative(rootDir, filePath).replace(/\\/g, "/");
|
|
1500
|
-
const outputPath = getSchemaOutputPath(relFile,
|
|
1747
|
+
const outputPath = getSchemaOutputPath(relFile, dist, rootDir);
|
|
1501
1748
|
const allTypes = allTypesByFile.get(filePath) ?? /* @__PURE__ */ new Map();
|
|
1502
1749
|
let relForDir = relFile;
|
|
1503
|
-
if (
|
|
1504
|
-
relForDir = relForDir.slice(
|
|
1750
|
+
if (relForDir.startsWith("src/")) {
|
|
1751
|
+
relForDir = relForDir.slice(4);
|
|
1505
1752
|
}
|
|
1506
1753
|
const dirIdx = relForDir.lastIndexOf("/");
|
|
1507
1754
|
const zodRelDir = dirIdx >= 0 ? relForDir.slice(0, dirIdx) : "";
|
|
@@ -1511,7 +1758,7 @@ async function generateSchemaFiles(routes, rootDir, appDir, outDir) {
|
|
|
1511
1758
|
}
|
|
1512
1759
|
const allSourceCode = fileEntries.map((e) => e.source).join("\n");
|
|
1513
1760
|
if (usesCoerceHelpers(allSourceCode)) {
|
|
1514
|
-
const helpersPath = path6.resolve(rootDir,
|
|
1761
|
+
const helpersPath = path6.resolve(rootDir, dist, HELPERS_FILENAME);
|
|
1515
1762
|
await writeSchemaFile(helpersPath, generateHelpersFileSource());
|
|
1516
1763
|
}
|
|
1517
1764
|
await Promise.all(
|
|
@@ -1604,20 +1851,20 @@ var init_loadMiddlewares = __esm({
|
|
|
1604
1851
|
// src/cli/generateRoutes.ts
|
|
1605
1852
|
import fs6 from "fs";
|
|
1606
1853
|
import path7 from "path";
|
|
1607
|
-
function toProdFilePath(filePath,
|
|
1854
|
+
function toProdFilePath(filePath, dist) {
|
|
1608
1855
|
let rel = filePath.replace(/\\/g, "/");
|
|
1609
|
-
if (
|
|
1610
|
-
rel = rel.slice(
|
|
1856
|
+
if (rel.startsWith("src/")) {
|
|
1857
|
+
rel = rel.slice(4);
|
|
1611
1858
|
}
|
|
1612
1859
|
const jsPath = rel.replace(/\.ts$/, ".js");
|
|
1613
|
-
return jsPath.startsWith(`${
|
|
1860
|
+
return jsPath.startsWith(`${dist}/`) ? jsPath : `${dist}/${jsPath}`;
|
|
1614
1861
|
}
|
|
1615
|
-
function serializeRoutes(routes, wsRoutes, rootDir,
|
|
1862
|
+
function serializeRoutes(routes, wsRoutes, rootDir, dist = "dist") {
|
|
1616
1863
|
const serialize = (route) => {
|
|
1617
|
-
const middlewarePaths = extractMiddlewarePaths(route.filePath, rootDir,
|
|
1864
|
+
const middlewarePaths = extractMiddlewarePaths(route.filePath, rootDir, dist);
|
|
1618
1865
|
const serialized = {
|
|
1619
1866
|
urlPath: route.urlPath,
|
|
1620
|
-
filePath: toProdFilePath(route.filePath,
|
|
1867
|
+
filePath: toProdFilePath(route.filePath, dist),
|
|
1621
1868
|
paramNames: route.paramNames,
|
|
1622
1869
|
isDynamic: route.isDynamic,
|
|
1623
1870
|
isCatchAll: route.isCatchAll,
|
|
@@ -1633,7 +1880,7 @@ function serializeRoutes(routes, wsRoutes, rootDir, appDir = "src", prodDir = "d
|
|
|
1633
1880
|
wsRoutes: wsRoutes.map(serialize)
|
|
1634
1881
|
};
|
|
1635
1882
|
}
|
|
1636
|
-
function extractMiddlewarePaths(routeFilePath, rootDir,
|
|
1883
|
+
function extractMiddlewarePaths(routeFilePath, rootDir, dist) {
|
|
1637
1884
|
const routeDir = path7.dirname(routeFilePath);
|
|
1638
1885
|
const resolvedRoot = path7.resolve(rootDir);
|
|
1639
1886
|
const paths = [];
|
|
@@ -1646,7 +1893,7 @@ function extractMiddlewarePaths(routeFilePath, rootDir, appDir, prodDir) {
|
|
|
1646
1893
|
const absMwPath = fs6.existsSync(absTsPath) ? absTsPath : fs6.existsSync(absJsPath) ? absJsPath : null;
|
|
1647
1894
|
if (absMwPath) {
|
|
1648
1895
|
const relMwPath = path7.relative(rootDir, absMwPath);
|
|
1649
|
-
const prodAbsPath = path7.resolve(rootDir, toProdFilePath(relMwPath,
|
|
1896
|
+
const prodAbsPath = path7.resolve(rootDir, toProdFilePath(relMwPath, dist));
|
|
1650
1897
|
paths.push(prodAbsPath);
|
|
1651
1898
|
}
|
|
1652
1899
|
if (currentDir === resolvedRoot) break;
|
|
@@ -1776,8 +2023,8 @@ function isCatchAllSegment(segment) {
|
|
|
1776
2023
|
function isRouteGroup(segment) {
|
|
1777
2024
|
return /^\(.+\)$/.test(segment);
|
|
1778
2025
|
}
|
|
1779
|
-
function filePathToUrlPath(filePath
|
|
1780
|
-
const withoutPrefix = filePath.startsWith(
|
|
2026
|
+
function filePathToUrlPath(filePath) {
|
|
2027
|
+
const withoutPrefix = filePath.startsWith("src/") ? filePath.slice(4) : filePath;
|
|
1781
2028
|
const lastSlashIndex = withoutPrefix.lastIndexOf("/");
|
|
1782
2029
|
const dirPath = lastSlashIndex === -1 ? "" : withoutPrefix.slice(0, lastSlashIndex);
|
|
1783
2030
|
if (!dirPath) {
|
|
@@ -1797,24 +2044,24 @@ var init_parseRouteFile = __esm({
|
|
|
1797
2044
|
import fg2 from "fast-glob";
|
|
1798
2045
|
import path8 from "path";
|
|
1799
2046
|
import fs7 from "fs";
|
|
1800
|
-
function toProdAbsPath(sourceAbsPath, rootDir,
|
|
2047
|
+
function toProdAbsPath(sourceAbsPath, rootDir, dist) {
|
|
1801
2048
|
let rel = path8.relative(rootDir, sourceAbsPath).replace(/\\/g, "/");
|
|
1802
|
-
if (
|
|
1803
|
-
rel = rel.slice(
|
|
2049
|
+
if (rel.startsWith(`${APP_DIR3}/`)) {
|
|
2050
|
+
rel = rel.slice(APP_DIR3.length + 1);
|
|
1804
2051
|
}
|
|
1805
|
-
const prodRel = `${
|
|
2052
|
+
const prodRel = `${dist}/${rel.replace(/\.ts$/, ".js")}`;
|
|
1806
2053
|
return path8.resolve(rootDir, prodRel);
|
|
1807
2054
|
}
|
|
1808
|
-
async function findMergedMiddlewares(routeFilePath, rootDir,
|
|
2055
|
+
async function findMergedMiddlewares(routeFilePath, rootDir, dist) {
|
|
1809
2056
|
const routeDir = path8.dirname(routeFilePath);
|
|
1810
2057
|
const resolvedRoot = path8.resolve(rootDir);
|
|
1811
2058
|
const mwPaths = [];
|
|
1812
2059
|
let currentDir = path8.resolve(rootDir, routeDir);
|
|
1813
2060
|
while (true) {
|
|
1814
|
-
if (
|
|
2061
|
+
if (dist) {
|
|
1815
2062
|
const mwPath = path8.join(currentDir, "middlewares.js");
|
|
1816
2063
|
const absMwPath = path8.resolve(rootDir, mwPath);
|
|
1817
|
-
const prodAbsMwPath = toProdAbsPath(absMwPath, rootDir,
|
|
2064
|
+
const prodAbsMwPath = toProdAbsPath(absMwPath, rootDir, dist);
|
|
1818
2065
|
if (fs7.existsSync(prodAbsMwPath)) {
|
|
1819
2066
|
mwPaths.push(prodAbsMwPath);
|
|
1820
2067
|
}
|
|
@@ -1879,8 +2126,7 @@ async function hasWsExport(absPath) {
|
|
|
1879
2126
|
return false;
|
|
1880
2127
|
}
|
|
1881
2128
|
}
|
|
1882
|
-
async function scanRoutes(rootDir, patterns,
|
|
1883
|
-
const dir = appDir ?? ".";
|
|
2129
|
+
async function scanRoutes(rootDir, patterns, dist) {
|
|
1884
2130
|
const files = await fg2(patterns, {
|
|
1885
2131
|
cwd: rootDir,
|
|
1886
2132
|
onlyFiles: true,
|
|
@@ -1893,12 +2139,12 @@ async function scanRoutes(rootDir, patterns, appDir, prodDir) {
|
|
|
1893
2139
|
const fileName = normalizedFile.split("/").pop();
|
|
1894
2140
|
if (fileName === "handler.ts" || fileName === "handler.js") {
|
|
1895
2141
|
const absPath = path8.resolve(rootDir, normalizedFile);
|
|
1896
|
-
const importPath =
|
|
1897
|
-
const urlPath = filePathToUrlPath(normalizedFile
|
|
2142
|
+
const importPath = dist ? toProdAbsPath(absPath, rootDir, dist) : absPath;
|
|
2143
|
+
const urlPath = filePathToUrlPath(normalizedFile);
|
|
1898
2144
|
const paramNames = extractParamNames(urlPath);
|
|
1899
2145
|
const isDynamic = paramNames.length > 0;
|
|
1900
2146
|
const isCatchAll = normalizedFile.split("/").some(isCatchAllSegment);
|
|
1901
|
-
const middlewareBundle = await findMergedMiddlewares(normalizedFile, rootDir,
|
|
2147
|
+
const middlewareBundle = await findMergedMiddlewares(normalizedFile, rootDir, dist);
|
|
1902
2148
|
const methods = await extractMethodsFromHandler(importPath);
|
|
1903
2149
|
for (const method of methods) {
|
|
1904
2150
|
routes.push({
|
|
@@ -1929,6 +2175,7 @@ async function scanRoutes(rootDir, patterns, appDir, prodDir) {
|
|
|
1929
2175
|
}
|
|
1930
2176
|
return { routes, wsRoutes };
|
|
1931
2177
|
}
|
|
2178
|
+
var APP_DIR3;
|
|
1932
2179
|
var init_scanRoutes = __esm({
|
|
1933
2180
|
"src/router/scanRoutes.ts"() {
|
|
1934
2181
|
"use strict";
|
|
@@ -1936,6 +2183,7 @@ var init_scanRoutes = __esm({
|
|
|
1936
2183
|
init_parseRouteFile();
|
|
1937
2184
|
init_loadMiddlewares();
|
|
1938
2185
|
init_importWithCacheBust();
|
|
2186
|
+
APP_DIR3 = "src";
|
|
1939
2187
|
}
|
|
1940
2188
|
});
|
|
1941
2189
|
|
|
@@ -1965,8 +2213,8 @@ var init_sortRoutes = __esm({
|
|
|
1965
2213
|
// src/config/loadConfig.ts
|
|
1966
2214
|
import path9 from "path";
|
|
1967
2215
|
import fs8 from "fs";
|
|
1968
|
-
async function loadConfig(rootDir,
|
|
1969
|
-
const configProductPath = path9.resolve(rootDir,
|
|
2216
|
+
async function loadConfig(rootDir, dist) {
|
|
2217
|
+
const configProductPath = path9.resolve(rootDir, dist, CONFIG_PRODUCT_FILE);
|
|
1970
2218
|
if (fs8.existsSync(configProductPath)) {
|
|
1971
2219
|
const module = await importWithCacheBust(configProductPath);
|
|
1972
2220
|
return module.default ?? {};
|
|
@@ -1974,7 +2222,7 @@ async function loadConfig(rootDir, outDir) {
|
|
|
1974
2222
|
const hasSourceConfig = fs8.existsSync(path9.join(rootDir, "faapi.config.ts")) || fs8.existsSync(path9.join(rootDir, "faapi.config.js"));
|
|
1975
2223
|
if (hasSourceConfig) {
|
|
1976
2224
|
throw new Error(
|
|
1977
|
-
`[faapi] ${
|
|
2225
|
+
`[faapi] ${dist}/${CONFIG_PRODUCT_FILE} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C \`faapi build\`\uFF08\u6216 \`faapi dev\`\uFF09\u751F\u6210\u4EA7\u7269\u3002`
|
|
1978
2226
|
);
|
|
1979
2227
|
}
|
|
1980
2228
|
return null;
|
|
@@ -3700,7 +3948,7 @@ var init_esm2 = __esm({
|
|
|
3700
3948
|
// src/cli/watcher.ts
|
|
3701
3949
|
import path10 from "path";
|
|
3702
3950
|
function startWatcher(options) {
|
|
3703
|
-
const { rootDir,
|
|
3951
|
+
const { rootDir, app, devDist } = options;
|
|
3704
3952
|
let rebuildTimer = null;
|
|
3705
3953
|
let pendingFiles = /* @__PURE__ */ new Set();
|
|
3706
3954
|
async function rebuildRoutes() {
|
|
@@ -3710,12 +3958,11 @@ function startWatcher(options) {
|
|
|
3710
3958
|
if (filesToCompile.length > 0) {
|
|
3711
3959
|
await compileDevRoutes({
|
|
3712
3960
|
rootDir,
|
|
3713
|
-
|
|
3714
|
-
outDir: DEV_OUT_DIR,
|
|
3961
|
+
dist: devDist,
|
|
3715
3962
|
files: filesToCompile
|
|
3716
3963
|
});
|
|
3717
3964
|
}
|
|
3718
|
-
await compileConfig({ rootDir,
|
|
3965
|
+
await compileConfig({ rootDir, dist: devDist });
|
|
3719
3966
|
await app.reloadRoutes();
|
|
3720
3967
|
const recompiledCount = filesToCompile.length;
|
|
3721
3968
|
console.log(
|
|
@@ -3738,12 +3985,12 @@ function startWatcher(options) {
|
|
|
3738
3985
|
"faapi.config.production.ts",
|
|
3739
3986
|
"faapi.config.production.js"
|
|
3740
3987
|
];
|
|
3741
|
-
const watchPaths = [
|
|
3988
|
+
const watchPaths = ["src", ...CONFIG_FILES];
|
|
3742
3989
|
const watcher = esm_default.watch(watchPaths, {
|
|
3743
3990
|
cwd: rootDir,
|
|
3744
3991
|
ignoreInitial: true,
|
|
3745
3992
|
ignored: (filePath, stats) => {
|
|
3746
|
-
if (filePath.includes("node_modules") || filePath.includes(".faapi") || filePath.includes(
|
|
3993
|
+
if (filePath.includes("node_modules") || filePath.includes(".faapi") || filePath.includes(devDist) || filePath.includes(".git")) {
|
|
3747
3994
|
return true;
|
|
3748
3995
|
}
|
|
3749
3996
|
if (!stats) return false;
|
|
@@ -3773,14 +4020,12 @@ function startWatcher(options) {
|
|
|
3773
4020
|
});
|
|
3774
4021
|
console.log("- Watch mode enabled");
|
|
3775
4022
|
}
|
|
3776
|
-
var DEV_OUT_DIR;
|
|
3777
4023
|
var init_watcher = __esm({
|
|
3778
4024
|
"src/cli/watcher.ts"() {
|
|
3779
4025
|
"use strict";
|
|
3780
4026
|
init_esm2();
|
|
3781
4027
|
init_compileDevRoutes();
|
|
3782
4028
|
init_compileConfig();
|
|
3783
|
-
DEV_OUT_DIR = ".faapi/dev";
|
|
3784
4029
|
}
|
|
3785
4030
|
});
|
|
3786
4031
|
|
|
@@ -4473,6 +4718,10 @@ function getBuiltinInjectionValue(type, ctx, body) {
|
|
|
4473
4718
|
return ctx.ip;
|
|
4474
4719
|
case "body":
|
|
4475
4720
|
return body;
|
|
4721
|
+
// form 与 body 共享解析结果(resolveInput 已按 Content-Type 解析 form-urlencoded)
|
|
4722
|
+
// 差异仅在 schema 校验(form coerce=true,由 collectRouteSchemaSources 标记)
|
|
4723
|
+
case "form":
|
|
4724
|
+
return body;
|
|
4476
4725
|
case "files":
|
|
4477
4726
|
if (body && typeof body === "object" && "files" in body) {
|
|
4478
4727
|
return body.files;
|
|
@@ -4891,6 +5140,44 @@ var init_helmet = __esm({
|
|
|
4891
5140
|
}
|
|
4892
5141
|
});
|
|
4893
5142
|
|
|
5143
|
+
// src/middleware/logger.ts
|
|
5144
|
+
function logger(options = {}) {
|
|
5145
|
+
return async (ctx, next) => {
|
|
5146
|
+
const log = options.log ?? console.log;
|
|
5147
|
+
const start = Date.now();
|
|
5148
|
+
try {
|
|
5149
|
+
const response = await next();
|
|
5150
|
+
const duration = Date.now() - start;
|
|
5151
|
+
const entry = {
|
|
5152
|
+
method: ctx.method,
|
|
5153
|
+
path: ctx.path,
|
|
5154
|
+
status: response.status,
|
|
5155
|
+
durationMs: duration
|
|
5156
|
+
};
|
|
5157
|
+
log(entry, `${ctx.method} ${ctx.path} ${response.status} ${duration}ms`);
|
|
5158
|
+
return response;
|
|
5159
|
+
} catch (err) {
|
|
5160
|
+
const duration = Date.now() - start;
|
|
5161
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5162
|
+
const status = err?.statusCode ?? 500;
|
|
5163
|
+
const entry = {
|
|
5164
|
+
method: ctx.method,
|
|
5165
|
+
path: ctx.path,
|
|
5166
|
+
status,
|
|
5167
|
+
durationMs: duration,
|
|
5168
|
+
error: message
|
|
5169
|
+
};
|
|
5170
|
+
log(entry, `${ctx.method} ${ctx.path} ${status} ${duration}ms - ${message}`);
|
|
5171
|
+
throw err;
|
|
5172
|
+
}
|
|
5173
|
+
};
|
|
5174
|
+
}
|
|
5175
|
+
var init_logger = __esm({
|
|
5176
|
+
"src/middleware/logger.ts"() {
|
|
5177
|
+
"use strict";
|
|
5178
|
+
}
|
|
5179
|
+
});
|
|
5180
|
+
|
|
4894
5181
|
// src/errors/formatErrorResponse.ts
|
|
4895
5182
|
function formatErrorResponse(error) {
|
|
4896
5183
|
if (error instanceof ValidationError) {
|
|
@@ -5210,8 +5497,7 @@ function createServer(options) {
|
|
|
5210
5497
|
const {
|
|
5211
5498
|
routes,
|
|
5212
5499
|
rootDir,
|
|
5213
|
-
|
|
5214
|
-
outDir,
|
|
5500
|
+
dist,
|
|
5215
5501
|
cors: corsOption,
|
|
5216
5502
|
onError,
|
|
5217
5503
|
config,
|
|
@@ -5219,6 +5505,7 @@ function createServer(options) {
|
|
|
5219
5505
|
middlewares: globalMiddlewares,
|
|
5220
5506
|
injectors: globalInjectors,
|
|
5221
5507
|
helmet: helmetOption,
|
|
5508
|
+
logger: loggerOption,
|
|
5222
5509
|
bodyLimit = DEFAULT_BODY_LIMIT,
|
|
5223
5510
|
http2: http2Option
|
|
5224
5511
|
} = options;
|
|
@@ -5230,6 +5517,8 @@ function createServer(options) {
|
|
|
5230
5517
|
const helmOpts = typeof helmetOption === "object" ? helmetOption : {};
|
|
5231
5518
|
configMiddlewares.push(helmet(helmOpts));
|
|
5232
5519
|
}
|
|
5520
|
+
const loggerMiddlewareInst = loggerOption === false ? null : loggerOption === true || loggerOption === void 0 ? logger() : logger(loggerOption);
|
|
5521
|
+
if (loggerMiddlewareInst) configMiddlewares.push(loggerMiddlewareInst);
|
|
5233
5522
|
const server = (() => {
|
|
5234
5523
|
if (http2Option) {
|
|
5235
5524
|
const h2Opts = typeof http2Option === "object" ? http2Option : {};
|
|
@@ -5246,8 +5535,7 @@ function createServer(options) {
|
|
|
5246
5535
|
handleRequest(
|
|
5247
5536
|
currentRoutes,
|
|
5248
5537
|
rootDir,
|
|
5249
|
-
|
|
5250
|
-
outDir,
|
|
5538
|
+
dist,
|
|
5251
5539
|
req,
|
|
5252
5540
|
res,
|
|
5253
5541
|
configMiddlewares,
|
|
@@ -5266,7 +5554,7 @@ function createServer(options) {
|
|
|
5266
5554
|
}
|
|
5267
5555
|
return { server, routesRef };
|
|
5268
5556
|
}
|
|
5269
|
-
async function handleRequest(routes, rootDir,
|
|
5557
|
+
async function handleRequest(routes, rootDir, dist, req, res, configMiddlewares, onError, config, globalMiddlewares, globalInjectors, bodyLimit) {
|
|
5270
5558
|
const request = toWebRequest(req, bodyLimit);
|
|
5271
5559
|
const method = request.method.toUpperCase();
|
|
5272
5560
|
const urlPath = new URL(request.url).pathname;
|
|
@@ -5287,7 +5575,7 @@ async function handleRequest(routes, rootDir, appDir, outDir, req, res, configMi
|
|
|
5287
5575
|
const routeModule = await loadRouteModule(absoluteFilePath, route.method);
|
|
5288
5576
|
const input = await resolveInput(route.method, request);
|
|
5289
5577
|
const inputType = getInputTypeForMethod(route.method);
|
|
5290
|
-
const schemaPath = getRuntimeSchemaPath(route.filePath,
|
|
5578
|
+
const schemaPath = getRuntimeSchemaPath(route.filePath, dist, rootDir);
|
|
5291
5579
|
const result = await validateInput(schemaPath, route.method, inputType, input);
|
|
5292
5580
|
if (!result.valid) {
|
|
5293
5581
|
throw new ValidationError("\u53C2\u6570\u6821\u9A8C\u5931\u8D25", result.issues);
|
|
@@ -5343,6 +5631,7 @@ var init_createServer = __esm({
|
|
|
5343
5631
|
init_getClientIp();
|
|
5344
5632
|
init_cors();
|
|
5345
5633
|
init_helmet();
|
|
5634
|
+
init_logger();
|
|
5346
5635
|
init_handleWsUpgrade();
|
|
5347
5636
|
init_serverUtils();
|
|
5348
5637
|
init_generateSchemaFiles();
|
|
@@ -5456,16 +5745,14 @@ function isFaapiConfigKey(key) {
|
|
|
5456
5745
|
}
|
|
5457
5746
|
async function createAppBase(options) {
|
|
5458
5747
|
const rootDir = options?.rootDir ?? process.cwd();
|
|
5459
|
-
const
|
|
5460
|
-
const routesPath = path13.resolve(rootDir,
|
|
5748
|
+
const dist = options?.dist ?? process.env.FAAPI_DIST ?? DEFAULT_DIST;
|
|
5749
|
+
const routesPath = path13.resolve(rootDir, dist, ROUTES_FILE);
|
|
5461
5750
|
if (!fs9.existsSync(routesPath)) {
|
|
5462
5751
|
throw new Error(
|
|
5463
|
-
`[faapi] ${
|
|
5752
|
+
`[faapi] ${dist}/${ROUTES_FILE} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C \`faapi build\`\uFF08\u6216 \`faapi dev\`\uFF09\u751F\u6210\u4EA7\u7269\u3002`
|
|
5464
5753
|
);
|
|
5465
5754
|
}
|
|
5466
|
-
const config = await loadConfig(rootDir,
|
|
5467
|
-
const appDir = options?.appDir ?? process.env.FAAPI_APP_DIR ?? DEFAULT_APP_DIR;
|
|
5468
|
-
const patterns = appDir === "." ? ["api/**/*.ts"] : [`${appDir}/api/**/*.ts`];
|
|
5755
|
+
const config = await loadConfig(rootDir, dist);
|
|
5469
5756
|
const serialized = await importWithCacheBust(routesPath);
|
|
5470
5757
|
const hydrated = await hydrateRoutes(serialized);
|
|
5471
5758
|
let sorted = sortRoutes(hydrated.routes);
|
|
@@ -5483,8 +5770,7 @@ async function createAppBase(options) {
|
|
|
5483
5770
|
const { server, routesRef } = createServer({
|
|
5484
5771
|
routes: sorted,
|
|
5485
5772
|
rootDir,
|
|
5486
|
-
|
|
5487
|
-
outDir,
|
|
5773
|
+
dist,
|
|
5488
5774
|
cors: config?.cors ?? true,
|
|
5489
5775
|
onError: config?.lifecycle?.onError,
|
|
5490
5776
|
config: config ?? void 0,
|
|
@@ -5492,6 +5778,7 @@ async function createAppBase(options) {
|
|
|
5492
5778
|
middlewares: config?.middlewares,
|
|
5493
5779
|
injectors: config?.injectors,
|
|
5494
5780
|
helmet: config?.helmet,
|
|
5781
|
+
logger: config?.logger,
|
|
5495
5782
|
bodyLimit: config?.bodyLimit,
|
|
5496
5783
|
http2: config?.http2
|
|
5497
5784
|
});
|
|
@@ -5644,9 +5931,8 @@ async function createAppBase(options) {
|
|
|
5644
5931
|
};
|
|
5645
5932
|
const ctx = {
|
|
5646
5933
|
rootDir,
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
patterns,
|
|
5934
|
+
dist,
|
|
5935
|
+
patterns: PATTERNS,
|
|
5650
5936
|
server,
|
|
5651
5937
|
routesRef,
|
|
5652
5938
|
config,
|
|
@@ -5661,7 +5947,7 @@ async function createAppBase(options) {
|
|
|
5661
5947
|
};
|
|
5662
5948
|
return { app, ctx };
|
|
5663
5949
|
}
|
|
5664
|
-
var
|
|
5950
|
+
var DEFAULT_DIST, DEFAULT_PORT, ROUTES_FILE, PATTERNS, FAAPI_CONFIG_KEYS;
|
|
5665
5951
|
var init_createAppCore = __esm({
|
|
5666
5952
|
"src/cli/createAppCore.ts"() {
|
|
5667
5953
|
"use strict";
|
|
@@ -5673,10 +5959,10 @@ var init_createAppCore = __esm({
|
|
|
5673
5959
|
init_generateRoutes();
|
|
5674
5960
|
init_loadPlugins();
|
|
5675
5961
|
init_importWithCacheBust();
|
|
5676
|
-
|
|
5677
|
-
DEFAULT_APP_DIR = "src";
|
|
5962
|
+
DEFAULT_DIST = "dist";
|
|
5678
5963
|
DEFAULT_PORT = 3e3;
|
|
5679
5964
|
ROUTES_FILE = "faapi-routes.js";
|
|
5965
|
+
PATTERNS = ["src/api/**/*.ts"];
|
|
5680
5966
|
FAAPI_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
5681
5967
|
"cors",
|
|
5682
5968
|
"lifecycle",
|
|
@@ -5701,9 +5987,9 @@ async function createDevApp(options) {
|
|
|
5701
5987
|
invalidateMiddlewareCache();
|
|
5702
5988
|
invalidateProgramCache();
|
|
5703
5989
|
invalidateSchemaCache();
|
|
5704
|
-
const reScanned = await scanRoutes(ctx.rootDir, ctx.patterns, ctx.
|
|
5990
|
+
const reScanned = await scanRoutes(ctx.rootDir, ctx.patterns, ctx.dist);
|
|
5705
5991
|
const sorted = sortRoutes(reScanned.routes);
|
|
5706
|
-
await generateSchemaFiles(sorted, ctx.rootDir, ctx.
|
|
5992
|
+
await generateSchemaFiles(sorted, ctx.rootDir, ctx.dist);
|
|
5707
5993
|
ctx.updateRoutes(sorted, reScanned.wsRoutes);
|
|
5708
5994
|
};
|
|
5709
5995
|
return devApp;
|
|
@@ -5729,34 +6015,33 @@ __export(devCommand_exports, {
|
|
|
5729
6015
|
generateRouteArtifacts: () => generateRouteArtifacts
|
|
5730
6016
|
});
|
|
5731
6017
|
import path14 from "path";
|
|
5732
|
-
async function devCommand() {
|
|
6018
|
+
async function devCommand(options) {
|
|
5733
6019
|
const rootDir = process.cwd();
|
|
5734
|
-
|
|
6020
|
+
const devDist = DEV_DIST;
|
|
6021
|
+
process.env.FAAPI_DIST = devDist;
|
|
5735
6022
|
if (!process.env.NODE_ENV) process.env.NODE_ENV = "development";
|
|
5736
6023
|
console.log("- Development mode");
|
|
5737
6024
|
console.log("- Compiling config...");
|
|
5738
|
-
await compileConfig({ rootDir,
|
|
5739
|
-
const _config = await loadConfig(rootDir,
|
|
5740
|
-
const appDir = process.env.FAAPI_APP_DIR ?? "src";
|
|
5741
|
-
const patterns = appDir === "." ? ["api/**/*.ts"] : [`${appDir}/api/**/*.ts`];
|
|
6025
|
+
await compileConfig({ rootDir, dist: devDist });
|
|
6026
|
+
const _config = await loadConfig(rootDir, devDist);
|
|
5742
6027
|
console.log("- Compiling TypeScript...");
|
|
5743
|
-
await compileDevRoutes({ rootDir,
|
|
6028
|
+
await compileDevRoutes({ rootDir, dist: devDist });
|
|
5744
6029
|
console.log("- Generating route manifest and schema...");
|
|
5745
|
-
await generateRouteArtifacts(rootDir,
|
|
6030
|
+
await generateRouteArtifacts(rootDir, PATTERNS2, devDist);
|
|
5746
6031
|
console.log("- Starting dev app...");
|
|
5747
|
-
const app = await createDevApp({ rootDir });
|
|
6032
|
+
const app = await createDevApp({ rootDir, port: options?.port });
|
|
5748
6033
|
await app.listen();
|
|
5749
|
-
startWatcher({ rootDir,
|
|
6034
|
+
startWatcher({ rootDir, app, devDist });
|
|
5750
6035
|
}
|
|
5751
|
-
async function generateRouteArtifacts(rootDir,
|
|
5752
|
-
const { routes, wsRoutes } = await scanRoutes(rootDir, patterns,
|
|
6036
|
+
async function generateRouteArtifacts(rootDir, patterns, dist) {
|
|
6037
|
+
const { routes, wsRoutes } = await scanRoutes(rootDir, patterns, dist);
|
|
5753
6038
|
const sorted = sortRoutes(routes);
|
|
5754
|
-
const routesPath = path14.resolve(rootDir,
|
|
5755
|
-
const serialized = serializeRoutes(sorted, wsRoutes, rootDir,
|
|
6039
|
+
const routesPath = path14.resolve(rootDir, dist, ROUTES_FILE2);
|
|
6040
|
+
const serialized = serializeRoutes(sorted, wsRoutes, rootDir, dist);
|
|
5756
6041
|
await writeRoutesModule(serialized, routesPath);
|
|
5757
|
-
await generateSchemaFiles(sorted, rootDir,
|
|
6042
|
+
await generateSchemaFiles(sorted, rootDir, dist);
|
|
5758
6043
|
}
|
|
5759
|
-
var
|
|
6044
|
+
var DEV_DIST, ROUTES_FILE2, PATTERNS2;
|
|
5760
6045
|
var init_devCommand = __esm({
|
|
5761
6046
|
"src/cli/devCommand.ts"() {
|
|
5762
6047
|
"use strict";
|
|
@@ -5769,54 +6054,54 @@ var init_devCommand = __esm({
|
|
|
5769
6054
|
init_loadConfig();
|
|
5770
6055
|
init_watcher();
|
|
5771
6056
|
init_createDevApp();
|
|
5772
|
-
|
|
6057
|
+
DEV_DIST = ".faapi";
|
|
5773
6058
|
ROUTES_FILE2 = "faapi-routes.js";
|
|
6059
|
+
PATTERNS2 = ["src/api/**/*.ts"];
|
|
5774
6060
|
}
|
|
5775
6061
|
});
|
|
5776
6062
|
|
|
5777
6063
|
// src/cli/compileBuildRoutes.ts
|
|
5778
6064
|
import path15 from "path";
|
|
5779
6065
|
import fs10 from "fs";
|
|
6066
|
+
import fg3 from "fast-glob";
|
|
5780
6067
|
async function compileBuildRoutes(options) {
|
|
5781
|
-
const {
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
logLevel = "silent"
|
|
5790
|
-
} = options;
|
|
5791
|
-
if (entries.length === 0) {
|
|
6068
|
+
const { rootDir, dist, files, logLevel = "silent" } = options;
|
|
6069
|
+
const entryPoints = files ?? await fg3([`${APP_DIR4}/**/*.ts`], {
|
|
6070
|
+
cwd: rootDir,
|
|
6071
|
+
onlyFiles: true,
|
|
6072
|
+
absolute: true,
|
|
6073
|
+
ignore: ["**/*.test.ts", "**/*.e2e.test.ts", "**/*.d.ts"]
|
|
6074
|
+
});
|
|
6075
|
+
if (entryPoints.length === 0) {
|
|
5792
6076
|
return { compiledFiles: [] };
|
|
5793
6077
|
}
|
|
5794
|
-
const
|
|
5795
|
-
await fs10.promises.mkdir(
|
|
6078
|
+
const absDist = path15.resolve(rootDir, dist);
|
|
6079
|
+
await fs10.promises.mkdir(absDist, { recursive: true });
|
|
5796
6080
|
const plugins = buildAliasPlugins(rootDir);
|
|
5797
6081
|
const esbuild = await import("esbuild");
|
|
5798
|
-
const outbase =
|
|
6082
|
+
const outbase = path15.resolve(rootDir, APP_DIR4);
|
|
5799
6083
|
await esbuild.build({
|
|
5800
|
-
entryPoints
|
|
5801
|
-
outdir:
|
|
6084
|
+
entryPoints,
|
|
6085
|
+
outdir: absDist,
|
|
5802
6086
|
outbase,
|
|
5803
|
-
bundle:
|
|
5804
|
-
splitting,
|
|
6087
|
+
bundle: false,
|
|
5805
6088
|
platform: "node",
|
|
5806
6089
|
format: "esm",
|
|
5807
6090
|
sourcemap: true,
|
|
5808
6091
|
packages: "external",
|
|
5809
6092
|
plugins,
|
|
5810
|
-
define,
|
|
5811
|
-
minifySyntax,
|
|
6093
|
+
define: { "process.env.NODE_ENV": '"production"' },
|
|
6094
|
+
minifySyntax: true,
|
|
5812
6095
|
logLevel
|
|
5813
6096
|
});
|
|
5814
|
-
return { compiledFiles:
|
|
6097
|
+
return { compiledFiles: entryPoints };
|
|
5815
6098
|
}
|
|
6099
|
+
var APP_DIR4;
|
|
5816
6100
|
var init_compileBuildRoutes = __esm({
|
|
5817
6101
|
"src/cli/compileBuildRoutes.ts"() {
|
|
5818
6102
|
"use strict";
|
|
5819
6103
|
init_aliasPlugin();
|
|
6104
|
+
APP_DIR4 = "src";
|
|
5820
6105
|
}
|
|
5821
6106
|
});
|
|
5822
6107
|
|
|
@@ -5827,66 +6112,35 @@ __export(buildCommand_exports, {
|
|
|
5827
6112
|
});
|
|
5828
6113
|
import path16 from "path";
|
|
5829
6114
|
import fs11 from "fs";
|
|
5830
|
-
import fg3 from "fast-glob";
|
|
5831
|
-
async function collectBundleEntries(rootDir, patterns, appDir) {
|
|
5832
|
-
const entries = /* @__PURE__ */ new Set();
|
|
5833
|
-
const handlerFiles = await fg3(patterns, {
|
|
5834
|
-
cwd: rootDir,
|
|
5835
|
-
onlyFiles: true,
|
|
5836
|
-
absolute: true
|
|
5837
|
-
});
|
|
5838
|
-
for (const f of handlerFiles) {
|
|
5839
|
-
if (f.endsWith("handler.ts")) entries.add(f);
|
|
5840
|
-
}
|
|
5841
|
-
const mwGlob = appDir === "." ? "**/middlewares.ts" : appDir + "/**/middlewares.ts";
|
|
5842
|
-
const mwFiles = await fg3([mwGlob], {
|
|
5843
|
-
cwd: rootDir,
|
|
5844
|
-
onlyFiles: true,
|
|
5845
|
-
absolute: true,
|
|
5846
|
-
ignore: ["**/*.test.ts", "**/*.e2e.test.ts", "**/*.d.ts"]
|
|
5847
|
-
});
|
|
5848
|
-
for (const f of mwFiles) entries.add(f);
|
|
5849
|
-
return Array.from(entries);
|
|
5850
|
-
}
|
|
5851
6115
|
async function buildCommand(options) {
|
|
5852
6116
|
const rootDir = options?.rootDir ?? process.cwd();
|
|
5853
|
-
const outdir =
|
|
5854
|
-
await compileConfig({ rootDir,
|
|
6117
|
+
const outdir = options?.dist ?? DEFAULT_DIST2;
|
|
6118
|
+
await compileConfig({ rootDir, dist: outdir });
|
|
5855
6119
|
const _config = await loadConfig(rootDir, outdir);
|
|
5856
|
-
const appDir = process.env.FAAPI_APP_DIR ?? "src";
|
|
5857
|
-
const patterns = appDir === "." ? ["api/**/*.ts"] : [`${appDir}/api/**/*.ts`];
|
|
5858
6120
|
console.log("faapi build started");
|
|
5859
6121
|
console.log(`- Root: ${rootDir}`);
|
|
5860
|
-
console.log(`-
|
|
6122
|
+
console.log(`- Source: src/`);
|
|
5861
6123
|
console.log(`- Output: ${outdir}`);
|
|
5862
|
-
console.log("\n[1/
|
|
5863
|
-
const entries = await collectBundleEntries(rootDir, patterns, appDir);
|
|
5864
|
-
console.log(` ${entries.length} entry file(s)`);
|
|
5865
|
-
if (entries.length === 0) {
|
|
5866
|
-
console.warn(" ! No entry files found, nothing to build");
|
|
5867
|
-
return;
|
|
5868
|
-
}
|
|
5869
|
-
console.log("\n[2/7] Compiling TypeScript (bundle mode)...");
|
|
6124
|
+
console.log("\n[1/6] Compiling TypeScript (bundle: false)...");
|
|
5870
6125
|
const result = await compileBuildRoutes({
|
|
5871
6126
|
rootDir,
|
|
5872
|
-
|
|
5873
|
-
outDir: outdir,
|
|
5874
|
-
entries,
|
|
5875
|
-
splitting: true,
|
|
5876
|
-
define: { "process.env.NODE_ENV": JSON.stringify("production") },
|
|
5877
|
-
minifySyntax: true,
|
|
6127
|
+
dist: outdir,
|
|
5878
6128
|
logLevel: "silent"
|
|
5879
6129
|
});
|
|
5880
|
-
console.log(` Compiled ${result.compiledFiles.length}
|
|
5881
|
-
|
|
5882
|
-
|
|
6130
|
+
console.log(` Compiled ${result.compiledFiles.length} file(s)`);
|
|
6131
|
+
if (result.compiledFiles.length === 0) {
|
|
6132
|
+
console.warn(" ! No source files found, nothing to build");
|
|
6133
|
+
return;
|
|
6134
|
+
}
|
|
6135
|
+
console.log("\n[2/6] Compiling config...");
|
|
6136
|
+
const configResult = await compileConfig({ rootDir, dist: outdir });
|
|
5883
6137
|
if (configResult.generated) {
|
|
5884
6138
|
console.log(` Written to ${configResult.outputFile}`);
|
|
5885
6139
|
} else {
|
|
5886
6140
|
console.log(" No config file found, skipped");
|
|
5887
6141
|
}
|
|
5888
|
-
console.log("\n[
|
|
5889
|
-
const { routes, wsRoutes } = await scanRoutes(rootDir,
|
|
6142
|
+
console.log("\n[3/6] Scanning routes...");
|
|
6143
|
+
const { routes, wsRoutes } = await scanRoutes(rootDir, PATTERNS3, outdir);
|
|
5890
6144
|
const sorted = sortRoutes(routes);
|
|
5891
6145
|
console.log(` Found ${sorted.length} routes, ${wsRoutes.length} WS routes`);
|
|
5892
6146
|
const conflicts = detectRouteConflicts(sorted);
|
|
@@ -5899,27 +6153,28 @@ async function buildCommand(options) {
|
|
|
5899
6153
|
}
|
|
5900
6154
|
}
|
|
5901
6155
|
}
|
|
5902
|
-
console.log("\n[
|
|
5903
|
-
await generateSchemaFiles(sorted, rootDir,
|
|
6156
|
+
console.log("\n[4/6] Generating schema...");
|
|
6157
|
+
await generateSchemaFiles(sorted, rootDir, outdir);
|
|
5904
6158
|
console.log(` Schema: zod.js files under ${path16.resolve(rootDir, outdir)}`);
|
|
5905
|
-
console.log("\n[6
|
|
6159
|
+
console.log("\n[5/6] Generating routes manifest...");
|
|
5906
6160
|
const routesPath = path16.resolve(rootDir, outdir, "faapi-routes.js");
|
|
5907
|
-
const serialized = serializeRoutes(sorted, wsRoutes, rootDir,
|
|
6161
|
+
const serialized = serializeRoutes(sorted, wsRoutes, rootDir, outdir);
|
|
5908
6162
|
await writeRoutesModule(serialized, routesPath);
|
|
5909
6163
|
console.log(` Written to ${routesPath}`);
|
|
5910
|
-
console.log("\n[
|
|
6164
|
+
console.log("\n[6/6] Generating entry file...");
|
|
5911
6165
|
const mainPath = path16.resolve(rootDir, outdir, "main.js");
|
|
6166
|
+
const createProdAppArgs = options?.dist && options.dist !== DEFAULT_DIST2 ? `{ dist: '${outdir}' }` : "";
|
|
5912
6167
|
const mainContent = `// \u7531 faapi build \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u7F16\u8F91
|
|
5913
6168
|
import { createProdApp } from '@faapi/faapi';
|
|
5914
6169
|
|
|
5915
|
-
const app = await createProdApp();
|
|
6170
|
+
const app = await createProdApp(${createProdAppArgs});
|
|
5916
6171
|
await app.listen();
|
|
5917
6172
|
`;
|
|
5918
6173
|
await fs11.promises.writeFile(mainPath, mainContent, "utf-8");
|
|
5919
6174
|
console.log(` Written to ${mainPath}`);
|
|
5920
6175
|
console.log("\nfaapi build completed");
|
|
5921
6176
|
}
|
|
5922
|
-
var
|
|
6177
|
+
var DEFAULT_DIST2, PATTERNS3;
|
|
5923
6178
|
var init_buildCommand = __esm({
|
|
5924
6179
|
"src/cli/buildCommand.ts"() {
|
|
5925
6180
|
"use strict";
|
|
@@ -5931,7 +6186,8 @@ var init_buildCommand = __esm({
|
|
|
5931
6186
|
init_compileBuildRoutes();
|
|
5932
6187
|
init_compileConfig();
|
|
5933
6188
|
init_loadConfig();
|
|
5934
|
-
|
|
6189
|
+
DEFAULT_DIST2 = "dist";
|
|
6190
|
+
PATTERNS3 = ["src/api/**/*.ts"];
|
|
5935
6191
|
}
|
|
5936
6192
|
});
|
|
5937
6193
|
|
|
@@ -6522,13 +6778,13 @@ var cac = (name = "") => new CAC(name);
|
|
|
6522
6778
|
|
|
6523
6779
|
// src/cli/index.ts
|
|
6524
6780
|
var cli = cac("faapi");
|
|
6525
|
-
cli.command("").alias("dev").action(async () => {
|
|
6781
|
+
cli.command("").alias("dev").option("--port <number>", "\u670D\u52A1\u7AEF\u53E3\uFF08\u9ED8\u8BA4 3000\uFF09").action(async (options) => {
|
|
6526
6782
|
const { devCommand: devCommand2 } = await Promise.resolve().then(() => (init_devCommand(), devCommand_exports));
|
|
6527
|
-
await devCommand2();
|
|
6783
|
+
await devCommand2(options);
|
|
6528
6784
|
});
|
|
6529
|
-
cli.command("build", "Build for production").action(async () => {
|
|
6785
|
+
cli.command("build", "Build for production").option("--dist <dir>", "\u4EA7\u7269\u8F93\u51FA\u76EE\u5F55\uFF0C\u9ED8\u8BA4 dist").action(async (options) => {
|
|
6530
6786
|
const { buildCommand: buildCommand2 } = await Promise.resolve().then(() => (init_buildCommand(), buildCommand_exports));
|
|
6531
|
-
await buildCommand2();
|
|
6787
|
+
await buildCommand2(options);
|
|
6532
6788
|
});
|
|
6533
6789
|
cli.help();
|
|
6534
6790
|
cli.parse();
|