-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Expand file tree
/
Copy pathutils.js
More file actions
161 lines (139 loc) · 4.56 KB
/
utils.js
File metadata and controls
161 lines (139 loc) · 4.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import process from 'node:process';
/** @param {import("@sveltejs/kit").RouteDefinition<any>} route */
export function get_pathname(route) {
let i = 1;
const pathname = route.segments
.map((segment) => {
if (!segment.dynamic) {
return '/' + segment.content;
}
const parts = segment.content.split(/\[(.+?)\](?!\])/);
if (
parts.length === 3 &&
!parts[0] &&
!parts[2] &&
(parts[1].startsWith('...') || parts[1][0] === '[')
) {
// Special case: segment is a single optional or rest parameter.
// In that case we don't prepend a slash (also see comment in pattern_to_src).
return `$${i++}`;
} else {
return (
'/' +
parts
.map((content, j) => {
if (j % 2) {
return `$${i++}`;
} else {
return content;
}
})
.join('')
);
}
})
.join('');
return pathname[0] === '/' ? pathname.slice(1) : pathname;
}
/**
* Adjusts the stringified route regex for Vercel's routing system
* @param {string} pattern stringified route regex
*/
export function pattern_to_src(pattern) {
let src = pattern
// remove leading / and trailing $/
.slice(1, -2)
// replace escaped \/ with /
.replace(/\\\//g, '/');
// replace the root route "^/" with "^/?"
if (src === '^/') {
src = '^/?';
}
// Move non-capturing groups that swallow slashes into their following capturing groups.
// This is necessary because during ISR we're using the regex to construct the __pathname
// query parameter: In case of a route like [required]/[...rest] we need to turn them
// into $1$2 and not $1/$2, because if [...rest] is empty, we don't want to have a trailing
// slash in the __pathname query parameter which wasn't there in the original URL, as that
// could result in a false trailing slash redirect in the SvelteKit runtime, leading to infinite redirects.
src = src.replace(/\(\?:\/\((.+?)\)\)/g, '(/$1)');
return src;
}
const integer = /^\d+$/;
/**
* @param {false | string | number} value
* @param {string} route_id
* @returns {number | false}
*/
export function parse_isr_expiration(value, route_id) {
if (value === false || value === 'false') return false; // 1 year
/** @param {string} desc */
const err = (desc) => {
throw new Error(
`Invalid isr.expiration value: ${JSON.stringify(value)} (${desc}, in ${route_id})`
);
};
let parsed;
if (typeof value === 'string') {
if (!integer.test(value)) {
err('value was a string but could not be parsed as an integer');
}
parsed = Number.parseInt(value, 10);
} else {
if (!Number.isInteger(value)) {
err('should be an integer');
}
parsed = value;
}
if (Number.isNaN(parsed)) {
err('should be a number');
}
if (parsed < 0) {
err('should be non-negative');
}
return parsed;
}
/**
* @param {string | undefined} default_key
* @param {string | undefined} [override_key]
* @returns {RuntimeKey}
*/
export function resolve_runtime(default_key, override_key) {
const key = (override_key ?? default_key ?? get_default_runtime()).replace('experimental_', '');
assert_is_valid_runtime(key);
return key;
}
const valid_node_versions = [20, 22, 24];
const formatter = new Intl.ListFormat('en', { type: 'disjunction' });
/** @returns {RuntimeKey} */
function get_default_runtime() {
// TODO may someday need to auto-detect Bun, but this will be complicated because you may want to run your build
// with Bun but not have your serverless runtime be in Bun. Vercel will likely have to attach something to `globalThis` or similar
// to tell us what the bun configuration is.
const major = Number(process.version.slice(1).split('.')[0]);
if (!valid_node_versions.includes(major)) {
throw new Error(
`Unsupported Node.js version: ${process.version}. Please use Node ${formatter.format(valid_node_versions.map((v) => `${v}`))} to build your project, or explicitly specify a runtime in your adapter configuration.`
);
}
return `nodejs${/** @type {20 | 22 | 24} */ (major)}.x`;
}
const valid_runtimes = /** @type {const} */ ([
'nodejs20.x',
'nodejs22.x',
'nodejs24.x',
'bun1.x',
'edge'
]);
/**
* @param {string} key
* @returns {asserts key is RuntimeKey}
*/
function assert_is_valid_runtime(key) {
if (!valid_runtimes.includes(/** @type {RuntimeKey} */ (key))) {
throw new Error(
`Unsupported runtime: ${key}. Supported runtimes are: ${valid_runtimes.join(', ')}. See the Node.js Version section in your Vercel project settings for info on the currently supported versions.`
);
}
}
/** @typedef {Exclude<RuntimeKey, 'bun1.x'> | 'experimental_bun1.x'} RuntimeConfigKey */
/** @typedef {typeof valid_runtimes[number]} RuntimeKey */