@vercel/fs-detectors 5.8.15 → 5.8.16

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.
@@ -126,12 +126,14 @@ async function inferWorkspaceFromNearestManifest({
126
126
  async function detectFrameworkFromWorkspace({
127
127
  fs,
128
128
  workspace,
129
- serviceName
129
+ serviceName,
130
+ runtime
130
131
  }) {
131
132
  const serviceFs = workspace === "." ? fs : fs.chdir(workspace);
133
+ const frameworkCandidates = (0, import_utils.filterFrameworksByRuntime)(import_frameworks.default, runtime);
132
134
  const frameworks = await (0, import_detect_framework.detectFrameworks)({
133
135
  fs: serviceFs,
134
- frameworkList: import_frameworks.default
136
+ frameworkList: frameworkCandidates
135
137
  });
136
138
  if (frameworks.length > 1) {
137
139
  const frameworkNames = frameworks.map((f) => f.name).join(", ");
@@ -212,6 +214,16 @@ function validateServiceConfig(name, config) {
212
214
  serviceName: name
213
215
  };
214
216
  }
217
+ if (config.runtime && config.framework) {
218
+ const frameworkRuntime = (0, import_utils.inferRuntimeFromFramework)(config.framework);
219
+ if (frameworkRuntime && frameworkRuntime !== config.runtime) {
220
+ return {
221
+ code: "RUNTIME_FRAMEWORK_MISMATCH",
222
+ message: `Service "${name}" has conflicting runtime/framework: runtime "${config.runtime}" is incompatible with framework "${config.framework}" (runtime "${frameworkRuntime}").`,
223
+ serviceName: name
224
+ };
225
+ }
226
+ }
215
227
  const hasFramework = Boolean(config.framework);
216
228
  const hasBuilderOrRuntime = Boolean(config.builder || config.runtime);
217
229
  const hasEntrypoint = Boolean(config.entrypoint);
@@ -395,30 +407,56 @@ async function resolveAllConfiguredServices(services, fs, routePrefixSource = "c
395
407
  }
396
408
  }
397
409
  let resolvedConfig = serviceConfig;
398
- const shouldDetectFramework = !serviceConfig.framework && Boolean(resolvedEntrypoint?.isDirectory);
399
- if (shouldDetectFramework) {
400
- const workspace = resolvedEntrypoint.normalized;
401
- const { framework, error } = await detectFrameworkFromWorkspace({
402
- fs,
403
- workspace,
404
- serviceName: name
405
- });
406
- if (error) {
407
- errors.push(error);
408
- continue;
409
- }
410
- if (!framework) {
411
- errors.push({
412
- code: "MISSING_SERVICE_FRAMEWORK",
413
- message: `Service "${name}" uses directory entrypoint "${serviceConfig.entrypoint}" but no framework could be detected in "${workspace}". Specify "framework" explicitly or use a file entrypoint.`,
410
+ if (!serviceConfig.framework && resolvedEntrypoint) {
411
+ if (resolvedEntrypoint.isDirectory) {
412
+ const workspace = resolvedEntrypoint.normalized;
413
+ const { framework, error } = await detectFrameworkFromWorkspace({
414
+ fs,
415
+ workspace,
414
416
  serviceName: name
415
417
  });
416
- continue;
418
+ if (error) {
419
+ errors.push(error);
420
+ continue;
421
+ }
422
+ if (!framework) {
423
+ errors.push({
424
+ code: "MISSING_SERVICE_FRAMEWORK",
425
+ message: `Service "${name}" uses directory entrypoint "${serviceConfig.entrypoint}" but no framework could be detected in "${workspace}". Specify "framework" explicitly or use a file entrypoint.`,
426
+ serviceName: name
427
+ });
428
+ continue;
429
+ }
430
+ resolvedConfig = {
431
+ ...resolvedConfig,
432
+ framework
433
+ };
434
+ } else {
435
+ const inferredRuntime = (0, import_utils.inferServiceRuntime)({
436
+ ...serviceConfig,
437
+ entrypoint: resolvedEntrypoint.normalized
438
+ });
439
+ if (inferredRuntime) {
440
+ const inferredWorkspace = await inferWorkspaceFromNearestManifest({
441
+ fs,
442
+ entrypoint: resolvedEntrypoint.normalized,
443
+ runtime: inferredRuntime
444
+ });
445
+ const workspace = inferredWorkspace ?? import_path.posix.dirname(resolvedEntrypoint.normalized);
446
+ const detection = await detectFrameworkFromWorkspace({
447
+ fs,
448
+ workspace,
449
+ serviceName: name,
450
+ runtime: inferredRuntime
451
+ });
452
+ if (detection.framework) {
453
+ resolvedConfig = {
454
+ ...resolvedConfig,
455
+ framework: detection.framework
456
+ };
457
+ }
458
+ }
417
459
  }
418
- resolvedConfig = {
419
- ...serviceConfig,
420
- framework
421
- };
422
460
  }
423
461
  const service = await resolveConfiguredService({
424
462
  name,
@@ -17,6 +17,18 @@ export declare function isStaticBuild(service: ResolvedService): boolean;
17
17
  * rewrites for them — instead, we rely on the builder's own `routes[]`.
18
18
  */
19
19
  export declare function isRouteOwningBuilder(service: ResolvedService): boolean;
20
+ /**
21
+ * Infer runtime from a framework slug.
22
+ *
23
+ * Examples:
24
+ * - `python` -> `python`
25
+ * - `fastapi` -> `python`
26
+ * - `express` -> `node`
27
+ */
28
+ export declare function inferRuntimeFromFramework(framework: string | null | undefined): ServiceRuntime | undefined;
29
+ export declare function filterFrameworksByRuntime<T extends {
30
+ slug?: string | null;
31
+ }>(frameworks: readonly T[], runtime?: ServiceRuntime): T[];
20
32
  /**
21
33
  * Infer runtime from available service configuration.
22
34
  *
@@ -19,9 +19,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
19
  var utils_exports = {};
20
20
  __export(utils_exports, {
21
21
  INTERNAL_SERVICE_PREFIX: () => INTERNAL_SERVICE_PREFIX,
22
+ filterFrameworksByRuntime: () => filterFrameworksByRuntime,
22
23
  getBuilderForRuntime: () => getBuilderForRuntime,
23
24
  getInternalServiceFunctionPath: () => getInternalServiceFunctionPath,
24
25
  hasFile: () => hasFile,
26
+ inferRuntimeFromFramework: () => inferRuntimeFromFramework,
25
27
  inferServiceRuntime: () => inferServiceRuntime,
26
28
  isRouteOwningBuilder: () => isRouteOwningBuilder,
27
29
  isStaticBuild: () => isStaticBuild,
@@ -54,19 +56,37 @@ function isStaticBuild(service) {
54
56
  function isRouteOwningBuilder(service) {
55
57
  return import_types.ROUTE_OWNING_BUILDERS.has(service.builder.use);
56
58
  }
57
- function inferServiceRuntime(config) {
58
- if (config.runtime && config.runtime in import_types.RUNTIME_BUILDERS) {
59
- return config.runtime;
59
+ function inferRuntimeFromFramework(framework) {
60
+ if (!framework) {
61
+ return void 0;
60
62
  }
61
- if (config.framework && config.framework in import_types.RUNTIME_BUILDERS) {
62
- return config.framework;
63
+ if (framework in import_types.RUNTIME_BUILDERS) {
64
+ return framework;
63
65
  }
64
- if ((0, import_framework_helpers.isPythonFramework)(config.framework)) {
66
+ if ((0, import_framework_helpers.isPythonFramework)(framework)) {
65
67
  return "python";
66
68
  }
67
- if ((0, import_framework_helpers.isBackendFramework)(config.framework)) {
69
+ if ((0, import_framework_helpers.isBackendFramework)(framework)) {
68
70
  return "node";
69
71
  }
72
+ return void 0;
73
+ }
74
+ function filterFrameworksByRuntime(frameworks, runtime) {
75
+ if (!runtime) {
76
+ return [...frameworks];
77
+ }
78
+ return frameworks.filter(
79
+ (framework) => inferRuntimeFromFramework(framework.slug) === runtime
80
+ );
81
+ }
82
+ function inferServiceRuntime(config) {
83
+ if (config.runtime && config.runtime in import_types.RUNTIME_BUILDERS) {
84
+ return config.runtime;
85
+ }
86
+ const frameworkRuntime = inferRuntimeFromFramework(config.framework);
87
+ if (frameworkRuntime) {
88
+ return frameworkRuntime;
89
+ }
70
90
  if (config.builder) {
71
91
  for (const [runtime, builderName] of Object.entries(import_types.RUNTIME_BUILDERS)) {
72
92
  if (config.builder === builderName) {
@@ -105,9 +125,11 @@ async function readVercelConfig(fs) {
105
125
  // Annotate the CommonJS export names for ESM import in node:
106
126
  0 && (module.exports = {
107
127
  INTERNAL_SERVICE_PREFIX,
128
+ filterFrameworksByRuntime,
108
129
  getBuilderForRuntime,
109
130
  getInternalServiceFunctionPath,
110
131
  hasFile,
132
+ inferRuntimeFromFramework,
111
133
  inferServiceRuntime,
112
134
  isRouteOwningBuilder,
113
135
  isStaticBuild,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/fs-detectors",
3
- "version": "5.8.15",
3
+ "version": "5.8.16",
4
4
  "description": "Vercel filesystem detectors",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -20,8 +20,8 @@
20
20
  "minimatch": "3.1.2",
21
21
  "semver": "6.3.1",
22
22
  "@vercel/error-utils": "2.0.3",
23
- "@vercel/frameworks": "3.19.1",
24
- "@vercel/routing-utils": "6.0.1"
23
+ "@vercel/frameworks": "3.20.0",
24
+ "@vercel/routing-utils": "6.0.2"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/glob": "7.2.0",