@vercel/fs-detectors 6.15.10 → 7.0.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.
- package/dist/detect-builders.d.ts +2 -9
- package/dist/detect-builders.js +3 -47
- package/dist/index.d.ts +1 -9
- package/dist/index.js +2 -40
- package/package.json +4 -6
- package/dist/services/auto-detect.d.ts +0 -46
- package/dist/services/auto-detect.js +0 -268
- package/dist/services/detect-procfile.d.ts +0 -22
- package/dist/services/detect-procfile.js +0 -249
- package/dist/services/detect-railway.d.ts +0 -22
- package/dist/services/detect-railway.js +0 -241
- package/dist/services/detect-render.d.ts +0 -14
- package/dist/services/detect-render.js +0 -230
- package/dist/services/detect-services.d.ts +0 -48
- package/dist/services/detect-services.js +0 -423
- package/dist/services/get-services-builders.d.ts +0 -43
- package/dist/services/get-services-builders.js +0 -161
- package/dist/services/resolve-v2.d.ts +0 -11
- package/dist/services/resolve-v2.js +0 -395
- package/dist/services/resolve.d.ts +0 -69
- package/dist/services/resolve.js +0 -914
- package/dist/services/types.d.ts +0 -134
- package/dist/services/types.js +0 -77
- package/dist/services/utils.d.ts +0 -95
- package/dist/services/utils.js +0 -265
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
-
var detect_render_exports = {};
|
|
30
|
-
__export(detect_render_exports, {
|
|
31
|
-
detectRenderServices: () => detectRenderServices
|
|
32
|
-
});
|
|
33
|
-
module.exports = __toCommonJS(detect_render_exports);
|
|
34
|
-
var import_js_yaml = __toESM(require("js-yaml"));
|
|
35
|
-
var import_detect_framework = require("../detect-framework");
|
|
36
|
-
var import_types = require("./types");
|
|
37
|
-
var import_utils = require("./utils");
|
|
38
|
-
const RENDER_YAML = "render.yaml";
|
|
39
|
-
const SERVICE_TYPE_MAP = {
|
|
40
|
-
web: "web",
|
|
41
|
-
static: "web"
|
|
42
|
-
};
|
|
43
|
-
async function detectRenderServices(options) {
|
|
44
|
-
const { fs, detectEntrypoint } = options;
|
|
45
|
-
const raw = await readRenderYaml(fs);
|
|
46
|
-
if (raw.warning) {
|
|
47
|
-
return { services: null, errors: [], warnings: [raw.warning] };
|
|
48
|
-
} else if (!raw.content) {
|
|
49
|
-
return { services: null, errors: [], warnings: [] };
|
|
50
|
-
}
|
|
51
|
-
const parsed = tryParseRenderConfig(raw.content);
|
|
52
|
-
if (parsed.warning) {
|
|
53
|
-
return { services: null, errors: [], warnings: [parsed.warning] };
|
|
54
|
-
} else if (!parsed.config) {
|
|
55
|
-
return { services: null, errors: [], warnings: [] };
|
|
56
|
-
}
|
|
57
|
-
const renderServices = parsed.config.services;
|
|
58
|
-
if (!Array.isArray(renderServices) || renderServices.length === 0) {
|
|
59
|
-
return { services: null, errors: [], warnings: [] };
|
|
60
|
-
}
|
|
61
|
-
const services = {};
|
|
62
|
-
const serviceNames = /* @__PURE__ */ new Set();
|
|
63
|
-
const errors = [];
|
|
64
|
-
const warnings = [];
|
|
65
|
-
for (const rs of renderServices) {
|
|
66
|
-
const serviceType = rs.type;
|
|
67
|
-
if (serviceType === "cron") {
|
|
68
|
-
const name = rs.name ?? "unnamed";
|
|
69
|
-
const schedule = rs.schedule;
|
|
70
|
-
const runtime = rs.runtime && rs.runtime in import_types.RUNTIME_BUILDERS ? rs.runtime : void 0;
|
|
71
|
-
const hint = {
|
|
72
|
-
type: "cron",
|
|
73
|
-
...schedule ? { schedule } : {},
|
|
74
|
-
entrypoint: "<path-to-handler>",
|
|
75
|
-
...runtime ? { runtime } : {}
|
|
76
|
-
};
|
|
77
|
-
warnings.push({
|
|
78
|
-
code: "RENDER_CRON_HINT",
|
|
79
|
-
message: `Found Render cron service "${name}"` + (schedule ? ` (schedule: "${schedule}")` : "") + `. Vercel crons work with a file entrypoint. You can add the following to define this cron service:
|
|
80
|
-
"${name}": ${JSON.stringify(hint, null, 2)}`
|
|
81
|
-
});
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
if (serviceType === "worker") {
|
|
85
|
-
const name = rs.name ?? "unnamed";
|
|
86
|
-
const runtime = rs.runtime ?? "unknown";
|
|
87
|
-
if (runtime === "python") {
|
|
88
|
-
const hint = {
|
|
89
|
-
type: "worker",
|
|
90
|
-
entrypoint: "<path-to-celery-app>",
|
|
91
|
-
runtime: "python"
|
|
92
|
-
};
|
|
93
|
-
warnings.push({
|
|
94
|
-
code: "RENDER_WORKER_HINT",
|
|
95
|
-
message: `Found Render worker service "${name}". Python workers using Celery are supported. You can add the following to define this worker:
|
|
96
|
-
"${name}": ${JSON.stringify(hint, null, 2)}`
|
|
97
|
-
});
|
|
98
|
-
} else {
|
|
99
|
-
warnings.push({
|
|
100
|
-
code: "RENDER_WORKER_HINT",
|
|
101
|
-
message: `Found Render worker service "${name}" with runtime "${runtime}". Only Python workers are currently supported.`
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
if (serviceType === "pserv") {
|
|
107
|
-
const name = rs.name ?? "unnamed";
|
|
108
|
-
const hint = {
|
|
109
|
-
entrypoint: rs.rootDir ?? "<path-to-entrypoint>",
|
|
110
|
-
mountPath: `/api/${name}`
|
|
111
|
-
};
|
|
112
|
-
warnings.push({
|
|
113
|
-
code: "RENDER_PSERV_HINT",
|
|
114
|
-
message: `Found Render private service "${name}". Private services are not yet supported. If you'd like to deploy it as a regular web service, you can add the following:
|
|
115
|
-
"${name}": ${JSON.stringify(hint, null, 2)}`
|
|
116
|
-
});
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
if (!serviceType || !(serviceType in SERVICE_TYPE_MAP)) {
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
const serviceName = rs.name;
|
|
123
|
-
if (!serviceName) {
|
|
124
|
-
warnings.push({
|
|
125
|
-
code: "RENDER_CONFIG_ERROR",
|
|
126
|
-
message: "Skipped a Render service with no name. Each service in render.yaml must have a name."
|
|
127
|
-
});
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
|
-
if (serviceNames.has(serviceName)) {
|
|
131
|
-
errors.push({
|
|
132
|
-
code: "DUPLICATE_SERVICE",
|
|
133
|
-
message: `Duplicate service name "${serviceName}" in render.yaml.`,
|
|
134
|
-
serviceName
|
|
135
|
-
});
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
serviceNames.add(serviceName);
|
|
139
|
-
const rootDir = rs.rootDir || ".";
|
|
140
|
-
const serviceFs = rootDir === "." ? fs : fs.chdir(rootDir);
|
|
141
|
-
const frameworks = await (0, import_detect_framework.detectFrameworks)({
|
|
142
|
-
fs: serviceFs,
|
|
143
|
-
frameworkList: import_utils.DETECTION_FRAMEWORKS,
|
|
144
|
-
useExperimentalFrameworks: true
|
|
145
|
-
});
|
|
146
|
-
if (frameworks.length === 0) {
|
|
147
|
-
warnings.push({
|
|
148
|
-
code: "SERVICE_SKIPPED",
|
|
149
|
-
message: `Skipped Render service "${serviceName}": no framework detected. Configure it manually in services.`
|
|
150
|
-
});
|
|
151
|
-
continue;
|
|
152
|
-
}
|
|
153
|
-
if (frameworks.length > 1) {
|
|
154
|
-
const names = frameworks.map((f) => f.name).join(", ");
|
|
155
|
-
errors.push({
|
|
156
|
-
code: "MULTIPLE_FRAMEWORKS_SERVICE",
|
|
157
|
-
message: `Multiple frameworks detected for Render service "${serviceName}": ${names}. Use explicit services config.`,
|
|
158
|
-
serviceName
|
|
159
|
-
});
|
|
160
|
-
continue;
|
|
161
|
-
}
|
|
162
|
-
const framework = frameworks[0];
|
|
163
|
-
const vercelType = SERVICE_TYPE_MAP[serviceType];
|
|
164
|
-
const serviceConfig = {
|
|
165
|
-
root: rootDir,
|
|
166
|
-
type: vercelType,
|
|
167
|
-
framework: framework.slug ?? void 0
|
|
168
|
-
};
|
|
169
|
-
if (rootDir !== "." && detectEntrypoint && !(0, import_utils.isFrontendFramework)(serviceConfig.framework)) {
|
|
170
|
-
const detected = await detectEntrypoint({
|
|
171
|
-
workPath: rootDir,
|
|
172
|
-
framework: serviceConfig.framework
|
|
173
|
-
});
|
|
174
|
-
if (detected) {
|
|
175
|
-
serviceConfig.entrypoint = detected.entrypoint;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
const buildCommand = (0, import_utils.combineBuildCommand)(
|
|
179
|
-
rs.buildCommand,
|
|
180
|
-
rs.preDeployCommand
|
|
181
|
-
);
|
|
182
|
-
if (buildCommand) {
|
|
183
|
-
serviceConfig.buildCommand = buildCommand;
|
|
184
|
-
}
|
|
185
|
-
services[serviceName] = serviceConfig;
|
|
186
|
-
}
|
|
187
|
-
if (errors.length > 0) {
|
|
188
|
-
return { services: null, errors, warnings };
|
|
189
|
-
}
|
|
190
|
-
if (Object.keys(services).length === 0) {
|
|
191
|
-
return { services: null, errors: [], warnings };
|
|
192
|
-
}
|
|
193
|
-
warnings.push(...(0, import_utils.assignMountPaths)(services));
|
|
194
|
-
return { services, errors: [], warnings };
|
|
195
|
-
}
|
|
196
|
-
async function readRenderYaml(fs) {
|
|
197
|
-
try {
|
|
198
|
-
const exists = await fs.isFile(RENDER_YAML);
|
|
199
|
-
if (!exists)
|
|
200
|
-
return { content: null };
|
|
201
|
-
const buf = await fs.readFile(RENDER_YAML);
|
|
202
|
-
return { content: buf.toString("utf-8") };
|
|
203
|
-
} catch (err) {
|
|
204
|
-
return {
|
|
205
|
-
content: null,
|
|
206
|
-
warning: {
|
|
207
|
-
code: "RENDER_CONFIG_ERROR",
|
|
208
|
-
message: `Failed to read ${RENDER_YAML}: ${err instanceof Error ? err.message : String(err)}`
|
|
209
|
-
}
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
function tryParseRenderConfig(content) {
|
|
214
|
-
try {
|
|
215
|
-
const config = import_js_yaml.default.load(content);
|
|
216
|
-
return { config };
|
|
217
|
-
} catch (err) {
|
|
218
|
-
return {
|
|
219
|
-
config: null,
|
|
220
|
-
warning: {
|
|
221
|
-
code: "RENDER_PARSE_ERROR",
|
|
222
|
-
message: `Failed to parse ${RENDER_YAML}: ${err instanceof Error ? err.message : String(err)}`
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
228
|
-
0 && (module.exports = {
|
|
229
|
-
detectRenderServices
|
|
230
|
-
});
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import type { Rewrite } from '@vercel/routing-utils';
|
|
2
|
-
import type { DetectServicesOptions, DetectServicesResult, InferredServicesConfig, Service, ServicesRoutes } from './types';
|
|
3
|
-
/**
|
|
4
|
-
* Detect and resolve services within a project.
|
|
5
|
-
*
|
|
6
|
-
* Reads vercel.json and resolves configured services into Service objects.
|
|
7
|
-
* Returns an error if no services are configured.
|
|
8
|
-
*/
|
|
9
|
-
export declare function detectServices(options: DetectServicesOptions): Promise<DetectServicesResult>;
|
|
10
|
-
/**
|
|
11
|
-
* Generate top-level service-targeted rewrites from inferred mount paths.
|
|
12
|
-
*
|
|
13
|
-
* Produces `Rewrite` objects (same format as vercel.json `rewrites`) that
|
|
14
|
-
* delegate public traffic into services based on their `mountPath`.
|
|
15
|
-
*
|
|
16
|
-
* Rewrites are ordered by mount path length (longest first) so more
|
|
17
|
-
* specific paths match before broader ones. The root service (`/`) is
|
|
18
|
-
* always last as a catch-all.
|
|
19
|
-
*/
|
|
20
|
-
export declare function generateServiceRewrites(services: InferredServicesConfig): Rewrite[];
|
|
21
|
-
/**
|
|
22
|
-
* Generate routing rules for services.
|
|
23
|
-
*
|
|
24
|
-
* Routes are ordered by prefix length (longest first) to ensure more specific
|
|
25
|
-
* routes match before broader ones. For example, `/api/users` must be checked
|
|
26
|
-
* before `/api`, which must be checked before the catch-all `/`.
|
|
27
|
-
*
|
|
28
|
-
* Services routing only generates *synthetic* routes for builders that do not
|
|
29
|
-
* provide their own route tables:
|
|
30
|
-
*
|
|
31
|
-
* - **Static/SPA services** (`@vercel/static-build`, `@vercel/static`):
|
|
32
|
-
* SPA fallback routes to index.html under the service prefix.
|
|
33
|
-
*
|
|
34
|
-
* - **Runtime services** (`@vercel/python`, `@vercel/go`, `@vercel/ruby`, etc.):
|
|
35
|
-
* Prefix rewrites to an internal runtime destination (`/_svc/{name}/index`)
|
|
36
|
-
* with `check: true`.
|
|
37
|
-
*
|
|
38
|
-
* Builders that provide their own routing (`@vercel/next`, `@vercel/backends`,
|
|
39
|
-
* Build Output API builders, etc.) are not given synthetic routes here.
|
|
40
|
-
*
|
|
41
|
-
* - Worker and queue-triggered job services:
|
|
42
|
-
* Use private path routing. The generated function is not publicly accessible.
|
|
43
|
-
*
|
|
44
|
-
* - Schedule-triggered job services:
|
|
45
|
-
* Internal cron callback routes under `/_svc/{serviceName}/crons/{entry}/{handler}`
|
|
46
|
-
* that rewrite to `/_svc/{serviceName}/index`.
|
|
47
|
-
*/
|
|
48
|
-
export declare function generateServicesRoutes(allServices: Service[]): ServicesRoutes;
|
|
@@ -1,423 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
var detect_services_exports = {};
|
|
20
|
-
__export(detect_services_exports, {
|
|
21
|
-
detectServices: () => detectServices,
|
|
22
|
-
generateServiceRewrites: () => generateServiceRewrites,
|
|
23
|
-
generateServicesRoutes: () => generateServicesRoutes
|
|
24
|
-
});
|
|
25
|
-
module.exports = __toCommonJS(detect_services_exports);
|
|
26
|
-
var import_build_utils = require("@vercel/build-utils");
|
|
27
|
-
var import_routing_utils = require("@vercel/routing-utils");
|
|
28
|
-
var import_utils = require("./utils");
|
|
29
|
-
var import_resolve = require("./resolve");
|
|
30
|
-
var import_resolve_v2 = require("./resolve-v2");
|
|
31
|
-
var import_auto_detect = require("./auto-detect");
|
|
32
|
-
var import_detect_railway = require("./detect-railway");
|
|
33
|
-
var import_detect_render = require("./detect-render");
|
|
34
|
-
var import_detect_procfile = require("./detect-procfile");
|
|
35
|
-
const PREVIEW_DOMAIN_MISSING = [
|
|
36
|
-
{ type: "host", value: { suf: ".vercel.app" } },
|
|
37
|
-
{ type: "host", value: { suf: ".vercel.dev" } }
|
|
38
|
-
];
|
|
39
|
-
function emptyRoutes() {
|
|
40
|
-
return {
|
|
41
|
-
hostRewrites: [],
|
|
42
|
-
rewrites: [],
|
|
43
|
-
defaults: [],
|
|
44
|
-
fallbacks: [],
|
|
45
|
-
crons: [],
|
|
46
|
-
workers: []
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
function withResolvedResult(resolved, inferred = null) {
|
|
50
|
-
return {
|
|
51
|
-
services: resolved.services,
|
|
52
|
-
source: resolved.source,
|
|
53
|
-
useImplicitEnvInjection: resolved.useImplicitEnvInjection,
|
|
54
|
-
routes: resolved.routes,
|
|
55
|
-
rewrites: resolved.rewrites,
|
|
56
|
-
errors: resolved.errors,
|
|
57
|
-
warnings: resolved.warnings,
|
|
58
|
-
resolved,
|
|
59
|
-
inferred
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
function toInferredLayoutConfig(services) {
|
|
63
|
-
const inferredConfig = {};
|
|
64
|
-
for (const [name, service] of Object.entries(services)) {
|
|
65
|
-
const serviceConfig = {
|
|
66
|
-
root: service.root
|
|
67
|
-
};
|
|
68
|
-
if (service.type) {
|
|
69
|
-
serviceConfig.type = service.type;
|
|
70
|
-
}
|
|
71
|
-
if (typeof service.entrypoint === "string") {
|
|
72
|
-
serviceConfig.entrypoint = service.entrypoint;
|
|
73
|
-
}
|
|
74
|
-
if (typeof service.mountPath === "string") {
|
|
75
|
-
serviceConfig.mountPath = service.mountPath;
|
|
76
|
-
}
|
|
77
|
-
if ((0, import_utils.isFrontendFramework)(service.framework)) {
|
|
78
|
-
serviceConfig.framework = service.framework;
|
|
79
|
-
}
|
|
80
|
-
if (typeof service.buildCommand === "string") {
|
|
81
|
-
serviceConfig.buildCommand = service.buildCommand;
|
|
82
|
-
}
|
|
83
|
-
if (typeof service.runtime === "string") {
|
|
84
|
-
serviceConfig.runtime = service.runtime;
|
|
85
|
-
}
|
|
86
|
-
inferredConfig[name] = serviceConfig;
|
|
87
|
-
}
|
|
88
|
-
return inferredConfig;
|
|
89
|
-
}
|
|
90
|
-
async function detectServices(options) {
|
|
91
|
-
const {
|
|
92
|
-
fs,
|
|
93
|
-
workPath,
|
|
94
|
-
detectEntrypoint,
|
|
95
|
-
configuredServices: providedConfiguredServices,
|
|
96
|
-
configuredServicesType: providedConfiguredServicesType
|
|
97
|
-
} = options;
|
|
98
|
-
const scopedFs = workPath ? fs.chdir(workPath) : fs;
|
|
99
|
-
const { config: vercelConfig, error: configError } = await (0, import_utils.readVercelConfig)(scopedFs);
|
|
100
|
-
if (configError) {
|
|
101
|
-
return withResolvedResult({
|
|
102
|
-
services: [],
|
|
103
|
-
source: "configured",
|
|
104
|
-
useImplicitEnvInjection: true,
|
|
105
|
-
routes: emptyRoutes(),
|
|
106
|
-
rewrites: [],
|
|
107
|
-
errors: [configError],
|
|
108
|
-
warnings: []
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
if (vercelConfig?.services != null && vercelConfig.experimentalServicesV2 != null) {
|
|
112
|
-
return withResolvedResult({
|
|
113
|
-
services: [],
|
|
114
|
-
source: "configured",
|
|
115
|
-
useImplicitEnvInjection: false,
|
|
116
|
-
routes: emptyRoutes(),
|
|
117
|
-
rewrites: [],
|
|
118
|
-
errors: [
|
|
119
|
-
{
|
|
120
|
-
code: "SERVICES_AND_EXPERIMENTAL_SERVICES_V2",
|
|
121
|
-
message: "The `services` property cannot be used in conjunction with its deprecated alias `experimentalServicesV2`. Please use only `services`."
|
|
122
|
-
}
|
|
123
|
-
],
|
|
124
|
-
warnings: []
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
const hasProvidedConfiguredServices = providedConfiguredServices && Object.keys(providedConfiguredServices).length > 0;
|
|
128
|
-
const experimentalServicesV2 = hasProvidedConfiguredServices && (providedConfiguredServicesType === "services" || providedConfiguredServicesType === "experimentalServicesV2") ? providedConfiguredServices : hasProvidedConfiguredServices ? void 0 : vercelConfig?.services ?? vercelConfig?.experimentalServicesV2;
|
|
129
|
-
if (experimentalServicesV2 && Object.keys(experimentalServicesV2).length > 0) {
|
|
130
|
-
const result = await (0, import_resolve_v2.resolveAllConfiguredServicesV2)(
|
|
131
|
-
experimentalServicesV2,
|
|
132
|
-
scopedFs
|
|
133
|
-
);
|
|
134
|
-
return withResolvedResult({
|
|
135
|
-
services: result.services,
|
|
136
|
-
source: "configured",
|
|
137
|
-
// V2 uses explicit `bindings`, so no implicit `{NAME}_URL` injection.
|
|
138
|
-
useImplicitEnvInjection: false,
|
|
139
|
-
// V2 routes are explicitly carried per-service to output them separately.
|
|
140
|
-
routes: emptyRoutes(),
|
|
141
|
-
rewrites: [],
|
|
142
|
-
errors: result.errors,
|
|
143
|
-
warnings: []
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
const experimentalServicesV1 = hasProvidedConfiguredServices ? providedConfiguredServices : vercelConfig?.experimentalServices;
|
|
147
|
-
const hasExperimentalServicesV1 = experimentalServicesV1 && Object.keys(experimentalServicesV1).length > 0;
|
|
148
|
-
if (hasExperimentalServicesV1) {
|
|
149
|
-
const result = await (0, import_resolve.resolveAllConfiguredServices)(
|
|
150
|
-
experimentalServicesV1,
|
|
151
|
-
scopedFs,
|
|
152
|
-
"configured"
|
|
153
|
-
);
|
|
154
|
-
const routes = generateServicesRoutes(result.services);
|
|
155
|
-
return withResolvedResult({
|
|
156
|
-
services: result.services,
|
|
157
|
-
source: "configured",
|
|
158
|
-
// experimentalServices uses the legacy `{NAME}_URL` injection.
|
|
159
|
-
useImplicitEnvInjection: true,
|
|
160
|
-
routes,
|
|
161
|
-
rewrites: [],
|
|
162
|
-
errors: result.errors,
|
|
163
|
-
warnings: []
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
const detectors = [
|
|
167
|
-
{ detect: import_detect_railway.detectRailwayServices, source: "railway" },
|
|
168
|
-
{ detect: import_detect_render.detectRenderServices, source: "render" },
|
|
169
|
-
{ detect: import_detect_procfile.detectProcfileServices, source: "procfile" },
|
|
170
|
-
{ detect: import_auto_detect.autoDetectServices, source: "layout" }
|
|
171
|
-
];
|
|
172
|
-
for (const { detect, source } of detectors) {
|
|
173
|
-
const detectResult = await detect({ fs: scopedFs, detectEntrypoint });
|
|
174
|
-
const match = await tryResolveInferred(detectResult, source, scopedFs);
|
|
175
|
-
if (match)
|
|
176
|
-
return match;
|
|
177
|
-
}
|
|
178
|
-
return withResolvedResult({
|
|
179
|
-
services: [],
|
|
180
|
-
source: "auto-detected",
|
|
181
|
-
useImplicitEnvInjection: true,
|
|
182
|
-
routes: emptyRoutes(),
|
|
183
|
-
rewrites: [],
|
|
184
|
-
errors: [
|
|
185
|
-
{
|
|
186
|
-
code: "NO_SERVICES_CONFIGURED",
|
|
187
|
-
message: "No services configured. Add `services` to vercel.json."
|
|
188
|
-
}
|
|
189
|
-
],
|
|
190
|
-
warnings: []
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
async function tryResolveInferred(detectResult, source, scopedFs) {
|
|
194
|
-
if (detectResult.errors.length > 0) {
|
|
195
|
-
return withResolvedResult({
|
|
196
|
-
services: [],
|
|
197
|
-
source: "auto-detected",
|
|
198
|
-
useImplicitEnvInjection: source !== "layout",
|
|
199
|
-
routes: emptyRoutes(),
|
|
200
|
-
rewrites: [],
|
|
201
|
-
errors: detectResult.errors,
|
|
202
|
-
warnings: detectResult.warnings
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
if (!detectResult.services) {
|
|
206
|
-
return null;
|
|
207
|
-
}
|
|
208
|
-
if (source === "layout") {
|
|
209
|
-
const v2Services = {};
|
|
210
|
-
for (const [name, svc] of Object.entries(detectResult.services)) {
|
|
211
|
-
v2Services[name] = {
|
|
212
|
-
root: svc.root,
|
|
213
|
-
...svc.framework ? { framework: svc.framework } : {},
|
|
214
|
-
...svc.entrypoint ? { entrypoint: svc.entrypoint } : {}
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
const result2 = await (0, import_resolve_v2.resolveAllConfiguredServicesV2)(v2Services, scopedFs);
|
|
218
|
-
const rootServices = Object.values(detectResult.services).filter(
|
|
219
|
-
(svc) => svc.mountPath === "/" && typeof svc.framework === "string"
|
|
220
|
-
);
|
|
221
|
-
const shouldInfer2 = result2.errors.length === 0 && rootServices.length === 1 && result2.services.length > 1;
|
|
222
|
-
const inferred2 = shouldInfer2 ? {
|
|
223
|
-
source,
|
|
224
|
-
config: toInferredLayoutConfig(detectResult.services),
|
|
225
|
-
services: result2.services,
|
|
226
|
-
warnings: detectResult.warnings
|
|
227
|
-
} : null;
|
|
228
|
-
return withResolvedResult(
|
|
229
|
-
{
|
|
230
|
-
services: shouldInfer2 ? result2.services : [],
|
|
231
|
-
source: "auto-detected",
|
|
232
|
-
useImplicitEnvInjection: false,
|
|
233
|
-
routes: emptyRoutes(),
|
|
234
|
-
rewrites: shouldInfer2 ? generateServiceRewrites(detectResult.services) : [],
|
|
235
|
-
experimentalServicesV2: shouldInfer2 ? v2Services : void 0,
|
|
236
|
-
errors: result2.errors,
|
|
237
|
-
warnings: detectResult.warnings
|
|
238
|
-
},
|
|
239
|
-
inferred2
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
const v1Services = {};
|
|
243
|
-
for (const [name, svc] of Object.entries(detectResult.services)) {
|
|
244
|
-
v1Services[name] = {
|
|
245
|
-
root: svc.root === "." ? void 0 : svc.root,
|
|
246
|
-
...svc.framework ? { framework: svc.framework } : {},
|
|
247
|
-
...svc.entrypoint ? { entrypoint: svc.entrypoint } : {},
|
|
248
|
-
...svc.type ? { type: svc.type } : {},
|
|
249
|
-
...svc.buildCommand ? { buildCommand: svc.buildCommand } : {},
|
|
250
|
-
...svc.preDeployCommand ? { preDeployCommand: svc.preDeployCommand } : {},
|
|
251
|
-
...svc.mountPath ? { routePrefix: svc.mountPath } : {}
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
const result = await (0, import_resolve.resolveAllConfiguredServices)(
|
|
255
|
-
v1Services,
|
|
256
|
-
scopedFs,
|
|
257
|
-
"generated"
|
|
258
|
-
);
|
|
259
|
-
const shouldInfer = result.errors.length === 0 && result.services.length > 0;
|
|
260
|
-
const inferred = shouldInfer ? {
|
|
261
|
-
source,
|
|
262
|
-
config: toInferredLayoutConfig(detectResult.services),
|
|
263
|
-
services: result.services,
|
|
264
|
-
warnings: detectResult.warnings
|
|
265
|
-
} : null;
|
|
266
|
-
return withResolvedResult(
|
|
267
|
-
{
|
|
268
|
-
services: [],
|
|
269
|
-
source: "auto-detected",
|
|
270
|
-
useImplicitEnvInjection: true,
|
|
271
|
-
routes: emptyRoutes(),
|
|
272
|
-
rewrites: [],
|
|
273
|
-
errors: result.errors,
|
|
274
|
-
warnings: detectResult.warnings
|
|
275
|
-
},
|
|
276
|
-
inferred
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
function generateServiceRewrites(services) {
|
|
280
|
-
const entries = Object.entries(services).filter(
|
|
281
|
-
([, svc]) => typeof svc.mountPath === "string" && (!svc.type || svc.type === "web")
|
|
282
|
-
).sort(([, a], [, b]) => b.mountPath.length - a.mountPath.length);
|
|
283
|
-
return entries.map(([name, svc]) => {
|
|
284
|
-
const mountPath = svc.mountPath;
|
|
285
|
-
if (mountPath === "/") {
|
|
286
|
-
return {
|
|
287
|
-
source: "/(.*)",
|
|
288
|
-
destination: { type: "service", service: name }
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
const prefix = mountPath.startsWith("/") ? mountPath.slice(1) : mountPath;
|
|
292
|
-
return {
|
|
293
|
-
source: `/${prefix}(/.*)?`,
|
|
294
|
-
destination: { type: "service", service: name }
|
|
295
|
-
};
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
function generateServicesRoutes(allServices) {
|
|
299
|
-
const services = allServices.filter(import_build_utils.isExperimentalService);
|
|
300
|
-
const hostRewrites = [];
|
|
301
|
-
const rewrites = [];
|
|
302
|
-
const defaults = [];
|
|
303
|
-
const fallbacks = [];
|
|
304
|
-
const crons = [];
|
|
305
|
-
const workers = [];
|
|
306
|
-
const sortedWebServices = services.filter(
|
|
307
|
-
(s) => s.type === "web" && typeof s.routePrefix === "string"
|
|
308
|
-
).sort((a, b) => b.routePrefix.length - a.routePrefix.length);
|
|
309
|
-
const allWebPrefixes = getWebRoutePrefixes(sortedWebServices);
|
|
310
|
-
const explicitHostPrefixGuard = getExplicitHostPrefixNegativeLookahead(allWebPrefixes);
|
|
311
|
-
for (const service of sortedWebServices) {
|
|
312
|
-
const { routePrefix } = service;
|
|
313
|
-
const normalizedPrefix = routePrefix.slice(1);
|
|
314
|
-
const ownershipGuard = (0, import_routing_utils.getOwnershipGuard)(routePrefix, allWebPrefixes);
|
|
315
|
-
const hostCondition = getHostCondition(service);
|
|
316
|
-
if (hostCondition && routePrefix !== "/") {
|
|
317
|
-
const normalizedRoutePrefix = (0, import_routing_utils.normalizeRoutePrefix)(routePrefix);
|
|
318
|
-
hostRewrites.push({
|
|
319
|
-
src: "^/$",
|
|
320
|
-
dest: normalizedRoutePrefix,
|
|
321
|
-
has: hostCondition,
|
|
322
|
-
missing: PREVIEW_DOMAIN_MISSING,
|
|
323
|
-
check: true
|
|
324
|
-
});
|
|
325
|
-
hostRewrites.push({
|
|
326
|
-
// Preserve explicit service prefixes so canonical paths like /_/api
|
|
327
|
-
// keep routing to their target service even on another service's host.
|
|
328
|
-
src: `^/${explicitHostPrefixGuard}(.*)$`,
|
|
329
|
-
dest: `${normalizedRoutePrefix}/$1`,
|
|
330
|
-
has: hostCondition,
|
|
331
|
-
missing: PREVIEW_DOMAIN_MISSING,
|
|
332
|
-
check: true
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
if ((0, import_utils.isRouteOwningBuilder)(service)) {
|
|
336
|
-
continue;
|
|
337
|
-
}
|
|
338
|
-
if ((0, import_utils.isStaticBuild)(service)) {
|
|
339
|
-
if (routePrefix === "/") {
|
|
340
|
-
fallbacks.push({
|
|
341
|
-
src: (0, import_routing_utils.scopeRouteSourceToOwnership)("/(.*)", ownershipGuard),
|
|
342
|
-
dest: "/index.html"
|
|
343
|
-
});
|
|
344
|
-
} else {
|
|
345
|
-
fallbacks.push({
|
|
346
|
-
src: (0, import_routing_utils.scopeRouteSourceToOwnership)(
|
|
347
|
-
`^/${normalizedPrefix}(?:/.*)?$`,
|
|
348
|
-
ownershipGuard
|
|
349
|
-
),
|
|
350
|
-
dest: `/${normalizedPrefix}/index.html`
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
} else if (service.runtime) {
|
|
354
|
-
const functionPath = (0, import_utils.getInternalServiceFunctionPath)(service.name);
|
|
355
|
-
const check = service.runtime === "container" ? void 0 : true;
|
|
356
|
-
if (routePrefix === "/") {
|
|
357
|
-
defaults.push({
|
|
358
|
-
src: (0, import_routing_utils.scopeRouteSourceToOwnership)("^/(.*)$", ownershipGuard),
|
|
359
|
-
dest: functionPath,
|
|
360
|
-
...check ? { check } : {}
|
|
361
|
-
});
|
|
362
|
-
} else {
|
|
363
|
-
rewrites.push({
|
|
364
|
-
src: (0, import_routing_utils.scopeRouteSourceToOwnership)(
|
|
365
|
-
`^/${normalizedPrefix}(?:/.*)?$`,
|
|
366
|
-
ownershipGuard
|
|
367
|
-
),
|
|
368
|
-
dest: functionPath,
|
|
369
|
-
...check ? { check } : {}
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
const cronServices = services.filter(import_build_utils.isScheduleTriggeredService);
|
|
375
|
-
for (const service of cronServices) {
|
|
376
|
-
const cronPrefix = (0, import_utils.getInternalServiceCronPathPrefix)(service.name);
|
|
377
|
-
const functionPath = (0, import_utils.getInternalServiceFunctionPath)(service.name);
|
|
378
|
-
crons.push({
|
|
379
|
-
src: `^${escapeRegex(cronPrefix)}/.*$`,
|
|
380
|
-
dest: functionPath,
|
|
381
|
-
check: true
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
return { hostRewrites, rewrites, defaults, fallbacks, crons, workers };
|
|
385
|
-
}
|
|
386
|
-
function escapeRegex(str) {
|
|
387
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
388
|
-
}
|
|
389
|
-
function getWebRoutePrefixes(services) {
|
|
390
|
-
const unique = /* @__PURE__ */ new Set();
|
|
391
|
-
for (const service of services) {
|
|
392
|
-
if (service.type !== "web" || typeof service.routePrefix !== "string") {
|
|
393
|
-
continue;
|
|
394
|
-
}
|
|
395
|
-
unique.add((0, import_routing_utils.normalizeRoutePrefix)(service.routePrefix));
|
|
396
|
-
}
|
|
397
|
-
return Array.from(unique);
|
|
398
|
-
}
|
|
399
|
-
function getExplicitHostPrefixNegativeLookahead(routePrefixes) {
|
|
400
|
-
const explicitPrefixes = routePrefixes.map(import_routing_utils.normalizeRoutePrefix).filter((prefix) => prefix !== "/").sort((a, b) => b.length - a.length).map((prefix) => escapeRegex(prefix.slice(1)));
|
|
401
|
-
if (explicitPrefixes.length === 0) {
|
|
402
|
-
return "";
|
|
403
|
-
}
|
|
404
|
-
if (explicitPrefixes.length === 1) {
|
|
405
|
-
return `(?!${explicitPrefixes[0]}(?:/|$))`;
|
|
406
|
-
}
|
|
407
|
-
return `(?!(?:${explicitPrefixes.join("|")})(?:/|$))`;
|
|
408
|
-
}
|
|
409
|
-
function getHostCondition(service) {
|
|
410
|
-
if (service.type !== "web") {
|
|
411
|
-
return void 0;
|
|
412
|
-
}
|
|
413
|
-
if (typeof service.subdomain === "string" && service.subdomain.length > 0) {
|
|
414
|
-
return [{ type: "host", value: { pre: `${service.subdomain}.` } }];
|
|
415
|
-
}
|
|
416
|
-
return void 0;
|
|
417
|
-
}
|
|
418
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
419
|
-
0 && (module.exports = {
|
|
420
|
-
detectServices,
|
|
421
|
-
generateServiceRewrites,
|
|
422
|
-
generateServicesRoutes
|
|
423
|
-
});
|