@faapi/faapi 0.0.0-canary.9a79cdf → 0.0.0-canary.b2b338c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +131 -149
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +8 -11
- package/dist/index.js +47 -53
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -563,9 +563,8 @@ interface LifecycleContext {
|
|
|
563
563
|
* 环境覆盖通过 faapi.config.{NODE_ENV}.ts 实现(如 faapi.config.production.ts)
|
|
564
564
|
*
|
|
565
565
|
* 框架元信息通过环境变量配置(不放在 config 内):
|
|
566
|
-
* - `FAAPI_APP_DIR`:源码目录前缀,默认 'src',设为 '.' 表示源码在项目根目录
|
|
567
566
|
* - `PORT`:服务端口,默认 3000
|
|
568
|
-
* - `
|
|
567
|
+
* - `FAAPI_DIST`:产物输出目录,dev 固定为 `.faapi`(不可修改),prod 默认为 `dist`(可通过 `--dist` 修改)
|
|
569
568
|
*/
|
|
570
569
|
interface FaapiConfig {
|
|
571
570
|
/** CORS 配置,false 禁用 */
|
|
@@ -995,8 +994,8 @@ declare function collectRouteSchemaSources(routes: RouteManifest, rootDir?: stri
|
|
|
995
994
|
/**
|
|
996
995
|
* 加载 faapi 配置文件
|
|
997
996
|
*
|
|
998
|
-
* 统一读取 `<
|
|
999
|
-
* - dev 模式:`faapi dev` 启动时由 `compileConfig` 生成 `.faapi/
|
|
997
|
+
* 统一读取 `<dist>/faapi-config.js` 产物:
|
|
998
|
+
* - dev 模式:`faapi dev` 启动时由 `compileConfig` 生成 `.faapi/faapi-config.js`
|
|
1000
999
|
* - prod 模式:`faapi build` 时由 `compileConfig` 生成 `dist/faapi-config.js`
|
|
1001
1000
|
*
|
|
1002
1001
|
* 产物由 `compileConfig` 在构建阶段合并 env 后固化,运行时不读源码、不现场编译、不按 env 合并。
|
|
@@ -1006,10 +1005,10 @@ declare function collectRouteSchemaSources(routes: RouteManifest, rootDir?: stri
|
|
|
1006
1005
|
* - 源码也无配置文件 → 返回 `null`(配置可选)
|
|
1007
1006
|
*
|
|
1008
1007
|
* @param rootDir 项目根目录
|
|
1009
|
-
* @param
|
|
1008
|
+
* @param dist 产物目录(如 'dist' 或 '.faapi')
|
|
1010
1009
|
* @returns 配置对象,无配置文件时返回 null
|
|
1011
1010
|
*/
|
|
1012
|
-
declare function loadConfig(rootDir: string,
|
|
1011
|
+
declare function loadConfig(rootDir: string, dist: string): Promise<Partial<FaapiConfig> | null>;
|
|
1013
1012
|
|
|
1014
1013
|
declare const VALIDATION_ERROR = "VALIDATION_ERROR";
|
|
1015
1014
|
declare const ROUTE_NOT_FOUND = "ROUTE_NOT_FOUND";
|
|
@@ -1080,10 +1079,8 @@ interface InjectResponse {
|
|
|
1080
1079
|
interface CreateAppOptions {
|
|
1081
1080
|
/** 项目根目录,默认 process.cwd() */
|
|
1082
1081
|
rootDir?: string;
|
|
1083
|
-
/**
|
|
1084
|
-
|
|
1085
|
-
/** 产物输出目录,覆盖环境变量 FAAPI_OUT_DIR,默认 'dist' */
|
|
1086
|
-
outDir?: string;
|
|
1082
|
+
/** 产物输出目录(如 dist 或 .faapi),覆盖环境变量 FAAPI_DIST,默认 'dist' */
|
|
1083
|
+
dist?: string;
|
|
1087
1084
|
/** 端口号,也可在 listen() 时传入;默认环境变量 PORT 或 3000 */
|
|
1088
1085
|
port?: number;
|
|
1089
1086
|
}
|
|
@@ -1131,7 +1128,7 @@ interface DevApp extends AppBase {
|
|
|
1131
1128
|
* // devCommand 内部
|
|
1132
1129
|
* const app = await createDevApp();
|
|
1133
1130
|
* await app.listen();
|
|
1134
|
-
* startWatcher({ rootDir,
|
|
1131
|
+
* startWatcher({ rootDir, app, devDist });
|
|
1135
1132
|
* ```
|
|
1136
1133
|
*/
|
|
1137
1134
|
declare function createDevApp(options?: CreateAppOptions): Promise<DevApp>;
|
package/dist/index.js
CHANGED
|
@@ -1028,8 +1028,8 @@ async function importWithCacheBust(filePath) {
|
|
|
1028
1028
|
|
|
1029
1029
|
// src/config/loadConfig.ts
|
|
1030
1030
|
var CONFIG_PRODUCT_FILE = "faapi-config.js";
|
|
1031
|
-
async function loadConfig(rootDir,
|
|
1032
|
-
const configProductPath = path2.resolve(rootDir,
|
|
1031
|
+
async function loadConfig(rootDir, dist) {
|
|
1032
|
+
const configProductPath = path2.resolve(rootDir, dist, CONFIG_PRODUCT_FILE);
|
|
1033
1033
|
if (fs.existsSync(configProductPath)) {
|
|
1034
1034
|
const module = await importWithCacheBust(configProductPath);
|
|
1035
1035
|
return module.default ?? {};
|
|
@@ -1037,7 +1037,7 @@ async function loadConfig(rootDir, outDir) {
|
|
|
1037
1037
|
const hasSourceConfig = fs.existsSync(path2.join(rootDir, "faapi.config.ts")) || fs.existsSync(path2.join(rootDir, "faapi.config.js"));
|
|
1038
1038
|
if (hasSourceConfig) {
|
|
1039
1039
|
throw new Error(
|
|
1040
|
-
`[faapi] ${
|
|
1040
|
+
`[faapi] ${dist}/${CONFIG_PRODUCT_FILE} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C \`faapi build\`\uFF08\u6216 \`faapi dev\`\uFF09\u751F\u6210\u4EA7\u7269\u3002`
|
|
1041
1041
|
);
|
|
1042
1042
|
}
|
|
1043
1043
|
return null;
|
|
@@ -2456,25 +2456,25 @@ function generateZodSchemaSource(typeInfo, resolveType, exportName, coerce = fal
|
|
|
2456
2456
|
}
|
|
2457
2457
|
|
|
2458
2458
|
// src/cli/generateSchemaFiles.ts
|
|
2459
|
-
function getSchemaOutputPath(sourceFile,
|
|
2459
|
+
function getSchemaOutputPath(sourceFile, dist, rootDir) {
|
|
2460
2460
|
let rel = sourceFile.replace(/\\/g, "/");
|
|
2461
|
-
if (
|
|
2462
|
-
rel = rel.slice(
|
|
2461
|
+
if (rel.startsWith("src/")) {
|
|
2462
|
+
rel = rel.slice(4);
|
|
2463
2463
|
}
|
|
2464
2464
|
const idx = rel.lastIndexOf("/");
|
|
2465
2465
|
const relDir = idx >= 0 ? rel.slice(0, idx) : "";
|
|
2466
|
-
return path4.resolve(rootDir,
|
|
2466
|
+
return path4.resolve(rootDir, dist, relDir, "zod.js");
|
|
2467
2467
|
}
|
|
2468
|
-
function getRuntimeSchemaPath(filePath,
|
|
2468
|
+
function getRuntimeSchemaPath(filePath, dist, rootDir) {
|
|
2469
2469
|
let rel = filePath.replace(/\\/g, "/");
|
|
2470
|
-
if (
|
|
2471
|
-
rel = rel.slice(
|
|
2472
|
-
} else if (rel.startsWith(`${
|
|
2473
|
-
rel = rel.slice(
|
|
2470
|
+
if (rel.startsWith("src/")) {
|
|
2471
|
+
rel = rel.slice(4);
|
|
2472
|
+
} else if (rel.startsWith(`${dist}/`)) {
|
|
2473
|
+
rel = rel.slice(dist.length + 1);
|
|
2474
2474
|
}
|
|
2475
2475
|
const idx = rel.lastIndexOf("/");
|
|
2476
2476
|
const relDir = idx >= 0 ? rel.slice(0, idx) : "";
|
|
2477
|
-
return path4.resolve(rootDir,
|
|
2477
|
+
return path4.resolve(rootDir, dist, relDir, "zod.js");
|
|
2478
2478
|
}
|
|
2479
2479
|
function getHelpersImportPath(relDir) {
|
|
2480
2480
|
if (!relDir) return `./${HELPERS_FILENAME}`;
|
|
@@ -2510,7 +2510,7 @@ function generateSchemaFileSource(sources, allTypes, helpersImportPath) {
|
|
|
2510
2510
|
lines.push(...schemaBlocks);
|
|
2511
2511
|
return lines.join("\n").replace(/\n+$/, "\n");
|
|
2512
2512
|
}
|
|
2513
|
-
async function generateSchemaFiles(routes, rootDir,
|
|
2513
|
+
async function generateSchemaFiles(routes, rootDir, dist) {
|
|
2514
2514
|
if (routes.length === 0) return;
|
|
2515
2515
|
const { sources, allTypesByFile } = collectRouteSchemaSources(routes, rootDir);
|
|
2516
2516
|
const sourcesByFile = /* @__PURE__ */ new Map();
|
|
@@ -2525,11 +2525,11 @@ async function generateSchemaFiles(routes, rootDir, appDir, outDir) {
|
|
|
2525
2525
|
const fileEntries = [];
|
|
2526
2526
|
for (const [filePath, fileSources] of sourcesByFile) {
|
|
2527
2527
|
const relFile = path4.relative(rootDir, filePath).replace(/\\/g, "/");
|
|
2528
|
-
const outputPath = getSchemaOutputPath(relFile,
|
|
2528
|
+
const outputPath = getSchemaOutputPath(relFile, dist, rootDir);
|
|
2529
2529
|
const allTypes = allTypesByFile.get(filePath) ?? /* @__PURE__ */ new Map();
|
|
2530
2530
|
let relForDir = relFile;
|
|
2531
|
-
if (
|
|
2532
|
-
relForDir = relForDir.slice(
|
|
2531
|
+
if (relForDir.startsWith("src/")) {
|
|
2532
|
+
relForDir = relForDir.slice(4);
|
|
2533
2533
|
}
|
|
2534
2534
|
const dirIdx = relForDir.lastIndexOf("/");
|
|
2535
2535
|
const zodRelDir = dirIdx >= 0 ? relForDir.slice(0, dirIdx) : "";
|
|
@@ -2539,7 +2539,7 @@ async function generateSchemaFiles(routes, rootDir, appDir, outDir) {
|
|
|
2539
2539
|
}
|
|
2540
2540
|
const allSourceCode = fileEntries.map((e) => e.source).join("\n");
|
|
2541
2541
|
if (usesCoerceHelpers(allSourceCode)) {
|
|
2542
|
-
const helpersPath = path4.resolve(rootDir,
|
|
2542
|
+
const helpersPath = path4.resolve(rootDir, dist, HELPERS_FILENAME);
|
|
2543
2543
|
await writeSchemaFile(helpersPath, generateHelpersFileSource());
|
|
2544
2544
|
}
|
|
2545
2545
|
await Promise.all(
|
|
@@ -2616,8 +2616,7 @@ function createServer(options) {
|
|
|
2616
2616
|
const {
|
|
2617
2617
|
routes,
|
|
2618
2618
|
rootDir,
|
|
2619
|
-
|
|
2620
|
-
outDir,
|
|
2619
|
+
dist,
|
|
2621
2620
|
cors: corsOption,
|
|
2622
2621
|
onError,
|
|
2623
2622
|
config,
|
|
@@ -2655,8 +2654,7 @@ function createServer(options) {
|
|
|
2655
2654
|
handleRequest(
|
|
2656
2655
|
currentRoutes,
|
|
2657
2656
|
rootDir,
|
|
2658
|
-
|
|
2659
|
-
outDir,
|
|
2657
|
+
dist,
|
|
2660
2658
|
req,
|
|
2661
2659
|
res,
|
|
2662
2660
|
configMiddlewares,
|
|
@@ -2675,7 +2673,7 @@ function createServer(options) {
|
|
|
2675
2673
|
}
|
|
2676
2674
|
return { server, routesRef };
|
|
2677
2675
|
}
|
|
2678
|
-
async function handleRequest(routes, rootDir,
|
|
2676
|
+
async function handleRequest(routes, rootDir, dist, req, res, configMiddlewares, onError, config, globalMiddlewares, globalInjectors, bodyLimit) {
|
|
2679
2677
|
const request = toWebRequest(req, bodyLimit);
|
|
2680
2678
|
const method = request.method.toUpperCase();
|
|
2681
2679
|
const urlPath = new URL(request.url).pathname;
|
|
@@ -2696,7 +2694,7 @@ async function handleRequest(routes, rootDir, appDir, outDir, req, res, configMi
|
|
|
2696
2694
|
const routeModule = await loadRouteModule(absoluteFilePath, route.method);
|
|
2697
2695
|
const input = await resolveInput(route.method, request);
|
|
2698
2696
|
const inputType = getInputTypeForMethod(route.method);
|
|
2699
|
-
const schemaPath = getRuntimeSchemaPath(route.filePath,
|
|
2697
|
+
const schemaPath = getRuntimeSchemaPath(route.filePath, dist, rootDir);
|
|
2700
2698
|
const result = await validateInput(schemaPath, route.method, inputType, input);
|
|
2701
2699
|
if (!result.valid) {
|
|
2702
2700
|
throw new ValidationError("\u53C2\u6570\u6821\u9A8C\u5931\u8D25", result.issues);
|
|
@@ -2923,10 +2921,10 @@ function resolveDeclaration(decl) {
|
|
|
2923
2921
|
}
|
|
2924
2922
|
|
|
2925
2923
|
// src/cli/createAppCore.ts
|
|
2926
|
-
var
|
|
2927
|
-
var DEFAULT_APP_DIR = "src";
|
|
2924
|
+
var DEFAULT_DIST = "dist";
|
|
2928
2925
|
var DEFAULT_PORT = 3e3;
|
|
2929
2926
|
var ROUTES_FILE = "faapi-routes.js";
|
|
2927
|
+
var PATTERNS = ["src/api/**/*.ts"];
|
|
2930
2928
|
var FAAPI_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
2931
2929
|
"cors",
|
|
2932
2930
|
"lifecycle",
|
|
@@ -2944,16 +2942,14 @@ function isFaapiConfigKey(key) {
|
|
|
2944
2942
|
}
|
|
2945
2943
|
async function createAppBase(options) {
|
|
2946
2944
|
const rootDir = options?.rootDir ?? process.cwd();
|
|
2947
|
-
const
|
|
2948
|
-
const routesPath = path7.resolve(rootDir,
|
|
2945
|
+
const dist = options?.dist ?? process.env.FAAPI_DIST ?? DEFAULT_DIST;
|
|
2946
|
+
const routesPath = path7.resolve(rootDir, dist, ROUTES_FILE);
|
|
2949
2947
|
if (!fs4.existsSync(routesPath)) {
|
|
2950
2948
|
throw new Error(
|
|
2951
|
-
`[faapi] ${
|
|
2949
|
+
`[faapi] ${dist}/${ROUTES_FILE} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C \`faapi build\`\uFF08\u6216 \`faapi dev\`\uFF09\u751F\u6210\u4EA7\u7269\u3002`
|
|
2952
2950
|
);
|
|
2953
2951
|
}
|
|
2954
|
-
const config = await loadConfig(rootDir,
|
|
2955
|
-
const appDir = options?.appDir ?? process.env.FAAPI_APP_DIR ?? DEFAULT_APP_DIR;
|
|
2956
|
-
const patterns = appDir === "." ? ["api/**/*.ts"] : [`${appDir}/api/**/*.ts`];
|
|
2952
|
+
const config = await loadConfig(rootDir, dist);
|
|
2957
2953
|
const serialized = await importWithCacheBust(routesPath);
|
|
2958
2954
|
const hydrated = await hydrateRoutes(serialized);
|
|
2959
2955
|
let sorted = sortRoutes(hydrated.routes);
|
|
@@ -2971,8 +2967,7 @@ async function createAppBase(options) {
|
|
|
2971
2967
|
const { server, routesRef } = createServer({
|
|
2972
2968
|
routes: sorted,
|
|
2973
2969
|
rootDir,
|
|
2974
|
-
|
|
2975
|
-
outDir,
|
|
2970
|
+
dist,
|
|
2976
2971
|
cors: config?.cors ?? true,
|
|
2977
2972
|
onError: config?.lifecycle?.onError,
|
|
2978
2973
|
config: config ?? void 0,
|
|
@@ -3133,9 +3128,8 @@ async function createAppBase(options) {
|
|
|
3133
3128
|
};
|
|
3134
3129
|
const ctx = {
|
|
3135
3130
|
rootDir,
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
patterns,
|
|
3131
|
+
dist,
|
|
3132
|
+
patterns: PATTERNS,
|
|
3139
3133
|
server,
|
|
3140
3134
|
routesRef,
|
|
3141
3135
|
config,
|
|
@@ -3201,8 +3195,8 @@ function isCatchAllSegment(segment) {
|
|
|
3201
3195
|
function isRouteGroup(segment) {
|
|
3202
3196
|
return /^\(.+\)$/.test(segment);
|
|
3203
3197
|
}
|
|
3204
|
-
function filePathToUrlPath(filePath
|
|
3205
|
-
const withoutPrefix = filePath.startsWith(
|
|
3198
|
+
function filePathToUrlPath(filePath) {
|
|
3199
|
+
const withoutPrefix = filePath.startsWith("src/") ? filePath.slice(4) : filePath;
|
|
3206
3200
|
const lastSlashIndex = withoutPrefix.lastIndexOf("/");
|
|
3207
3201
|
const dirPath = lastSlashIndex === -1 ? "" : withoutPrefix.slice(0, lastSlashIndex);
|
|
3208
3202
|
if (!dirPath) {
|
|
@@ -3213,24 +3207,25 @@ function filePathToUrlPath(filePath, appDir = ".") {
|
|
|
3213
3207
|
}
|
|
3214
3208
|
|
|
3215
3209
|
// src/router/scanRoutes.ts
|
|
3216
|
-
|
|
3210
|
+
var APP_DIR = "src";
|
|
3211
|
+
function toProdAbsPath(sourceAbsPath, rootDir, dist) {
|
|
3217
3212
|
let rel = path8.relative(rootDir, sourceAbsPath).replace(/\\/g, "/");
|
|
3218
|
-
if (
|
|
3219
|
-
rel = rel.slice(
|
|
3213
|
+
if (rel.startsWith(`${APP_DIR}/`)) {
|
|
3214
|
+
rel = rel.slice(APP_DIR.length + 1);
|
|
3220
3215
|
}
|
|
3221
|
-
const prodRel = `${
|
|
3216
|
+
const prodRel = `${dist}/${rel.replace(/\.ts$/, ".js")}`;
|
|
3222
3217
|
return path8.resolve(rootDir, prodRel);
|
|
3223
3218
|
}
|
|
3224
|
-
async function findMergedMiddlewares(routeFilePath, rootDir,
|
|
3219
|
+
async function findMergedMiddlewares(routeFilePath, rootDir, dist) {
|
|
3225
3220
|
const routeDir = path8.dirname(routeFilePath);
|
|
3226
3221
|
const resolvedRoot = path8.resolve(rootDir);
|
|
3227
3222
|
const mwPaths = [];
|
|
3228
3223
|
let currentDir = path8.resolve(rootDir, routeDir);
|
|
3229
3224
|
while (true) {
|
|
3230
|
-
if (
|
|
3225
|
+
if (dist) {
|
|
3231
3226
|
const mwPath = path8.join(currentDir, "middlewares.js");
|
|
3232
3227
|
const absMwPath = path8.resolve(rootDir, mwPath);
|
|
3233
|
-
const prodAbsMwPath = toProdAbsPath(absMwPath, rootDir,
|
|
3228
|
+
const prodAbsMwPath = toProdAbsPath(absMwPath, rootDir, dist);
|
|
3234
3229
|
if (fs5.existsSync(prodAbsMwPath)) {
|
|
3235
3230
|
mwPaths.push(prodAbsMwPath);
|
|
3236
3231
|
}
|
|
@@ -3295,8 +3290,7 @@ async function hasWsExport(absPath) {
|
|
|
3295
3290
|
return false;
|
|
3296
3291
|
}
|
|
3297
3292
|
}
|
|
3298
|
-
async function scanRoutes(rootDir, patterns,
|
|
3299
|
-
const dir = appDir ?? ".";
|
|
3293
|
+
async function scanRoutes(rootDir, patterns, dist) {
|
|
3300
3294
|
const files = await fg(patterns, {
|
|
3301
3295
|
cwd: rootDir,
|
|
3302
3296
|
onlyFiles: true,
|
|
@@ -3309,12 +3303,12 @@ async function scanRoutes(rootDir, patterns, appDir, prodDir) {
|
|
|
3309
3303
|
const fileName = normalizedFile.split("/").pop();
|
|
3310
3304
|
if (fileName === "handler.ts" || fileName === "handler.js") {
|
|
3311
3305
|
const absPath = path8.resolve(rootDir, normalizedFile);
|
|
3312
|
-
const importPath =
|
|
3313
|
-
const urlPath = filePathToUrlPath(normalizedFile
|
|
3306
|
+
const importPath = dist ? toProdAbsPath(absPath, rootDir, dist) : absPath;
|
|
3307
|
+
const urlPath = filePathToUrlPath(normalizedFile);
|
|
3314
3308
|
const paramNames = extractParamNames(urlPath);
|
|
3315
3309
|
const isDynamic = paramNames.length > 0;
|
|
3316
3310
|
const isCatchAll = normalizedFile.split("/").some(isCatchAllSegment);
|
|
3317
|
-
const middlewareBundle = await findMergedMiddlewares(normalizedFile, rootDir,
|
|
3311
|
+
const middlewareBundle = await findMergedMiddlewares(normalizedFile, rootDir, dist);
|
|
3318
3312
|
const methods = await extractMethodsFromHandler(importPath);
|
|
3319
3313
|
for (const method of methods) {
|
|
3320
3314
|
routes.push({
|
|
@@ -3355,9 +3349,9 @@ async function createDevApp(options) {
|
|
|
3355
3349
|
invalidateMiddlewareCache();
|
|
3356
3350
|
invalidateProgramCache();
|
|
3357
3351
|
invalidateSchemaCache();
|
|
3358
|
-
const reScanned = await scanRoutes(ctx.rootDir, ctx.patterns, ctx.
|
|
3352
|
+
const reScanned = await scanRoutes(ctx.rootDir, ctx.patterns, ctx.dist);
|
|
3359
3353
|
const sorted = sortRoutes(reScanned.routes);
|
|
3360
|
-
await generateSchemaFiles(sorted, ctx.rootDir, ctx.
|
|
3354
|
+
await generateSchemaFiles(sorted, ctx.rootDir, ctx.dist);
|
|
3361
3355
|
ctx.updateRoutes(sorted, reScanned.wsRoutes);
|
|
3362
3356
|
};
|
|
3363
3357
|
return devApp;
|