@sveltejs/adapter-vercel 5.0.0 → 5.1.1

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.
Files changed (3) hide show
  1. package/index.js +59 -26
  2. package/package.json +2 -2
  3. package/utils.js +58 -12
package/index.js CHANGED
@@ -3,7 +3,7 @@ import path from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { nodeFileTrace } from '@vercel/nft';
5
5
  import esbuild from 'esbuild';
6
- import { get_pathname } from './utils.js';
6
+ import { get_pathname, pattern_to_src } from './utils.js';
7
7
 
8
8
  const name = '@sveltejs/adapter-vercel';
9
9
  const DEFAULT_FUNCTION_NAME = 'fn';
@@ -18,6 +18,9 @@ const get_default_runtime = () => {
18
18
  );
19
19
  };
20
20
 
21
+ // https://vercel.com/docs/functions/edge-functions/edge-runtime#compatible-node.js-modules
22
+ const compatible_node_modules = ['async_hooks', 'events', 'buffer', 'assert', 'util'];
23
+
21
24
  /** @type {import('.').default} **/
22
25
  const plugin = function (defaults = {}) {
23
26
  if ('edge' in defaults) {
@@ -109,20 +112,61 @@ const plugin = function (defaults = {}) {
109
112
  `export const manifest = ${builder.generateManifest({ relativePath, routes })};\n`
110
113
  );
111
114
 
112
- await esbuild.build({
113
- entryPoints: [`${tmp}/edge.js`],
114
- outfile: `${dirs.functions}/${name}.func/index.js`,
115
- target: 'es2020', // TODO verify what the edge runtime supports
116
- bundle: true,
117
- platform: 'browser',
118
- format: 'esm',
119
- external: config.external,
120
- sourcemap: 'linked',
121
- banner: { js: 'globalThis.global = globalThis;' },
122
- loader: {
123
- '.wasm': 'copy'
115
+ try {
116
+ const result = await esbuild.build({
117
+ entryPoints: [`${tmp}/edge.js`],
118
+ outfile: `${dirs.functions}/${name}.func/index.js`,
119
+ target: 'es2020', // TODO verify what the edge runtime supports
120
+ bundle: true,
121
+ platform: 'browser',
122
+ format: 'esm',
123
+ external: [
124
+ ...compatible_node_modules,
125
+ ...compatible_node_modules.map((id) => `node:${id}`),
126
+ ...(config.external || [])
127
+ ],
128
+ sourcemap: 'linked',
129
+ banner: { js: 'globalThis.global = globalThis;' },
130
+ loader: {
131
+ '.wasm': 'copy'
132
+ }
133
+ });
134
+
135
+ if (result.warnings.length > 0) {
136
+ const formatted = await esbuild.formatMessages(result.warnings, {
137
+ kind: 'warning',
138
+ color: true
139
+ });
140
+
141
+ console.error(formatted.join('\n'));
124
142
  }
125
- });
143
+ } catch (error) {
144
+ for (const e of error.errors) {
145
+ for (const node of e.notes) {
146
+ const match =
147
+ /The package "(.+)" wasn't found on the file system but is built into node/.exec(
148
+ node.text
149
+ );
150
+
151
+ if (match) {
152
+ node.text = `Cannot use "${match[1]}" when deploying to Vercel Edge Functions.`;
153
+ }
154
+ }
155
+ }
156
+
157
+ const formatted = await esbuild.formatMessages(error.errors, {
158
+ kind: 'error',
159
+ color: true
160
+ });
161
+
162
+ console.error(formatted.join('\n'));
163
+
164
+ throw new Error(
165
+ `Bundling with esbuild failed with ${error.errors.length} ${
166
+ error.errors.length === 1 ? 'error' : 'errors'
167
+ }`
168
+ );
169
+ }
126
170
 
127
171
  write(
128
172
  `${dirs.functions}/${name}.func/.vc-config.json`,
@@ -261,18 +305,7 @@ const plugin = function (defaults = {}) {
261
305
  if (is_prerendered(route)) continue;
262
306
 
263
307
  const pattern = route.pattern.toString();
264
-
265
- let src = pattern
266
- // remove leading / and trailing $/
267
- .slice(1, -2)
268
- // replace escaped \/ with /
269
- .replace(/\\\//g, '/');
270
-
271
- // replace the root route "^/" with "^/?"
272
- if (src === '^/') {
273
- src = '^/?';
274
- }
275
-
308
+ const src = pattern_to_src(pattern);
276
309
  const name = functions.get(pattern) ?? 'fn-0';
277
310
 
278
311
  const isr = isr_config.get(route);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/adapter-vercel",
3
- "version": "5.0.0",
3
+ "version": "5.1.1",
4
4
  "description": "A SvelteKit adapter that creates a Vercel app",
5
5
  "repository": {
6
6
  "type": "git",
@@ -33,7 +33,7 @@
33
33
  "@types/node": "^18.19.3",
34
34
  "typescript": "^5.3.3",
35
35
  "vitest": "^1.2.0",
36
- "@sveltejs/kit": "^2.4.0"
36
+ "@sveltejs/kit": "^2.5.3"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "@sveltejs/kit": "^2.4.0"
package/utils.js CHANGED
@@ -2,22 +2,68 @@
2
2
  export function get_pathname(route) {
3
3
  let i = 1;
4
4
 
5
- return route.segments
5
+ const pathname = route.segments
6
6
  .map((segment) => {
7
7
  if (!segment.dynamic) {
8
- return segment.content;
8
+ return '/' + segment.content;
9
9
  }
10
10
 
11
11
  const parts = segment.content.split(/\[(.+?)\](?!\])/);
12
- return parts
13
- .map((content, j) => {
14
- if (j % 2) {
15
- return `$${i++}`;
16
- } else {
17
- return content;
18
- }
19
- })
20
- .join('');
12
+ let result = '';
13
+
14
+ if (
15
+ parts.length === 3 &&
16
+ !parts[0] &&
17
+ !parts[2] &&
18
+ (parts[1].startsWith('...') || parts[1][0] === '[')
19
+ ) {
20
+ // Special case: segment is a single optional or rest parameter.
21
+ // In that case we don't prepend a slash (also see comment in pattern_to_src).
22
+ result = `$${i++}`;
23
+ } else {
24
+ result =
25
+ '/' +
26
+ parts
27
+ .map((content, j) => {
28
+ if (j % 2) {
29
+ return `$${i++}`;
30
+ } else {
31
+ return content;
32
+ }
33
+ })
34
+ .join('');
35
+ }
36
+
37
+ return result;
21
38
  })
22
- .join('/');
39
+ .join('');
40
+
41
+ return pathname[0] === '/' ? pathname.slice(1) : pathname;
42
+ }
43
+
44
+ /**
45
+ * Adjusts the stringified route regex for Vercel's routing system
46
+ * @param {string} pattern stringified route regex
47
+ */
48
+ export function pattern_to_src(pattern) {
49
+ let src = pattern
50
+ // remove leading / and trailing $/
51
+ .slice(1, -2)
52
+ // replace escaped \/ with /
53
+ .replace(/\\\//g, '/');
54
+
55
+ // replace the root route "^/" with "^/?"
56
+ if (src === '^/') {
57
+ src = '^/?';
58
+ }
59
+
60
+ // Move non-capturing groups that swallow slashes into their following capturing groups.
61
+ // This is necessary because during ISR we're using the regex to construct the __pathname
62
+ // query parameter: In case of a route like [required]/[...rest] we need to turn them
63
+ // into $1$2 and not $1/$2, because if [...rest] is empty, we don't want to have a trailing
64
+ // slash in the __pathname query parameter which wasn't there in the original URL, as that
65
+ // could result in a false trailing slash redirect in the SvelteKit runtime, leading to infinite redirects.
66
+ src = src.replace(/\(\?:\/\((.+?)\)\)/g, '(/$1)');
67
+
68
+ return src;
23
69
  }