@faapi/faapi 0.0.0-canary.22b65a2 → 0.0.0-canary.a3f7014
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/README.md +7 -1
- package/dist/cli/index.js +1634 -884
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +151 -27
- package/dist/index.js +379 -221
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/cli/index.js
CHANGED
|
@@ -9,351 +9,277 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
// src/
|
|
13
|
-
function
|
|
14
|
-
|
|
12
|
+
// src/utils/resolveAlias.ts
|
|
13
|
+
function resolveAlias(specifier, config) {
|
|
14
|
+
const candidates = [];
|
|
15
|
+
for (const [pattern, targets] of Object.entries(config.paths)) {
|
|
16
|
+
const wildcardIndex = pattern.indexOf("*");
|
|
17
|
+
if (wildcardIndex === -1) {
|
|
18
|
+
if (specifier === pattern) {
|
|
19
|
+
candidates.push(...targets);
|
|
20
|
+
}
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
const prefix = pattern.slice(0, wildcardIndex);
|
|
24
|
+
const suffix = pattern.slice(wildcardIndex + 1);
|
|
25
|
+
if (specifier.startsWith(prefix) && specifier.endsWith(suffix) && specifier.length >= prefix.length + suffix.length) {
|
|
26
|
+
const captured = specifier.slice(prefix.length, specifier.length - suffix.length);
|
|
27
|
+
for (const target of targets) {
|
|
28
|
+
candidates.push(target.replace("*", captured));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return candidates;
|
|
15
33
|
}
|
|
16
|
-
var
|
|
17
|
-
|
|
18
|
-
"src/router/constants.ts"() {
|
|
34
|
+
var init_resolveAlias = __esm({
|
|
35
|
+
"src/utils/resolveAlias.ts"() {
|
|
19
36
|
"use strict";
|
|
20
|
-
HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
|
|
21
|
-
HTTP_METHOD_SET = new Set(HTTP_METHODS);
|
|
22
37
|
}
|
|
23
38
|
});
|
|
24
39
|
|
|
25
|
-
// src/utils/
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (
|
|
32
|
-
|
|
40
|
+
// src/utils/readTsconfig.ts
|
|
41
|
+
import ts from "typescript";
|
|
42
|
+
import path from "path";
|
|
43
|
+
import fs from "fs";
|
|
44
|
+
function readTsconfig(rootDir) {
|
|
45
|
+
const tsconfigPath = path.resolve(rootDir, "tsconfig.json");
|
|
46
|
+
if (!fs.existsSync(tsconfigPath)) return null;
|
|
47
|
+
const configFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
|
|
48
|
+
if (configFile.error || !configFile.config) return null;
|
|
49
|
+
const parsed = ts.parseJsonConfigFileContent(configFile.config, ts.sys, rootDir);
|
|
50
|
+
const baseUrl = parsed.options.baseUrl ?? rootDir;
|
|
51
|
+
const rawPaths = parsed.options.paths;
|
|
52
|
+
if (!rawPaths) return null;
|
|
53
|
+
const paths = {};
|
|
54
|
+
for (const [pattern, targets] of Object.entries(rawPaths)) {
|
|
55
|
+
paths[pattern] = targets.map((t) => path.resolve(baseUrl, t));
|
|
33
56
|
}
|
|
34
|
-
return
|
|
57
|
+
return { baseUrl, paths };
|
|
35
58
|
}
|
|
36
|
-
var
|
|
37
|
-
"src/utils/
|
|
59
|
+
var init_readTsconfig = __esm({
|
|
60
|
+
"src/utils/readTsconfig.ts"() {
|
|
38
61
|
"use strict";
|
|
39
62
|
}
|
|
40
63
|
});
|
|
41
64
|
|
|
42
|
-
// src/
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return
|
|
65
|
+
// src/cli/aliasPlugin.ts
|
|
66
|
+
import path2 from "path";
|
|
67
|
+
import fs2 from "fs";
|
|
68
|
+
function toProdExtension(filePath) {
|
|
69
|
+
if (filePath.endsWith(".ts")) return filePath.slice(0, -3) + ".js";
|
|
70
|
+
if (filePath.endsWith(".tsx")) return filePath.slice(0, -4) + ".js";
|
|
71
|
+
if (filePath.endsWith(".jsx")) return filePath.slice(0, -4) + ".js";
|
|
72
|
+
return filePath;
|
|
49
73
|
}
|
|
50
|
-
function
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
} else if (segment.startsWith(":")) {
|
|
57
|
-
params.push(segment.slice(1));
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return params;
|
|
74
|
+
function toProdImportPath(sourceFile, importer) {
|
|
75
|
+
const importerDir = path2.dirname(importer);
|
|
76
|
+
let rel = path2.relative(importerDir, sourceFile);
|
|
77
|
+
rel = rel.split(path2.sep).join("/");
|
|
78
|
+
if (!rel.startsWith(".")) rel = "./" + rel;
|
|
79
|
+
return toProdExtension(rel);
|
|
61
80
|
}
|
|
62
|
-
function
|
|
63
|
-
|
|
81
|
+
function createAliasPlugin(config) {
|
|
82
|
+
const EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
83
|
+
const INDEX_EXTS = [
|
|
84
|
+
"/index.ts",
|
|
85
|
+
"/index.tsx",
|
|
86
|
+
"/index.js",
|
|
87
|
+
"/index.jsx",
|
|
88
|
+
"/index.mjs",
|
|
89
|
+
"/index.cjs"
|
|
90
|
+
];
|
|
91
|
+
const SPEC_RE = /(\bfrom\s*|import\s*\(\s*)(['"])([^'"]+)\2/g;
|
|
92
|
+
return {
|
|
93
|
+
name: "faapi-alias",
|
|
94
|
+
setup(build) {
|
|
95
|
+
build.onLoad({ filter: /\.(ts|tsx|js|jsx|mjs|cjs)$/ }, (args) => {
|
|
96
|
+
let source;
|
|
97
|
+
try {
|
|
98
|
+
source = fs2.readFileSync(args.path, "utf8");
|
|
99
|
+
} catch {
|
|
100
|
+
return void 0;
|
|
101
|
+
}
|
|
102
|
+
const importer = args.path;
|
|
103
|
+
let modified = false;
|
|
104
|
+
const newSource = source.replace(SPEC_RE, (full, prefix, quote, specifier) => {
|
|
105
|
+
if (specifier.startsWith(".") || specifier.startsWith("/") || specifier.startsWith("file:") || specifier.startsWith("node:")) {
|
|
106
|
+
return full;
|
|
107
|
+
}
|
|
108
|
+
const candidates = resolveAlias(specifier, config);
|
|
109
|
+
for (const candidate of candidates) {
|
|
110
|
+
for (const ext of EXTS) {
|
|
111
|
+
const file = candidate + ext;
|
|
112
|
+
if (fs2.existsSync(file)) {
|
|
113
|
+
modified = true;
|
|
114
|
+
return `${prefix}${quote}${toProdImportPath(file, importer)}${quote}`;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
for (const indexExt of INDEX_EXTS) {
|
|
118
|
+
const file = candidate + indexExt;
|
|
119
|
+
if (fs2.existsSync(file)) {
|
|
120
|
+
modified = true;
|
|
121
|
+
return `${prefix}${quote}${toProdImportPath(file, importer)}${quote}`;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return full;
|
|
126
|
+
});
|
|
127
|
+
if (!modified) return void 0;
|
|
128
|
+
return { contents: newSource, loader: "default" };
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
};
|
|
64
132
|
}
|
|
65
|
-
function
|
|
66
|
-
|
|
133
|
+
function buildAliasPlugins(rootDir) {
|
|
134
|
+
const tsconfig = readTsconfig(rootDir);
|
|
135
|
+
return tsconfig ? [createAliasPlugin(tsconfig)] : [];
|
|
67
136
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return "";
|
|
137
|
+
var init_aliasPlugin = __esm({
|
|
138
|
+
"src/cli/aliasPlugin.ts"() {
|
|
139
|
+
"use strict";
|
|
140
|
+
init_resolveAlias();
|
|
141
|
+
init_readTsconfig();
|
|
74
142
|
}
|
|
75
|
-
|
|
76
|
-
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// src/cli/compileDevRoutes.ts
|
|
146
|
+
import path3 from "path";
|
|
147
|
+
import fs3 from "fs";
|
|
148
|
+
import fg from "fast-glob";
|
|
149
|
+
async function compileDevRoutes(options) {
|
|
150
|
+
const { rootDir, appDir, outDir, files, logLevel = "silent" } = options;
|
|
151
|
+
const entryPoints = files ?? await fg([`${appDir}/**/*.ts`], {
|
|
152
|
+
cwd: rootDir,
|
|
153
|
+
onlyFiles: true,
|
|
154
|
+
absolute: true,
|
|
155
|
+
ignore: ["**/*.test.ts", "**/*.e2e.test.ts", "**/*.d.ts"]
|
|
156
|
+
});
|
|
157
|
+
if (entryPoints.length === 0) {
|
|
158
|
+
return { compiledFiles: [] };
|
|
159
|
+
}
|
|
160
|
+
const absOutDir = path3.resolve(rootDir, outDir);
|
|
161
|
+
await fs3.promises.mkdir(absOutDir, { recursive: true });
|
|
162
|
+
const plugins = buildAliasPlugins(rootDir);
|
|
163
|
+
const esbuild = await import("esbuild");
|
|
164
|
+
const outbase = appDir === "." ? rootDir : path3.resolve(rootDir, appDir);
|
|
165
|
+
await esbuild.build({
|
|
166
|
+
entryPoints,
|
|
167
|
+
outdir: absOutDir,
|
|
168
|
+
outbase,
|
|
169
|
+
bundle: false,
|
|
170
|
+
platform: "node",
|
|
171
|
+
format: "esm",
|
|
172
|
+
sourcemap: true,
|
|
173
|
+
packages: "external",
|
|
174
|
+
plugins,
|
|
175
|
+
logLevel
|
|
176
|
+
});
|
|
177
|
+
return { compiledFiles: entryPoints };
|
|
77
178
|
}
|
|
78
|
-
var
|
|
79
|
-
"src/
|
|
179
|
+
var init_compileDevRoutes = __esm({
|
|
180
|
+
"src/cli/compileDevRoutes.ts"() {
|
|
80
181
|
"use strict";
|
|
81
|
-
|
|
182
|
+
init_aliasPlugin();
|
|
82
183
|
}
|
|
83
184
|
});
|
|
84
185
|
|
|
85
|
-
// src/
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
186
|
+
// src/config/deepMerge.ts
|
|
187
|
+
function deepMerge(base, override) {
|
|
188
|
+
const result = { ...base };
|
|
189
|
+
for (const key of Object.keys(override)) {
|
|
190
|
+
const baseVal = base[key];
|
|
191
|
+
const overVal = override[key];
|
|
192
|
+
if (baseVal instanceof Date || overVal instanceof Date || baseVal instanceof RegExp || overVal instanceof RegExp || baseVal instanceof Map || overVal instanceof Map || baseVal instanceof Set || overVal instanceof Set) {
|
|
193
|
+
result[key] = overVal;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (baseVal !== null && overVal !== null && typeof baseVal === "object" && typeof overVal === "object" && !Array.isArray(baseVal) && !Array.isArray(overVal) && !(baseVal instanceof Function) && !(overVal instanceof Function)) {
|
|
197
|
+
result[key] = deepMerge(
|
|
198
|
+
baseVal,
|
|
199
|
+
overVal
|
|
200
|
+
);
|
|
201
|
+
} else {
|
|
202
|
+
result[key] = overVal;
|
|
203
|
+
}
|
|
94
204
|
}
|
|
95
|
-
return
|
|
205
|
+
return result;
|
|
96
206
|
}
|
|
97
|
-
var
|
|
98
|
-
var
|
|
99
|
-
"src/
|
|
207
|
+
var DEEP_MERGE_SOURCE;
|
|
208
|
+
var init_deepMerge = __esm({
|
|
209
|
+
"src/config/deepMerge.ts"() {
|
|
100
210
|
"use strict";
|
|
211
|
+
DEEP_MERGE_SOURCE = `const deepMerge = ${deepMerge.toString()};`;
|
|
101
212
|
}
|
|
102
213
|
});
|
|
103
214
|
|
|
104
|
-
// src/
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
return middlewareCache.get(absPath);
|
|
215
|
+
// src/cli/compileConfig.ts
|
|
216
|
+
import path4 from "path";
|
|
217
|
+
import fs4 from "fs";
|
|
218
|
+
function getEnv() {
|
|
219
|
+
return process.env.FAAPI_ENV || process.env.NODE_ENV || "development";
|
|
110
220
|
}
|
|
111
|
-
function
|
|
112
|
-
|
|
221
|
+
function findBaseConfig(rootDir) {
|
|
222
|
+
for (const f of BASE_CONFIG_FILES) {
|
|
223
|
+
if (fs4.existsSync(path4.join(rootDir, f))) {
|
|
224
|
+
return f.replace(/\.(ts|js)$/, "");
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
113
228
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
console.warn(`[faapi] middlewares.ts \u5E94\u5BFC\u51FA\u6570\u7EC4\uFF0C\u5DF2\u5FFD\u7565: ${filePath}`);
|
|
120
|
-
return { middlewares: [], injectors: {} };
|
|
229
|
+
function findEnvConfig(rootDir, env) {
|
|
230
|
+
for (const ext of ENV_CONFIG_EXTS) {
|
|
231
|
+
const f = `faapi.config.${env}${ext}`;
|
|
232
|
+
if (fs4.existsSync(path4.join(rootDir, f))) {
|
|
233
|
+
return `faapi.config.${env}`;
|
|
121
234
|
}
|
|
122
|
-
const validMiddlewares = middlewares.filter((m) => {
|
|
123
|
-
if (typeof m !== "function") {
|
|
124
|
-
console.warn(`[faapi] \u65E0\u6548\u7684\u4E2D\u95F4\u4EF6\u9879\uFF08\u5E94\u4E3A\u51FD\u6570\uFF09\uFF0C\u5DF2\u5FFD\u7565: ${typeof m}`);
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
return true;
|
|
128
|
-
});
|
|
129
|
-
const injectors = module.injectors ?? {};
|
|
130
|
-
if (typeof injectors !== "object" || injectors === null) {
|
|
131
|
-
console.warn(`[faapi] injectors \u5E94\u5BFC\u51FA\u5BF9\u8C61\uFF0C\u5DF2\u5FFD\u7565: ${filePath}`);
|
|
132
|
-
return { middlewares: validMiddlewares, injectors: {} };
|
|
133
|
-
}
|
|
134
|
-
const validInjectors = {};
|
|
135
|
-
for (const [name, injector] of Object.entries(injectors)) {
|
|
136
|
-
if (typeof injector !== "function") {
|
|
137
|
-
console.warn(`[faapi] \u6CE8\u5165\u5668 ${name} \u5E94\u4E3A\u51FD\u6570\uFF0C\u5DF2\u5FFD\u7565`);
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
validInjectors[name] = injector;
|
|
141
|
-
}
|
|
142
|
-
return { middlewares: validMiddlewares, injectors: validInjectors };
|
|
143
|
-
} catch {
|
|
144
|
-
return { middlewares: [], injectors: {} };
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
var middlewareCache;
|
|
148
|
-
var init_loadMiddlewares = __esm({
|
|
149
|
-
"src/middleware/loadMiddlewares.ts"() {
|
|
150
|
-
"use strict";
|
|
151
|
-
init_importWithCacheBust();
|
|
152
|
-
middlewareCache = /* @__PURE__ */ new Map();
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
// src/router/scanRoutes.ts
|
|
157
|
-
import fg from "fast-glob";
|
|
158
|
-
import path from "path";
|
|
159
|
-
import fs from "fs";
|
|
160
|
-
function toProdAbsPath(sourceAbsPath, rootDir, appDir, prodDir) {
|
|
161
|
-
let rel = path.relative(rootDir, sourceAbsPath).replace(/\\/g, "/");
|
|
162
|
-
if (appDir !== "." && rel.startsWith(`${appDir}/`)) {
|
|
163
|
-
rel = rel.slice(appDir.length + 1);
|
|
164
|
-
}
|
|
165
|
-
const prodRel = `${prodDir}/${rel.replace(/\.ts$/, ".js")}`;
|
|
166
|
-
return path.resolve(rootDir, prodRel);
|
|
167
|
-
}
|
|
168
|
-
async function findMergedMiddlewares(routeFilePath, rootDir, appDir, prodDir) {
|
|
169
|
-
const routeDir = path.dirname(routeFilePath);
|
|
170
|
-
const resolvedRoot = path.resolve(rootDir);
|
|
171
|
-
const mwPaths = [];
|
|
172
|
-
let currentDir = path.resolve(rootDir, routeDir);
|
|
173
|
-
while (true) {
|
|
174
|
-
if (prodDir) {
|
|
175
|
-
const mwPath = path.join(currentDir, "middlewares.js");
|
|
176
|
-
const absMwPath = path.resolve(rootDir, mwPath);
|
|
177
|
-
const prodAbsMwPath = toProdAbsPath(absMwPath, rootDir, appDir, prodDir);
|
|
178
|
-
if (fs.existsSync(prodAbsMwPath)) {
|
|
179
|
-
mwPaths.push(prodAbsMwPath);
|
|
180
|
-
}
|
|
181
|
-
} else {
|
|
182
|
-
for (const ext of [".ts", ".js"]) {
|
|
183
|
-
const mwPath = path.join(currentDir, `middlewares${ext}`);
|
|
184
|
-
const absMwPath = path.resolve(rootDir, mwPath);
|
|
185
|
-
if (fs.existsSync(absMwPath)) {
|
|
186
|
-
mwPaths.push(absMwPath);
|
|
187
|
-
break;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
if (currentDir === resolvedRoot) break;
|
|
192
|
-
const parentDir = path.dirname(currentDir);
|
|
193
|
-
if (parentDir === currentDir) break;
|
|
194
|
-
currentDir = parentDir;
|
|
195
|
-
}
|
|
196
|
-
if (mwPaths.length === 0) return void 0;
|
|
197
|
-
mwPaths.reverse();
|
|
198
|
-
const mergedMiddlewares = [];
|
|
199
|
-
const mergedInjectors = {};
|
|
200
|
-
for (const absMwPath of mwPaths) {
|
|
201
|
-
let bundle = getCachedMiddlewares(absMwPath);
|
|
202
|
-
if (bundle === void 0) {
|
|
203
|
-
bundle = await loadMiddlewaresFile(absMwPath);
|
|
204
|
-
setCachedMiddlewares(absMwPath, bundle);
|
|
205
|
-
}
|
|
206
|
-
mergedMiddlewares.push(...bundle.middlewares);
|
|
207
|
-
for (const [name, injector] of Object.entries(bundle.injectors)) {
|
|
208
|
-
mergedInjectors[name] = injector;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
if (mergedMiddlewares.length === 0 && Object.keys(mergedInjectors).length === 0) {
|
|
212
|
-
return void 0;
|
|
213
|
-
}
|
|
214
|
-
return { middlewares: mergedMiddlewares, injectors: mergedInjectors };
|
|
215
|
-
}
|
|
216
|
-
async function extractMethodsFromHandler(absPath) {
|
|
217
|
-
try {
|
|
218
|
-
const module = await importWithCacheBust(absPath);
|
|
219
|
-
const methods = [];
|
|
220
|
-
for (const key of Object.keys(module)) {
|
|
221
|
-
if (isHttpMethod(key) && typeof module[key] === "function") {
|
|
222
|
-
methods.push(key);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
return methods;
|
|
226
|
-
} catch (err) {
|
|
227
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
228
|
-
console.warn(`[faapi] \u52A0\u8F7D\u8DEF\u7531\u6587\u4EF6\u5931\u8D25 ${absPath}: ${reason}`);
|
|
229
|
-
return [];
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
async function hasWsExport(absPath) {
|
|
233
|
-
try {
|
|
234
|
-
const module = await importWithCacheBust(absPath);
|
|
235
|
-
return typeof module["WS"] === "function";
|
|
236
|
-
} catch (err) {
|
|
237
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
238
|
-
console.warn(`[faapi] \u52A0\u8F7D\u8DEF\u7531\u6587\u4EF6\u5931\u8D25\uFF08WS \u68C0\u6D4B\uFF09${absPath}: ${reason}`);
|
|
239
|
-
return false;
|
|
240
235
|
}
|
|
236
|
+
return null;
|
|
241
237
|
}
|
|
242
|
-
async function
|
|
243
|
-
const
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
absolute: false
|
|
248
|
-
});
|
|
249
|
-
const routes = [];
|
|
250
|
-
const wsRoutes = [];
|
|
251
|
-
for (const file of files) {
|
|
252
|
-
const normalizedFile = file.replace(/\\/g, "/");
|
|
253
|
-
const fileName = normalizedFile.split("/").pop();
|
|
254
|
-
if (fileName === "handler.ts" || fileName === "handler.js") {
|
|
255
|
-
const absPath = path.resolve(rootDir, normalizedFile);
|
|
256
|
-
const importPath = prodDir ? toProdAbsPath(absPath, rootDir, dir, prodDir) : absPath;
|
|
257
|
-
const urlPath = filePathToUrlPath(normalizedFile, dir);
|
|
258
|
-
const paramNames = extractParamNames(urlPath);
|
|
259
|
-
const isDynamic = paramNames.length > 0;
|
|
260
|
-
const isCatchAll = normalizedFile.split("/").some(isCatchAllSegment);
|
|
261
|
-
const middlewareBundle = await findMergedMiddlewares(normalizedFile, rootDir, dir, prodDir);
|
|
262
|
-
const methods = await extractMethodsFromHandler(importPath);
|
|
263
|
-
for (const method of methods) {
|
|
264
|
-
routes.push({
|
|
265
|
-
method,
|
|
266
|
-
urlPath,
|
|
267
|
-
filePath: normalizedFile,
|
|
268
|
-
paramNames,
|
|
269
|
-
isDynamic,
|
|
270
|
-
isCatchAll: isCatchAll || void 0,
|
|
271
|
-
middlewares: middlewareBundle?.middlewares,
|
|
272
|
-
injectors: middlewareBundle?.injectors
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
const hasWs = await hasWsExport(importPath);
|
|
276
|
-
if (hasWs) {
|
|
277
|
-
wsRoutes.push({
|
|
278
|
-
urlPath,
|
|
279
|
-
filePath: normalizedFile,
|
|
280
|
-
paramNames,
|
|
281
|
-
isDynamic,
|
|
282
|
-
isCatchAll: isCatchAll || void 0,
|
|
283
|
-
middlewares: middlewareBundle?.middlewares,
|
|
284
|
-
injectors: middlewareBundle?.injectors
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
continue;
|
|
288
|
-
}
|
|
238
|
+
async function compileConfig(options) {
|
|
239
|
+
const { rootDir, outDir } = options;
|
|
240
|
+
const baseConfig = findBaseConfig(rootDir);
|
|
241
|
+
if (!baseConfig) {
|
|
242
|
+
return { generated: false, outputFile: "" };
|
|
289
243
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
244
|
+
const env = getEnv();
|
|
245
|
+
const envConfig = findEnvConfig(rootDir, env);
|
|
246
|
+
const imports = [`import base from './${baseConfig}';`];
|
|
247
|
+
let exportDefault;
|
|
248
|
+
if (envConfig) {
|
|
249
|
+
imports.push(`import env from './${envConfig}';`);
|
|
250
|
+
exportDefault = "export default deepMerge(base, env);";
|
|
251
|
+
} else {
|
|
252
|
+
exportDefault = "export default base;";
|
|
299
253
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
return aSegments - bSegments;
|
|
315
|
-
}
|
|
316
|
-
return a.urlPath.localeCompare(b.urlPath);
|
|
254
|
+
const entryCode = [...imports, DEEP_MERGE_SOURCE, exportDefault].join("\n");
|
|
255
|
+
const outputFile = path4.resolve(rootDir, outDir, "faapi-config.js");
|
|
256
|
+
await fs4.promises.mkdir(path4.dirname(outputFile), { recursive: true });
|
|
257
|
+
const esbuild = await import("esbuild");
|
|
258
|
+
await esbuild.build({
|
|
259
|
+
stdin: { contents: entryCode, resolveDir: rootDir, loader: "ts" },
|
|
260
|
+
outfile: outputFile,
|
|
261
|
+
bundle: true,
|
|
262
|
+
format: "esm",
|
|
263
|
+
platform: "node",
|
|
264
|
+
target: "node20",
|
|
265
|
+
sourcemap: true,
|
|
266
|
+
packages: "external",
|
|
267
|
+
logLevel: "silent"
|
|
317
268
|
});
|
|
269
|
+
return { generated: true, outputFile };
|
|
318
270
|
}
|
|
319
|
-
var
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
}
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
// src/router/detectRouteConflicts.ts
|
|
326
|
-
function detectRouteConflicts(routes) {
|
|
327
|
-
const map = /* @__PURE__ */ new Map();
|
|
328
|
-
for (const route of routes) {
|
|
329
|
-
const key = `${route.method} ${route.urlPath}`;
|
|
330
|
-
const existing = map.get(key);
|
|
331
|
-
if (existing) {
|
|
332
|
-
existing.files.push(route.filePath);
|
|
333
|
-
} else {
|
|
334
|
-
map.set(key, {
|
|
335
|
-
method: route.method,
|
|
336
|
-
urlPath: route.urlPath,
|
|
337
|
-
files: [route.filePath]
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
const conflicts = [];
|
|
342
|
-
for (const conflict of map.values()) {
|
|
343
|
-
if (conflict.files.length > 1) {
|
|
344
|
-
conflicts.push(conflict);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
return conflicts;
|
|
348
|
-
}
|
|
349
|
-
var init_detectRouteConflicts = __esm({
|
|
350
|
-
"src/router/detectRouteConflicts.ts"() {
|
|
271
|
+
var BASE_CONFIG_FILES, ENV_CONFIG_EXTS;
|
|
272
|
+
var init_compileConfig = __esm({
|
|
273
|
+
"src/cli/compileConfig.ts"() {
|
|
351
274
|
"use strict";
|
|
275
|
+
init_deepMerge();
|
|
276
|
+
BASE_CONFIG_FILES = ["faapi.config.ts", "faapi.config.js"];
|
|
277
|
+
ENV_CONFIG_EXTS = [".ts", ".js"];
|
|
352
278
|
}
|
|
353
279
|
});
|
|
354
280
|
|
|
355
281
|
// src/ast/createProgram.ts
|
|
356
|
-
import
|
|
282
|
+
import ts2 from "typescript";
|
|
357
283
|
function invalidateProgramCache() {
|
|
358
284
|
programCache.clear();
|
|
359
285
|
}
|
|
@@ -362,11 +288,11 @@ function createProgram(filePath) {
|
|
|
362
288
|
if (cached) {
|
|
363
289
|
return cached;
|
|
364
290
|
}
|
|
365
|
-
const program =
|
|
291
|
+
const program = ts2.createProgram([filePath], {
|
|
366
292
|
strict: true,
|
|
367
|
-
target:
|
|
368
|
-
module:
|
|
369
|
-
moduleResolution:
|
|
293
|
+
target: ts2.ScriptTarget.ES2022,
|
|
294
|
+
module: ts2.ModuleKind.NodeNext,
|
|
295
|
+
moduleResolution: ts2.ModuleResolutionKind.NodeNext,
|
|
370
296
|
skipLibCheck: true,
|
|
371
297
|
noEmit: true
|
|
372
298
|
});
|
|
@@ -382,83 +308,83 @@ var init_createProgram = __esm({
|
|
|
382
308
|
});
|
|
383
309
|
|
|
384
310
|
// src/ast/resolveTypeNode.ts
|
|
385
|
-
import
|
|
311
|
+
import ts3 from "typescript";
|
|
386
312
|
function resolveTypeNode(typeNode, checker, visited = /* @__PURE__ */ new Set()) {
|
|
387
313
|
const kind = typeNode.kind;
|
|
388
314
|
switch (kind) {
|
|
389
|
-
case
|
|
315
|
+
case ts3.SyntaxKind.StringKeyword:
|
|
390
316
|
return { kind: "string" };
|
|
391
|
-
case
|
|
317
|
+
case ts3.SyntaxKind.NumberKeyword:
|
|
392
318
|
return { kind: "number" };
|
|
393
|
-
case
|
|
319
|
+
case ts3.SyntaxKind.BooleanKeyword:
|
|
394
320
|
return { kind: "boolean" };
|
|
395
|
-
case
|
|
321
|
+
case ts3.SyntaxKind.BigIntKeyword:
|
|
396
322
|
throw new SchemaExtractionError(
|
|
397
323
|
typeNode.getText(),
|
|
398
324
|
"bigint \u65E0\u6CD5\u901A\u8FC7 HTTP/JSON \u4F20\u8F93,\u8BF7\u6539\u7528 string \u6216 number"
|
|
399
325
|
);
|
|
400
|
-
case
|
|
326
|
+
case ts3.SyntaxKind.SymbolKeyword:
|
|
401
327
|
throw new SchemaExtractionError(typeNode.getText(), "symbol \u65E0\u6CD5\u901A\u8FC7 HTTP/JSON \u4F20\u8F93");
|
|
402
|
-
case
|
|
328
|
+
case ts3.SyntaxKind.NullKeyword:
|
|
403
329
|
return { kind: "null" };
|
|
404
|
-
case
|
|
330
|
+
case ts3.SyntaxKind.UndefinedKeyword:
|
|
405
331
|
return { kind: "undefined" };
|
|
406
|
-
case
|
|
332
|
+
case ts3.SyntaxKind.UnknownKeyword:
|
|
407
333
|
return { kind: "any" };
|
|
408
|
-
case
|
|
334
|
+
case ts3.SyntaxKind.AnyKeyword:
|
|
409
335
|
throw new SchemaExtractionError(typeNode.getText(), "any \u4E0D\u652F\u6301\uFF0C\u8BF7\u4F7F\u7528 unknown \u8868\u793A\u4E0D\u6821\u9A8C");
|
|
410
|
-
case
|
|
336
|
+
case ts3.SyntaxKind.VoidKeyword:
|
|
411
337
|
throw new SchemaExtractionError(typeNode.getText(), "void \u4E0D\u652F\u6301\u8FD0\u884C\u65F6\u6821\u9A8C");
|
|
412
|
-
case
|
|
338
|
+
case ts3.SyntaxKind.NeverKeyword:
|
|
413
339
|
throw new SchemaExtractionError(typeNode.getText(), "never \u4E0D\u652F\u6301\u8FD0\u884C\u65F6\u6821\u9A8C");
|
|
414
|
-
case
|
|
340
|
+
case ts3.SyntaxKind.ObjectKeyword:
|
|
415
341
|
throw new SchemaExtractionError(
|
|
416
342
|
typeNode.getText(),
|
|
417
343
|
"object \u4E0D\u652F\u6301\uFF0C\u8BF7\u4F7F\u7528\u5177\u4F53\u5BF9\u8C61\u7C7B\u578B\u6216 unknown"
|
|
418
344
|
);
|
|
419
345
|
}
|
|
420
|
-
if (
|
|
346
|
+
if (ts3.isLiteralTypeNode(typeNode)) {
|
|
421
347
|
const literal = typeNode.literal;
|
|
422
|
-
if (
|
|
348
|
+
if (ts3.isStringLiteral(literal)) {
|
|
423
349
|
return { kind: "literal", value: literal.text };
|
|
424
350
|
}
|
|
425
|
-
if (
|
|
351
|
+
if (ts3.isNumericLiteral(literal)) {
|
|
426
352
|
return { kind: "literal", value: Number(literal.text) };
|
|
427
353
|
}
|
|
428
|
-
if (literal.kind ===
|
|
354
|
+
if (literal.kind === ts3.SyntaxKind.TrueKeyword) {
|
|
429
355
|
return { kind: "literal", value: true };
|
|
430
356
|
}
|
|
431
|
-
if (literal.kind ===
|
|
357
|
+
if (literal.kind === ts3.SyntaxKind.FalseKeyword) {
|
|
432
358
|
return { kind: "literal", value: false };
|
|
433
359
|
}
|
|
434
|
-
if (literal.kind ===
|
|
360
|
+
if (literal.kind === ts3.SyntaxKind.NullKeyword) {
|
|
435
361
|
return { kind: "null" };
|
|
436
362
|
}
|
|
437
363
|
throw new SchemaExtractionError(typeNode.getText(), "\u4E0D\u652F\u6301\u7684\u5B57\u9762\u91CF\u7C7B\u578B");
|
|
438
364
|
}
|
|
439
|
-
if (
|
|
365
|
+
if (ts3.isArrayTypeNode(typeNode)) {
|
|
440
366
|
return {
|
|
441
367
|
kind: "array",
|
|
442
368
|
element: resolveTypeNode(typeNode.elementType, checker, visited)
|
|
443
369
|
};
|
|
444
370
|
}
|
|
445
|
-
if (
|
|
371
|
+
if (ts3.isTupleTypeNode(typeNode)) {
|
|
446
372
|
const elements = typeNode.elements.map((e) => {
|
|
447
|
-
if (
|
|
373
|
+
if (ts3.isRestTypeNode(e)) {
|
|
448
374
|
const inner = resolveTypeNode(e.type, checker, visited);
|
|
449
375
|
if (inner.kind === "array") {
|
|
450
376
|
return { type: inner.element, optional: false, rest: true };
|
|
451
377
|
}
|
|
452
378
|
return { type: inner, optional: false, rest: true };
|
|
453
379
|
}
|
|
454
|
-
if (
|
|
380
|
+
if (ts3.isNamedTupleMember(e)) {
|
|
455
381
|
return {
|
|
456
382
|
type: resolveTypeNode(e.type, checker, visited),
|
|
457
383
|
optional: !!e.questionToken,
|
|
458
384
|
rest: false
|
|
459
385
|
};
|
|
460
386
|
}
|
|
461
|
-
if (
|
|
387
|
+
if (ts3.isOptionalTypeNode(e)) {
|
|
462
388
|
return {
|
|
463
389
|
type: resolveTypeNode(e.type, checker, visited),
|
|
464
390
|
optional: true,
|
|
@@ -473,11 +399,11 @@ function resolveTypeNode(typeNode, checker, visited = /* @__PURE__ */ new Set())
|
|
|
473
399
|
});
|
|
474
400
|
return { kind: "tuple", elements };
|
|
475
401
|
}
|
|
476
|
-
if (
|
|
402
|
+
if (ts3.isUnionTypeNode(typeNode)) {
|
|
477
403
|
const members = typeNode.types.map((t) => resolveTypeNode(t, checker, visited));
|
|
478
404
|
return { kind: "union", members };
|
|
479
405
|
}
|
|
480
|
-
if (
|
|
406
|
+
if (ts3.isIntersectionTypeNode(typeNode)) {
|
|
481
407
|
const properties = [];
|
|
482
408
|
for (const t of typeNode.types) {
|
|
483
409
|
const resolved = resolveTypeNode(t, checker, visited);
|
|
@@ -487,13 +413,16 @@ function resolveTypeNode(typeNode, checker, visited = /* @__PURE__ */ new Set())
|
|
|
487
413
|
}
|
|
488
414
|
return { kind: "object", properties };
|
|
489
415
|
}
|
|
490
|
-
if (
|
|
416
|
+
if (ts3.isTypeLiteralNode(typeNode)) {
|
|
491
417
|
return resolveTypeLiteral(typeNode, checker, visited);
|
|
492
418
|
}
|
|
493
|
-
if (
|
|
419
|
+
if (ts3.isTypeOperatorNode(typeNode) && typeNode.operator === ts3.SyntaxKind.KeyOfKeyword) {
|
|
494
420
|
return resolveKeyOf(typeNode, checker);
|
|
495
421
|
}
|
|
496
|
-
if (
|
|
422
|
+
if (ts3.isTypeOperatorNode(typeNode) && typeNode.operator === ts3.SyntaxKind.ReadonlyKeyword) {
|
|
423
|
+
return resolveTypeNode(typeNode.type, checker, visited);
|
|
424
|
+
}
|
|
425
|
+
if (ts3.isTypeReferenceNode(typeNode)) {
|
|
497
426
|
return resolveTypeReference(typeNode, checker, visited);
|
|
498
427
|
}
|
|
499
428
|
throw new SchemaExtractionError(typeNode.getText(), "\u4E0D\u652F\u6301\u7684\u7C7B\u578B\u8BED\u6CD5");
|
|
@@ -501,13 +430,17 @@ function resolveTypeNode(typeNode, checker, visited = /* @__PURE__ */ new Set())
|
|
|
501
430
|
function resolveTypeLiteral(typeNode, checker, visited = /* @__PURE__ */ new Set()) {
|
|
502
431
|
const properties = [];
|
|
503
432
|
for (const member of typeNode.members) {
|
|
504
|
-
if (
|
|
433
|
+
if (ts3.isPropertySignature(member) && member.name) {
|
|
505
434
|
const name = member.name.getText();
|
|
506
435
|
const optional = !!member.questionToken;
|
|
507
436
|
const type = member.type ? resolveTypeNode(member.type, checker, visited) : { kind: "any" };
|
|
508
|
-
|
|
437
|
+
const constraints = extractConstraintsFromJsDoc(member, name);
|
|
438
|
+
validateConstraints(constraints, type, name);
|
|
439
|
+
properties.push(
|
|
440
|
+
constraints.length > 0 ? { name, type, optional, constraints } : { name, type, optional }
|
|
441
|
+
);
|
|
509
442
|
}
|
|
510
|
-
if (
|
|
443
|
+
if (ts3.isIndexSignatureDeclaration(member)) {
|
|
511
444
|
const keyType = member.parameters[0]?.type ? resolveTypeNode(member.parameters[0].type, checker, visited) : { kind: "any" };
|
|
512
445
|
const valueType = member.type ? resolveTypeNode(member.type, checker, visited) : { kind: "any" };
|
|
513
446
|
return { kind: "record", key: keyType, value: valueType };
|
|
@@ -587,7 +520,7 @@ function resolveTypeReference(typeNode, checker, visited = /* @__PURE__ */ new S
|
|
|
587
520
|
if (typeName === "Date") {
|
|
588
521
|
return { kind: "date" };
|
|
589
522
|
}
|
|
590
|
-
if (typeName === "Array" && typeNode.typeArguments?.length === 1) {
|
|
523
|
+
if ((typeName === "Array" || typeName === "ReadonlyArray") && typeNode.typeArguments?.length === 1) {
|
|
591
524
|
return {
|
|
592
525
|
kind: "array",
|
|
593
526
|
element: resolveTypeNode(typeNode.typeArguments[0], checker, visited)
|
|
@@ -650,17 +583,17 @@ function resolveTypeReference(typeNode, checker, visited = /* @__PURE__ */ new S
|
|
|
650
583
|
}
|
|
651
584
|
visited.add(typeName);
|
|
652
585
|
if (checker) {
|
|
653
|
-
const symbol = typeNode.typeName.kind ===
|
|
586
|
+
const symbol = typeNode.typeName.kind === ts3.SyntaxKind.Identifier ? checker.getSymbolAtLocation(typeNode.typeName) : void 0;
|
|
654
587
|
if (symbol) {
|
|
655
588
|
const declaration = symbol.declarations?.[0];
|
|
656
589
|
if (declaration) {
|
|
657
|
-
if (
|
|
590
|
+
if (ts3.isInterfaceDeclaration(declaration)) {
|
|
658
591
|
return resolveInterfaceDeclaration(declaration, checker, visited);
|
|
659
592
|
}
|
|
660
|
-
if (
|
|
593
|
+
if (ts3.isTypeAliasDeclaration(declaration)) {
|
|
661
594
|
return resolveTypeNode(declaration.type, checker, visited);
|
|
662
595
|
}
|
|
663
|
-
if (
|
|
596
|
+
if (ts3.isEnumDeclaration(declaration)) {
|
|
664
597
|
return resolveEnumDeclaration(declaration);
|
|
665
598
|
}
|
|
666
599
|
}
|
|
@@ -673,9 +606,9 @@ function resolveEnumDeclaration(node) {
|
|
|
673
606
|
let nextNumericValue = 0;
|
|
674
607
|
for (const member of node.members) {
|
|
675
608
|
if (member.initializer) {
|
|
676
|
-
if (
|
|
609
|
+
if (ts3.isStringLiteral(member.initializer)) {
|
|
677
610
|
members.push({ kind: "literal", value: member.initializer.text });
|
|
678
|
-
} else if (
|
|
611
|
+
} else if (ts3.isNumericLiteral(member.initializer)) {
|
|
679
612
|
const num = Number(member.initializer.text);
|
|
680
613
|
members.push({ kind: "literal", value: num });
|
|
681
614
|
nextNumericValue = num + 1;
|
|
@@ -696,7 +629,7 @@ function resolveInterfaceDeclaration(node, checker, visited = /* @__PURE__ */ ne
|
|
|
696
629
|
const properties = [];
|
|
697
630
|
const propMap = /* @__PURE__ */ new Map();
|
|
698
631
|
for (const heritageClause of node.heritageClauses ?? []) {
|
|
699
|
-
if (heritageClause.token ===
|
|
632
|
+
if (heritageClause.token === ts3.SyntaxKind.ExtendsKeyword) {
|
|
700
633
|
for (const expr of heritageClause.types) {
|
|
701
634
|
const parentType = resolveTypeNode(expr, checker, visited);
|
|
702
635
|
if (parentType.kind === "object") {
|
|
@@ -708,13 +641,18 @@ function resolveInterfaceDeclaration(node, checker, visited = /* @__PURE__ */ ne
|
|
|
708
641
|
}
|
|
709
642
|
}
|
|
710
643
|
for (const member of node.members) {
|
|
711
|
-
if (
|
|
644
|
+
if (ts3.isPropertySignature(member) && member.name) {
|
|
712
645
|
const name = member.name.getText();
|
|
713
646
|
const optional = !!member.questionToken;
|
|
714
647
|
const type = member.type ? resolveTypeNode(member.type, checker, visited) : { kind: "any" };
|
|
715
|
-
|
|
648
|
+
const constraints = extractConstraintsFromJsDoc(member, name);
|
|
649
|
+
validateConstraints(constraints, type, name);
|
|
650
|
+
propMap.set(
|
|
651
|
+
name,
|
|
652
|
+
constraints.length > 0 ? { name, type, optional, constraints } : { name, type, optional }
|
|
653
|
+
);
|
|
716
654
|
}
|
|
717
|
-
if (
|
|
655
|
+
if (ts3.isIndexSignatureDeclaration(member)) {
|
|
718
656
|
const keyType = member.parameters[0]?.type ? resolveTypeNode(member.parameters[0].type, checker, visited) : { kind: "any" };
|
|
719
657
|
const valueType = member.type ? resolveTypeNode(member.type, checker, visited) : { kind: "any" };
|
|
720
658
|
return { kind: "record", key: keyType, value: valueType };
|
|
@@ -725,33 +663,169 @@ function resolveInterfaceDeclaration(node, checker, visited = /* @__PURE__ */ ne
|
|
|
725
663
|
}
|
|
726
664
|
return { kind: "object", properties };
|
|
727
665
|
}
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
666
|
+
function extractConstraintsFromJsDoc(node, fieldName) {
|
|
667
|
+
const jsDocs = ts3.getJSDocCommentsAndTags(node).filter((entry) => ts3.isJSDoc(entry));
|
|
668
|
+
if (jsDocs.length === 0) return [];
|
|
669
|
+
const constraints = [];
|
|
670
|
+
for (const jsDoc of jsDocs) {
|
|
671
|
+
if (!jsDoc.tags) continue;
|
|
672
|
+
for (const tag of jsDoc.tags) {
|
|
673
|
+
const constraint = parseJsDocTag(tag, fieldName);
|
|
674
|
+
if (constraint) constraints.push(constraint);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
return constraints;
|
|
678
|
+
}
|
|
679
|
+
function getTagCommentText(tag) {
|
|
680
|
+
const comment = tag.comment;
|
|
681
|
+
if (typeof comment === "string") return comment;
|
|
682
|
+
return void 0;
|
|
683
|
+
}
|
|
684
|
+
function parseJsDocTag(tag, fieldName) {
|
|
685
|
+
const tagName = tag.tagName.text;
|
|
686
|
+
switch (tagName) {
|
|
687
|
+
// 数值约束(带值)
|
|
688
|
+
case "max":
|
|
689
|
+
case "min": {
|
|
690
|
+
const value = parseNumberValue(tag, fieldName, tagName);
|
|
691
|
+
return { kind: tagName, value };
|
|
692
|
+
}
|
|
693
|
+
// 长度约束(带值)
|
|
694
|
+
case "maxLength":
|
|
695
|
+
case "minLength":
|
|
696
|
+
case "length": {
|
|
697
|
+
const value = parseNumberValue(tag, fieldName, tagName);
|
|
698
|
+
return { kind: tagName, value };
|
|
699
|
+
}
|
|
700
|
+
// 正则约束(带 /pattern/flags 值)
|
|
701
|
+
case "regex":
|
|
702
|
+
case "pattern": {
|
|
703
|
+
const text = getTagCommentText(tag);
|
|
704
|
+
if (!text) {
|
|
705
|
+
throw new SchemaExtractionError(fieldName, `@${tagName} \u6807\u7B7E\u9700\u8981 /pattern/flags \u5F62\u5F0F\u7684\u503C`);
|
|
706
|
+
}
|
|
707
|
+
const regex = parseRegexLiteral(text.trim(), fieldName);
|
|
708
|
+
return { kind: "regex", pattern: regex.pattern, flags: regex.flags };
|
|
709
|
+
}
|
|
710
|
+
// 数值约束(无值)
|
|
711
|
+
case "int":
|
|
712
|
+
case "positive":
|
|
713
|
+
case "negative":
|
|
714
|
+
case "nonnegative":
|
|
715
|
+
case "nonpositive":
|
|
716
|
+
return { kind: tagName };
|
|
717
|
+
// 字符串格式约束(无值)
|
|
718
|
+
case "email":
|
|
719
|
+
case "url":
|
|
720
|
+
case "uuid":
|
|
721
|
+
return { kind: tagName };
|
|
722
|
+
default:
|
|
723
|
+
return null;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
function parseNumberValue(tag, fieldName, tagName) {
|
|
727
|
+
const text = getTagCommentText(tag);
|
|
728
|
+
if (!text) {
|
|
729
|
+
throw new SchemaExtractionError(fieldName, `@${tagName} \u6807\u7B7E\u9700\u8981\u4E00\u4E2A\u6570\u5B57\u503C`);
|
|
730
|
+
}
|
|
731
|
+
const trimmed = text.trim();
|
|
732
|
+
const num = Number(trimmed);
|
|
733
|
+
if (!Number.isFinite(num)) {
|
|
734
|
+
throw new SchemaExtractionError(fieldName, `@${tagName} \u6807\u7B7E\u7684\u503C "${trimmed}" \u4E0D\u662F\u6709\u6548\u6570\u5B57`);
|
|
735
|
+
}
|
|
736
|
+
return num;
|
|
737
|
+
}
|
|
738
|
+
function parseRegexLiteral(text, fieldName) {
|
|
739
|
+
const match = /^\/(.+)\/([gimsuy]*)$/.exec(text);
|
|
740
|
+
if (!match) {
|
|
741
|
+
throw new SchemaExtractionError(fieldName, `\u6B63\u5219\u503C "${text}" \u4E0D\u662F /pattern/flags \u5F62\u5F0F`);
|
|
742
|
+
}
|
|
743
|
+
const [, pattern, flags] = match;
|
|
744
|
+
if (!pattern) {
|
|
745
|
+
throw new SchemaExtractionError(fieldName, `\u6B63\u5219\u503C "${text}" \u7684 pattern \u90E8\u5206\u4E3A\u7A7A`);
|
|
746
|
+
}
|
|
747
|
+
return flags ? { pattern, flags } : { pattern };
|
|
748
|
+
}
|
|
749
|
+
function validateConstraints(constraints, type, fieldName) {
|
|
750
|
+
if (constraints.length === 0) return;
|
|
751
|
+
for (const constraint of constraints) {
|
|
752
|
+
const kind = constraint.kind;
|
|
753
|
+
if (NUMBER_CONSTRAINT_KINDS.has(kind)) {
|
|
754
|
+
if (type.kind !== "number") {
|
|
755
|
+
throw new SchemaExtractionError(
|
|
756
|
+
fieldName,
|
|
757
|
+
`@${kind} \u7EA6\u675F\u4EC5\u9002\u7528\u4E8E number \u5B57\u6BB5\uFF0C\u5B9E\u9645\u4E3A ${type.kind}`
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
if (LENGTH_CONSTRAINT_KINDS.has(kind)) {
|
|
763
|
+
if (type.kind !== "string" && type.kind !== "array") {
|
|
764
|
+
throw new SchemaExtractionError(
|
|
765
|
+
fieldName,
|
|
766
|
+
`@${kind} \u7EA6\u675F\u4EC5\u9002\u7528\u4E8E string \u6216 array \u5B57\u6BB5\uFF0C\u5B9E\u9645\u4E3A ${type.kind}`
|
|
767
|
+
);
|
|
768
|
+
}
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
if (STRING_FORMAT_CONSTRAINT_KINDS.has(kind)) {
|
|
772
|
+
if (type.kind !== "string") {
|
|
773
|
+
throw new SchemaExtractionError(
|
|
774
|
+
fieldName,
|
|
775
|
+
`@${kind} \u7EA6\u675F\u4EC5\u9002\u7528\u4E8E string \u5B57\u6BB5\uFF0C\u5B9E\u9645\u4E3A ${type.kind}`
|
|
776
|
+
);
|
|
777
|
+
}
|
|
778
|
+
continue;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
var SchemaExtractionError, NUMBER_CONSTRAINT_KINDS, LENGTH_CONSTRAINT_KINDS, STRING_FORMAT_CONSTRAINT_KINDS;
|
|
783
|
+
var init_resolveTypeNode = __esm({
|
|
784
|
+
"src/ast/resolveTypeNode.ts"() {
|
|
785
|
+
"use strict";
|
|
786
|
+
SchemaExtractionError = class extends Error {
|
|
787
|
+
constructor(typeText, reason, options) {
|
|
788
|
+
super(`\u65E0\u6CD5\u89E3\u6790\u7C7B\u578B "${typeText}": ${reason}`, options);
|
|
789
|
+
this.typeText = typeText;
|
|
736
790
|
this.reason = reason;
|
|
737
791
|
this.name = "SchemaExtractionError";
|
|
738
792
|
}
|
|
739
793
|
typeText;
|
|
740
794
|
reason;
|
|
741
795
|
};
|
|
796
|
+
NUMBER_CONSTRAINT_KINDS = /* @__PURE__ */ new Set([
|
|
797
|
+
"max",
|
|
798
|
+
"min",
|
|
799
|
+
"int",
|
|
800
|
+
"positive",
|
|
801
|
+
"negative",
|
|
802
|
+
"nonnegative",
|
|
803
|
+
"nonpositive"
|
|
804
|
+
]);
|
|
805
|
+
LENGTH_CONSTRAINT_KINDS = /* @__PURE__ */ new Set([
|
|
806
|
+
"maxLength",
|
|
807
|
+
"minLength",
|
|
808
|
+
"length"
|
|
809
|
+
]);
|
|
810
|
+
STRING_FORMAT_CONSTRAINT_KINDS = /* @__PURE__ */ new Set([
|
|
811
|
+
"regex",
|
|
812
|
+
"email",
|
|
813
|
+
"url",
|
|
814
|
+
"uuid"
|
|
815
|
+
]);
|
|
742
816
|
}
|
|
743
817
|
});
|
|
744
818
|
|
|
745
819
|
// src/ast/extractHandlerTypes.ts
|
|
746
|
-
import
|
|
820
|
+
import ts4 from "typescript";
|
|
747
821
|
function extractTypeInfo(program, filePath, typeName) {
|
|
748
822
|
const sourceFile = program.getSourceFile(filePath);
|
|
749
823
|
if (!sourceFile) return null;
|
|
750
824
|
const checker = program.getTypeChecker();
|
|
751
825
|
let result = null;
|
|
752
|
-
|
|
826
|
+
ts4.forEachChild(sourceFile, (node) => {
|
|
753
827
|
if (result) return;
|
|
754
|
-
if (
|
|
828
|
+
if (ts4.isInterfaceDeclaration(node) && node.name.text === typeName) {
|
|
755
829
|
const visited = /* @__PURE__ */ new Set();
|
|
756
830
|
visited.add(typeName);
|
|
757
831
|
const runtimeType = withFileContext(
|
|
@@ -766,7 +840,7 @@ function extractTypeInfo(program, filePath, typeName) {
|
|
|
766
840
|
};
|
|
767
841
|
return;
|
|
768
842
|
}
|
|
769
|
-
if (
|
|
843
|
+
if (ts4.isTypeAliasDeclaration(node) && node.name.text === typeName) {
|
|
770
844
|
const visited = /* @__PURE__ */ new Set();
|
|
771
845
|
visited.add(typeName);
|
|
772
846
|
const runtimeType = withFileContext(
|
|
@@ -789,8 +863,8 @@ function extractAllTypes(program, filePath) {
|
|
|
789
863
|
if (!sourceFile) return /* @__PURE__ */ new Map();
|
|
790
864
|
const checker = program.getTypeChecker();
|
|
791
865
|
const result = /* @__PURE__ */ new Map();
|
|
792
|
-
|
|
793
|
-
if (
|
|
866
|
+
ts4.forEachChild(sourceFile, (node) => {
|
|
867
|
+
if (ts4.isInterfaceDeclaration(node)) {
|
|
794
868
|
const visited = /* @__PURE__ */ new Set();
|
|
795
869
|
visited.add(node.name.text);
|
|
796
870
|
const runtimeType = withFileContext(
|
|
@@ -805,7 +879,7 @@ function extractAllTypes(program, filePath) {
|
|
|
805
879
|
});
|
|
806
880
|
return;
|
|
807
881
|
}
|
|
808
|
-
if (
|
|
882
|
+
if (ts4.isTypeAliasDeclaration(node)) {
|
|
809
883
|
const visited = /* @__PURE__ */ new Set();
|
|
810
884
|
visited.add(node.name.text);
|
|
811
885
|
const runtimeType = withFileContext(
|
|
@@ -875,7 +949,7 @@ var init_schemaName = __esm({
|
|
|
875
949
|
});
|
|
876
950
|
|
|
877
951
|
// src/injection/resolveInjection.ts
|
|
878
|
-
import
|
|
952
|
+
import ts5 from "typescript";
|
|
879
953
|
function resolveInjection(fn) {
|
|
880
954
|
const fnStr = fn.toString();
|
|
881
955
|
const params = extractParamsWithAst(fnStr);
|
|
@@ -890,53 +964,53 @@ function resolveInjection(fn) {
|
|
|
890
964
|
});
|
|
891
965
|
}
|
|
892
966
|
function extractParamsWithAst(fnStr) {
|
|
893
|
-
const sourceFile =
|
|
967
|
+
const sourceFile = ts5.createSourceFile(
|
|
894
968
|
"__faapi_injection__.ts",
|
|
895
969
|
fnStr,
|
|
896
|
-
|
|
970
|
+
ts5.ScriptTarget.Latest,
|
|
897
971
|
true
|
|
898
972
|
);
|
|
899
973
|
const paramNames = [];
|
|
900
974
|
function visit(node) {
|
|
901
|
-
if (
|
|
975
|
+
if (ts5.isFunctionDeclaration(node) && node.parameters.length > 0) {
|
|
902
976
|
for (const param of node.parameters) {
|
|
903
977
|
extractParamName(param, paramNames);
|
|
904
978
|
}
|
|
905
979
|
return;
|
|
906
980
|
}
|
|
907
|
-
if ((
|
|
981
|
+
if ((ts5.isArrowFunction(node) || ts5.isFunctionExpression(node)) && node.parameters.length > 0) {
|
|
908
982
|
for (const param of node.parameters) {
|
|
909
983
|
extractParamName(param, paramNames);
|
|
910
984
|
}
|
|
911
985
|
return;
|
|
912
986
|
}
|
|
913
|
-
|
|
987
|
+
ts5.forEachChild(node, visit);
|
|
914
988
|
}
|
|
915
989
|
visit(sourceFile);
|
|
916
990
|
return paramNames.map((name) => ({ name }));
|
|
917
991
|
}
|
|
918
992
|
function extractParamName(param, names) {
|
|
919
993
|
const name = param.name;
|
|
920
|
-
if (
|
|
994
|
+
if (ts5.isIdentifier(name)) {
|
|
921
995
|
names.push(name.text);
|
|
922
996
|
return;
|
|
923
997
|
}
|
|
924
|
-
if (
|
|
998
|
+
if (ts5.isObjectBindingPattern(name)) {
|
|
925
999
|
for (const element of name.elements) {
|
|
926
|
-
if (
|
|
1000
|
+
if (ts5.isBindingElement(element)) {
|
|
927
1001
|
const elemName = element.name;
|
|
928
|
-
if (
|
|
1002
|
+
if (ts5.isIdentifier(elemName)) {
|
|
929
1003
|
names.push(elemName.text);
|
|
930
1004
|
}
|
|
931
1005
|
}
|
|
932
1006
|
}
|
|
933
1007
|
return;
|
|
934
1008
|
}
|
|
935
|
-
if (
|
|
1009
|
+
if (ts5.isArrayBindingPattern(name)) {
|
|
936
1010
|
for (const element of name.elements) {
|
|
937
|
-
if (element &&
|
|
1011
|
+
if (element && ts5.isBindingElement(element)) {
|
|
938
1012
|
const elemName = element.name;
|
|
939
|
-
if (
|
|
1013
|
+
if (ts5.isIdentifier(elemName)) {
|
|
940
1014
|
names.push(elemName.text);
|
|
941
1015
|
}
|
|
942
1016
|
}
|
|
@@ -965,12 +1039,12 @@ var init_resolveInjection = __esm({
|
|
|
965
1039
|
});
|
|
966
1040
|
|
|
967
1041
|
// src/injection/analyzeInjection.ts
|
|
968
|
-
import
|
|
1042
|
+
import ts6 from "typescript";
|
|
969
1043
|
function analyzeInjection(code, functionName) {
|
|
970
|
-
const sourceFile =
|
|
1044
|
+
const sourceFile = ts6.createSourceFile("temp.ts", code, ts6.ScriptTarget.Latest, true);
|
|
971
1045
|
const params = [];
|
|
972
|
-
|
|
973
|
-
if (
|
|
1046
|
+
ts6.forEachChild(sourceFile, (node) => {
|
|
1047
|
+
if (ts6.isFunctionDeclaration(node) && node.name?.text === functionName) {
|
|
974
1048
|
for (const param of node.parameters) {
|
|
975
1049
|
const paramMeta = analyzeParam(param, sourceFile);
|
|
976
1050
|
params.push(paramMeta);
|
|
@@ -984,9 +1058,9 @@ function analyzeParam(param, sourceFile) {
|
|
|
984
1058
|
const type = PARAM_TYPE_MAP[name] || "unknown";
|
|
985
1059
|
const result = { name, type };
|
|
986
1060
|
if (param.type) {
|
|
987
|
-
if (
|
|
1061
|
+
if (ts6.isTypeReferenceNode(param.type)) {
|
|
988
1062
|
result.typeName = param.type.typeName.getText(sourceFile);
|
|
989
|
-
} else if (
|
|
1063
|
+
} else if (ts6.isTypeLiteralNode(param.type)) {
|
|
990
1064
|
result.schema = extractSchema(param.type, sourceFile);
|
|
991
1065
|
}
|
|
992
1066
|
}
|
|
@@ -995,7 +1069,7 @@ function analyzeParam(param, sourceFile) {
|
|
|
995
1069
|
function extractSchema(typeNode, sourceFile) {
|
|
996
1070
|
const schema = [];
|
|
997
1071
|
for (const member of typeNode.members) {
|
|
998
|
-
if (
|
|
1072
|
+
if (ts6.isPropertySignature(member) && member.name && ts6.isIdentifier(member.name)) {
|
|
999
1073
|
const propName = member.name.text;
|
|
1000
1074
|
const optional = !!member.questionToken;
|
|
1001
1075
|
const propType = member.type?.getText(sourceFile) || "unknown";
|
|
@@ -1016,11 +1090,11 @@ var init_analyzeInjection = __esm({
|
|
|
1016
1090
|
});
|
|
1017
1091
|
|
|
1018
1092
|
// src/cli/collectRouteSchemaSources.ts
|
|
1019
|
-
import
|
|
1093
|
+
import path5 from "path";
|
|
1020
1094
|
function collectRouteSchemaSources(routes, rootDir) {
|
|
1021
1095
|
const methodsByFile = /* @__PURE__ */ new Map();
|
|
1022
1096
|
for (const route of routes) {
|
|
1023
|
-
const filePath = rootDir ?
|
|
1097
|
+
const filePath = rootDir ? path5.resolve(rootDir, route.filePath) : route.filePath;
|
|
1024
1098
|
let entry = methodsByFile.get(filePath);
|
|
1025
1099
|
if (!entry) {
|
|
1026
1100
|
entry = { urlPath: route.urlPath, methods: /* @__PURE__ */ new Set() };
|
|
@@ -1115,12 +1189,51 @@ function collectNamedTypes(type, ctx) {
|
|
|
1115
1189
|
}
|
|
1116
1190
|
}
|
|
1117
1191
|
}
|
|
1118
|
-
function runtimeTypeToZodExpression(type, ctx) {
|
|
1192
|
+
function runtimeTypeToZodExpression(type, ctx, constraints) {
|
|
1119
1193
|
const expr = baseExpression(type, ctx);
|
|
1194
|
+
const withConstraints = constraints && constraints.length > 0 ? applyConstraints(expr, constraints, type.kind) : expr;
|
|
1120
1195
|
if (ctx.coerce && (type.kind === "number" || type.kind === "boolean")) {
|
|
1121
|
-
return wrapCoercePreprocess(type.kind,
|
|
1196
|
+
return wrapCoercePreprocess(type.kind, withConstraints);
|
|
1197
|
+
}
|
|
1198
|
+
return withConstraints;
|
|
1199
|
+
}
|
|
1200
|
+
function applyConstraints(baseExpr, constraints, typeKind) {
|
|
1201
|
+
const suffix = constraints.map((c) => constraintToZodChain(c, typeKind)).join("");
|
|
1202
|
+
return `${baseExpr}${suffix}`;
|
|
1203
|
+
}
|
|
1204
|
+
function constraintToZodChain(constraint, _typeKind) {
|
|
1205
|
+
switch (constraint.kind) {
|
|
1206
|
+
case "max":
|
|
1207
|
+
return `.max(${constraint.value})`;
|
|
1208
|
+
case "min":
|
|
1209
|
+
return `.min(${constraint.value})`;
|
|
1210
|
+
case "int":
|
|
1211
|
+
return ".int()";
|
|
1212
|
+
case "positive":
|
|
1213
|
+
return ".positive()";
|
|
1214
|
+
case "negative":
|
|
1215
|
+
return ".negative()";
|
|
1216
|
+
case "nonnegative":
|
|
1217
|
+
return ".nonnegative()";
|
|
1218
|
+
case "nonpositive":
|
|
1219
|
+
return ".nonpositive()";
|
|
1220
|
+
case "maxLength":
|
|
1221
|
+
return `.max(${constraint.value})`;
|
|
1222
|
+
case "minLength":
|
|
1223
|
+
return `.min(${constraint.value})`;
|
|
1224
|
+
case "length":
|
|
1225
|
+
return `.length(${constraint.value})`;
|
|
1226
|
+
case "regex": {
|
|
1227
|
+
const flags = constraint.flags ?? "";
|
|
1228
|
+
return `.regex(new RegExp(${JSON.stringify(constraint.pattern)}${flags ? `, ${JSON.stringify(flags)}` : ""}))`;
|
|
1229
|
+
}
|
|
1230
|
+
case "email":
|
|
1231
|
+
return ".email()";
|
|
1232
|
+
case "url":
|
|
1233
|
+
return ".url()";
|
|
1234
|
+
case "uuid":
|
|
1235
|
+
return ".uuid()";
|
|
1122
1236
|
}
|
|
1123
|
-
return expr;
|
|
1124
1237
|
}
|
|
1125
1238
|
function baseExpression(type, ctx) {
|
|
1126
1239
|
switch (type.kind) {
|
|
@@ -1215,8 +1328,9 @@ function generateTupleExpression(elements, ctx) {
|
|
|
1215
1328
|
}
|
|
1216
1329
|
function generateObjectExpression(properties, ctx) {
|
|
1217
1330
|
const fields = properties.map((prop) => {
|
|
1218
|
-
const expr = runtimeTypeToZodExpression(prop.type, ctx);
|
|
1219
|
-
|
|
1331
|
+
const expr = runtimeTypeToZodExpression(prop.type, ctx, prop.constraints);
|
|
1332
|
+
const finalExpr = prop.optional ? `${expr}.optional()` : expr;
|
|
1333
|
+
return `${JSON.stringify(prop.name)}: ${finalExpr}`;
|
|
1220
1334
|
});
|
|
1221
1335
|
return `z.object({ ${fields.join(", ")} })`;
|
|
1222
1336
|
}
|
|
@@ -1314,8 +1428,8 @@ var init_generateZodSchema = __esm({
|
|
|
1314
1428
|
});
|
|
1315
1429
|
|
|
1316
1430
|
// src/cli/generateSchemaFiles.ts
|
|
1317
|
-
import
|
|
1318
|
-
import
|
|
1431
|
+
import path6 from "path";
|
|
1432
|
+
import fs5 from "fs/promises";
|
|
1319
1433
|
function getSchemaOutputPath(sourceFile, appDir, outDir, rootDir) {
|
|
1320
1434
|
let rel = sourceFile.replace(/\\/g, "/");
|
|
1321
1435
|
if (appDir !== "." && rel.startsWith(`${appDir}/`)) {
|
|
@@ -1323,7 +1437,7 @@ function getSchemaOutputPath(sourceFile, appDir, outDir, rootDir) {
|
|
|
1323
1437
|
}
|
|
1324
1438
|
const idx = rel.lastIndexOf("/");
|
|
1325
1439
|
const relDir = idx >= 0 ? rel.slice(0, idx) : "";
|
|
1326
|
-
return
|
|
1440
|
+
return path6.resolve(rootDir, outDir, relDir, "zod.js");
|
|
1327
1441
|
}
|
|
1328
1442
|
function getRuntimeSchemaPath(filePath, appDir, outDir, rootDir) {
|
|
1329
1443
|
let rel = filePath.replace(/\\/g, "/");
|
|
@@ -1334,7 +1448,7 @@ function getRuntimeSchemaPath(filePath, appDir, outDir, rootDir) {
|
|
|
1334
1448
|
}
|
|
1335
1449
|
const idx = rel.lastIndexOf("/");
|
|
1336
1450
|
const relDir = idx >= 0 ? rel.slice(0, idx) : "";
|
|
1337
|
-
return
|
|
1451
|
+
return path6.resolve(rootDir, outDir, relDir, "zod.js");
|
|
1338
1452
|
}
|
|
1339
1453
|
function getHelpersImportPath(relDir) {
|
|
1340
1454
|
if (!relDir) return `./${HELPERS_FILENAME}`;
|
|
@@ -1382,7 +1496,7 @@ async function generateSchemaFiles(routes, rootDir, appDir, outDir) {
|
|
|
1382
1496
|
}
|
|
1383
1497
|
const fileEntries = [];
|
|
1384
1498
|
for (const [filePath, fileSources] of sourcesByFile) {
|
|
1385
|
-
const relFile =
|
|
1499
|
+
const relFile = path6.relative(rootDir, filePath).replace(/\\/g, "/");
|
|
1386
1500
|
const outputPath = getSchemaOutputPath(relFile, appDir, outDir, rootDir);
|
|
1387
1501
|
const allTypes = allTypesByFile.get(filePath) ?? /* @__PURE__ */ new Map();
|
|
1388
1502
|
let relForDir = relFile;
|
|
@@ -1397,7 +1511,7 @@ async function generateSchemaFiles(routes, rootDir, appDir, outDir) {
|
|
|
1397
1511
|
}
|
|
1398
1512
|
const allSourceCode = fileEntries.map((e) => e.source).join("\n");
|
|
1399
1513
|
if (usesCoerceHelpers(allSourceCode)) {
|
|
1400
|
-
const helpersPath =
|
|
1514
|
+
const helpersPath = path6.resolve(rootDir, outDir, HELPERS_FILENAME);
|
|
1401
1515
|
await writeSchemaFile(helpersPath, generateHelpersFileSource());
|
|
1402
1516
|
}
|
|
1403
1517
|
await Promise.all(
|
|
@@ -1405,8 +1519,8 @@ async function generateSchemaFiles(routes, rootDir, appDir, outDir) {
|
|
|
1405
1519
|
);
|
|
1406
1520
|
}
|
|
1407
1521
|
async function writeSchemaFile(outputPath, source) {
|
|
1408
|
-
await
|
|
1409
|
-
await
|
|
1522
|
+
await fs5.mkdir(path6.dirname(outputPath), { recursive: true });
|
|
1523
|
+
await fs5.writeFile(outputPath, source, "utf-8");
|
|
1410
1524
|
}
|
|
1411
1525
|
var init_generateSchemaFiles = __esm({
|
|
1412
1526
|
"src/cli/generateSchemaFiles.ts"() {
|
|
@@ -1416,9 +1530,80 @@ var init_generateSchemaFiles = __esm({
|
|
|
1416
1530
|
}
|
|
1417
1531
|
});
|
|
1418
1532
|
|
|
1533
|
+
// src/utils/importWithCacheBust.ts
|
|
1534
|
+
import { pathToFileURL } from "url";
|
|
1535
|
+
function setLoadTimestamp(ts7) {
|
|
1536
|
+
loadTs = ts7;
|
|
1537
|
+
}
|
|
1538
|
+
async function importWithCacheBust(filePath) {
|
|
1539
|
+
let url = pathToFileURL(filePath).href;
|
|
1540
|
+
if (loadTs !== void 0) {
|
|
1541
|
+
url += `?t=${loadTs}`;
|
|
1542
|
+
}
|
|
1543
|
+
return await import(url);
|
|
1544
|
+
}
|
|
1545
|
+
var loadTs;
|
|
1546
|
+
var init_importWithCacheBust = __esm({
|
|
1547
|
+
"src/utils/importWithCacheBust.ts"() {
|
|
1548
|
+
"use strict";
|
|
1549
|
+
}
|
|
1550
|
+
});
|
|
1551
|
+
|
|
1552
|
+
// src/middleware/loadMiddlewares.ts
|
|
1553
|
+
function invalidateMiddlewareCache() {
|
|
1554
|
+
middlewareCache.clear();
|
|
1555
|
+
}
|
|
1556
|
+
function getCachedMiddlewares(absPath) {
|
|
1557
|
+
return middlewareCache.get(absPath);
|
|
1558
|
+
}
|
|
1559
|
+
function setCachedMiddlewares(absPath, bundle) {
|
|
1560
|
+
middlewareCache.set(absPath, bundle);
|
|
1561
|
+
}
|
|
1562
|
+
async function loadMiddlewaresFile(filePath) {
|
|
1563
|
+
try {
|
|
1564
|
+
const module = await importWithCacheBust(filePath);
|
|
1565
|
+
const middlewares = module.default ?? module.middlewares ?? [];
|
|
1566
|
+
if (!Array.isArray(middlewares)) {
|
|
1567
|
+
console.warn(`[faapi] middlewares.ts \u5E94\u5BFC\u51FA\u6570\u7EC4\uFF0C\u5DF2\u5FFD\u7565: ${filePath}`);
|
|
1568
|
+
return { middlewares: [], injectors: {} };
|
|
1569
|
+
}
|
|
1570
|
+
const validMiddlewares = middlewares.filter((m) => {
|
|
1571
|
+
if (typeof m !== "function") {
|
|
1572
|
+
console.warn(`[faapi] \u65E0\u6548\u7684\u4E2D\u95F4\u4EF6\u9879\uFF08\u5E94\u4E3A\u51FD\u6570\uFF09\uFF0C\u5DF2\u5FFD\u7565: ${typeof m}`);
|
|
1573
|
+
return false;
|
|
1574
|
+
}
|
|
1575
|
+
return true;
|
|
1576
|
+
});
|
|
1577
|
+
const injectors = module.injectors ?? {};
|
|
1578
|
+
if (typeof injectors !== "object" || injectors === null) {
|
|
1579
|
+
console.warn(`[faapi] injectors \u5E94\u5BFC\u51FA\u5BF9\u8C61\uFF0C\u5DF2\u5FFD\u7565: ${filePath}`);
|
|
1580
|
+
return { middlewares: validMiddlewares, injectors: {} };
|
|
1581
|
+
}
|
|
1582
|
+
const validInjectors = {};
|
|
1583
|
+
for (const [name, injector] of Object.entries(injectors)) {
|
|
1584
|
+
if (typeof injector !== "function") {
|
|
1585
|
+
console.warn(`[faapi] \u6CE8\u5165\u5668 ${name} \u5E94\u4E3A\u51FD\u6570\uFF0C\u5DF2\u5FFD\u7565`);
|
|
1586
|
+
continue;
|
|
1587
|
+
}
|
|
1588
|
+
validInjectors[name] = injector;
|
|
1589
|
+
}
|
|
1590
|
+
return { middlewares: validMiddlewares, injectors: validInjectors };
|
|
1591
|
+
} catch {
|
|
1592
|
+
return { middlewares: [], injectors: {} };
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
var middlewareCache;
|
|
1596
|
+
var init_loadMiddlewares = __esm({
|
|
1597
|
+
"src/middleware/loadMiddlewares.ts"() {
|
|
1598
|
+
"use strict";
|
|
1599
|
+
init_importWithCacheBust();
|
|
1600
|
+
middlewareCache = /* @__PURE__ */ new Map();
|
|
1601
|
+
}
|
|
1602
|
+
});
|
|
1603
|
+
|
|
1419
1604
|
// src/cli/generateRoutes.ts
|
|
1420
|
-
import
|
|
1421
|
-
import
|
|
1605
|
+
import fs6 from "fs";
|
|
1606
|
+
import path7 from "path";
|
|
1422
1607
|
function toProdFilePath(filePath, appDir, prodDir) {
|
|
1423
1608
|
let rel = filePath.replace(/\\/g, "/");
|
|
1424
1609
|
if (appDir !== "." && rel.startsWith(`${appDir}/`)) {
|
|
@@ -1449,23 +1634,23 @@ function serializeRoutes(routes, wsRoutes, rootDir, appDir = "src", prodDir = "d
|
|
|
1449
1634
|
};
|
|
1450
1635
|
}
|
|
1451
1636
|
function extractMiddlewarePaths(routeFilePath, rootDir, appDir, prodDir) {
|
|
1452
|
-
const routeDir =
|
|
1453
|
-
const resolvedRoot =
|
|
1637
|
+
const routeDir = path7.dirname(routeFilePath);
|
|
1638
|
+
const resolvedRoot = path7.resolve(rootDir);
|
|
1454
1639
|
const paths = [];
|
|
1455
|
-
let currentDir =
|
|
1640
|
+
let currentDir = path7.resolve(rootDir, routeDir);
|
|
1456
1641
|
while (true) {
|
|
1457
|
-
const mwTsPath =
|
|
1458
|
-
const mwJsPath =
|
|
1459
|
-
const absTsPath =
|
|
1460
|
-
const absJsPath =
|
|
1461
|
-
const absMwPath =
|
|
1642
|
+
const mwTsPath = path7.join(currentDir, "middlewares.ts");
|
|
1643
|
+
const mwJsPath = path7.join(currentDir, "middlewares.js");
|
|
1644
|
+
const absTsPath = path7.resolve(rootDir, mwTsPath);
|
|
1645
|
+
const absJsPath = path7.resolve(rootDir, mwJsPath);
|
|
1646
|
+
const absMwPath = fs6.existsSync(absTsPath) ? absTsPath : fs6.existsSync(absJsPath) ? absJsPath : null;
|
|
1462
1647
|
if (absMwPath) {
|
|
1463
|
-
const relMwPath =
|
|
1464
|
-
const prodAbsPath =
|
|
1648
|
+
const relMwPath = path7.relative(rootDir, absMwPath);
|
|
1649
|
+
const prodAbsPath = path7.resolve(rootDir, toProdFilePath(relMwPath, appDir, prodDir));
|
|
1465
1650
|
paths.push(prodAbsPath);
|
|
1466
1651
|
}
|
|
1467
1652
|
if (currentDir === resolvedRoot) break;
|
|
1468
|
-
const parentDir =
|
|
1653
|
+
const parentDir = path7.dirname(currentDir);
|
|
1469
1654
|
if (parentDir === currentDir) break;
|
|
1470
1655
|
currentDir = parentDir;
|
|
1471
1656
|
}
|
|
@@ -1473,13 +1658,13 @@ function extractMiddlewarePaths(routeFilePath, rootDir, appDir, prodDir) {
|
|
|
1473
1658
|
return paths;
|
|
1474
1659
|
}
|
|
1475
1660
|
async function writeRoutesModule(manifest, outputPath) {
|
|
1476
|
-
const dir =
|
|
1477
|
-
await
|
|
1661
|
+
const dir = path7.dirname(outputPath);
|
|
1662
|
+
await fs6.promises.mkdir(dir, { recursive: true });
|
|
1478
1663
|
const content = `// \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u7F16\u8F91\uFF08faapi build \u4EA7\u7269\uFF09
|
|
1479
1664
|
export const routes = ${JSON.stringify(manifest.routes, null, 2)};
|
|
1480
1665
|
export const wsRoutes = ${JSON.stringify(manifest.wsRoutes, null, 2)};
|
|
1481
1666
|
`;
|
|
1482
|
-
await
|
|
1667
|
+
await fs6.promises.writeFile(outputPath, content, "utf-8");
|
|
1483
1668
|
}
|
|
1484
1669
|
async function hydrateRoutes(manifest) {
|
|
1485
1670
|
const hydrateRoute = async (serialized) => {
|
|
@@ -1535,281 +1720,249 @@ var init_generateRoutes = __esm({
|
|
|
1535
1720
|
}
|
|
1536
1721
|
});
|
|
1537
1722
|
|
|
1538
|
-
// src/
|
|
1539
|
-
function
|
|
1540
|
-
|
|
1541
|
-
for (const [pattern, targets] of Object.entries(config.paths)) {
|
|
1542
|
-
const wildcardIndex = pattern.indexOf("*");
|
|
1543
|
-
if (wildcardIndex === -1) {
|
|
1544
|
-
if (specifier === pattern) {
|
|
1545
|
-
candidates.push(...targets);
|
|
1546
|
-
}
|
|
1547
|
-
continue;
|
|
1548
|
-
}
|
|
1549
|
-
const prefix = pattern.slice(0, wildcardIndex);
|
|
1550
|
-
const suffix = pattern.slice(wildcardIndex + 1);
|
|
1551
|
-
if (specifier.startsWith(prefix) && specifier.endsWith(suffix) && specifier.length >= prefix.length + suffix.length) {
|
|
1552
|
-
const captured = specifier.slice(prefix.length, specifier.length - suffix.length);
|
|
1553
|
-
for (const target of targets) {
|
|
1554
|
-
candidates.push(target.replace("*", captured));
|
|
1555
|
-
}
|
|
1556
|
-
}
|
|
1557
|
-
}
|
|
1558
|
-
return candidates;
|
|
1723
|
+
// src/router/constants.ts
|
|
1724
|
+
function isHttpMethod(value) {
|
|
1725
|
+
return HTTP_METHOD_SET.has(value);
|
|
1559
1726
|
}
|
|
1560
|
-
var
|
|
1561
|
-
|
|
1727
|
+
var HTTP_METHODS, HTTP_METHOD_SET;
|
|
1728
|
+
var init_constants = __esm({
|
|
1729
|
+
"src/router/constants.ts"() {
|
|
1562
1730
|
"use strict";
|
|
1731
|
+
HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
|
|
1732
|
+
HTTP_METHOD_SET = new Set(HTTP_METHODS);
|
|
1563
1733
|
}
|
|
1564
1734
|
});
|
|
1565
1735
|
|
|
1566
|
-
// src/utils/
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
if (!
|
|
1573
|
-
|
|
1574
|
-
if (configFile.error || !configFile.config) return null;
|
|
1575
|
-
const parsed = ts6.parseJsonConfigFileContent(configFile.config, ts6.sys, rootDir);
|
|
1576
|
-
const baseUrl = parsed.options.baseUrl ?? rootDir;
|
|
1577
|
-
const rawPaths = parsed.options.paths;
|
|
1578
|
-
if (!rawPaths) return null;
|
|
1579
|
-
const paths = {};
|
|
1580
|
-
for (const [pattern, targets] of Object.entries(rawPaths)) {
|
|
1581
|
-
paths[pattern] = targets.map((t) => path5.resolve(baseUrl, t));
|
|
1736
|
+
// src/utils/normalizePath.ts
|
|
1737
|
+
function normalizePath(path17) {
|
|
1738
|
+
if (!path17) return "";
|
|
1739
|
+
let result = path17.replace(/\\/g, "/");
|
|
1740
|
+
result = result.replace(/\/+/g, "/");
|
|
1741
|
+
result = result.replace(/\/+$/, "");
|
|
1742
|
+
if (result && !result.startsWith("/")) {
|
|
1743
|
+
result = "/" + result;
|
|
1582
1744
|
}
|
|
1583
|
-
return
|
|
1745
|
+
return result;
|
|
1584
1746
|
}
|
|
1585
|
-
var
|
|
1586
|
-
"src/utils/
|
|
1747
|
+
var init_normalizePath = __esm({
|
|
1748
|
+
"src/utils/normalizePath.ts"() {
|
|
1587
1749
|
"use strict";
|
|
1588
1750
|
}
|
|
1589
1751
|
});
|
|
1590
1752
|
|
|
1591
|
-
// src/
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
return filePath;
|
|
1599
|
-
}
|
|
1600
|
-
function toProdImportPath(sourceFile, importer) {
|
|
1601
|
-
const importerDir = path6.dirname(importer);
|
|
1602
|
-
let rel = path6.relative(importerDir, sourceFile);
|
|
1603
|
-
rel = rel.split(path6.sep).join("/");
|
|
1604
|
-
if (!rel.startsWith(".")) rel = "./" + rel;
|
|
1605
|
-
return toProdExtension(rel);
|
|
1753
|
+
// src/router/parseRouteFile.ts
|
|
1754
|
+
function dynamicSegmentToParam(segment) {
|
|
1755
|
+
const match = segment.match(/^\[(.+)\]$/);
|
|
1756
|
+
if (match) {
|
|
1757
|
+
return ":" + match[1];
|
|
1758
|
+
}
|
|
1759
|
+
return segment;
|
|
1606
1760
|
}
|
|
1607
|
-
function
|
|
1608
|
-
const
|
|
1609
|
-
const
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
"/index.cjs"
|
|
1616
|
-
];
|
|
1617
|
-
const SPEC_RE = /(\bfrom\s*|import\s*\(\s*)(['"])([^'"]+)\2/g;
|
|
1618
|
-
return {
|
|
1619
|
-
name: "faapi-alias",
|
|
1620
|
-
setup(build) {
|
|
1621
|
-
build.onLoad({ filter: /\.(ts|tsx|js|jsx|mjs|cjs)$/ }, (args) => {
|
|
1622
|
-
let source;
|
|
1623
|
-
try {
|
|
1624
|
-
source = fs5.readFileSync(args.path, "utf8");
|
|
1625
|
-
} catch {
|
|
1626
|
-
return void 0;
|
|
1627
|
-
}
|
|
1628
|
-
const importer = args.path;
|
|
1629
|
-
let modified = false;
|
|
1630
|
-
const newSource = source.replace(SPEC_RE, (full, prefix, quote, specifier) => {
|
|
1631
|
-
if (specifier.startsWith(".") || specifier.startsWith("/") || specifier.startsWith("file:") || specifier.startsWith("node:")) {
|
|
1632
|
-
return full;
|
|
1633
|
-
}
|
|
1634
|
-
const candidates = resolveAlias(specifier, config);
|
|
1635
|
-
for (const candidate of candidates) {
|
|
1636
|
-
for (const ext of EXTS) {
|
|
1637
|
-
const file = candidate + ext;
|
|
1638
|
-
if (fs5.existsSync(file)) {
|
|
1639
|
-
modified = true;
|
|
1640
|
-
return `${prefix}${quote}${toProdImportPath(file, importer)}${quote}`;
|
|
1641
|
-
}
|
|
1642
|
-
}
|
|
1643
|
-
for (const indexExt of INDEX_EXTS) {
|
|
1644
|
-
const file = candidate + indexExt;
|
|
1645
|
-
if (fs5.existsSync(file)) {
|
|
1646
|
-
modified = true;
|
|
1647
|
-
return `${prefix}${quote}${toProdImportPath(file, importer)}${quote}`;
|
|
1648
|
-
}
|
|
1649
|
-
}
|
|
1650
|
-
}
|
|
1651
|
-
return full;
|
|
1652
|
-
});
|
|
1653
|
-
if (!modified) return void 0;
|
|
1654
|
-
return { contents: newSource, loader: "default" };
|
|
1655
|
-
});
|
|
1761
|
+
function extractParamNames(urlPath) {
|
|
1762
|
+
const params = [];
|
|
1763
|
+
const segments = urlPath.split("/");
|
|
1764
|
+
for (const segment of segments) {
|
|
1765
|
+
if (segment.startsWith(":...")) {
|
|
1766
|
+
params.push(segment.slice(4));
|
|
1767
|
+
} else if (segment.startsWith(":")) {
|
|
1768
|
+
params.push(segment.slice(1));
|
|
1656
1769
|
}
|
|
1657
|
-
}
|
|
1770
|
+
}
|
|
1771
|
+
return params;
|
|
1658
1772
|
}
|
|
1659
|
-
function
|
|
1660
|
-
|
|
1661
|
-
return tsconfig ? [createAliasPlugin(tsconfig)] : [];
|
|
1773
|
+
function isCatchAllSegment(segment) {
|
|
1774
|
+
return /^\[\.\.\..+\]$/.test(segment);
|
|
1662
1775
|
}
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
"use strict";
|
|
1666
|
-
init_resolveAlias();
|
|
1667
|
-
init_readTsconfig();
|
|
1668
|
-
}
|
|
1669
|
-
});
|
|
1670
|
-
|
|
1671
|
-
// src/cli/compileBuildRoutes.ts
|
|
1672
|
-
import path7 from "path";
|
|
1673
|
-
import fs6 from "fs";
|
|
1674
|
-
async function compileBuildRoutes(options) {
|
|
1675
|
-
const {
|
|
1676
|
-
rootDir,
|
|
1677
|
-
appDir,
|
|
1678
|
-
outDir,
|
|
1679
|
-
entries,
|
|
1680
|
-
splitting = true,
|
|
1681
|
-
define,
|
|
1682
|
-
minifySyntax = true,
|
|
1683
|
-
logLevel = "silent"
|
|
1684
|
-
} = options;
|
|
1685
|
-
if (entries.length === 0) {
|
|
1686
|
-
return { compiledFiles: [] };
|
|
1687
|
-
}
|
|
1688
|
-
const absOutDir = path7.resolve(rootDir, outDir);
|
|
1689
|
-
await fs6.promises.mkdir(absOutDir, { recursive: true });
|
|
1690
|
-
const plugins = buildAliasPlugins(rootDir);
|
|
1691
|
-
const esbuild = await import("esbuild");
|
|
1692
|
-
const outbase = appDir === "." ? rootDir : path7.resolve(rootDir, appDir);
|
|
1693
|
-
await esbuild.build({
|
|
1694
|
-
entryPoints: entries,
|
|
1695
|
-
outdir: absOutDir,
|
|
1696
|
-
outbase,
|
|
1697
|
-
bundle: true,
|
|
1698
|
-
splitting,
|
|
1699
|
-
platform: "node",
|
|
1700
|
-
format: "esm",
|
|
1701
|
-
sourcemap: true,
|
|
1702
|
-
packages: "external",
|
|
1703
|
-
plugins,
|
|
1704
|
-
define,
|
|
1705
|
-
minifySyntax,
|
|
1706
|
-
logLevel
|
|
1707
|
-
});
|
|
1708
|
-
return { compiledFiles: entries };
|
|
1776
|
+
function isRouteGroup(segment) {
|
|
1777
|
+
return /^\(.+\)$/.test(segment);
|
|
1709
1778
|
}
|
|
1710
|
-
|
|
1711
|
-
"
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
// src/config/deepMerge.ts
|
|
1718
|
-
function deepMerge(base, override) {
|
|
1719
|
-
const result = { ...base };
|
|
1720
|
-
for (const key of Object.keys(override)) {
|
|
1721
|
-
const baseVal = base[key];
|
|
1722
|
-
const overVal = override[key];
|
|
1723
|
-
if (baseVal instanceof Date || overVal instanceof Date || baseVal instanceof RegExp || overVal instanceof RegExp || baseVal instanceof Map || overVal instanceof Map || baseVal instanceof Set || overVal instanceof Set) {
|
|
1724
|
-
result[key] = overVal;
|
|
1725
|
-
continue;
|
|
1726
|
-
}
|
|
1727
|
-
if (baseVal !== null && overVal !== null && typeof baseVal === "object" && typeof overVal === "object" && !Array.isArray(baseVal) && !Array.isArray(overVal) && !(baseVal instanceof Function) && !(overVal instanceof Function)) {
|
|
1728
|
-
result[key] = deepMerge(
|
|
1729
|
-
baseVal,
|
|
1730
|
-
overVal
|
|
1731
|
-
);
|
|
1732
|
-
} else {
|
|
1733
|
-
result[key] = overVal;
|
|
1734
|
-
}
|
|
1779
|
+
function filePathToUrlPath(filePath, appDir = ".") {
|
|
1780
|
+
const withoutPrefix = filePath.startsWith(appDir + "/") ? filePath.slice(appDir.length + 1) : filePath;
|
|
1781
|
+
const lastSlashIndex = withoutPrefix.lastIndexOf("/");
|
|
1782
|
+
const dirPath = lastSlashIndex === -1 ? "" : withoutPrefix.slice(0, lastSlashIndex);
|
|
1783
|
+
if (!dirPath) {
|
|
1784
|
+
return "";
|
|
1735
1785
|
}
|
|
1736
|
-
|
|
1786
|
+
const segments = dirPath.split("/").filter((s) => !isRouteGroup(s)).map(dynamicSegmentToParam);
|
|
1787
|
+
return normalizePath(segments.join("/"));
|
|
1737
1788
|
}
|
|
1738
|
-
var
|
|
1739
|
-
|
|
1740
|
-
"src/config/deepMerge.ts"() {
|
|
1789
|
+
var init_parseRouteFile = __esm({
|
|
1790
|
+
"src/router/parseRouteFile.ts"() {
|
|
1741
1791
|
"use strict";
|
|
1742
|
-
|
|
1792
|
+
init_normalizePath();
|
|
1743
1793
|
}
|
|
1744
1794
|
});
|
|
1745
1795
|
|
|
1746
|
-
// src/
|
|
1796
|
+
// src/router/scanRoutes.ts
|
|
1797
|
+
import fg2 from "fast-glob";
|
|
1747
1798
|
import path8 from "path";
|
|
1748
1799
|
import fs7 from "fs";
|
|
1749
|
-
function
|
|
1750
|
-
|
|
1800
|
+
function toProdAbsPath(sourceAbsPath, rootDir, appDir, prodDir) {
|
|
1801
|
+
let rel = path8.relative(rootDir, sourceAbsPath).replace(/\\/g, "/");
|
|
1802
|
+
if (appDir !== "." && rel.startsWith(`${appDir}/`)) {
|
|
1803
|
+
rel = rel.slice(appDir.length + 1);
|
|
1804
|
+
}
|
|
1805
|
+
const prodRel = `${prodDir}/${rel.replace(/\.ts$/, ".js")}`;
|
|
1806
|
+
return path8.resolve(rootDir, prodRel);
|
|
1751
1807
|
}
|
|
1752
|
-
function
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1808
|
+
async function findMergedMiddlewares(routeFilePath, rootDir, appDir, prodDir) {
|
|
1809
|
+
const routeDir = path8.dirname(routeFilePath);
|
|
1810
|
+
const resolvedRoot = path8.resolve(rootDir);
|
|
1811
|
+
const mwPaths = [];
|
|
1812
|
+
let currentDir = path8.resolve(rootDir, routeDir);
|
|
1813
|
+
while (true) {
|
|
1814
|
+
if (prodDir) {
|
|
1815
|
+
const mwPath = path8.join(currentDir, "middlewares.js");
|
|
1816
|
+
const absMwPath = path8.resolve(rootDir, mwPath);
|
|
1817
|
+
const prodAbsMwPath = toProdAbsPath(absMwPath, rootDir, appDir, prodDir);
|
|
1818
|
+
if (fs7.existsSync(prodAbsMwPath)) {
|
|
1819
|
+
mwPaths.push(prodAbsMwPath);
|
|
1820
|
+
}
|
|
1821
|
+
} else {
|
|
1822
|
+
for (const ext of [".ts", ".js"]) {
|
|
1823
|
+
const mwPath = path8.join(currentDir, `middlewares${ext}`);
|
|
1824
|
+
const absMwPath = path8.resolve(rootDir, mwPath);
|
|
1825
|
+
if (fs7.existsSync(absMwPath)) {
|
|
1826
|
+
mwPaths.push(absMwPath);
|
|
1827
|
+
break;
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1756
1830
|
}
|
|
1831
|
+
if (currentDir === resolvedRoot) break;
|
|
1832
|
+
const parentDir = path8.dirname(currentDir);
|
|
1833
|
+
if (parentDir === currentDir) break;
|
|
1834
|
+
currentDir = parentDir;
|
|
1757
1835
|
}
|
|
1758
|
-
return
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1836
|
+
if (mwPaths.length === 0) return void 0;
|
|
1837
|
+
mwPaths.reverse();
|
|
1838
|
+
const mergedMiddlewares = [];
|
|
1839
|
+
const mergedInjectors = {};
|
|
1840
|
+
for (const absMwPath of mwPaths) {
|
|
1841
|
+
let bundle = getCachedMiddlewares(absMwPath);
|
|
1842
|
+
if (bundle === void 0) {
|
|
1843
|
+
bundle = await loadMiddlewaresFile(absMwPath);
|
|
1844
|
+
setCachedMiddlewares(absMwPath, bundle);
|
|
1845
|
+
}
|
|
1846
|
+
mergedMiddlewares.push(...bundle.middlewares);
|
|
1847
|
+
for (const [name, injector] of Object.entries(bundle.injectors)) {
|
|
1848
|
+
mergedInjectors[name] = injector;
|
|
1765
1849
|
}
|
|
1766
1850
|
}
|
|
1767
|
-
|
|
1851
|
+
if (mergedMiddlewares.length === 0 && Object.keys(mergedInjectors).length === 0) {
|
|
1852
|
+
return void 0;
|
|
1853
|
+
}
|
|
1854
|
+
return { middlewares: mergedMiddlewares, injectors: mergedInjectors };
|
|
1768
1855
|
}
|
|
1769
|
-
async function
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1856
|
+
async function extractMethodsFromHandler(absPath) {
|
|
1857
|
+
try {
|
|
1858
|
+
const module = await importWithCacheBust(absPath);
|
|
1859
|
+
const methods = [];
|
|
1860
|
+
for (const key of Object.keys(module)) {
|
|
1861
|
+
if (isHttpMethod(key) && typeof module[key] === "function") {
|
|
1862
|
+
methods.push(key);
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
return methods;
|
|
1866
|
+
} catch (err) {
|
|
1867
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
1868
|
+
console.warn(`[faapi] \u52A0\u8F7D\u8DEF\u7531\u6587\u4EF6\u5931\u8D25 ${absPath}: ${reason}`);
|
|
1869
|
+
return [];
|
|
1774
1870
|
}
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1871
|
+
}
|
|
1872
|
+
async function hasWsExport(absPath) {
|
|
1873
|
+
try {
|
|
1874
|
+
const module = await importWithCacheBust(absPath);
|
|
1875
|
+
return typeof module["WS"] === "function";
|
|
1876
|
+
} catch (err) {
|
|
1877
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
1878
|
+
console.warn(`[faapi] \u52A0\u8F7D\u8DEF\u7531\u6587\u4EF6\u5931\u8D25\uFF08WS \u68C0\u6D4B\uFF09${absPath}: ${reason}`);
|
|
1879
|
+
return false;
|
|
1784
1880
|
}
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
const
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
bundle: true,
|
|
1793
|
-
format: "esm",
|
|
1794
|
-
platform: "node",
|
|
1795
|
-
target: "node20",
|
|
1796
|
-
sourcemap: true,
|
|
1797
|
-
packages: "external",
|
|
1798
|
-
logLevel: "silent"
|
|
1881
|
+
}
|
|
1882
|
+
async function scanRoutes(rootDir, patterns, appDir, prodDir) {
|
|
1883
|
+
const dir = appDir ?? ".";
|
|
1884
|
+
const files = await fg2(patterns, {
|
|
1885
|
+
cwd: rootDir,
|
|
1886
|
+
onlyFiles: true,
|
|
1887
|
+
absolute: false
|
|
1799
1888
|
});
|
|
1800
|
-
|
|
1889
|
+
const routes = [];
|
|
1890
|
+
const wsRoutes = [];
|
|
1891
|
+
for (const file of files) {
|
|
1892
|
+
const normalizedFile = file.replace(/\\/g, "/");
|
|
1893
|
+
const fileName = normalizedFile.split("/").pop();
|
|
1894
|
+
if (fileName === "handler.ts" || fileName === "handler.js") {
|
|
1895
|
+
const absPath = path8.resolve(rootDir, normalizedFile);
|
|
1896
|
+
const importPath = prodDir ? toProdAbsPath(absPath, rootDir, dir, prodDir) : absPath;
|
|
1897
|
+
const urlPath = filePathToUrlPath(normalizedFile, dir);
|
|
1898
|
+
const paramNames = extractParamNames(urlPath);
|
|
1899
|
+
const isDynamic = paramNames.length > 0;
|
|
1900
|
+
const isCatchAll = normalizedFile.split("/").some(isCatchAllSegment);
|
|
1901
|
+
const middlewareBundle = await findMergedMiddlewares(normalizedFile, rootDir, dir, prodDir);
|
|
1902
|
+
const methods = await extractMethodsFromHandler(importPath);
|
|
1903
|
+
for (const method of methods) {
|
|
1904
|
+
routes.push({
|
|
1905
|
+
method,
|
|
1906
|
+
urlPath,
|
|
1907
|
+
filePath: normalizedFile,
|
|
1908
|
+
paramNames,
|
|
1909
|
+
isDynamic,
|
|
1910
|
+
isCatchAll: isCatchAll || void 0,
|
|
1911
|
+
middlewares: middlewareBundle?.middlewares,
|
|
1912
|
+
injectors: middlewareBundle?.injectors
|
|
1913
|
+
});
|
|
1914
|
+
}
|
|
1915
|
+
const hasWs = await hasWsExport(importPath);
|
|
1916
|
+
if (hasWs) {
|
|
1917
|
+
wsRoutes.push({
|
|
1918
|
+
urlPath,
|
|
1919
|
+
filePath: normalizedFile,
|
|
1920
|
+
paramNames,
|
|
1921
|
+
isDynamic,
|
|
1922
|
+
isCatchAll: isCatchAll || void 0,
|
|
1923
|
+
middlewares: middlewareBundle?.middlewares,
|
|
1924
|
+
injectors: middlewareBundle?.injectors
|
|
1925
|
+
});
|
|
1926
|
+
}
|
|
1927
|
+
continue;
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
return { routes, wsRoutes };
|
|
1801
1931
|
}
|
|
1802
|
-
var
|
|
1803
|
-
|
|
1804
|
-
"src/cli/compileConfig.ts"() {
|
|
1932
|
+
var init_scanRoutes = __esm({
|
|
1933
|
+
"src/router/scanRoutes.ts"() {
|
|
1805
1934
|
"use strict";
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1935
|
+
init_constants();
|
|
1936
|
+
init_parseRouteFile();
|
|
1937
|
+
init_loadMiddlewares();
|
|
1938
|
+
init_importWithCacheBust();
|
|
1809
1939
|
}
|
|
1810
1940
|
});
|
|
1811
1941
|
|
|
1812
|
-
// src/
|
|
1942
|
+
// src/router/sortRoutes.ts
|
|
1943
|
+
function sortRoutes(routes) {
|
|
1944
|
+
return [...routes].sort((a, b) => {
|
|
1945
|
+
if (a.isDynamic !== b.isDynamic) {
|
|
1946
|
+
return a.isDynamic ? 1 : -1;
|
|
1947
|
+
}
|
|
1948
|
+
if (a.isCatchAll !== b.isCatchAll) {
|
|
1949
|
+
return a.isCatchAll ? 1 : -1;
|
|
1950
|
+
}
|
|
1951
|
+
const aSegments = a.urlPath.split("/").filter(Boolean).length;
|
|
1952
|
+
const bSegments = b.urlPath.split("/").filter(Boolean).length;
|
|
1953
|
+
if (aSegments !== bSegments) {
|
|
1954
|
+
return aSegments - bSegments;
|
|
1955
|
+
}
|
|
1956
|
+
return a.urlPath.localeCompare(b.urlPath);
|
|
1957
|
+
});
|
|
1958
|
+
}
|
|
1959
|
+
var init_sortRoutes = __esm({
|
|
1960
|
+
"src/router/sortRoutes.ts"() {
|
|
1961
|
+
"use strict";
|
|
1962
|
+
}
|
|
1963
|
+
});
|
|
1964
|
+
|
|
1965
|
+
// src/config/loadConfig.ts
|
|
1813
1966
|
import path9 from "path";
|
|
1814
1967
|
import fs8 from "fs";
|
|
1815
1968
|
async function loadConfig(rootDir, outDir) {
|
|
@@ -1835,162 +1988,6 @@ var init_loadConfig = __esm({
|
|
|
1835
1988
|
}
|
|
1836
1989
|
});
|
|
1837
1990
|
|
|
1838
|
-
// src/cli/buildCommand.ts
|
|
1839
|
-
var buildCommand_exports = {};
|
|
1840
|
-
__export(buildCommand_exports, {
|
|
1841
|
-
buildCommand: () => buildCommand
|
|
1842
|
-
});
|
|
1843
|
-
import path10 from "path";
|
|
1844
|
-
import fs9 from "fs";
|
|
1845
|
-
import fg2 from "fast-glob";
|
|
1846
|
-
async function collectBundleEntries(rootDir, patterns, appDir) {
|
|
1847
|
-
const entries = /* @__PURE__ */ new Set();
|
|
1848
|
-
const handlerFiles = await fg2(patterns, {
|
|
1849
|
-
cwd: rootDir,
|
|
1850
|
-
onlyFiles: true,
|
|
1851
|
-
absolute: true
|
|
1852
|
-
});
|
|
1853
|
-
for (const f of handlerFiles) {
|
|
1854
|
-
if (f.endsWith("handler.ts")) entries.add(f);
|
|
1855
|
-
}
|
|
1856
|
-
const mwGlob = appDir === "." ? "**/middlewares.ts" : appDir + "/**/middlewares.ts";
|
|
1857
|
-
const mwFiles = await fg2([mwGlob], {
|
|
1858
|
-
cwd: rootDir,
|
|
1859
|
-
onlyFiles: true,
|
|
1860
|
-
absolute: true,
|
|
1861
|
-
ignore: ["**/*.test.ts", "**/*.e2e.test.ts", "**/*.d.ts"]
|
|
1862
|
-
});
|
|
1863
|
-
for (const f of mwFiles) entries.add(f);
|
|
1864
|
-
return Array.from(entries);
|
|
1865
|
-
}
|
|
1866
|
-
async function buildCommand(options) {
|
|
1867
|
-
const rootDir = options?.rootDir ?? process.cwd();
|
|
1868
|
-
const outdir = PROD_OUT_DIR;
|
|
1869
|
-
await compileConfig({ rootDir, outDir: outdir });
|
|
1870
|
-
const _config = await loadConfig(rootDir, outdir);
|
|
1871
|
-
const appDir = process.env.FAAPI_APP_DIR ?? "src";
|
|
1872
|
-
const patterns = appDir === "." ? ["api/**/*.ts"] : [`${appDir}/api/**/*.ts`];
|
|
1873
|
-
console.log("faapi build started");
|
|
1874
|
-
console.log(`- Root: ${rootDir}`);
|
|
1875
|
-
console.log(`- AppDir: ${appDir}`);
|
|
1876
|
-
console.log(`- Output: ${outdir}`);
|
|
1877
|
-
console.log("\n[1/7] Collecting bundle entries...");
|
|
1878
|
-
const entries = await collectBundleEntries(rootDir, patterns, appDir);
|
|
1879
|
-
console.log(` ${entries.length} entry file(s)`);
|
|
1880
|
-
if (entries.length === 0) {
|
|
1881
|
-
console.warn(" ! No entry files found, nothing to build");
|
|
1882
|
-
return;
|
|
1883
|
-
}
|
|
1884
|
-
console.log("\n[2/7] Compiling TypeScript (bundle mode)...");
|
|
1885
|
-
const result = await compileBuildRoutes({
|
|
1886
|
-
rootDir,
|
|
1887
|
-
appDir,
|
|
1888
|
-
outDir: outdir,
|
|
1889
|
-
entries,
|
|
1890
|
-
splitting: true,
|
|
1891
|
-
define: { "process.env.NODE_ENV": JSON.stringify("production") },
|
|
1892
|
-
minifySyntax: true,
|
|
1893
|
-
logLevel: "silent"
|
|
1894
|
-
});
|
|
1895
|
-
console.log(` Compiled ${result.compiledFiles.length} entry file(s)`);
|
|
1896
|
-
console.log("\n[3/7] Compiling config...");
|
|
1897
|
-
const configResult = await compileConfig({ rootDir, outDir: outdir });
|
|
1898
|
-
if (configResult.generated) {
|
|
1899
|
-
console.log(` Written to ${configResult.outputFile}`);
|
|
1900
|
-
} else {
|
|
1901
|
-
console.log(" No config file found, skipped");
|
|
1902
|
-
}
|
|
1903
|
-
console.log("\n[4/7] Scanning routes...");
|
|
1904
|
-
const { routes, wsRoutes } = await scanRoutes(rootDir, patterns, appDir, outdir);
|
|
1905
|
-
const sorted = sortRoutes(routes);
|
|
1906
|
-
console.log(` Found ${sorted.length} routes, ${wsRoutes.length} WS routes`);
|
|
1907
|
-
const conflicts = detectRouteConflicts(sorted);
|
|
1908
|
-
if (conflicts.length > 0) {
|
|
1909
|
-
console.warn("! \u68C0\u6D4B\u5230\u8DEF\u7531\u51B2\u7A81\uFF1A");
|
|
1910
|
-
for (const conflict of conflicts) {
|
|
1911
|
-
console.warn(` ${conflict.method} ${conflict.urlPath}`);
|
|
1912
|
-
for (const file of conflict.files) {
|
|
1913
|
-
console.warn(` - ${file}`);
|
|
1914
|
-
}
|
|
1915
|
-
}
|
|
1916
|
-
}
|
|
1917
|
-
console.log("\n[5/7] Generating schema...");
|
|
1918
|
-
await generateSchemaFiles(sorted, rootDir, appDir, outdir);
|
|
1919
|
-
console.log(` Schema: zod.js files under ${path10.resolve(rootDir, outdir)}`);
|
|
1920
|
-
console.log("\n[6/7] Generating routes manifest...");
|
|
1921
|
-
const routesPath = path10.resolve(rootDir, outdir, "faapi-routes.js");
|
|
1922
|
-
const serialized = serializeRoutes(sorted, wsRoutes, rootDir, appDir, outdir);
|
|
1923
|
-
await writeRoutesModule(serialized, routesPath);
|
|
1924
|
-
console.log(` Written to ${routesPath}`);
|
|
1925
|
-
console.log("\n[7/7] Generating entry file...");
|
|
1926
|
-
const mainPath = path10.resolve(rootDir, outdir, "main.js");
|
|
1927
|
-
const mainContent = `// \u7531 faapi build \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u7F16\u8F91
|
|
1928
|
-
import { createProdApp } from '@faapi/faapi';
|
|
1929
|
-
|
|
1930
|
-
const app = await createProdApp();
|
|
1931
|
-
await app.listen();
|
|
1932
|
-
`;
|
|
1933
|
-
await fs9.promises.writeFile(mainPath, mainContent, "utf-8");
|
|
1934
|
-
console.log(` Written to ${mainPath}`);
|
|
1935
|
-
console.log("\nfaapi build completed");
|
|
1936
|
-
}
|
|
1937
|
-
var PROD_OUT_DIR;
|
|
1938
|
-
var init_buildCommand = __esm({
|
|
1939
|
-
"src/cli/buildCommand.ts"() {
|
|
1940
|
-
"use strict";
|
|
1941
|
-
init_scanRoutes();
|
|
1942
|
-
init_sortRoutes();
|
|
1943
|
-
init_detectRouteConflicts();
|
|
1944
|
-
init_generateSchemaFiles();
|
|
1945
|
-
init_generateRoutes();
|
|
1946
|
-
init_compileBuildRoutes();
|
|
1947
|
-
init_compileConfig();
|
|
1948
|
-
init_loadConfig();
|
|
1949
|
-
PROD_OUT_DIR = "dist";
|
|
1950
|
-
}
|
|
1951
|
-
});
|
|
1952
|
-
|
|
1953
|
-
// src/cli/compileDevRoutes.ts
|
|
1954
|
-
import path11 from "path";
|
|
1955
|
-
import fs10 from "fs";
|
|
1956
|
-
import fg3 from "fast-glob";
|
|
1957
|
-
async function compileDevRoutes(options) {
|
|
1958
|
-
const { rootDir, appDir, outDir, files, logLevel = "silent" } = options;
|
|
1959
|
-
const entryPoints = files ?? await fg3([`${appDir}/**/*.ts`], {
|
|
1960
|
-
cwd: rootDir,
|
|
1961
|
-
onlyFiles: true,
|
|
1962
|
-
absolute: true,
|
|
1963
|
-
ignore: ["**/*.test.ts", "**/*.e2e.test.ts", "**/*.d.ts"]
|
|
1964
|
-
});
|
|
1965
|
-
if (entryPoints.length === 0) {
|
|
1966
|
-
return { compiledFiles: [] };
|
|
1967
|
-
}
|
|
1968
|
-
const absOutDir = path11.resolve(rootDir, outDir);
|
|
1969
|
-
await fs10.promises.mkdir(absOutDir, { recursive: true });
|
|
1970
|
-
const plugins = buildAliasPlugins(rootDir);
|
|
1971
|
-
const esbuild = await import("esbuild");
|
|
1972
|
-
const outbase = appDir === "." ? rootDir : path11.resolve(rootDir, appDir);
|
|
1973
|
-
await esbuild.build({
|
|
1974
|
-
entryPoints,
|
|
1975
|
-
outdir: absOutDir,
|
|
1976
|
-
outbase,
|
|
1977
|
-
bundle: false,
|
|
1978
|
-
platform: "node",
|
|
1979
|
-
format: "esm",
|
|
1980
|
-
sourcemap: true,
|
|
1981
|
-
packages: "external",
|
|
1982
|
-
plugins,
|
|
1983
|
-
logLevel
|
|
1984
|
-
});
|
|
1985
|
-
return { compiledFiles: entryPoints };
|
|
1986
|
-
}
|
|
1987
|
-
var init_compileDevRoutes = __esm({
|
|
1988
|
-
"src/cli/compileDevRoutes.ts"() {
|
|
1989
|
-
"use strict";
|
|
1990
|
-
init_aliasPlugin();
|
|
1991
|
-
}
|
|
1992
|
-
});
|
|
1993
|
-
|
|
1994
1991
|
// ../../node_modules/.pnpm/readdirp@4.1.2/node_modules/readdirp/esm/index.js
|
|
1995
1992
|
import { stat, lstat, readdir, realpath } from "fs/promises";
|
|
1996
1993
|
import { Readable } from "stream";
|
|
@@ -2542,10 +2539,10 @@ var init_handler = __esm({
|
|
|
2542
2539
|
fn(val);
|
|
2543
2540
|
}
|
|
2544
2541
|
};
|
|
2545
|
-
addAndConvert = (
|
|
2546
|
-
let container =
|
|
2542
|
+
addAndConvert = (main, prop, item) => {
|
|
2543
|
+
let container = main[prop];
|
|
2547
2544
|
if (!(container instanceof Set)) {
|
|
2548
|
-
|
|
2545
|
+
main[prop] = container = /* @__PURE__ */ new Set([container]);
|
|
2549
2546
|
}
|
|
2550
2547
|
container.add(item);
|
|
2551
2548
|
};
|
|
@@ -2557,12 +2554,12 @@ var init_handler = __esm({
|
|
|
2557
2554
|
delete cont[key];
|
|
2558
2555
|
}
|
|
2559
2556
|
};
|
|
2560
|
-
delFromSet = (
|
|
2561
|
-
const container =
|
|
2557
|
+
delFromSet = (main, prop, item) => {
|
|
2558
|
+
const container = main[prop];
|
|
2562
2559
|
if (container instanceof Set) {
|
|
2563
2560
|
container.delete(item);
|
|
2564
2561
|
} else if (container === item) {
|
|
2565
|
-
delete
|
|
2562
|
+
delete main[prop];
|
|
2566
2563
|
}
|
|
2567
2564
|
};
|
|
2568
2565
|
isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
@@ -2981,7 +2978,7 @@ var init_handler = __esm({
|
|
|
2981
2978
|
// ../../node_modules/.pnpm/chokidar@4.0.3/node_modules/chokidar/esm/index.js
|
|
2982
2979
|
import { stat as statcb } from "fs";
|
|
2983
2980
|
import { stat as stat3, readdir as readdir2 } from "fs/promises";
|
|
2984
|
-
import { EventEmitter } from "events";
|
|
2981
|
+
import { EventEmitter as EventEmitter2 } from "events";
|
|
2985
2982
|
import * as sysPath2 from "path";
|
|
2986
2983
|
function arrify(item) {
|
|
2987
2984
|
return Array.isArray(item) ? item : [item];
|
|
@@ -3184,7 +3181,7 @@ var init_esm2 = __esm({
|
|
|
3184
3181
|
return this.fsw._isntIgnored(this.entryPath(entry), entry.stats);
|
|
3185
3182
|
}
|
|
3186
3183
|
};
|
|
3187
|
-
FSWatcher = class extends
|
|
3184
|
+
FSWatcher = class extends EventEmitter2 {
|
|
3188
3185
|
// Not indenting methods for history sake; for now.
|
|
3189
3186
|
constructor(_opts = {}) {
|
|
3190
3187
|
super();
|
|
@@ -3701,7 +3698,7 @@ var init_esm2 = __esm({
|
|
|
3701
3698
|
});
|
|
3702
3699
|
|
|
3703
3700
|
// src/cli/watcher.ts
|
|
3704
|
-
import
|
|
3701
|
+
import path10 from "path";
|
|
3705
3702
|
function startWatcher(options) {
|
|
3706
3703
|
const { rootDir, appDir, app } = options;
|
|
3707
3704
|
let rebuildTimer = null;
|
|
@@ -3755,11 +3752,11 @@ function startWatcher(options) {
|
|
|
3755
3752
|
}
|
|
3756
3753
|
});
|
|
3757
3754
|
watcher.on("add", (file) => {
|
|
3758
|
-
pendingFiles.add(
|
|
3755
|
+
pendingFiles.add(path10.resolve(rootDir, file));
|
|
3759
3756
|
scheduleRebuild();
|
|
3760
3757
|
});
|
|
3761
3758
|
watcher.on("change", (file) => {
|
|
3762
|
-
pendingFiles.add(
|
|
3759
|
+
pendingFiles.add(path10.resolve(rootDir, file));
|
|
3763
3760
|
scheduleRebuild();
|
|
3764
3761
|
});
|
|
3765
3762
|
watcher.on("unlink", () => {
|
|
@@ -3787,6 +3784,36 @@ var init_watcher = __esm({
|
|
|
3787
3784
|
}
|
|
3788
3785
|
});
|
|
3789
3786
|
|
|
3787
|
+
// src/router/detectRouteConflicts.ts
|
|
3788
|
+
function detectRouteConflicts(routes) {
|
|
3789
|
+
const map = /* @__PURE__ */ new Map();
|
|
3790
|
+
for (const route of routes) {
|
|
3791
|
+
const key = `${route.method} ${route.urlPath}`;
|
|
3792
|
+
const existing = map.get(key);
|
|
3793
|
+
if (existing) {
|
|
3794
|
+
existing.files.push(route.filePath);
|
|
3795
|
+
} else {
|
|
3796
|
+
map.set(key, {
|
|
3797
|
+
method: route.method,
|
|
3798
|
+
urlPath: route.urlPath,
|
|
3799
|
+
files: [route.filePath]
|
|
3800
|
+
});
|
|
3801
|
+
}
|
|
3802
|
+
}
|
|
3803
|
+
const conflicts = [];
|
|
3804
|
+
for (const conflict of map.values()) {
|
|
3805
|
+
if (conflict.files.length > 1) {
|
|
3806
|
+
conflicts.push(conflict);
|
|
3807
|
+
}
|
|
3808
|
+
}
|
|
3809
|
+
return conflicts;
|
|
3810
|
+
}
|
|
3811
|
+
var init_detectRouteConflicts = __esm({
|
|
3812
|
+
"src/router/detectRouteConflicts.ts"() {
|
|
3813
|
+
"use strict";
|
|
3814
|
+
}
|
|
3815
|
+
});
|
|
3816
|
+
|
|
3790
3817
|
// src/router/matchRoute.ts
|
|
3791
3818
|
function matchRoute(routes, method, path17) {
|
|
3792
3819
|
for (const route of routes) {
|
|
@@ -4335,10 +4362,6 @@ var init_isPlainObject = __esm({
|
|
|
4335
4362
|
});
|
|
4336
4363
|
|
|
4337
4364
|
// src/response/toResponse.ts
|
|
4338
|
-
var toResponse_exports = {};
|
|
4339
|
-
__export(toResponse_exports, {
|
|
4340
|
-
toResponse: () => toResponse
|
|
4341
|
-
});
|
|
4342
4365
|
async function toResponse(value, meta) {
|
|
4343
4366
|
if (value instanceof Promise) {
|
|
4344
4367
|
return toResponse(await value, meta);
|
|
@@ -4672,16 +4695,13 @@ function mapZodCode(zodCode, message) {
|
|
|
4672
4695
|
return "TYPE_MISMATCH";
|
|
4673
4696
|
case "unrecognized_keys":
|
|
4674
4697
|
return "INVALID_FORMAT";
|
|
4675
|
-
case "
|
|
4698
|
+
case "invalid_value":
|
|
4676
4699
|
case "invalid_string":
|
|
4677
|
-
case "invalid_date":
|
|
4678
4700
|
case "too_small":
|
|
4679
4701
|
case "too_big":
|
|
4680
4702
|
case "invalid_intersection_types":
|
|
4681
4703
|
case "not_multiple_of":
|
|
4682
4704
|
return "INVALID_VALUE";
|
|
4683
|
-
case "not_finite":
|
|
4684
|
-
return "COERCE_FAILED";
|
|
4685
4705
|
case "custom":
|
|
4686
4706
|
return "INVALID_VALUE";
|
|
4687
4707
|
default:
|
|
@@ -4717,7 +4737,7 @@ function getClientIp(req) {
|
|
|
4717
4737
|
const first = xff.split(",")[0]?.trim();
|
|
4718
4738
|
if (first) return first;
|
|
4719
4739
|
}
|
|
4720
|
-
const remote = req.socket
|
|
4740
|
+
const remote = req.socket?.remoteAddress;
|
|
4721
4741
|
if (remote) {
|
|
4722
4742
|
if (remote.startsWith("::ffff:")) {
|
|
4723
4743
|
return remote.slice(7);
|
|
@@ -4937,14 +4957,7 @@ function nodeHttpToWebHeaders(req) {
|
|
|
4937
4957
|
}
|
|
4938
4958
|
return headers;
|
|
4939
4959
|
}
|
|
4940
|
-
function buildErrorResponse(err
|
|
4941
|
-
if (errorFormat) {
|
|
4942
|
-
try {
|
|
4943
|
-
const res = errorFormat(err, ctx);
|
|
4944
|
-
if (res) return res;
|
|
4945
|
-
} catch {
|
|
4946
|
-
}
|
|
4947
|
-
}
|
|
4960
|
+
function buildErrorResponse(err) {
|
|
4948
4961
|
try {
|
|
4949
4962
|
return formatErrorResponse(err);
|
|
4950
4963
|
} catch {
|
|
@@ -4987,7 +5000,7 @@ var init_wsHandler = __esm({
|
|
|
4987
5000
|
|
|
4988
5001
|
// src/server/handleWsUpgrade.ts
|
|
4989
5002
|
import { WebSocketServer, WebSocket } from "ws";
|
|
4990
|
-
import
|
|
5003
|
+
import path11 from "path";
|
|
4991
5004
|
function getPathname(req) {
|
|
4992
5005
|
const url = req.url ?? "/";
|
|
4993
5006
|
const idx = url.indexOf("?");
|
|
@@ -5047,7 +5060,7 @@ async function sendResponseToSocket(socket, response) {
|
|
|
5047
5060
|
socket.destroy();
|
|
5048
5061
|
}
|
|
5049
5062
|
function attachWebSocket(options) {
|
|
5050
|
-
const { server, routesRef, rootDir, config,
|
|
5063
|
+
const { server, routesRef, rootDir, config, globalMiddlewares } = options;
|
|
5051
5064
|
const wss = new WebSocketServer({ noServer: true });
|
|
5052
5065
|
server.on("upgrade", async (req, socket, head) => {
|
|
5053
5066
|
const currentWsRoutes = routesRef.wsCurrent;
|
|
@@ -5069,7 +5082,7 @@ function attachWebSocket(options) {
|
|
|
5069
5082
|
const finalHandler = async () => {
|
|
5070
5083
|
let handlers;
|
|
5071
5084
|
try {
|
|
5072
|
-
const absoluteFilePath =
|
|
5085
|
+
const absoluteFilePath = path11.resolve(rootDir, route.filePath);
|
|
5073
5086
|
handlers = await loadWsHandler(absoluteFilePath, ctx);
|
|
5074
5087
|
} catch (err) {
|
|
5075
5088
|
const reason = err instanceof Error ? err.message : String(err);
|
|
@@ -5104,7 +5117,7 @@ function attachWebSocket(options) {
|
|
|
5104
5117
|
console.error("[faapi] WS \u63E1\u624B\u540E\u4E2D\u95F4\u4EF6\u629B\u9519:", err);
|
|
5105
5118
|
return;
|
|
5106
5119
|
}
|
|
5107
|
-
response = buildErrorResponse(err
|
|
5120
|
+
response = buildErrorResponse(err);
|
|
5108
5121
|
}
|
|
5109
5122
|
if (upgraded) {
|
|
5110
5123
|
return;
|
|
@@ -5133,7 +5146,7 @@ import {
|
|
|
5133
5146
|
import { createSecureServer as createHttp2SecureServer } from "http2";
|
|
5134
5147
|
import { readFileSync } from "fs";
|
|
5135
5148
|
import { Readable as Readable3 } from "stream";
|
|
5136
|
-
import
|
|
5149
|
+
import path12 from "path";
|
|
5137
5150
|
function toWebRequest(req, bodyLimit = DEFAULT_BODY_LIMIT) {
|
|
5138
5151
|
const forwardedProto = req.headers["x-forwarded-proto"];
|
|
5139
5152
|
const protocol = Array.isArray(forwardedProto) ? forwardedProto[0]?.split(",")[0]?.trim() ?? "http" : forwardedProto?.split(",")[0]?.trim() ?? "http";
|
|
@@ -5200,8 +5213,6 @@ function createServer(options) {
|
|
|
5200
5213
|
appDir,
|
|
5201
5214
|
outDir,
|
|
5202
5215
|
cors: corsOption,
|
|
5203
|
-
responseFormat,
|
|
5204
|
-
errorFormat,
|
|
5205
5216
|
onError,
|
|
5206
5217
|
config,
|
|
5207
5218
|
wsRoutes,
|
|
@@ -5240,8 +5251,6 @@ function createServer(options) {
|
|
|
5240
5251
|
req,
|
|
5241
5252
|
res,
|
|
5242
5253
|
configMiddlewares,
|
|
5243
|
-
responseFormat,
|
|
5244
|
-
errorFormat,
|
|
5245
5254
|
onError,
|
|
5246
5255
|
config,
|
|
5247
5256
|
globalMiddlewares,
|
|
@@ -5253,11 +5262,11 @@ function createServer(options) {
|
|
|
5253
5262
|
});
|
|
5254
5263
|
});
|
|
5255
5264
|
if (routesRef.wsCurrent.length > 0) {
|
|
5256
|
-
attachWebSocket({ server, routesRef, rootDir, config,
|
|
5265
|
+
attachWebSocket({ server, routesRef, rootDir, config, globalMiddlewares });
|
|
5257
5266
|
}
|
|
5258
5267
|
return { server, routesRef };
|
|
5259
5268
|
}
|
|
5260
|
-
async function handleRequest(routes, rootDir, appDir, outDir, req, res, configMiddlewares,
|
|
5269
|
+
async function handleRequest(routes, rootDir, appDir, outDir, req, res, configMiddlewares, onError, config, globalMiddlewares, globalInjectors, bodyLimit) {
|
|
5261
5270
|
const request = toWebRequest(req, bodyLimit);
|
|
5262
5271
|
const method = request.method.toUpperCase();
|
|
5263
5272
|
const urlPath = new URL(request.url).pathname;
|
|
@@ -5274,7 +5283,7 @@ async function handleRequest(routes, rootDir, appDir, outDir, req, res, configMi
|
|
|
5274
5283
|
}
|
|
5275
5284
|
ctx.params = match.params;
|
|
5276
5285
|
const { route } = match;
|
|
5277
|
-
const absoluteFilePath =
|
|
5286
|
+
const absoluteFilePath = path12.resolve(rootDir, route.filePath);
|
|
5278
5287
|
const routeModule = await loadRouteModule(absoluteFilePath, route.method);
|
|
5279
5288
|
const input = await resolveInput(route.method, request);
|
|
5280
5289
|
const inputType = getInputTypeForMethod(route.method);
|
|
@@ -5285,22 +5294,13 @@ async function handleRequest(routes, rootDir, appDir, outDir, req, res, configMi
|
|
|
5285
5294
|
}
|
|
5286
5295
|
const body = hasBody(route.method) ? result.data : void 0;
|
|
5287
5296
|
const mergedInjectors = globalInjectors ? { ...globalInjectors, ...route.injectors } : route.injectors;
|
|
5288
|
-
|
|
5297
|
+
const response = await invokeHandler(
|
|
5289
5298
|
routeModule.handler,
|
|
5290
5299
|
ctx,
|
|
5291
5300
|
body,
|
|
5292
5301
|
route.middlewares,
|
|
5293
5302
|
mergedInjectors
|
|
5294
5303
|
);
|
|
5295
|
-
if (responseFormat && response.status >= 200 && response.status < 300) {
|
|
5296
|
-
const contentType = response.headers.get("Content-Type") ?? "";
|
|
5297
|
-
if (contentType.includes("application/json")) {
|
|
5298
|
-
const data = await response.json();
|
|
5299
|
-
const formatted = responseFormat(data, ctx);
|
|
5300
|
-
const { toResponse: toResponse2 } = await Promise.resolve().then(() => (init_toResponse(), toResponse_exports));
|
|
5301
|
-
response = await toResponse2(formatted, meta);
|
|
5302
|
-
}
|
|
5303
|
-
}
|
|
5304
5304
|
return response;
|
|
5305
5305
|
};
|
|
5306
5306
|
try {
|
|
@@ -5317,7 +5317,7 @@ async function handleRequest(routes, rootDir, appDir, outDir, req, res, configMi
|
|
|
5317
5317
|
}
|
|
5318
5318
|
await sendNodeResponse(response, res);
|
|
5319
5319
|
} catch (err) {
|
|
5320
|
-
const errorResponse = buildErrorResponse(err
|
|
5320
|
+
const errorResponse = buildErrorResponse(err);
|
|
5321
5321
|
await sendNodeResponse(mergeMeta(errorResponse, meta), res);
|
|
5322
5322
|
if (onError) {
|
|
5323
5323
|
try {
|
|
@@ -5448,8 +5448,8 @@ var init_loadPlugins = __esm({
|
|
|
5448
5448
|
});
|
|
5449
5449
|
|
|
5450
5450
|
// src/cli/createAppCore.ts
|
|
5451
|
-
import
|
|
5452
|
-
import
|
|
5451
|
+
import fs9 from "fs";
|
|
5452
|
+
import path13 from "path";
|
|
5453
5453
|
import { PassThrough } from "stream";
|
|
5454
5454
|
function isFaapiConfigKey(key) {
|
|
5455
5455
|
return FAAPI_CONFIG_KEYS.has(key);
|
|
@@ -5457,8 +5457,8 @@ function isFaapiConfigKey(key) {
|
|
|
5457
5457
|
async function createAppBase(options) {
|
|
5458
5458
|
const rootDir = options?.rootDir ?? process.cwd();
|
|
5459
5459
|
const outDir = process.env.FAAPI_OUT_DIR ?? DEFAULT_OUT_DIR;
|
|
5460
|
-
const routesPath =
|
|
5461
|
-
if (!
|
|
5460
|
+
const routesPath = path13.resolve(rootDir, outDir, ROUTES_FILE);
|
|
5461
|
+
if (!fs9.existsSync(routesPath)) {
|
|
5462
5462
|
throw new Error(
|
|
5463
5463
|
`[faapi] ${outDir}/${ROUTES_FILE} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C \`faapi build\`\uFF08\u6216 \`faapi dev\`\uFF09\u751F\u6210\u4EA7\u7269\u3002`
|
|
5464
5464
|
);
|
|
@@ -5486,8 +5486,6 @@ async function createAppBase(options) {
|
|
|
5486
5486
|
appDir,
|
|
5487
5487
|
outDir,
|
|
5488
5488
|
cors: config?.cors ?? true,
|
|
5489
|
-
responseFormat: config?.responseFormat,
|
|
5490
|
-
errorFormat: config?.errorFormat,
|
|
5491
5489
|
onError: config?.lifecycle?.onError,
|
|
5492
5490
|
config: config ?? void 0,
|
|
5493
5491
|
wsRoutes,
|
|
@@ -5500,6 +5498,7 @@ async function createAppBase(options) {
|
|
|
5500
5498
|
const { handlerWrappers, upgradeWrappers } = await loadPlugins(config?.plugins, {
|
|
5501
5499
|
rootDir,
|
|
5502
5500
|
routes: sorted,
|
|
5501
|
+
getRoutes: () => sorted,
|
|
5503
5502
|
server,
|
|
5504
5503
|
config: pluginConfig
|
|
5505
5504
|
});
|
|
@@ -5565,6 +5564,11 @@ async function createAppBase(options) {
|
|
|
5565
5564
|
setHeader(name, value) {
|
|
5566
5565
|
this._headers[name.toLowerCase()] = value;
|
|
5567
5566
|
},
|
|
5567
|
+
appendHeader(name, value) {
|
|
5568
|
+
const key = name.toLowerCase();
|
|
5569
|
+
const existing = this._headers[key];
|
|
5570
|
+
this._headers[key] = existing ? `${existing}, ${value}` : value;
|
|
5571
|
+
},
|
|
5568
5572
|
writeHead(status, headers) {
|
|
5569
5573
|
this.statusCode = status;
|
|
5570
5574
|
if (headers) {
|
|
@@ -5606,6 +5610,7 @@ async function createAppBase(options) {
|
|
|
5606
5610
|
host: "localhost",
|
|
5607
5611
|
"content-type": body !== void 0 ? "application/json" : void 0
|
|
5608
5612
|
};
|
|
5613
|
+
mockReq.socket = { remoteAddress: "127.0.0.1" };
|
|
5609
5614
|
if (body !== void 0) {
|
|
5610
5615
|
mockReq.push(JSON.stringify(body));
|
|
5611
5616
|
}
|
|
@@ -5618,8 +5623,13 @@ async function createAppBase(options) {
|
|
|
5618
5623
|
async close() {
|
|
5619
5624
|
if (closed) return;
|
|
5620
5625
|
closed = true;
|
|
5621
|
-
server
|
|
5622
|
-
|
|
5626
|
+
const s = server;
|
|
5627
|
+
if (typeof s.closeIdleConnections === "function") {
|
|
5628
|
+
s.closeIdleConnections();
|
|
5629
|
+
}
|
|
5630
|
+
if (typeof s.closeAllConnections === "function") {
|
|
5631
|
+
s.closeAllConnections();
|
|
5632
|
+
}
|
|
5623
5633
|
if (config?.lifecycle?.onClose) {
|
|
5624
5634
|
await config.lifecycle.onClose({ rootDir, routes: sorted, server });
|
|
5625
5635
|
}
|
|
@@ -5669,8 +5679,6 @@ var init_createAppCore = __esm({
|
|
|
5669
5679
|
ROUTES_FILE = "faapi-routes.js";
|
|
5670
5680
|
FAAPI_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
5671
5681
|
"cors",
|
|
5672
|
-
"responseFormat",
|
|
5673
|
-
"errorFormat",
|
|
5674
5682
|
"lifecycle",
|
|
5675
5683
|
"middlewares",
|
|
5676
5684
|
"injectors",
|
|
@@ -5720,7 +5728,7 @@ __export(devCommand_exports, {
|
|
|
5720
5728
|
devCommand: () => devCommand,
|
|
5721
5729
|
generateRouteArtifacts: () => generateRouteArtifacts
|
|
5722
5730
|
});
|
|
5723
|
-
import
|
|
5731
|
+
import path14 from "path";
|
|
5724
5732
|
async function devCommand() {
|
|
5725
5733
|
const rootDir = process.cwd();
|
|
5726
5734
|
process.env.FAAPI_OUT_DIR = DEV_OUT_DIR2;
|
|
@@ -5743,7 +5751,7 @@ async function devCommand() {
|
|
|
5743
5751
|
async function generateRouteArtifacts(rootDir, appDir, patterns) {
|
|
5744
5752
|
const { routes, wsRoutes } = await scanRoutes(rootDir, patterns, appDir, DEV_OUT_DIR2);
|
|
5745
5753
|
const sorted = sortRoutes(routes);
|
|
5746
|
-
const routesPath =
|
|
5754
|
+
const routesPath = path14.resolve(rootDir, DEV_OUT_DIR2, ROUTES_FILE2);
|
|
5747
5755
|
const serialized = serializeRoutes(sorted, wsRoutes, rootDir, appDir, DEV_OUT_DIR2);
|
|
5748
5756
|
await writeRoutesModule(serialized, routesPath);
|
|
5749
5757
|
await generateSchemaFiles(sorted, rootDir, appDir, DEV_OUT_DIR2);
|
|
@@ -5766,22 +5774,764 @@ var init_devCommand = __esm({
|
|
|
5766
5774
|
}
|
|
5767
5775
|
});
|
|
5768
5776
|
|
|
5769
|
-
// src/cli/
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5777
|
+
// src/cli/compileBuildRoutes.ts
|
|
5778
|
+
import path15 from "path";
|
|
5779
|
+
import fs10 from "fs";
|
|
5780
|
+
async function compileBuildRoutes(options) {
|
|
5781
|
+
const {
|
|
5782
|
+
rootDir,
|
|
5783
|
+
appDir,
|
|
5784
|
+
outDir,
|
|
5785
|
+
entries,
|
|
5786
|
+
splitting = true,
|
|
5787
|
+
define,
|
|
5788
|
+
minifySyntax = true,
|
|
5789
|
+
logLevel = "silent"
|
|
5790
|
+
} = options;
|
|
5791
|
+
if (entries.length === 0) {
|
|
5792
|
+
return { compiledFiles: [] };
|
|
5793
|
+
}
|
|
5794
|
+
const absOutDir = path15.resolve(rootDir, outDir);
|
|
5795
|
+
await fs10.promises.mkdir(absOutDir, { recursive: true });
|
|
5796
|
+
const plugins = buildAliasPlugins(rootDir);
|
|
5797
|
+
const esbuild = await import("esbuild");
|
|
5798
|
+
const outbase = appDir === "." ? rootDir : path15.resolve(rootDir, appDir);
|
|
5799
|
+
await esbuild.build({
|
|
5800
|
+
entryPoints: entries,
|
|
5801
|
+
outdir: absOutDir,
|
|
5802
|
+
outbase,
|
|
5803
|
+
bundle: true,
|
|
5804
|
+
splitting,
|
|
5805
|
+
platform: "node",
|
|
5806
|
+
format: "esm",
|
|
5807
|
+
sourcemap: true,
|
|
5808
|
+
packages: "external",
|
|
5809
|
+
plugins,
|
|
5810
|
+
define,
|
|
5811
|
+
minifySyntax,
|
|
5812
|
+
logLevel
|
|
5813
|
+
});
|
|
5814
|
+
return { compiledFiles: entries };
|
|
5815
|
+
}
|
|
5816
|
+
var init_compileBuildRoutes = __esm({
|
|
5817
|
+
"src/cli/compileBuildRoutes.ts"() {
|
|
5818
|
+
"use strict";
|
|
5819
|
+
init_aliasPlugin();
|
|
5820
|
+
}
|
|
5821
|
+
});
|
|
5822
|
+
|
|
5823
|
+
// src/cli/buildCommand.ts
|
|
5824
|
+
var buildCommand_exports = {};
|
|
5825
|
+
__export(buildCommand_exports, {
|
|
5826
|
+
buildCommand: () => buildCommand
|
|
5827
|
+
});
|
|
5828
|
+
import path16 from "path";
|
|
5829
|
+
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
|
+
async function buildCommand(options) {
|
|
5852
|
+
const rootDir = options?.rootDir ?? process.cwd();
|
|
5853
|
+
const outdir = PROD_OUT_DIR;
|
|
5854
|
+
await compileConfig({ rootDir, outDir: outdir });
|
|
5855
|
+
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
|
+
console.log("faapi build started");
|
|
5859
|
+
console.log(`- Root: ${rootDir}`);
|
|
5860
|
+
console.log(`- AppDir: ${appDir}`);
|
|
5861
|
+
console.log(`- Output: ${outdir}`);
|
|
5862
|
+
console.log("\n[1/7] Collecting bundle entries...");
|
|
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");
|
|
5776
5867
|
return;
|
|
5777
5868
|
}
|
|
5869
|
+
console.log("\n[2/7] Compiling TypeScript (bundle mode)...");
|
|
5870
|
+
const result = await compileBuildRoutes({
|
|
5871
|
+
rootDir,
|
|
5872
|
+
appDir,
|
|
5873
|
+
outDir: outdir,
|
|
5874
|
+
entries,
|
|
5875
|
+
splitting: true,
|
|
5876
|
+
define: { "process.env.NODE_ENV": JSON.stringify("production") },
|
|
5877
|
+
minifySyntax: true,
|
|
5878
|
+
logLevel: "silent"
|
|
5879
|
+
});
|
|
5880
|
+
console.log(` Compiled ${result.compiledFiles.length} entry file(s)`);
|
|
5881
|
+
console.log("\n[3/7] Compiling config...");
|
|
5882
|
+
const configResult = await compileConfig({ rootDir, outDir: outdir });
|
|
5883
|
+
if (configResult.generated) {
|
|
5884
|
+
console.log(` Written to ${configResult.outputFile}`);
|
|
5885
|
+
} else {
|
|
5886
|
+
console.log(" No config file found, skipped");
|
|
5887
|
+
}
|
|
5888
|
+
console.log("\n[4/7] Scanning routes...");
|
|
5889
|
+
const { routes, wsRoutes } = await scanRoutes(rootDir, patterns, appDir, outdir);
|
|
5890
|
+
const sorted = sortRoutes(routes);
|
|
5891
|
+
console.log(` Found ${sorted.length} routes, ${wsRoutes.length} WS routes`);
|
|
5892
|
+
const conflicts = detectRouteConflicts(sorted);
|
|
5893
|
+
if (conflicts.length > 0) {
|
|
5894
|
+
console.warn("! \u68C0\u6D4B\u5230\u8DEF\u7531\u51B2\u7A81\uFF1A");
|
|
5895
|
+
for (const conflict of conflicts) {
|
|
5896
|
+
console.warn(` ${conflict.method} ${conflict.urlPath}`);
|
|
5897
|
+
for (const file of conflict.files) {
|
|
5898
|
+
console.warn(` - ${file}`);
|
|
5899
|
+
}
|
|
5900
|
+
}
|
|
5901
|
+
}
|
|
5902
|
+
console.log("\n[5/7] Generating schema...");
|
|
5903
|
+
await generateSchemaFiles(sorted, rootDir, appDir, outdir);
|
|
5904
|
+
console.log(` Schema: zod.js files under ${path16.resolve(rootDir, outdir)}`);
|
|
5905
|
+
console.log("\n[6/7] Generating routes manifest...");
|
|
5906
|
+
const routesPath = path16.resolve(rootDir, outdir, "faapi-routes.js");
|
|
5907
|
+
const serialized = serializeRoutes(sorted, wsRoutes, rootDir, appDir, outdir);
|
|
5908
|
+
await writeRoutesModule(serialized, routesPath);
|
|
5909
|
+
console.log(` Written to ${routesPath}`);
|
|
5910
|
+
console.log("\n[7/7] Generating entry file...");
|
|
5911
|
+
const mainPath = path16.resolve(rootDir, outdir, "main.js");
|
|
5912
|
+
const mainContent = `// \u7531 faapi build \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u7F16\u8F91
|
|
5913
|
+
import { createProdApp } from '@faapi/faapi';
|
|
5914
|
+
|
|
5915
|
+
const app = await createProdApp();
|
|
5916
|
+
await app.listen();
|
|
5917
|
+
`;
|
|
5918
|
+
await fs11.promises.writeFile(mainPath, mainContent, "utf-8");
|
|
5919
|
+
console.log(` Written to ${mainPath}`);
|
|
5920
|
+
console.log("\nfaapi build completed");
|
|
5921
|
+
}
|
|
5922
|
+
var PROD_OUT_DIR;
|
|
5923
|
+
var init_buildCommand = __esm({
|
|
5924
|
+
"src/cli/buildCommand.ts"() {
|
|
5925
|
+
"use strict";
|
|
5926
|
+
init_scanRoutes();
|
|
5927
|
+
init_sortRoutes();
|
|
5928
|
+
init_detectRouteConflicts();
|
|
5929
|
+
init_generateSchemaFiles();
|
|
5930
|
+
init_generateRoutes();
|
|
5931
|
+
init_compileBuildRoutes();
|
|
5932
|
+
init_compileConfig();
|
|
5933
|
+
init_loadConfig();
|
|
5934
|
+
PROD_OUT_DIR = "dist";
|
|
5935
|
+
}
|
|
5936
|
+
});
|
|
5937
|
+
|
|
5938
|
+
// ../../node_modules/.pnpm/cac@6.7.14/node_modules/cac/dist/index.mjs
|
|
5939
|
+
import { EventEmitter } from "events";
|
|
5940
|
+
function toArr(any) {
|
|
5941
|
+
return any == null ? [] : Array.isArray(any) ? any : [any];
|
|
5942
|
+
}
|
|
5943
|
+
function toVal(out, key, val, opts) {
|
|
5944
|
+
var x, old = out[key], nxt = !!~opts.string.indexOf(key) ? val == null || val === true ? "" : String(val) : typeof val === "boolean" ? val : !!~opts.boolean.indexOf(key) ? val === "false" ? false : val === "true" || (out._.push((x = +val, x * 0 === 0) ? x : val), !!val) : (x = +val, x * 0 === 0) ? x : val;
|
|
5945
|
+
out[key] = old == null ? nxt : Array.isArray(old) ? old.concat(nxt) : [old, nxt];
|
|
5946
|
+
}
|
|
5947
|
+
function mri2(args, opts) {
|
|
5948
|
+
args = args || [];
|
|
5949
|
+
opts = opts || {};
|
|
5950
|
+
var k, arr, arg, name, val, out = { _: [] };
|
|
5951
|
+
var i = 0, j = 0, idx = 0, len = args.length;
|
|
5952
|
+
const alibi = opts.alias !== void 0;
|
|
5953
|
+
const strict = opts.unknown !== void 0;
|
|
5954
|
+
const defaults = opts.default !== void 0;
|
|
5955
|
+
opts.alias = opts.alias || {};
|
|
5956
|
+
opts.string = toArr(opts.string);
|
|
5957
|
+
opts.boolean = toArr(opts.boolean);
|
|
5958
|
+
if (alibi) {
|
|
5959
|
+
for (k in opts.alias) {
|
|
5960
|
+
arr = opts.alias[k] = toArr(opts.alias[k]);
|
|
5961
|
+
for (i = 0; i < arr.length; i++) {
|
|
5962
|
+
(opts.alias[arr[i]] = arr.concat(k)).splice(i, 1);
|
|
5963
|
+
}
|
|
5964
|
+
}
|
|
5965
|
+
}
|
|
5966
|
+
for (i = opts.boolean.length; i-- > 0; ) {
|
|
5967
|
+
arr = opts.alias[opts.boolean[i]] || [];
|
|
5968
|
+
for (j = arr.length; j-- > 0; ) opts.boolean.push(arr[j]);
|
|
5969
|
+
}
|
|
5970
|
+
for (i = opts.string.length; i-- > 0; ) {
|
|
5971
|
+
arr = opts.alias[opts.string[i]] || [];
|
|
5972
|
+
for (j = arr.length; j-- > 0; ) opts.string.push(arr[j]);
|
|
5973
|
+
}
|
|
5974
|
+
if (defaults) {
|
|
5975
|
+
for (k in opts.default) {
|
|
5976
|
+
name = typeof opts.default[k];
|
|
5977
|
+
arr = opts.alias[k] = opts.alias[k] || [];
|
|
5978
|
+
if (opts[name] !== void 0) {
|
|
5979
|
+
opts[name].push(k);
|
|
5980
|
+
for (i = 0; i < arr.length; i++) {
|
|
5981
|
+
opts[name].push(arr[i]);
|
|
5982
|
+
}
|
|
5983
|
+
}
|
|
5984
|
+
}
|
|
5985
|
+
}
|
|
5986
|
+
const keys = strict ? Object.keys(opts.alias) : [];
|
|
5987
|
+
for (i = 0; i < len; i++) {
|
|
5988
|
+
arg = args[i];
|
|
5989
|
+
if (arg === "--") {
|
|
5990
|
+
out._ = out._.concat(args.slice(++i));
|
|
5991
|
+
break;
|
|
5992
|
+
}
|
|
5993
|
+
for (j = 0; j < arg.length; j++) {
|
|
5994
|
+
if (arg.charCodeAt(j) !== 45) break;
|
|
5995
|
+
}
|
|
5996
|
+
if (j === 0) {
|
|
5997
|
+
out._.push(arg);
|
|
5998
|
+
} else if (arg.substring(j, j + 3) === "no-") {
|
|
5999
|
+
name = arg.substring(j + 3);
|
|
6000
|
+
if (strict && !~keys.indexOf(name)) {
|
|
6001
|
+
return opts.unknown(arg);
|
|
6002
|
+
}
|
|
6003
|
+
out[name] = false;
|
|
6004
|
+
} else {
|
|
6005
|
+
for (idx = j + 1; idx < arg.length; idx++) {
|
|
6006
|
+
if (arg.charCodeAt(idx) === 61) break;
|
|
6007
|
+
}
|
|
6008
|
+
name = arg.substring(j, idx);
|
|
6009
|
+
val = arg.substring(++idx) || (i + 1 === len || ("" + args[i + 1]).charCodeAt(0) === 45 || args[++i]);
|
|
6010
|
+
arr = j === 2 ? [name] : name;
|
|
6011
|
+
for (idx = 0; idx < arr.length; idx++) {
|
|
6012
|
+
name = arr[idx];
|
|
6013
|
+
if (strict && !~keys.indexOf(name)) return opts.unknown("-".repeat(j) + name);
|
|
6014
|
+
toVal(out, name, idx + 1 < arr.length || val, opts);
|
|
6015
|
+
}
|
|
6016
|
+
}
|
|
6017
|
+
}
|
|
6018
|
+
if (defaults) {
|
|
6019
|
+
for (k in opts.default) {
|
|
6020
|
+
if (out[k] === void 0) {
|
|
6021
|
+
out[k] = opts.default[k];
|
|
6022
|
+
}
|
|
6023
|
+
}
|
|
6024
|
+
}
|
|
6025
|
+
if (alibi) {
|
|
6026
|
+
for (k in out) {
|
|
6027
|
+
arr = opts.alias[k] || [];
|
|
6028
|
+
while (arr.length > 0) {
|
|
6029
|
+
out[arr.shift()] = out[k];
|
|
6030
|
+
}
|
|
6031
|
+
}
|
|
6032
|
+
}
|
|
6033
|
+
return out;
|
|
6034
|
+
}
|
|
6035
|
+
var removeBrackets = (v) => v.replace(/[<[].+/, "").trim();
|
|
6036
|
+
var findAllBrackets = (v) => {
|
|
6037
|
+
const ANGLED_BRACKET_RE_GLOBAL = /<([^>]+)>/g;
|
|
6038
|
+
const SQUARE_BRACKET_RE_GLOBAL = /\[([^\]]+)\]/g;
|
|
6039
|
+
const res = [];
|
|
6040
|
+
const parse = (match) => {
|
|
6041
|
+
let variadic = false;
|
|
6042
|
+
let value = match[1];
|
|
6043
|
+
if (value.startsWith("...")) {
|
|
6044
|
+
value = value.slice(3);
|
|
6045
|
+
variadic = true;
|
|
6046
|
+
}
|
|
6047
|
+
return {
|
|
6048
|
+
required: match[0].startsWith("<"),
|
|
6049
|
+
value,
|
|
6050
|
+
variadic
|
|
6051
|
+
};
|
|
6052
|
+
};
|
|
6053
|
+
let angledMatch;
|
|
6054
|
+
while (angledMatch = ANGLED_BRACKET_RE_GLOBAL.exec(v)) {
|
|
6055
|
+
res.push(parse(angledMatch));
|
|
6056
|
+
}
|
|
6057
|
+
let squareMatch;
|
|
6058
|
+
while (squareMatch = SQUARE_BRACKET_RE_GLOBAL.exec(v)) {
|
|
6059
|
+
res.push(parse(squareMatch));
|
|
6060
|
+
}
|
|
6061
|
+
return res;
|
|
6062
|
+
};
|
|
6063
|
+
var getMriOptions = (options) => {
|
|
6064
|
+
const result = { alias: {}, boolean: [] };
|
|
6065
|
+
for (const [index, option] of options.entries()) {
|
|
6066
|
+
if (option.names.length > 1) {
|
|
6067
|
+
result.alias[option.names[0]] = option.names.slice(1);
|
|
6068
|
+
}
|
|
6069
|
+
if (option.isBoolean) {
|
|
6070
|
+
if (option.negated) {
|
|
6071
|
+
const hasStringTypeOption = options.some((o, i) => {
|
|
6072
|
+
return i !== index && o.names.some((name) => option.names.includes(name)) && typeof o.required === "boolean";
|
|
6073
|
+
});
|
|
6074
|
+
if (!hasStringTypeOption) {
|
|
6075
|
+
result.boolean.push(option.names[0]);
|
|
6076
|
+
}
|
|
6077
|
+
} else {
|
|
6078
|
+
result.boolean.push(option.names[0]);
|
|
6079
|
+
}
|
|
6080
|
+
}
|
|
6081
|
+
}
|
|
6082
|
+
return result;
|
|
6083
|
+
};
|
|
6084
|
+
var findLongest = (arr) => {
|
|
6085
|
+
return arr.sort((a, b) => {
|
|
6086
|
+
return a.length > b.length ? -1 : 1;
|
|
6087
|
+
})[0];
|
|
6088
|
+
};
|
|
6089
|
+
var padRight = (str, length) => {
|
|
6090
|
+
return str.length >= length ? str : `${str}${" ".repeat(length - str.length)}`;
|
|
6091
|
+
};
|
|
6092
|
+
var camelcase = (input) => {
|
|
6093
|
+
return input.replace(/([a-z])-([a-z])/g, (_, p1, p2) => {
|
|
6094
|
+
return p1 + p2.toUpperCase();
|
|
6095
|
+
});
|
|
6096
|
+
};
|
|
6097
|
+
var setDotProp = (obj, keys, val) => {
|
|
6098
|
+
let i = 0;
|
|
6099
|
+
let length = keys.length;
|
|
6100
|
+
let t = obj;
|
|
6101
|
+
let x;
|
|
6102
|
+
for (; i < length; ++i) {
|
|
6103
|
+
x = t[keys[i]];
|
|
6104
|
+
t = t[keys[i]] = i === length - 1 ? val : x != null ? x : !!~keys[i + 1].indexOf(".") || !(+keys[i + 1] > -1) ? {} : [];
|
|
6105
|
+
}
|
|
6106
|
+
};
|
|
6107
|
+
var setByType = (obj, transforms) => {
|
|
6108
|
+
for (const key of Object.keys(transforms)) {
|
|
6109
|
+
const transform = transforms[key];
|
|
6110
|
+
if (transform.shouldTransform) {
|
|
6111
|
+
obj[key] = Array.prototype.concat.call([], obj[key]);
|
|
6112
|
+
if (typeof transform.transformFunction === "function") {
|
|
6113
|
+
obj[key] = obj[key].map(transform.transformFunction);
|
|
6114
|
+
}
|
|
6115
|
+
}
|
|
6116
|
+
}
|
|
6117
|
+
};
|
|
6118
|
+
var getFileName = (input) => {
|
|
6119
|
+
const m = /([^\\\/]+)$/.exec(input);
|
|
6120
|
+
return m ? m[1] : "";
|
|
6121
|
+
};
|
|
6122
|
+
var camelcaseOptionName = (name) => {
|
|
6123
|
+
return name.split(".").map((v, i) => {
|
|
6124
|
+
return i === 0 ? camelcase(v) : v;
|
|
6125
|
+
}).join(".");
|
|
6126
|
+
};
|
|
6127
|
+
var CACError = class extends Error {
|
|
6128
|
+
constructor(message) {
|
|
6129
|
+
super(message);
|
|
6130
|
+
this.name = this.constructor.name;
|
|
6131
|
+
if (typeof Error.captureStackTrace === "function") {
|
|
6132
|
+
Error.captureStackTrace(this, this.constructor);
|
|
6133
|
+
} else {
|
|
6134
|
+
this.stack = new Error(message).stack;
|
|
6135
|
+
}
|
|
6136
|
+
}
|
|
6137
|
+
};
|
|
6138
|
+
var Option = class {
|
|
6139
|
+
constructor(rawName, description, config) {
|
|
6140
|
+
this.rawName = rawName;
|
|
6141
|
+
this.description = description;
|
|
6142
|
+
this.config = Object.assign({}, config);
|
|
6143
|
+
rawName = rawName.replace(/\.\*/g, "");
|
|
6144
|
+
this.negated = false;
|
|
6145
|
+
this.names = removeBrackets(rawName).split(",").map((v) => {
|
|
6146
|
+
let name = v.trim().replace(/^-{1,2}/, "");
|
|
6147
|
+
if (name.startsWith("no-")) {
|
|
6148
|
+
this.negated = true;
|
|
6149
|
+
name = name.replace(/^no-/, "");
|
|
6150
|
+
}
|
|
6151
|
+
return camelcaseOptionName(name);
|
|
6152
|
+
}).sort((a, b) => a.length > b.length ? 1 : -1);
|
|
6153
|
+
this.name = this.names[this.names.length - 1];
|
|
6154
|
+
if (this.negated && this.config.default == null) {
|
|
6155
|
+
this.config.default = true;
|
|
6156
|
+
}
|
|
6157
|
+
if (rawName.includes("<")) {
|
|
6158
|
+
this.required = true;
|
|
6159
|
+
} else if (rawName.includes("[")) {
|
|
6160
|
+
this.required = false;
|
|
6161
|
+
} else {
|
|
6162
|
+
this.isBoolean = true;
|
|
6163
|
+
}
|
|
6164
|
+
}
|
|
6165
|
+
};
|
|
6166
|
+
var processArgs = process.argv;
|
|
6167
|
+
var platformInfo = `${process.platform}-${process.arch} node-${process.version}`;
|
|
6168
|
+
var Command = class {
|
|
6169
|
+
constructor(rawName, description, config = {}, cli2) {
|
|
6170
|
+
this.rawName = rawName;
|
|
6171
|
+
this.description = description;
|
|
6172
|
+
this.config = config;
|
|
6173
|
+
this.cli = cli2;
|
|
6174
|
+
this.options = [];
|
|
6175
|
+
this.aliasNames = [];
|
|
6176
|
+
this.name = removeBrackets(rawName);
|
|
6177
|
+
this.args = findAllBrackets(rawName);
|
|
6178
|
+
this.examples = [];
|
|
6179
|
+
}
|
|
6180
|
+
usage(text) {
|
|
6181
|
+
this.usageText = text;
|
|
6182
|
+
return this;
|
|
6183
|
+
}
|
|
6184
|
+
allowUnknownOptions() {
|
|
6185
|
+
this.config.allowUnknownOptions = true;
|
|
6186
|
+
return this;
|
|
6187
|
+
}
|
|
6188
|
+
ignoreOptionDefaultValue() {
|
|
6189
|
+
this.config.ignoreOptionDefaultValue = true;
|
|
6190
|
+
return this;
|
|
6191
|
+
}
|
|
6192
|
+
version(version, customFlags = "-v, --version") {
|
|
6193
|
+
this.versionNumber = version;
|
|
6194
|
+
this.option(customFlags, "Display version number");
|
|
6195
|
+
return this;
|
|
6196
|
+
}
|
|
6197
|
+
example(example) {
|
|
6198
|
+
this.examples.push(example);
|
|
6199
|
+
return this;
|
|
6200
|
+
}
|
|
6201
|
+
option(rawName, description, config) {
|
|
6202
|
+
const option = new Option(rawName, description, config);
|
|
6203
|
+
this.options.push(option);
|
|
6204
|
+
return this;
|
|
6205
|
+
}
|
|
6206
|
+
alias(name) {
|
|
6207
|
+
this.aliasNames.push(name);
|
|
6208
|
+
return this;
|
|
6209
|
+
}
|
|
6210
|
+
action(callback) {
|
|
6211
|
+
this.commandAction = callback;
|
|
6212
|
+
return this;
|
|
6213
|
+
}
|
|
6214
|
+
isMatched(name) {
|
|
6215
|
+
return this.name === name || this.aliasNames.includes(name);
|
|
6216
|
+
}
|
|
6217
|
+
get isDefaultCommand() {
|
|
6218
|
+
return this.name === "" || this.aliasNames.includes("!");
|
|
6219
|
+
}
|
|
6220
|
+
get isGlobalCommand() {
|
|
6221
|
+
return this instanceof GlobalCommand;
|
|
6222
|
+
}
|
|
6223
|
+
hasOption(name) {
|
|
6224
|
+
name = name.split(".")[0];
|
|
6225
|
+
return this.options.find((option) => {
|
|
6226
|
+
return option.names.includes(name);
|
|
6227
|
+
});
|
|
6228
|
+
}
|
|
6229
|
+
outputHelp() {
|
|
6230
|
+
const { name, commands } = this.cli;
|
|
6231
|
+
const {
|
|
6232
|
+
versionNumber,
|
|
6233
|
+
options: globalOptions,
|
|
6234
|
+
helpCallback
|
|
6235
|
+
} = this.cli.globalCommand;
|
|
6236
|
+
let sections = [
|
|
6237
|
+
{
|
|
6238
|
+
body: `${name}${versionNumber ? `/${versionNumber}` : ""}`
|
|
6239
|
+
}
|
|
6240
|
+
];
|
|
6241
|
+
sections.push({
|
|
6242
|
+
title: "Usage",
|
|
6243
|
+
body: ` $ ${name} ${this.usageText || this.rawName}`
|
|
6244
|
+
});
|
|
6245
|
+
const showCommands = (this.isGlobalCommand || this.isDefaultCommand) && commands.length > 0;
|
|
6246
|
+
if (showCommands) {
|
|
6247
|
+
const longestCommandName = findLongest(commands.map((command) => command.rawName));
|
|
6248
|
+
sections.push({
|
|
6249
|
+
title: "Commands",
|
|
6250
|
+
body: commands.map((command) => {
|
|
6251
|
+
return ` ${padRight(command.rawName, longestCommandName.length)} ${command.description}`;
|
|
6252
|
+
}).join("\n")
|
|
6253
|
+
});
|
|
6254
|
+
sections.push({
|
|
6255
|
+
title: `For more info, run any command with the \`--help\` flag`,
|
|
6256
|
+
body: commands.map((command) => ` $ ${name}${command.name === "" ? "" : ` ${command.name}`} --help`).join("\n")
|
|
6257
|
+
});
|
|
6258
|
+
}
|
|
6259
|
+
let options = this.isGlobalCommand ? globalOptions : [...this.options, ...globalOptions || []];
|
|
6260
|
+
if (!this.isGlobalCommand && !this.isDefaultCommand) {
|
|
6261
|
+
options = options.filter((option) => option.name !== "version");
|
|
6262
|
+
}
|
|
6263
|
+
if (options.length > 0) {
|
|
6264
|
+
const longestOptionName = findLongest(options.map((option) => option.rawName));
|
|
6265
|
+
sections.push({
|
|
6266
|
+
title: "Options",
|
|
6267
|
+
body: options.map((option) => {
|
|
6268
|
+
return ` ${padRight(option.rawName, longestOptionName.length)} ${option.description} ${option.config.default === void 0 ? "" : `(default: ${option.config.default})`}`;
|
|
6269
|
+
}).join("\n")
|
|
6270
|
+
});
|
|
6271
|
+
}
|
|
6272
|
+
if (this.examples.length > 0) {
|
|
6273
|
+
sections.push({
|
|
6274
|
+
title: "Examples",
|
|
6275
|
+
body: this.examples.map((example) => {
|
|
6276
|
+
if (typeof example === "function") {
|
|
6277
|
+
return example(name);
|
|
6278
|
+
}
|
|
6279
|
+
return example;
|
|
6280
|
+
}).join("\n")
|
|
6281
|
+
});
|
|
6282
|
+
}
|
|
6283
|
+
if (helpCallback) {
|
|
6284
|
+
sections = helpCallback(sections) || sections;
|
|
6285
|
+
}
|
|
6286
|
+
console.log(sections.map((section) => {
|
|
6287
|
+
return section.title ? `${section.title}:
|
|
6288
|
+
${section.body}` : section.body;
|
|
6289
|
+
}).join("\n\n"));
|
|
6290
|
+
}
|
|
6291
|
+
outputVersion() {
|
|
6292
|
+
const { name } = this.cli;
|
|
6293
|
+
const { versionNumber } = this.cli.globalCommand;
|
|
6294
|
+
if (versionNumber) {
|
|
6295
|
+
console.log(`${name}/${versionNumber} ${platformInfo}`);
|
|
6296
|
+
}
|
|
6297
|
+
}
|
|
6298
|
+
checkRequiredArgs() {
|
|
6299
|
+
const minimalArgsCount = this.args.filter((arg) => arg.required).length;
|
|
6300
|
+
if (this.cli.args.length < minimalArgsCount) {
|
|
6301
|
+
throw new CACError(`missing required args for command \`${this.rawName}\``);
|
|
6302
|
+
}
|
|
6303
|
+
}
|
|
6304
|
+
checkUnknownOptions() {
|
|
6305
|
+
const { options, globalCommand } = this.cli;
|
|
6306
|
+
if (!this.config.allowUnknownOptions) {
|
|
6307
|
+
for (const name of Object.keys(options)) {
|
|
6308
|
+
if (name !== "--" && !this.hasOption(name) && !globalCommand.hasOption(name)) {
|
|
6309
|
+
throw new CACError(`Unknown option \`${name.length > 1 ? `--${name}` : `-${name}`}\``);
|
|
6310
|
+
}
|
|
6311
|
+
}
|
|
6312
|
+
}
|
|
6313
|
+
}
|
|
6314
|
+
checkOptionValue() {
|
|
6315
|
+
const { options: parsedOptions, globalCommand } = this.cli;
|
|
6316
|
+
const options = [...globalCommand.options, ...this.options];
|
|
6317
|
+
for (const option of options) {
|
|
6318
|
+
const value = parsedOptions[option.name.split(".")[0]];
|
|
6319
|
+
if (option.required) {
|
|
6320
|
+
const hasNegated = options.some((o) => o.negated && o.names.includes(option.name));
|
|
6321
|
+
if (value === true || value === false && !hasNegated) {
|
|
6322
|
+
throw new CACError(`option \`${option.rawName}\` value is missing`);
|
|
6323
|
+
}
|
|
6324
|
+
}
|
|
6325
|
+
}
|
|
6326
|
+
}
|
|
6327
|
+
};
|
|
6328
|
+
var GlobalCommand = class extends Command {
|
|
6329
|
+
constructor(cli2) {
|
|
6330
|
+
super("@@global@@", "", {}, cli2);
|
|
6331
|
+
}
|
|
6332
|
+
};
|
|
6333
|
+
var __assign = Object.assign;
|
|
6334
|
+
var CAC = class extends EventEmitter {
|
|
6335
|
+
constructor(name = "") {
|
|
6336
|
+
super();
|
|
6337
|
+
this.name = name;
|
|
6338
|
+
this.commands = [];
|
|
6339
|
+
this.rawArgs = [];
|
|
6340
|
+
this.args = [];
|
|
6341
|
+
this.options = {};
|
|
6342
|
+
this.globalCommand = new GlobalCommand(this);
|
|
6343
|
+
this.globalCommand.usage("<command> [options]");
|
|
6344
|
+
}
|
|
6345
|
+
usage(text) {
|
|
6346
|
+
this.globalCommand.usage(text);
|
|
6347
|
+
return this;
|
|
6348
|
+
}
|
|
6349
|
+
command(rawName, description, config) {
|
|
6350
|
+
const command = new Command(rawName, description || "", config, this);
|
|
6351
|
+
command.globalCommand = this.globalCommand;
|
|
6352
|
+
this.commands.push(command);
|
|
6353
|
+
return command;
|
|
6354
|
+
}
|
|
6355
|
+
option(rawName, description, config) {
|
|
6356
|
+
this.globalCommand.option(rawName, description, config);
|
|
6357
|
+
return this;
|
|
6358
|
+
}
|
|
6359
|
+
help(callback) {
|
|
6360
|
+
this.globalCommand.option("-h, --help", "Display this message");
|
|
6361
|
+
this.globalCommand.helpCallback = callback;
|
|
6362
|
+
this.showHelpOnExit = true;
|
|
6363
|
+
return this;
|
|
6364
|
+
}
|
|
6365
|
+
version(version, customFlags = "-v, --version") {
|
|
6366
|
+
this.globalCommand.version(version, customFlags);
|
|
6367
|
+
this.showVersionOnExit = true;
|
|
6368
|
+
return this;
|
|
6369
|
+
}
|
|
6370
|
+
example(example) {
|
|
6371
|
+
this.globalCommand.example(example);
|
|
6372
|
+
return this;
|
|
6373
|
+
}
|
|
6374
|
+
outputHelp() {
|
|
6375
|
+
if (this.matchedCommand) {
|
|
6376
|
+
this.matchedCommand.outputHelp();
|
|
6377
|
+
} else {
|
|
6378
|
+
this.globalCommand.outputHelp();
|
|
6379
|
+
}
|
|
6380
|
+
}
|
|
6381
|
+
outputVersion() {
|
|
6382
|
+
this.globalCommand.outputVersion();
|
|
6383
|
+
}
|
|
6384
|
+
setParsedInfo({ args, options }, matchedCommand, matchedCommandName) {
|
|
6385
|
+
this.args = args;
|
|
6386
|
+
this.options = options;
|
|
6387
|
+
if (matchedCommand) {
|
|
6388
|
+
this.matchedCommand = matchedCommand;
|
|
6389
|
+
}
|
|
6390
|
+
if (matchedCommandName) {
|
|
6391
|
+
this.matchedCommandName = matchedCommandName;
|
|
6392
|
+
}
|
|
6393
|
+
return this;
|
|
6394
|
+
}
|
|
6395
|
+
unsetMatchedCommand() {
|
|
6396
|
+
this.matchedCommand = void 0;
|
|
6397
|
+
this.matchedCommandName = void 0;
|
|
6398
|
+
}
|
|
6399
|
+
parse(argv = processArgs, {
|
|
6400
|
+
run = true
|
|
6401
|
+
} = {}) {
|
|
6402
|
+
this.rawArgs = argv;
|
|
6403
|
+
if (!this.name) {
|
|
6404
|
+
this.name = argv[1] ? getFileName(argv[1]) : "cli";
|
|
6405
|
+
}
|
|
6406
|
+
let shouldParse = true;
|
|
6407
|
+
for (const command of this.commands) {
|
|
6408
|
+
const parsed = this.mri(argv.slice(2), command);
|
|
6409
|
+
const commandName = parsed.args[0];
|
|
6410
|
+
if (command.isMatched(commandName)) {
|
|
6411
|
+
shouldParse = false;
|
|
6412
|
+
const parsedInfo = __assign(__assign({}, parsed), {
|
|
6413
|
+
args: parsed.args.slice(1)
|
|
6414
|
+
});
|
|
6415
|
+
this.setParsedInfo(parsedInfo, command, commandName);
|
|
6416
|
+
this.emit(`command:${commandName}`, command);
|
|
6417
|
+
}
|
|
6418
|
+
}
|
|
6419
|
+
if (shouldParse) {
|
|
6420
|
+
for (const command of this.commands) {
|
|
6421
|
+
if (command.name === "") {
|
|
6422
|
+
shouldParse = false;
|
|
6423
|
+
const parsed = this.mri(argv.slice(2), command);
|
|
6424
|
+
this.setParsedInfo(parsed, command);
|
|
6425
|
+
this.emit(`command:!`, command);
|
|
6426
|
+
}
|
|
6427
|
+
}
|
|
6428
|
+
}
|
|
6429
|
+
if (shouldParse) {
|
|
6430
|
+
const parsed = this.mri(argv.slice(2));
|
|
6431
|
+
this.setParsedInfo(parsed);
|
|
6432
|
+
}
|
|
6433
|
+
if (this.options.help && this.showHelpOnExit) {
|
|
6434
|
+
this.outputHelp();
|
|
6435
|
+
run = false;
|
|
6436
|
+
this.unsetMatchedCommand();
|
|
6437
|
+
}
|
|
6438
|
+
if (this.options.version && this.showVersionOnExit && this.matchedCommandName == null) {
|
|
6439
|
+
this.outputVersion();
|
|
6440
|
+
run = false;
|
|
6441
|
+
this.unsetMatchedCommand();
|
|
6442
|
+
}
|
|
6443
|
+
const parsedArgv = { args: this.args, options: this.options };
|
|
6444
|
+
if (run) {
|
|
6445
|
+
this.runMatchedCommand();
|
|
6446
|
+
}
|
|
6447
|
+
if (!this.matchedCommand && this.args[0]) {
|
|
6448
|
+
this.emit("command:*");
|
|
6449
|
+
}
|
|
6450
|
+
return parsedArgv;
|
|
6451
|
+
}
|
|
6452
|
+
mri(argv, command) {
|
|
6453
|
+
const cliOptions = [
|
|
6454
|
+
...this.globalCommand.options,
|
|
6455
|
+
...command ? command.options : []
|
|
6456
|
+
];
|
|
6457
|
+
const mriOptions = getMriOptions(cliOptions);
|
|
6458
|
+
let argsAfterDoubleDashes = [];
|
|
6459
|
+
const doubleDashesIndex = argv.indexOf("--");
|
|
6460
|
+
if (doubleDashesIndex > -1) {
|
|
6461
|
+
argsAfterDoubleDashes = argv.slice(doubleDashesIndex + 1);
|
|
6462
|
+
argv = argv.slice(0, doubleDashesIndex);
|
|
6463
|
+
}
|
|
6464
|
+
let parsed = mri2(argv, mriOptions);
|
|
6465
|
+
parsed = Object.keys(parsed).reduce((res, name) => {
|
|
6466
|
+
return __assign(__assign({}, res), {
|
|
6467
|
+
[camelcaseOptionName(name)]: parsed[name]
|
|
6468
|
+
});
|
|
6469
|
+
}, { _: [] });
|
|
6470
|
+
const args = parsed._;
|
|
6471
|
+
const options = {
|
|
6472
|
+
"--": argsAfterDoubleDashes
|
|
6473
|
+
};
|
|
6474
|
+
const ignoreDefault = command && command.config.ignoreOptionDefaultValue ? command.config.ignoreOptionDefaultValue : this.globalCommand.config.ignoreOptionDefaultValue;
|
|
6475
|
+
let transforms = /* @__PURE__ */ Object.create(null);
|
|
6476
|
+
for (const cliOption of cliOptions) {
|
|
6477
|
+
if (!ignoreDefault && cliOption.config.default !== void 0) {
|
|
6478
|
+
for (const name of cliOption.names) {
|
|
6479
|
+
options[name] = cliOption.config.default;
|
|
6480
|
+
}
|
|
6481
|
+
}
|
|
6482
|
+
if (Array.isArray(cliOption.config.type)) {
|
|
6483
|
+
if (transforms[cliOption.name] === void 0) {
|
|
6484
|
+
transforms[cliOption.name] = /* @__PURE__ */ Object.create(null);
|
|
6485
|
+
transforms[cliOption.name]["shouldTransform"] = true;
|
|
6486
|
+
transforms[cliOption.name]["transformFunction"] = cliOption.config.type[0];
|
|
6487
|
+
}
|
|
6488
|
+
}
|
|
6489
|
+
}
|
|
6490
|
+
for (const key of Object.keys(parsed)) {
|
|
6491
|
+
if (key !== "_") {
|
|
6492
|
+
const keys = key.split(".");
|
|
6493
|
+
setDotProp(options, keys, parsed[key]);
|
|
6494
|
+
setByType(options, transforms);
|
|
6495
|
+
}
|
|
6496
|
+
}
|
|
6497
|
+
return {
|
|
6498
|
+
args,
|
|
6499
|
+
options
|
|
6500
|
+
};
|
|
6501
|
+
}
|
|
6502
|
+
runMatchedCommand() {
|
|
6503
|
+
const { args, options, matchedCommand: command } = this;
|
|
6504
|
+
if (!command || !command.commandAction)
|
|
6505
|
+
return;
|
|
6506
|
+
command.checkUnknownOptions();
|
|
6507
|
+
command.checkOptionValue();
|
|
6508
|
+
command.checkRequiredArgs();
|
|
6509
|
+
const actionArgs = [];
|
|
6510
|
+
command.args.forEach((arg, index) => {
|
|
6511
|
+
if (arg.variadic) {
|
|
6512
|
+
actionArgs.push(args.slice(index));
|
|
6513
|
+
} else {
|
|
6514
|
+
actionArgs.push(args[index]);
|
|
6515
|
+
}
|
|
6516
|
+
});
|
|
6517
|
+
actionArgs.push(options);
|
|
6518
|
+
return command.commandAction.apply(this, actionArgs);
|
|
6519
|
+
}
|
|
6520
|
+
};
|
|
6521
|
+
var cac = (name = "") => new CAC(name);
|
|
6522
|
+
|
|
6523
|
+
// src/cli/index.ts
|
|
6524
|
+
var cli = cac("faapi");
|
|
6525
|
+
cli.command("").alias("dev").action(async () => {
|
|
5778
6526
|
const { devCommand: devCommand2 } = await Promise.resolve().then(() => (init_devCommand(), devCommand_exports));
|
|
5779
6527
|
await devCommand2();
|
|
5780
|
-
}
|
|
5781
|
-
main().catch((err) => {
|
|
5782
|
-
console.error(err);
|
|
5783
|
-
process.exit(1);
|
|
5784
6528
|
});
|
|
6529
|
+
cli.command("build", "Build for production").action(async () => {
|
|
6530
|
+
const { buildCommand: buildCommand2 } = await Promise.resolve().then(() => (init_buildCommand(), buildCommand_exports));
|
|
6531
|
+
await buildCommand2();
|
|
6532
|
+
});
|
|
6533
|
+
cli.help();
|
|
6534
|
+
cli.parse();
|
|
5785
6535
|
/*! Bundled license information:
|
|
5786
6536
|
|
|
5787
6537
|
chokidar/esm/index.js:
|