@vercel/fs-detectors 5.8.18 → 5.9.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.
@@ -145,7 +145,6 @@ function generateServicesRoutes(services) {
145
145
  });
146
146
  }
147
147
  } else {
148
- continue;
149
148
  }
150
149
  }
151
150
  const workerServices = services.filter((s) => s.type === "worker");
@@ -165,7 +164,11 @@ function generateServicesRoutes(services) {
165
164
  const cronServices = services.filter((s) => s.type === "cron");
166
165
  for (const service of cronServices) {
167
166
  const cronEntrypoint = service.entrypoint || service.builder.src || "index";
168
- const cronPath = (0, import_utils.getInternalServiceCronPath)(service.name, cronEntrypoint);
167
+ const cronPath = (0, import_utils.getInternalServiceCronPath)(
168
+ service.name,
169
+ cronEntrypoint,
170
+ service.handlerFunction || "cron"
171
+ );
169
172
  const functionPath = (0, import_utils.getInternalServiceFunctionPath)(service.name);
170
173
  crons.push({
171
174
  src: `^${escapeRegex(cronPath)}$`,
@@ -41,6 +41,16 @@ var import_frameworks = __toESM(require("@vercel/frameworks"));
41
41
  var import_detect_framework = require("../detect-framework");
42
42
  var import_routing_utils = require("@vercel/routing-utils");
43
43
  const frameworksBySlug = new Map(import_frameworks.default.map((f) => [f.slug, f]));
44
+ const PYTHON_MODULE_ATTR_RE = /^([A-Za-z_][\w]*(?:\.[A-Za-z_][\w]*)*):([A-Za-z_][\w]*)$/;
45
+ function parsePyModuleAttrEntrypoint(entrypoint) {
46
+ const match = PYTHON_MODULE_ATTR_RE.exec(entrypoint);
47
+ if (!match)
48
+ return null;
49
+ return {
50
+ attrName: match[2],
51
+ filePath: match[1].replace(/\./g, "/") + ".py"
52
+ };
53
+ }
44
54
  const SERVICE_NAME_REGEX = /^[a-zA-Z]([a-zA-Z0-9_-]*[a-zA-Z0-9])?$/;
45
55
  function normalizeServiceEntrypoint(entrypoint) {
46
56
  const normalized = import_path.posix.normalize(entrypoint);
@@ -245,7 +255,9 @@ function validateServiceConfig(name, config) {
245
255
  }
246
256
  function validateServiceEntrypoint(name, config, resolvedEntrypoint) {
247
257
  if (!resolvedEntrypoint.isDirectory && !config.builder && !config.runtime && !config.framework) {
248
- const runtime = (0, import_utils.inferServiceRuntime)({ entrypoint: config.entrypoint });
258
+ const runtime = (0, import_utils.inferServiceRuntime)({
259
+ entrypoint: resolvedEntrypoint.normalized
260
+ });
249
261
  if (!runtime) {
250
262
  const supported = Object.keys(import_types.ENTRYPOINT_EXTENSIONS).join(", ");
251
263
  return {
@@ -268,12 +280,14 @@ async function resolveConfiguredService(options) {
268
280
  } = options;
269
281
  const type = config.type || "web";
270
282
  const rawEntrypoint = config.entrypoint;
283
+ const moduleAttrParsed = typeof rawEntrypoint === "string" && type === "cron" ? parsePyModuleAttrEntrypoint(rawEntrypoint) : null;
271
284
  let resolvedEntrypointPath = resolvedEntrypoint;
272
285
  if (!resolvedEntrypointPath && typeof rawEntrypoint === "string") {
286
+ const entrypointToResolve = moduleAttrParsed ? moduleAttrParsed.filePath : rawEntrypoint;
273
287
  const resolved = await resolveEntrypointPath({
274
288
  fs,
275
289
  serviceName: name,
276
- entrypoint: rawEntrypoint
290
+ entrypoint: entrypointToResolve
277
291
  });
278
292
  resolvedEntrypointPath = resolved.entrypoint;
279
293
  }
@@ -349,6 +363,9 @@ async function resolveConfiguredService(options) {
349
363
  if (config.framework) {
350
364
  builderConfig.framework = config.framework;
351
365
  }
366
+ if (moduleAttrParsed) {
367
+ builderConfig.handlerFunction = moduleAttrParsed.attrName;
368
+ }
352
369
  return {
353
370
  name,
354
371
  type,
@@ -367,6 +384,7 @@ async function resolveConfiguredService(options) {
367
384
  buildCommand: config.buildCommand,
368
385
  installCommand: config.installCommand,
369
386
  schedule: config.schedule,
387
+ handlerFunction: moduleAttrParsed?.attrName,
370
388
  topic,
371
389
  consumer
372
390
  };
@@ -383,11 +401,14 @@ async function resolveAllConfiguredServices(services, fs, routePrefixSource = "c
383
401
  continue;
384
402
  }
385
403
  let resolvedEntrypoint;
404
+ const serviceType = serviceConfig.type || "web";
386
405
  if (typeof serviceConfig.entrypoint === "string") {
406
+ const moduleAttr = serviceType === "cron" ? parsePyModuleAttrEntrypoint(serviceConfig.entrypoint) : null;
407
+ const entrypointToResolve = moduleAttr ? moduleAttr.filePath : serviceConfig.entrypoint;
387
408
  const resolvedPath = await resolveEntrypointPath({
388
409
  fs,
389
410
  serviceName: name,
390
- entrypoint: serviceConfig.entrypoint
411
+ entrypoint: entrypointToResolve
391
412
  });
392
413
  if (resolvedPath.error) {
393
414
  errors.push(resolvedPath.error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/fs-detectors",
3
- "version": "5.8.18",
3
+ "version": "5.9.0",
4
4
  "description": "Vercel filesystem detectors",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -19,9 +19,9 @@
19
19
  "json5": "2.2.2",
20
20
  "minimatch": "3.1.2",
21
21
  "semver": "6.3.1",
22
+ "@vercel/frameworks": "3.20.0",
22
23
  "@vercel/error-utils": "2.0.3",
23
- "@vercel/routing-utils": "6.0.2",
24
- "@vercel/frameworks": "3.20.0"
24
+ "@vercel/routing-utils": "6.0.2"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/glob": "7.2.0",
@@ -32,7 +32,7 @@
32
32
  "@types/semver": "7.3.10",
33
33
  "jest-junit": "16.0.0",
34
34
  "typescript": "4.9.5",
35
- "@vercel/build-utils": "13.6.3"
35
+ "@vercel/build-utils": "13.7.0"
36
36
  },
37
37
  "scripts": {
38
38
  "build": "node ../../utils/build.mjs",