@vercel/fs-detectors 6.5.0 → 6.6.0

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.
@@ -1,7 +1,13 @@
1
+ import type { DetectEntrypointFn } from '@vercel/build-utils';
1
2
  import type { DetectorFilesystem } from '../detectors/filesystem';
2
3
  import type { ExperimentalServices, ServiceDetectionError, ServiceDetectionWarning } from './types';
3
4
  export interface AutoDetectOptions {
4
5
  fs: DetectorFilesystem;
6
+ /**
7
+ * Optional callback used to enrich runtime services with a normalized
8
+ * entrypoint (file path or `module:attr` reference).
9
+ */
10
+ detectEntrypoint?: DetectEntrypointFn;
5
11
  }
6
12
  export interface AutoDetectResult {
7
13
  services: ExperimentalServices | null;
@@ -30,7 +30,7 @@ const BACKEND_DIR = "backend";
30
30
  const SERVICES_DIR = "services";
31
31
  const FRONTEND_LOCATIONS = [FRONTEND_DIR, APPS_WEB_DIR];
32
32
  async function autoDetectServices(options) {
33
- const { fs } = options;
33
+ const { fs, detectEntrypoint } = options;
34
34
  const rootFrameworks = await (0, import_detect_framework.detectFrameworks)({
35
35
  fs,
36
36
  frameworkList: import_frameworks.frameworkList
@@ -49,7 +49,7 @@ async function autoDetectServices(options) {
49
49
  };
50
50
  }
51
51
  if (rootFrameworks.length === 1) {
52
- return detectServicesAtRoot(fs, rootFrameworks[0]);
52
+ return detectServicesAtRoot(fs, rootFrameworks[0], detectEntrypoint);
53
53
  }
54
54
  for (const frontendLocation of FRONTEND_LOCATIONS) {
55
55
  const hasFrontendDir = await fs.hasPath(frontendLocation);
@@ -78,7 +78,8 @@ async function autoDetectServices(options) {
78
78
  return detectServicesFrontendSubdir(
79
79
  fs,
80
80
  frontendFrameworks[0],
81
- frontendLocation
81
+ frontendLocation,
82
+ detectEntrypoint
82
83
  );
83
84
  }
84
85
  }
@@ -93,13 +94,13 @@ async function autoDetectServices(options) {
93
94
  ]
94
95
  };
95
96
  }
96
- async function detectServicesAtRoot(fs, rootFramework) {
97
+ async function detectServicesAtRoot(fs, rootFramework, detectEntrypoint) {
97
98
  const services = {};
98
99
  services.frontend = {
99
100
  framework: rootFramework.slug ?? void 0,
100
101
  routePrefix: "/"
101
102
  };
102
- const backendResult = await detectBackendServices(fs);
103
+ const backendResult = await detectBackendServices(fs, detectEntrypoint);
103
104
  if (backendResult.error) {
104
105
  return {
105
106
  services: null,
@@ -121,15 +122,15 @@ async function detectServicesAtRoot(fs, rootFramework) {
121
122
  errors: []
122
123
  };
123
124
  }
124
- async function detectServicesFrontendSubdir(fs, frontendFramework, frontendLocation) {
125
+ async function detectServicesFrontendSubdir(fs, frontendFramework, frontendLocation, detectEntrypoint) {
125
126
  const services = {};
126
127
  const serviceName = frontendLocation.split("/").pop() || "frontend";
127
128
  services[serviceName] = {
128
129
  framework: frontendFramework.slug ?? void 0,
129
- entrypoint: frontendLocation,
130
+ root: frontendLocation,
130
131
  routePrefix: "/"
131
132
  };
132
- const backendResult = await detectBackendServices(fs);
133
+ const backendResult = await detectBackendServices(fs, detectEntrypoint);
133
134
  if (backendResult.error) {
134
135
  return {
135
136
  services: null,
@@ -156,16 +157,24 @@ async function detectServicesFrontendSubdir(fs, frontendFramework, frontendLocat
156
157
  errors: []
157
158
  };
158
159
  }
159
- async function detectBackendServices(fs) {
160
+ async function detectBackendServices(fs, detectEntrypoint) {
160
161
  const services = {};
161
- const backendResult = await detectServiceInDir(fs, BACKEND_DIR, "backend");
162
+ const backendResult = await detectServiceInDir(
163
+ fs,
164
+ BACKEND_DIR,
165
+ "backend",
166
+ detectEntrypoint
167
+ );
162
168
  if (backendResult.error) {
163
169
  return { services: {}, error: backendResult.error };
164
170
  }
165
171
  if (backendResult.service) {
166
172
  services.backend = backendResult.service;
167
173
  }
168
- const multiServicesResult = await detectServicesDirectory(fs);
174
+ const multiServicesResult = await detectServicesDirectory(
175
+ fs,
176
+ detectEntrypoint
177
+ );
169
178
  if (multiServicesResult.error) {
170
179
  return { services: {}, error: multiServicesResult.error };
171
180
  }
@@ -184,7 +193,7 @@ async function detectBackendServices(fs) {
184
193
  Object.assign(services, multiServicesResult.services);
185
194
  return { services };
186
195
  }
187
- async function detectServicesDirectory(fs) {
196
+ async function detectServicesDirectory(fs, detectEntrypoint) {
188
197
  const services = {};
189
198
  const hasServicesDir = await fs.hasPath(SERVICES_DIR);
190
199
  if (!hasServicesDir) {
@@ -198,7 +207,12 @@ async function detectServicesDirectory(fs) {
198
207
  }
199
208
  const serviceName = entry.name;
200
209
  const serviceDir = `${SERVICES_DIR}/${serviceName}`;
201
- const result = await detectServiceInDir(fs, serviceDir, serviceName);
210
+ const result = await detectServiceInDir(
211
+ fs,
212
+ serviceDir,
213
+ serviceName,
214
+ detectEntrypoint
215
+ );
202
216
  if (result.error) {
203
217
  return { services: {}, error: result.error };
204
218
  }
@@ -208,7 +222,7 @@ async function detectServicesDirectory(fs) {
208
222
  }
209
223
  return { services };
210
224
  }
211
- async function detectServiceInDir(fs, dirPath, serviceName) {
225
+ async function detectServiceInDir(fs, dirPath, serviceName, detectEntrypoint) {
212
226
  const hasDirPath = await fs.hasPath(dirPath);
213
227
  if (!hasDirPath) {
214
228
  return {};
@@ -229,17 +243,21 @@ async function detectServiceInDir(fs, dirPath, serviceName) {
229
243
  }
230
244
  };
231
245
  }
232
- if (frameworks.length === 1) {
233
- const framework = frameworks[0];
234
- return {
235
- service: {
236
- framework: framework.slug ?? void 0,
237
- entrypoint: dirPath,
238
- routePrefix: `/_/${serviceName}`
239
- }
240
- };
246
+ if (frameworks.length !== 1) {
247
+ return {};
241
248
  }
242
- return {};
249
+ const framework = frameworks[0];
250
+ const slug = framework.slug ?? void 0;
251
+ const routePrefix = `/_/${serviceName}`;
252
+ const detected = detectEntrypoint && !(0, import_utils.isFrontendFramework)(slug) ? await detectEntrypoint({ workPath: dirPath, framework: slug }) : null;
253
+ return {
254
+ service: {
255
+ framework: slug,
256
+ root: dirPath,
257
+ ...detected ? { entrypoint: detected.entrypoint } : {},
258
+ routePrefix
259
+ }
260
+ };
243
261
  }
244
262
  // Annotate the CommonJS export names for ESM import in node:
245
263
  0 && (module.exports = {
@@ -1,3 +1,4 @@
1
+ import type { DetectEntrypointFn } from '@vercel/build-utils';
1
2
  import type { DetectorFilesystem } from '../detectors/filesystem';
2
3
  import type { ExperimentalServices, ServiceDetectionError, ServiceDetectionWarning } from './types';
3
4
  export interface RailwayDetectResult {
@@ -17,4 +18,5 @@ export interface RailwayDetectResult {
17
18
  */
18
19
  export declare function detectRailwayServices(options: {
19
20
  fs: DetectorFilesystem;
21
+ detectEntrypoint?: DetectEntrypointFn;
20
22
  }): Promise<RailwayDetectResult>;
@@ -56,7 +56,7 @@ const SKIP_DIRS = /* @__PURE__ */ new Set([
56
56
  "CVS"
57
57
  ]);
58
58
  async function detectRailwayServices(options) {
59
- const { fs } = options;
59
+ const { fs, detectEntrypoint } = options;
60
60
  const { configs, warnings } = await findRailwayConfigs(fs);
61
61
  if (configs.length === 0) {
62
62
  return { services: null, errors: [], warnings };
@@ -119,10 +119,20 @@ async function detectRailwayServices(options) {
119
119
  continue;
120
120
  }
121
121
  const framework = frameworks[0];
122
+ const slug = framework.slug ?? void 0;
122
123
  let serviceConfig = {};
123
- serviceConfig.framework = framework.slug ?? void 0;
124
+ serviceConfig.framework = slug;
124
125
  if (cf.dirPath !== ".") {
125
- serviceConfig.entrypoint = cf.dirPath;
126
+ serviceConfig.root = cf.dirPath;
127
+ if (detectEntrypoint && !(0, import_utils.isFrontendFramework)(slug)) {
128
+ const detected = await detectEntrypoint({
129
+ workPath: cf.dirPath,
130
+ framework: slug
131
+ });
132
+ if (detected) {
133
+ serviceConfig.entrypoint = detected.entrypoint;
134
+ }
135
+ }
126
136
  }
127
137
  if (cf.config.build?.buildCommand) {
128
138
  serviceConfig.buildCommand = cf.config.build.buildCommand;
@@ -69,6 +69,9 @@ function toInferredLayoutConfig(services) {
69
69
  if (service.type) {
70
70
  serviceConfig.type = service.type;
71
71
  }
72
+ if (typeof service.root === "string") {
73
+ serviceConfig.root = service.root;
74
+ }
72
75
  if (typeof service.entrypoint === "string") {
73
76
  serviceConfig.entrypoint = service.entrypoint;
74
77
  }
@@ -89,7 +92,7 @@ function toInferredLayoutConfig(services) {
89
92
  return inferredConfig;
90
93
  }
91
94
  async function detectServices(options) {
92
- const { fs, workPath } = options;
95
+ const { fs, workPath, detectEntrypoint } = options;
93
96
  const scopedFs = workPath ? fs.chdir(workPath) : fs;
94
97
  const { config: vercelConfig, error: configError } = await (0, import_utils.readVercelConfig)(scopedFs);
95
98
  if (configError) {
@@ -113,7 +116,7 @@ async function detectServices(options) {
113
116
  { detect: import_auto_detect.autoDetectServices, source: "layout" }
114
117
  ];
115
118
  for (const { detect, source } of detectors) {
116
- const detectResult = await detect({ fs: scopedFs });
119
+ const detectResult = await detect({ fs: scopedFs, detectEntrypoint });
117
120
  const match = await tryResolveInferred(detectResult, source, scopedFs);
118
121
  if (match)
119
122
  return match;
@@ -1,7 +1,7 @@
1
1
  import type { Route } from '@vercel/routing-utils';
2
- import type { EnvVar, EnvVars, ExperimentalServiceConfig, ExperimentalServiceGroups, ExperimentalServices, ServiceConfig, Services, ServiceRuntime, ServiceType, ServiceRefEnvVar, Service, Builder } from '@vercel/build-utils';
2
+ import type { DetectEntrypointFn, EnvVar, EnvVars, ExperimentalServiceConfig, ExperimentalServiceGroups, ExperimentalServices, ServiceConfig, Services, ServiceRuntime, ServiceType, ServiceRefEnvVar, Service, Builder } from '@vercel/build-utils';
3
3
  import type { DetectorFilesystem } from '../detectors/filesystem';
4
- export type { EnvVar, EnvVars, ExperimentalServiceConfig, ExperimentalServiceGroups, ExperimentalServices, ServiceConfig, Services, ServiceRuntime, ServiceType, ServiceRefEnvVar, Service, Builder, };
4
+ export type { DetectEntrypointFn, EnvVar, EnvVars, ExperimentalServiceConfig, ExperimentalServiceGroups, ExperimentalServices, ServiceConfig, Services, ServiceRuntime, ServiceType, ServiceRefEnvVar, Service, Builder, };
5
5
  /**
6
6
  * @deprecated Use `Service` instead
7
7
  */
@@ -13,6 +13,12 @@ export interface DetectServicesOptions {
13
13
  * If provided, vercel.json is read from this path.
14
14
  */
15
15
  workPath?: string;
16
+ /**
17
+ * Optional callback that, given a candidate service directory and its
18
+ * detected framework, returns a normalized entrypoint (file path or
19
+ * `module:attr` reference). Used to suggested service configs.
20
+ */
21
+ detectEntrypoint?: DetectEntrypointFn;
16
22
  }
17
23
  export interface ServicesRoutes {
18
24
  /** Host-based rewrite routes for subdomain-mounted web services */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/fs-detectors",
3
- "version": "6.5.0",
3
+ "version": "6.6.0",
4
4
  "description": "Vercel filesystem detectors",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -20,10 +20,10 @@
20
20
  "minimatch": "3.1.2",
21
21
  "semver": "6.3.1",
22
22
  "smol-toml": "1.5.2",
23
- "@vercel/error-utils": "2.1.0",
24
- "@vercel/routing-utils": "6.2.0",
25
23
  "@vercel/build-utils": "13.26.0",
26
- "@vercel/frameworks": "3.26.1"
24
+ "@vercel/error-utils": "2.1.0",
25
+ "@vercel/frameworks": "3.26.1",
26
+ "@vercel/routing-utils": "6.2.0"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/glob": "7.2.0",