@sveltejs/adapter-netlify 1.0.0-next.35 → 1.0.0-next.39

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.js CHANGED
@@ -1,83 +1,164 @@
1
1
  import { appendFileSync, existsSync, readFileSync, writeFileSync } from 'fs';
2
2
  import { join, resolve } from 'path';
3
3
  import { fileURLToPath } from 'url';
4
+ import glob from 'tiny-glob/sync.js';
4
5
  import esbuild from 'esbuild';
5
6
  import toml from '@iarna/toml';
6
7
 
7
8
  /**
8
- * @typedef {import('esbuild').BuildOptions} BuildOptions
9
+ * @typedef {{
10
+ * build?: { publish?: string }
11
+ * functions?: { node_bundler?: 'zisi' | 'esbuild' }
12
+ * } & toml.JsonMap} NetlifyConfig
9
13
  */
10
14
 
15
+ const files = fileURLToPath(new URL('./files', import.meta.url));
16
+
11
17
  /** @type {import('.')} */
12
- export default function (options) {
18
+ export default function ({ split = false } = {}) {
13
19
  return {
14
20
  name: '@sveltejs/adapter-netlify',
15
21
 
16
- async adapt({ utils }) {
17
- // "build" is the default publish directory when Netlify detects SvelteKit
18
- const publish = get_publish_directory(utils) || 'build';
19
-
20
- utils.log.minor(`Publishing to "${publish}"`);
21
-
22
- utils.rimraf(publish);
22
+ async adapt(builder) {
23
+ const netlify_config = get_netlify_config();
23
24
 
24
- const files = fileURLToPath(new URL('./files', import.meta.url));
25
-
26
- utils.log.minor('Generating serverless function...');
27
- utils.copy(join(files, 'entry.js'), '.svelte-kit/netlify/entry.js');
28
-
29
- /** @type {BuildOptions} */
30
- const default_options = {
31
- entryPoints: ['.svelte-kit/netlify/entry.js'],
32
- // Any functions in ".netlify/functions-internal" are bundled in addition to user-defined Netlify functions.
33
- // See https://github.com/netlify/build/pull/3213 for more details
34
- outfile: '.netlify/functions-internal/__render.js',
35
- bundle: true,
36
- inject: [join(files, 'shims.js')],
37
- platform: 'node'
38
- };
25
+ // "build" is the default publish directory when Netlify detects SvelteKit
26
+ const publish = get_publish_directory(netlify_config, builder) || 'build';
39
27
 
40
- const build_options =
41
- options && options.esbuild ? await options.esbuild(default_options) : default_options;
28
+ // empty out existing build directories
29
+ builder.rimraf(publish);
30
+ builder.rimraf('.netlify/functions-internal');
31
+ builder.rimraf('.netlify/server');
32
+ builder.rimraf('.netlify/package.json');
33
+ builder.rimraf('.netlify/handler.js');
42
34
 
43
- await esbuild.build(build_options);
35
+ builder.mkdirp('.netlify/functions-internal');
44
36
 
45
- writeFileSync(join('.netlify', 'package.json'), JSON.stringify({ type: 'commonjs' }));
37
+ builder.log.minor(`Publishing to "${publish}"`);
46
38
 
47
- utils.log.minor('Prerendering static pages...');
48
- await utils.prerender({
39
+ builder.log.minor('Prerendering static pages...');
40
+ await builder.prerender({
49
41
  dest: publish
50
42
  });
51
43
 
52
- utils.log.minor('Copying assets...');
53
- utils.copy_static_files(publish);
54
- utils.copy_client_files(publish);
55
-
56
- utils.log.minor('Writing redirects...');
57
-
58
- const redirectPath = join(publish, '_redirects');
59
- utils.copy('_redirects', redirectPath);
60
- appendFileSync(redirectPath, '\n\n/* /.netlify/functions/__render 200');
44
+ builder.writeServer('.netlify/server');
45
+
46
+ // for esbuild, use ESM
47
+ // for zip-it-and-ship-it, use CJS until https://github.com/netlify/zip-it-and-ship-it/issues/750
48
+ const esm = netlify_config?.functions?.node_bundler === 'esbuild';
49
+
50
+ /** @type {string[]} */
51
+ const redirects = [];
52
+
53
+ if (esm) {
54
+ builder.copy(`${files}/esm`, '.netlify');
55
+ } else {
56
+ glob('**/*.js', { cwd: '.netlify/server' }).forEach((file) => {
57
+ const filepath = `.netlify/server/${file}`;
58
+ const input = readFileSync(filepath, 'utf8');
59
+ const output = esbuild.transformSync(input, { format: 'cjs', target: 'node12' }).code;
60
+ writeFileSync(filepath, output);
61
+ });
62
+
63
+ builder.copy(`${files}/cjs`, '.netlify');
64
+ writeFileSync(join('.netlify', 'package.json'), JSON.stringify({ type: 'commonjs' }));
65
+ }
66
+
67
+ if (split) {
68
+ builder.log.minor('Generating serverless functions...');
69
+
70
+ builder.createEntries((route) => {
71
+ const parts = [];
72
+
73
+ for (const segment of route.segments) {
74
+ if (segment.rest) {
75
+ parts.push('*');
76
+ break; // Netlify redirects don't allow anything after a *
77
+ } else if (segment.dynamic) {
78
+ parts.push(`:${parts.length}`);
79
+ } else {
80
+ parts.push(segment.content);
81
+ }
82
+ }
83
+
84
+ const pattern = `/${parts.join('/')}`;
85
+ const name = parts.join('-').replace(/[:.]/g, '_').replace('*', '__rest') || 'index';
86
+
87
+ return {
88
+ id: pattern,
89
+ filter: (other) => matches(route.segments, other.segments),
90
+ complete: (entry) => {
91
+ const manifest = entry.generateManifest({
92
+ relativePath: '../server',
93
+ format: esm ? 'esm' : 'cjs'
94
+ });
95
+
96
+ const fn = esm
97
+ ? `import { init } from '../handler.js';\n\nexport const handler = init(${manifest});\n`
98
+ : `const { init } = require('../handler.js');\n\nexports.handler = init(${manifest});\n`;
99
+
100
+ writeFileSync(`.netlify/functions-internal/${name}.js`, fn);
101
+
102
+ redirects.push(`${pattern} /.netlify/functions/${name} 200`);
103
+ }
104
+ };
105
+ });
106
+ } else {
107
+ builder.log.minor('Generating serverless functions...');
108
+
109
+ const manifest = builder.generateManifest({
110
+ relativePath: '../server',
111
+ format: esm ? 'esm' : 'cjs'
112
+ });
113
+
114
+ const fn = esm
115
+ ? `import { init } from '../handler.js';\n\nexport const handler = init(${manifest});\n`
116
+ : `const { init } = require('../handler.js');\n\nexports.handler = init(${manifest});\n`;
117
+
118
+ writeFileSync('.netlify/functions-internal/render.js', fn);
119
+
120
+ redirects.push('* /.netlify/functions/render 200');
121
+ }
122
+
123
+ builder.log.minor('Copying assets...');
124
+ builder.writeStatic(publish);
125
+ builder.writeClient(publish);
126
+
127
+ builder.log.minor('Writing redirects...');
128
+ const redirect_file = join(publish, '_redirects');
129
+ builder.copy('_redirects', redirect_file);
130
+ appendFileSync(redirect_file, `\n\n${redirects.join('\n')}`);
131
+
132
+ builder.log.minor('Writing custom headers...');
133
+ const headers_file = join(publish, '_headers');
134
+ builder.copy('_headers', headers_file);
135
+ appendFileSync(
136
+ headers_file,
137
+ `\n\n/${builder.appDir}/*\n cache-control: public\n cache-control: immutable\n cache-control: max-age=31536000\n`
138
+ );
61
139
  }
62
140
  };
63
141
  }
142
+
143
+ function get_netlify_config() {
144
+ if (!existsSync('netlify.toml')) return null;
145
+
146
+ try {
147
+ return /** @type {NetlifyConfig} */ (toml.parse(readFileSync('netlify.toml', 'utf-8')));
148
+ } catch (err) {
149
+ err.message = `Error parsing netlify.toml: ${err.message}`;
150
+ throw err;
151
+ }
152
+ }
153
+
64
154
  /**
65
- * @param {import('@sveltejs/kit').AdapterUtils} utils
155
+ * @param {NetlifyConfig} netlify_config
156
+ * @param {import('@sveltejs/kit').Builder} builder
66
157
  **/
67
- function get_publish_directory(utils) {
68
- if (existsSync('netlify.toml')) {
69
- /** @type {{ build?: { publish?: string }} & toml.JsonMap } */
70
- let netlify_config;
71
-
72
- try {
73
- netlify_config = toml.parse(readFileSync('netlify.toml', 'utf-8'));
74
- } catch (err) {
75
- err.message = `Error parsing netlify.toml: ${err.message}`;
76
- throw err;
77
- }
78
-
158
+ function get_publish_directory(netlify_config, builder) {
159
+ if (netlify_config) {
79
160
  if (!netlify_config.build || !netlify_config.build.publish) {
80
- utils.log.warn('No publish directory specified in netlify.toml, using default');
161
+ builder.log.warn('No publish directory specified in netlify.toml, using default');
81
162
  return;
82
163
  }
83
164
 
@@ -94,7 +175,43 @@ function get_publish_directory(utils) {
94
175
  return netlify_config.build.publish;
95
176
  }
96
177
 
97
- utils.log.warn(
178
+ builder.log.warn(
98
179
  'No netlify.toml found. Using default publish directory. Consult https://github.com/sveltejs/kit/tree/master/packages/adapter-netlify#configuration for more details '
99
180
  );
100
181
  }
182
+
183
+ /**
184
+ * @typedef {{ rest: boolean, dynamic: boolean, content: string }} RouteSegment
185
+ */
186
+
187
+ /**
188
+ * @param {RouteSegment[]} a
189
+ * @param {RouteSegment[]} b
190
+ * @returns {boolean}
191
+ */
192
+ function matches(a, b) {
193
+ if (a[0] && b[0]) {
194
+ if (b[0].rest) {
195
+ if (b.length === 1) return true;
196
+
197
+ const next_b = b.slice(1);
198
+
199
+ for (let i = 0; i < a.length; i += 1) {
200
+ if (matches(a.slice(i), next_b)) return true;
201
+ }
202
+
203
+ return false;
204
+ }
205
+
206
+ if (!b[0].dynamic) {
207
+ if (!a[0].dynamic && a[0].content !== b[0].content) return false;
208
+ }
209
+
210
+ if (a.length === 1 && b.length === 1) return true;
211
+ return matches(a.slice(1), b.slice(1));
212
+ } else if (a[0]) {
213
+ return a.length === 1 && a[0].rest;
214
+ } else {
215
+ return b.length === 1 && b[0].rest;
216
+ }
217
+ }
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@sveltejs/adapter-netlify",
3
- "version": "1.0.0-next.35",
3
+ "version": "1.0.0-next.39",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sveltejs/kit",
7
7
  "directory": "packages/adapter-netlify"
8
8
  },
9
+ "license": "MIT",
9
10
  "homepage": "https://kit.svelte.dev",
10
11
  "type": "module",
11
12
  "exports": {
@@ -22,12 +23,21 @@
22
23
  ],
23
24
  "dependencies": {
24
25
  "@iarna/toml": "^2.2.5",
25
- "esbuild": "^0.13.4"
26
+ "esbuild": "^0.13.15",
27
+ "tiny-glob": "^0.2.9"
26
28
  },
27
29
  "devDependencies": {
28
- "@sveltejs/kit": "1.0.0-next.190"
30
+ "@netlify/functions": "^0.10.0",
31
+ "@rollup/plugin-commonjs": "^21.0.0",
32
+ "@rollup/plugin-json": "^4.1.0",
33
+ "@rollup/plugin-node-resolve": "^13.0.5",
34
+ "@sveltejs/kit": "1.0.0-next.232",
35
+ "rimraf": "^3.0.2",
36
+ "rollup": "^2.58.0"
29
37
  },
30
38
  "scripts": {
39
+ "dev": "rimraf files && rollup -cw",
40
+ "build": "rimraf files && rollup -c",
31
41
  "lint": "eslint --ignore-path .gitignore \"**/*.{ts,js,svelte}\" && npm run check-format",
32
42
  "format": "npm run check-format -- --write",
33
43
  "check-format": "prettier --check . --config ../../.prettierrc --ignore-path .gitignore"
package/files/entry.js DELETED
@@ -1,71 +0,0 @@
1
- // TODO hardcoding the relative location makes this brittle
2
- import { init, render } from '../output/server/app.js';
3
-
4
- init();
5
-
6
- export async function handler(event) {
7
- const { path, httpMethod, headers, rawQuery, body, isBase64Encoded } = event;
8
-
9
- const query = new URLSearchParams(rawQuery);
10
-
11
- const encoding = isBase64Encoded ? 'base64' : headers['content-encoding'] || 'utf-8';
12
- const rawBody = typeof body === 'string' ? Buffer.from(body, encoding) : body;
13
-
14
- const rendered = await render({
15
- method: httpMethod,
16
- headers,
17
- path,
18
- query,
19
- rawBody
20
- });
21
-
22
- if (!rendered) {
23
- return {
24
- statusCode: 404,
25
- body: 'Not found'
26
- };
27
- }
28
-
29
- const partial_response = {
30
- statusCode: rendered.status,
31
- ...split_headers(rendered.headers)
32
- };
33
-
34
- if (rendered.body instanceof Uint8Array) {
35
- // Function responses should always be strings, and responses with binary
36
- // content should be base64 encoded and set isBase64Encoded to true.
37
- // https://github.com/netlify/functions/blob/main/src/function/response.d.ts
38
- return {
39
- ...partial_response,
40
- isBase64Encoded: true,
41
- body: Buffer.from(rendered.body).toString('base64')
42
- };
43
- }
44
-
45
- return {
46
- ...partial_response,
47
- body: rendered.body
48
- };
49
- }
50
-
51
- /**
52
- * Splits headers into two categories: single value and multi value
53
- * @param {Record<string, string | string[]>} headers
54
- * @returns {{
55
- * headers: Record<string, string>,
56
- * multiValueHeaders: Record<string, string[]>
57
- * }}
58
- */
59
- function split_headers(headers) {
60
- const h = {};
61
- const m = {};
62
- for (const key in headers) {
63
- const value = headers[key];
64
- const target = Array.isArray(value) ? m : h;
65
- target[key] = value;
66
- }
67
- return {
68
- headers: h,
69
- multiValueHeaders: m
70
- };
71
- }
package/files/shims.js DELETED
@@ -1 +0,0 @@
1
- export { fetch, Response, Request, Headers } from '@sveltejs/kit/install-fetch';