@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/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
- * - `FAAPI_OUT_DIR`:产物输出目录,dev 固定为 '.faapi/dev',prod 默认 'dist'
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
- * 统一读取 `<outDir>/faapi-config.js` 产物:
999
- * - dev 模式:`faapi dev` 启动时由 `compileConfig` 生成 `.faapi/dev/faapi-config.js`
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 outDir 产物目录(如 'dist' 或 '.faapi/dev')
1008
+ * @param dist 产物目录(如 'dist' 或 '.faapi')
1010
1009
  * @returns 配置对象,无配置文件时返回 null
1011
1010
  */
1012
- declare function loadConfig(rootDir: string, outDir: string): Promise<Partial<FaapiConfig> | null>;
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
- /** 源码目录前缀,覆盖环境变量 FAAPI_APP_DIR,默认 'src' */
1084
- appDir?: string;
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, appDir, app });
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, outDir) {
1032
- const configProductPath = path2.resolve(rootDir, outDir, CONFIG_PRODUCT_FILE);
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] ${outDir}/${CONFIG_PRODUCT_FILE} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C \`faapi build\`\uFF08\u6216 \`faapi dev\`\uFF09\u751F\u6210\u4EA7\u7269\u3002`
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, appDir, outDir, rootDir) {
2459
+ function getSchemaOutputPath(sourceFile, dist, rootDir) {
2460
2460
  let rel = sourceFile.replace(/\\/g, "/");
2461
- if (appDir !== "." && rel.startsWith(`${appDir}/`)) {
2462
- rel = rel.slice(appDir.length + 1);
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, outDir, relDir, "zod.js");
2466
+ return path4.resolve(rootDir, dist, relDir, "zod.js");
2467
2467
  }
2468
- function getRuntimeSchemaPath(filePath, appDir, outDir, rootDir) {
2468
+ function getRuntimeSchemaPath(filePath, dist, rootDir) {
2469
2469
  let rel = filePath.replace(/\\/g, "/");
2470
- if (appDir !== "." && rel.startsWith(`${appDir}/`)) {
2471
- rel = rel.slice(appDir.length + 1);
2472
- } else if (rel.startsWith(`${outDir}/`)) {
2473
- rel = rel.slice(outDir.length + 1);
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, outDir, relDir, "zod.js");
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, appDir, outDir) {
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, appDir, outDir, rootDir);
2528
+ const outputPath = getSchemaOutputPath(relFile, dist, rootDir);
2529
2529
  const allTypes = allTypesByFile.get(filePath) ?? /* @__PURE__ */ new Map();
2530
2530
  let relForDir = relFile;
2531
- if (appDir !== "." && relForDir.startsWith(`${appDir}/`)) {
2532
- relForDir = relForDir.slice(appDir.length + 1);
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, outDir, HELPERS_FILENAME);
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
- appDir,
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
- appDir,
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, appDir, outDir, req, res, configMiddlewares, onError, config, globalMiddlewares, globalInjectors, bodyLimit) {
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, appDir, outDir, rootDir);
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 DEFAULT_OUT_DIR = "dist";
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 outDir = options?.outDir ?? process.env.FAAPI_OUT_DIR ?? DEFAULT_OUT_DIR;
2948
- const routesPath = path7.resolve(rootDir, outDir, ROUTES_FILE);
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] ${outDir}/${ROUTES_FILE} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C \`faapi build\`\uFF08\u6216 \`faapi dev\`\uFF09\u751F\u6210\u4EA7\u7269\u3002`
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, outDir);
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
- appDir,
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
- appDir,
3137
- outDir,
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, appDir = ".") {
3205
- const withoutPrefix = filePath.startsWith(appDir + "/") ? filePath.slice(appDir.length + 1) : filePath;
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
- function toProdAbsPath(sourceAbsPath, rootDir, appDir, prodDir) {
3210
+ var APP_DIR = "src";
3211
+ function toProdAbsPath(sourceAbsPath, rootDir, dist) {
3217
3212
  let rel = path8.relative(rootDir, sourceAbsPath).replace(/\\/g, "/");
3218
- if (appDir !== "." && rel.startsWith(`${appDir}/`)) {
3219
- rel = rel.slice(appDir.length + 1);
3213
+ if (rel.startsWith(`${APP_DIR}/`)) {
3214
+ rel = rel.slice(APP_DIR.length + 1);
3220
3215
  }
3221
- const prodRel = `${prodDir}/${rel.replace(/\.ts$/, ".js")}`;
3216
+ const prodRel = `${dist}/${rel.replace(/\.ts$/, ".js")}`;
3222
3217
  return path8.resolve(rootDir, prodRel);
3223
3218
  }
3224
- async function findMergedMiddlewares(routeFilePath, rootDir, appDir, prodDir) {
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 (prodDir) {
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, appDir, prodDir);
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, appDir, prodDir) {
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 = prodDir ? toProdAbsPath(absPath, rootDir, dir, prodDir) : absPath;
3313
- const urlPath = filePathToUrlPath(normalizedFile, dir);
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, dir, prodDir);
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.appDir, ctx.outDir);
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.appDir, ctx.outDir);
3354
+ await generateSchemaFiles(sorted, ctx.rootDir, ctx.dist);
3361
3355
  ctx.updateRoutes(sorted, reScanned.wsRoutes);
3362
3356
  };
3363
3357
  return devApp;