@sveltejs/adapter-netlify 1.0.0-next.8 → 1.0.0-next.80

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/index.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { Adapter } from '@sveltejs/kit';
2
+ import './ambient.js';
3
+
4
+ export default function plugin(opts?: { split?: boolean; edge?: boolean }): Adapter;
package/index.js CHANGED
@@ -1,72 +1,305 @@
1
- const { existsSync, readFileSync, writeFileSync } = require('fs');
2
- const { join } = require('path');
3
- const esbuild = require('esbuild');
4
- const toml = require('toml');
5
-
6
- module.exports = function () {
7
- /** @type {import('@sveltejs/kit').Adapter} */
8
- const adapter = {
1
+ import { appendFileSync, existsSync, readFileSync, writeFileSync } from 'fs';
2
+ import { dirname, join, resolve, posix } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import esbuild from 'esbuild';
5
+ import toml from '@iarna/toml';
6
+
7
+ /**
8
+ * @typedef {{
9
+ * build?: { publish?: string }
10
+ * functions?: { node_bundler?: 'zisi' | 'esbuild' }
11
+ * } & toml.JsonMap} NetlifyConfig
12
+ */
13
+
14
+ /**
15
+ * @typedef {{
16
+ * functions: Array<
17
+ * | {
18
+ * function: string;
19
+ * path: string;
20
+ * }
21
+ * | {
22
+ * function: string;
23
+ * pattern: string;
24
+ * }
25
+ * >;
26
+ * version: 1;
27
+ * }} HandlerManifest
28
+ */
29
+
30
+ const files = fileURLToPath(new URL('./files', import.meta.url).href);
31
+
32
+ const edge_set_in_env_var =
33
+ process.env.NETLIFY_SVELTEKIT_USE_EDGE === 'true' ||
34
+ process.env.NETLIFY_SVELTEKIT_USE_EDGE === '1';
35
+
36
+ /** @type {import('.').default} */
37
+ export default function ({ split = false, edge = edge_set_in_env_var } = {}) {
38
+ return {
9
39
  name: '@sveltejs/adapter-netlify',
10
40
 
11
- async adapt(utils) {
12
- const { publish, functions } = validate_config().build;
41
+ async adapt(builder) {
42
+ const netlify_config = get_netlify_config();
43
+
44
+ // "build" is the default publish directory when Netlify detects SvelteKit
45
+ const publish = get_publish_directory(netlify_config, builder) || 'build';
46
+
47
+ // empty out existing build directories
48
+ builder.rimraf(publish);
49
+ builder.rimraf('.netlify/edge-functions');
50
+ builder.rimraf('.netlify/functions-internal');
51
+ builder.rimraf('.netlify/server');
52
+ builder.rimraf('.netlify/package.json');
53
+ builder.rimraf('.netlify/serverless.js');
13
54
 
14
- utils.rimraf(publish);
15
- utils.rimraf(functions);
55
+ builder.log.minor(`Publishing to "${publish}"`);
16
56
 
17
- const files = join(__dirname, 'files');
57
+ builder.log.minor('Copying assets...');
58
+ builder.writeClient(publish);
59
+ builder.writePrerendered(publish);
60
+
61
+ builder.log.minor('Writing custom headers...');
62
+ const headers_file = join(publish, '_headers');
63
+ builder.copy('_headers', headers_file);
64
+ appendFileSync(
65
+ headers_file,
66
+ `\n\n/${builder.config.kit.appDir}/immutable/*\n cache-control: public\n cache-control: immutable\n cache-control: max-age=31536000\n`
67
+ );
68
+
69
+ if (edge) {
70
+ if (split) {
71
+ throw new Error('Cannot use `split: true` alongside `edge: true`');
72
+ }
73
+
74
+ await generate_edge_functions({ builder });
75
+ } else {
76
+ await generate_lambda_functions({ builder, split, publish });
77
+ }
78
+ }
79
+ };
80
+ }
81
+ /**
82
+ * @param { object } params
83
+ * @param {import('@sveltejs/kit').Builder} params.builder
84
+ */
85
+ async function generate_edge_functions({ builder }) {
86
+ const tmp = builder.getBuildDirectory('netlify-tmp');
87
+ builder.rimraf(tmp);
88
+ builder.mkdirp(tmp);
18
89
 
19
- utils.log.minor('Generating serverless function...');
20
- utils.copy(join(files, 'entry.js'), '.svelte/netlify/entry.js');
90
+ builder.mkdirp('.netlify/edge-functions');
21
91
 
22
- await esbuild.build({
23
- entryPoints: ['.svelte/netlify/entry.js'],
24
- outfile: join(functions, 'render/index.js'),
25
- bundle: true,
26
- platform: 'node'
27
- });
92
+ // Don't match the static directory
93
+ const pattern = '^/.*$';
28
94
 
29
- writeFileSync(join(functions, 'package.json'), JSON.stringify({ type: 'commonjs' }));
95
+ // Go doesn't support lookarounds, so we can't do this
96
+ // const pattern = appDir ? `^/(?!${escapeStringRegexp(appDir)}).*$` : '^/.*$';
30
97
 
31
- utils.log.info('Prerendering static pages...');
32
- await utils.prerender({
33
- dest: publish
34
- });
98
+ /** @type {HandlerManifest} */
99
+ const edge_manifest = {
100
+ functions: [
101
+ {
102
+ function: 'render',
103
+ pattern
104
+ }
105
+ ],
106
+ version: 1
107
+ };
35
108
 
36
- utils.log.minor('Copying assets...');
37
- utils.copy_static_files(publish);
38
- utils.copy_client_files(publish);
109
+ builder.log.minor('Generating Edge Function...');
110
+ const relativePath = posix.relative(tmp, builder.getServerDirectory());
39
111
 
40
- utils.log.minor('Writing redirects...');
41
- utils.copy(`${files}/_redirects`, `${publish}/_redirects`);
112
+ builder.copy(`${files}/edge.js`, `${tmp}/entry.js`, {
113
+ replace: {
114
+ '0SERVER': `${relativePath}/index.js`,
115
+ MANIFEST: './manifest.js'
42
116
  }
117
+ });
118
+
119
+ const manifest = builder.generateManifest({
120
+ relativePath
121
+ });
122
+
123
+ writeFileSync(
124
+ `${tmp}/manifest.js`,
125
+ `export const manifest = ${manifest};\n\nexport const prerendered = new Set(${JSON.stringify(
126
+ builder.prerendered.paths
127
+ )});\n`
128
+ );
129
+
130
+ await esbuild.build({
131
+ entryPoints: [`${tmp}/entry.js`],
132
+ outfile: '.netlify/edge-functions/render.js',
133
+ bundle: true,
134
+ format: 'esm',
135
+ platform: 'browser',
136
+ sourcemap: 'linked',
137
+ target: 'es2020'
138
+ });
139
+
140
+ writeFileSync('.netlify/edge-functions/manifest.json', JSON.stringify(edge_manifest));
141
+ }
142
+ /**
143
+ * @param { object } params
144
+ * @param {import('@sveltejs/kit').Builder} params.builder
145
+ * @param { string } params.publish
146
+ * @param { boolean } params.split
147
+ */
148
+ async function generate_lambda_functions({ builder, publish, split }) {
149
+ builder.mkdirp('.netlify/functions-internal');
150
+
151
+ /** @type {string[]} */
152
+ const redirects = [];
153
+ builder.writeServer('.netlify/server');
154
+
155
+ const replace = {
156
+ '0SERVER': './server/index.js' // digit prefix prevents CJS build from using this as a variable name, which would also get replaced
43
157
  };
44
158
 
45
- return adapter;
46
- };
159
+ builder.copy(`${files}/esm`, '.netlify', { replace });
160
+
161
+ // Configuring the function to use ESM as the output format.
162
+ const fn_config = JSON.stringify({ config: { nodeModuleFormat: 'esm' }, version: 1 });
163
+
164
+ if (split) {
165
+ builder.log.minor('Generating serverless functions...');
166
+
167
+ await builder.createEntries((route) => {
168
+ const parts = [];
169
+ // Netlify's syntax uses '*' and ':param' as "splats" and "placeholders"
170
+ // https://docs.netlify.com/routing/redirects/redirect-options/#splats
171
+ for (const segment of route.segments) {
172
+ if (segment.rest) {
173
+ parts.push('*');
174
+ break; // Netlify redirects don't allow anything after a *
175
+ } else if (segment.dynamic) {
176
+ parts.push(`:${parts.length}`);
177
+ } else {
178
+ parts.push(segment.content);
179
+ }
180
+ }
181
+
182
+ const pattern = `/${parts.join('/')}`;
183
+ const name = parts.join('-').replace(/[:.]/g, '_').replace('*', '__rest') || 'index';
184
+
185
+ return {
186
+ id: pattern,
187
+ filter: (other) => matches(route.segments, other.segments),
188
+ complete: (entry) => {
189
+ const manifest = entry.generateManifest({
190
+ relativePath: '../server',
191
+ format: 'esm'
192
+ });
47
193
 
48
- function validate_config() {
49
- if (existsSync('netlify.toml')) {
50
- let netlify_config;
194
+ const fn = `import { init } from '../serverless.js';\n\nexport const handler = init(${manifest});\n`;
51
195
 
52
- try {
53
- netlify_config = toml.parse(readFileSync('netlify.toml', 'utf-8'));
54
- } catch (err) {
55
- err.message = `Error parsing netlify.toml: ${err.message}`;
56
- throw err;
196
+ writeFileSync(`.netlify/functions-internal/${name}.mjs`, fn);
197
+ writeFileSync(`.netlify/functions-internal/${name}.json`, fn_config);
198
+
199
+ redirects.push(`${pattern} /.netlify/functions/${name} 200`);
200
+ redirects.push(`${pattern}/__data.json /.netlify/functions/${name} 200`);
201
+ }
202
+ };
203
+ });
204
+ } else {
205
+ builder.log.minor('Generating serverless functions...');
206
+
207
+ const manifest = builder.generateManifest({
208
+ relativePath: '../server',
209
+ format: 'esm'
210
+ });
211
+
212
+ const fn = `import { init } from '../serverless.js';\n\nexport const handler = init(${manifest});\n`;
213
+
214
+ writeFileSync(`.netlify/functions-internal/render.json`, fn_config);
215
+ writeFileSync('.netlify/functions-internal/render.mjs', fn);
216
+ redirects.push('* /.netlify/functions/render 200');
217
+ }
218
+
219
+ // this should happen at the end, after builder.writeClient(...),
220
+ // so that generated redirects are appended to custom redirects
221
+ // rather than replaced by them
222
+ builder.log.minor('Writing redirects...');
223
+ const redirect_file = join(publish, '_redirects');
224
+ if (existsSync('_redirects')) {
225
+ builder.copy('_redirects', redirect_file);
226
+ }
227
+ builder.mkdirp(dirname(redirect_file));
228
+ appendFileSync(redirect_file, `\n\n${redirects.join('\n')}`);
229
+ }
230
+
231
+ function get_netlify_config() {
232
+ if (!existsSync('netlify.toml')) return null;
233
+
234
+ try {
235
+ return /** @type {NetlifyConfig} */ (toml.parse(readFileSync('netlify.toml', 'utf-8')));
236
+ } catch (err) {
237
+ err.message = `Error parsing netlify.toml: ${err.message}`;
238
+ throw err;
239
+ }
240
+ }
241
+
242
+ /**
243
+ * @param {NetlifyConfig} netlify_config
244
+ * @param {import('@sveltejs/kit').Builder} builder
245
+ **/
246
+ function get_publish_directory(netlify_config, builder) {
247
+ if (netlify_config) {
248
+ if (!netlify_config.build?.publish) {
249
+ builder.log.minor('No publish directory specified in netlify.toml, using default');
250
+ return;
57
251
  }
58
252
 
59
- if (!netlify_config.build || !netlify_config.build.publish || !netlify_config.build.functions) {
253
+ if (netlify_config.redirects) {
60
254
  throw new Error(
61
- 'You must specify build.publish and build.functions in netlify.toml. Consult https://github.com/sveltejs/kit/tree/master/packages/adapter-netlify#configuration'
255
+ "Redirects are not supported in netlify.toml. Use _redirects instead. For more details consult the readme's troubleshooting section."
62
256
  );
63
257
  }
64
-
65
- return netlify_config;
258
+ if (resolve(netlify_config.build.publish) === process.cwd()) {
259
+ throw new Error(
260
+ 'The publish directory cannot be set to the site root. Please change it to another value such as "build" in netlify.toml.'
261
+ );
262
+ }
263
+ return netlify_config.build.publish;
66
264
  }
67
265
 
68
- // TODO offer to create one?
69
- throw new Error(
70
- 'Missing a netlify.toml file. Consult https://github.com/sveltejs/kit/tree/master/packages/adapter-netlify#configuration'
266
+ builder.log.warn(
267
+ 'No netlify.toml found. Using default publish directory. Consult https://github.com/sveltejs/kit/tree/master/packages/adapter-netlify#configuration for more details '
71
268
  );
72
269
  }
270
+
271
+ /**
272
+ * @typedef {{ rest: boolean, dynamic: boolean, content: string }} RouteSegment
273
+ */
274
+
275
+ /**
276
+ * @param {RouteSegment[]} a
277
+ * @param {RouteSegment[]} b
278
+ * @returns {boolean}
279
+ */
280
+ function matches(a, b) {
281
+ if (a[0] && b[0]) {
282
+ if (b[0].rest) {
283
+ if (b.length === 1) return true;
284
+
285
+ const next_b = b.slice(1);
286
+
287
+ for (let i = 0; i < a.length; i += 1) {
288
+ if (matches(a.slice(i), next_b)) return true;
289
+ }
290
+
291
+ return false;
292
+ }
293
+
294
+ if (!b[0].dynamic) {
295
+ if (!a[0].dynamic && a[0].content !== b[0].content) return false;
296
+ }
297
+
298
+ if (a.length === 1 && b.length === 1) return true;
299
+ return matches(a.slice(1), b.slice(1));
300
+ } else if (a[0]) {
301
+ return a.length === 1 && a[0].rest;
302
+ } else {
303
+ return b.length === 1 && b[0].rest;
304
+ }
305
+ }
package/package.json CHANGED
@@ -1,21 +1,51 @@
1
1
  {
2
- "name": "@sveltejs/adapter-netlify",
3
- "version": "1.0.0-next.8",
4
- "main": "index.js",
5
- "files": [
6
- "files"
7
- ],
8
- "dependencies": {
9
- "@sveltejs/kit": "1.0.0-next.81",
10
- "esbuild": "^0.11.12",
11
- "toml": "^3.0.0"
12
- },
13
- "devDependencies": {
14
- "typescript": "^4.2.3"
15
- },
16
- "scripts": {
17
- "lint": "eslint --ignore-path .gitignore \"**/*.{ts,js,svelte}\" && npm run check-format",
18
- "format": "prettier --write . --config ../../.prettierrc --ignore-path .gitignore",
19
- "check-format": "prettier --check . --config ../../.prettierrc --ignore-path .gitignore"
20
- }
21
- }
2
+ "name": "@sveltejs/adapter-netlify",
3
+ "version": "1.0.0-next.80",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "https://github.com/sveltejs/kit",
7
+ "directory": "packages/adapter-netlify"
8
+ },
9
+ "license": "MIT",
10
+ "homepage": "https://kit.svelte.dev",
11
+ "type": "module",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./index.d.ts",
15
+ "import": "./index.js"
16
+ },
17
+ "./package.json": "./package.json"
18
+ },
19
+ "main": "index.js",
20
+ "types": "index.d.ts",
21
+ "files": [
22
+ "files",
23
+ "index.d.ts"
24
+ ],
25
+ "dependencies": {
26
+ "@iarna/toml": "^2.2.5",
27
+ "esbuild": "^0.15.7",
28
+ "set-cookie-parser": "^2.4.8"
29
+ },
30
+ "devDependencies": {
31
+ "@netlify/functions": "^1.0.0",
32
+ "@rollup/plugin-commonjs": "^23.0.0",
33
+ "@rollup/plugin-json": "^5.0.0",
34
+ "@rollup/plugin-node-resolve": "^15.0.0",
35
+ "@sveltejs/kit": "1.0.0-next.513",
36
+ "@types/node": "^16.11.36",
37
+ "@types/set-cookie-parser": "^2.4.2",
38
+ "rimraf": "^3.0.2",
39
+ "rollup": "^2.78.1",
40
+ "typescript": "^4.8.2",
41
+ "uvu": "^0.5.3"
42
+ },
43
+ "scripts": {
44
+ "dev": "rimraf files && rollup -cw",
45
+ "build": "rimraf files && rollup -c && cp src/edge.js files/edge.js",
46
+ "test": "uvu src \"(spec\\.js|test[\\\\/]index\\.js)\"",
47
+ "check": "tsc",
48
+ "lint": "prettier --check . --config ../../.prettierrc --ignore-path .gitignore",
49
+ "format": "npm run lint -- --write"
50
+ }
51
+ }
package/CHANGELOG.md DELETED
@@ -1,90 +0,0 @@
1
- # @sveltejs/adapter-netlify
2
-
3
- ## 1.0.0-next.8
4
-
5
- ### Patch Changes
6
-
7
- - 1237eb3: Fix dependencies
8
- - 1237eb3: Pass rawBody from netlify adapter
9
- - Updated dependencies [1237eb3]
10
- - Updated dependencies [1237eb3]
11
- - @sveltejs/kit@1.0.0-next.81
12
-
13
- ## 1.0.0-next.7
14
-
15
- ### Patch Changes
16
-
17
- - 0db2cf7: Fix serverless function
18
-
19
- ## 1.0.0-next.6
20
-
21
- ### Patch Changes
22
-
23
- - 7a4b351: Bundle serverless functions with esbuild
24
-
25
- ## 1.0.0-next.5
26
-
27
- ### Patch Changes
28
-
29
- - 6e27880: Move server-side fetch to adapters instead of build step
30
-
31
- ## 1.0.0-next.4
32
-
33
- ### Patch Changes
34
-
35
- - 8805c6d: Pass adapters directly to svelte.config.cjs
36
-
37
- ## 1.0.0-next.3
38
-
39
- ### Patch Changes
40
-
41
- - f35a5cd: Change adapter signature
42
-
43
- ## 1.0.0-next.2
44
-
45
- ### Patch Changes
46
-
47
- - 512b8c9: adapter-netlify: Use CJS entrypoint
48
-
49
- ## 1.0.0-next.1
50
-
51
- ### Patch Changes
52
-
53
- - Fix adapters and convert to ES modules
54
-
55
- ## 0.0.13
56
-
57
- ### Patch Changes
58
-
59
- - Add svelte-kit start command
60
-
61
- ## 0.0.12
62
-
63
- ### Patch Changes
64
-
65
- - b475ed4: Overhaul adapter API - fixes #166
66
-
67
- ## 0.0.11
68
-
69
- ### Patch Changes
70
-
71
- - 67eaeea: Move app-utils stuff into subpackages
72
-
73
- ## 0.0.10
74
-
75
- ### Patch Changes
76
-
77
- - Use setup
78
-
79
- ## 0.0.9
80
-
81
- ### Patch Changes
82
-
83
- - 0320208: Rename 'server route' to 'endpoint'
84
- - 5ca907c: Use shared mkdirp helper
85
-
86
- ## 0.0.8
87
-
88
- ### Patch Changes
89
-
90
- - various
package/files/_redirects DELETED
@@ -1 +0,0 @@
1
- /* /.netlify/functions/render 200
package/files/entry.js DELETED
@@ -1,38 +0,0 @@
1
- import '@sveltejs/kit/install-fetch'; // eslint-disable-line import/no-unresolved
2
-
3
- // TODO hardcoding the relative location makes this brittle
4
- import { render } from '../output/server/app.js'; // eslint-disable-line import/no-unresolved
5
-
6
- export async function handler(event) {
7
- const { path, httpMethod, headers, queryStringParameters, body, isBase64Encoded } = event;
8
-
9
- const query = new URLSearchParams();
10
- for (const k in queryStringParameters) {
11
- const value = queryStringParameters[k];
12
- value.split(', ').forEach((v) => {
13
- query.append(k, v);
14
- });
15
- }
16
-
17
- const rendered = await render({
18
- method: httpMethod,
19
- headers,
20
- path,
21
- query,
22
- rawBody: isBase64Encoded ? new TextEncoder('base64').encode(body).buffer : body
23
- });
24
-
25
- if (rendered) {
26
- return {
27
- isBase64Encoded: false,
28
- statusCode: rendered.status,
29
- headers: rendered.headers,
30
- body: rendered.body
31
- };
32
- }
33
-
34
- return {
35
- statusCode: 404,
36
- body: 'Not found'
37
- };
38
- }