@sveltejs/kit 1.0.0-next.222 → 1.0.0-next.227

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.
@@ -76,7 +76,8 @@ async function create_plugin(config, output, cwd) {
76
76
  const deps = new Set();
77
77
  find_deps(node, deps);
78
78
 
79
- const styles = new Set();
79
+ /** @type {Record<string, string>} */
80
+ const styles = {};
80
81
 
81
82
  for (const dep of deps) {
82
83
  const parsed = new URL(dep.url, 'http://localhost/');
@@ -89,7 +90,7 @@ async function create_plugin(config, output, cwd) {
89
90
  ) {
90
91
  try {
91
92
  const mod = await vite.ssrLoadModule(dep.url);
92
- styles.add(mod.default);
93
+ styles[dep.url] = mod.default;
93
94
  } catch {
94
95
  // this can happen with dynamically imported modules, I think
95
96
  // because the Vite module graph doesn't distinguish between
@@ -103,7 +104,7 @@ async function create_plugin(config, output, cwd) {
103
104
  entry: url.endsWith('.svelte') ? url : url + '?import',
104
105
  css: [],
105
106
  js: [],
106
- styles: Array.from(styles)
107
+ styles
107
108
  };
108
109
  };
109
110
  }),
@@ -225,6 +226,7 @@ async function create_plugin(config, output, cwd) {
225
226
  hooks,
226
227
  hydrate: config.kit.hydrate,
227
228
  manifest,
229
+ method_override: config.kit.methodOverride,
228
230
  paths: {
229
231
  base: config.kit.paths.base,
230
232
  assets: config.kit.paths.assets ? SVELTE_KIT_ASSETS : config.kit.paths.base
@@ -260,26 +260,12 @@ function generate_app(manifest_data) {
260
260
  ${pyramid.replace(/\n/g, '\n\t\t')}
261
261
 
262
262
  {#if mounted}
263
- <div id="svelte-announcer" aria-live="assertive" aria-atomic="true">
263
+ <div id="svelte-announcer" aria-live="assertive" aria-atomic="true" style="position: absolute; left: 0; top: 0; clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap; width: 1px; height: 1px">
264
264
  {#if navigated}
265
265
  {title}
266
266
  {/if}
267
267
  </div>
268
268
  {/if}
269
-
270
- <style>
271
- #svelte-announcer {
272
- position: absolute;
273
- left: 0;
274
- top: 0;
275
- clip: rect(0 0 0 0);
276
- clip-path: inset(50%);
277
- overflow: hidden;
278
- white-space: nowrap;
279
- width: 1px;
280
- height: 1px;
281
- }
282
- </style>
283
269
  `);
284
270
  }
285
271
 
@@ -307,6 +307,7 @@ export class App {
307
307
  hooks,
308
308
  hydrate: ${s(config.kit.hydrate)},
309
309
  manifest,
310
+ method_override: ${s(config.kit.methodOverride)},
310
311
  paths: { base, assets },
311
312
  prefix: assets + '/${config.kit.appDir}/',
312
313
  prerender: ${config.kit.prerender.enabled},
@@ -498,16 +499,23 @@ async function build_server(
498
499
  /** @type {import('vite').Manifest} */
499
500
  const vite_manifest = JSON.parse(fs__default.readFileSync(`${output_dir}/server/manifest.json`, 'utf-8'));
500
501
 
501
- const styles_lookup = new Map();
502
- if (config.kit.amp) {
503
- client.assets.forEach((asset) => {
504
- if (asset.fileName.endsWith('.css')) {
505
- styles_lookup.set(asset.fileName, asset.source);
502
+ mkdirp(`${output_dir}/server/nodes`);
503
+ mkdirp(`${output_dir}/server/stylesheets`);
504
+
505
+ const stylesheet_lookup = new Map();
506
+
507
+ client.assets.forEach((asset) => {
508
+ if (asset.fileName.endsWith('.css')) {
509
+ if (config.kit.amp || asset.source.length < config.kit.inlineStyleThreshold) {
510
+ const index = stylesheet_lookup.size;
511
+ const file = `${output_dir}/server/stylesheets/${index}.js`;
512
+
513
+ fs__default.writeFileSync(file, `// ${asset.fileName}\nexport default ${s(asset.source)};`);
514
+ stylesheet_lookup.set(asset.fileName, index);
506
515
  }
507
- });
508
- }
516
+ }
517
+ });
509
518
 
510
- mkdirp(`${output_dir}/server/nodes`);
511
519
  manifest_data.components.forEach((component, i) => {
512
520
  const file = `${output_dir}/server/nodes/${i}.js`;
513
521
 
@@ -515,17 +523,32 @@ async function build_server(
515
523
  const css = new Set();
516
524
  find_deps(component, client.vite_manifest, js, css);
517
525
 
518
- const styles = config.kit.amp && Array.from(css).map((file) => styles_lookup.get(file));
526
+ const imports = [`import * as module from '../${vite_manifest[component].file}';`];
527
+
528
+ const exports = [
529
+ 'export { module };',
530
+ `export const entry = '${client.vite_manifest[component].file}';`,
531
+ `export const js = ${s(Array.from(js))};`,
532
+ `export const css = ${s(Array.from(css))};`
533
+ ];
519
534
 
520
- const node = `import * as module from '../${vite_manifest[component].file}';
521
- export { module };
522
- export const entry = '${client.vite_manifest[component].file}';
523
- export const js = ${JSON.stringify(Array.from(js))};
524
- export const css = ${JSON.stringify(Array.from(css))};
525
- ${styles ? `export const styles = ${s(styles)}` : ''}
526
- `.replace(/^\t\t\t/gm, '');
535
+ /** @type {string[]} */
536
+ const styles = [];
537
+
538
+ css.forEach((file) => {
539
+ if (stylesheet_lookup.has(file)) {
540
+ const index = stylesheet_lookup.get(file);
541
+ const name = `stylesheet_${index}`;
542
+ imports.push(`import ${name} from '../stylesheets/${index}.js';`);
543
+ styles.push(`\t${s(file)}: ${name}`);
544
+ }
545
+ });
546
+
547
+ if (styles.length > 0) {
548
+ exports.push(`export const styles = {\n${styles.join(',\n')}\n};`);
549
+ }
527
550
 
528
- fs__default.writeFileSync(file, node);
551
+ fs__default.writeFileSync(file, `${imports.join('\n')}\n\n${exports.join('\n')}\n`);
529
552
  });
530
553
 
531
554
  return {
@@ -98,58 +98,207 @@ function queue(concurrency) {
98
98
  };
99
99
  }
100
100
 
101
- /**
102
- * @typedef {import('types/config').PrerenderErrorHandler} PrerenderErrorHandler
103
- * @typedef {import('types/config').PrerenderOnErrorValue} OnError
104
- * @typedef {import('types/internal').Logger} Logger
105
- */
101
+ const DOCTYPE = 'DOCTYPE';
102
+ const CDATA_OPEN = '[CDATA[';
103
+ const CDATA_CLOSE = ']]>';
104
+ const COMMENT_OPEN = '--';
105
+ const COMMENT_CLOSE = '-->';
106
+
107
+ const TAG_OPEN = /[a-zA-Z]/;
108
+ const TAG_CHAR = /[a-zA-Z0-9]/;
109
+ const ATTRIBUTE_NAME = /[^\t\n\f />"'=]/;
110
+
111
+ const EXTERNAL = /\bexternal\b/;
112
+
113
+ const WHITESPACE = /[\s\n\r]/;
106
114
 
107
115
  /** @param {string} html */
108
- function clean_html(html) {
109
- return html
110
- .replace(/<!\[CDATA\[[\s\S]*?\]\]>/gm, '')
111
- .replace(/(<script[\s\S]*?>)[\s\S]*?<\/script>/gm, '$1</' + 'script>')
112
- .replace(/(<style[\s\S]*?>)[\s\S]*?<\/style>/gm, '$1</' + 'style>')
113
- .replace(/<!--[\s\S]*?-->/gm, '');
114
- }
116
+ function crawl(html) {
117
+ /** @type {string[]} */
118
+ const hrefs = [];
115
119
 
116
- /** @param {string} attrs */
117
- function get_href(attrs) {
118
- const match = /(?:[\s'"]|^)href\s*=\s*(?:"(.*?)"|'(.*?)'|([^\s>]*))/.exec(attrs);
119
- return match && (match[1] || match[2] || match[3]);
120
- }
120
+ let i = 0;
121
+ main: while (i < html.length) {
122
+ const char = html[i];
121
123
 
122
- /** @param {string} attrs */
123
- function get_src(attrs) {
124
- const match = /(?:[\s'"]|^)src\s*=\s*(?:"(.*?)"|'(.*?)'|([^\s>]*))/.exec(attrs);
125
- return match && (match[1] || match[2] || match[3]);
126
- }
124
+ if (char === '<') {
125
+ if (html[i + 1] === '!') {
126
+ i += 2;
127
127
 
128
- /** @param {string} attrs */
129
- function is_rel_external(attrs) {
130
- const match = /rel\s*=\s*(?:["'][^>]*(external)[^>]*["']|(external))/.exec(attrs);
131
- return !!match;
132
- }
128
+ if (html.substr(i, DOCTYPE.length).toUpperCase() === DOCTYPE) {
129
+ i += DOCTYPE.length;
130
+ while (i < html.length) {
131
+ if (html[i++] === '>') {
132
+ continue main;
133
+ }
134
+ }
135
+ }
136
+
137
+ // skip cdata
138
+ if (html.substr(i, CDATA_OPEN.length) === CDATA_OPEN) {
139
+ i += CDATA_OPEN.length;
140
+ while (i < html.length) {
141
+ if (html.substr(i, CDATA_CLOSE.length) === CDATA_CLOSE) {
142
+ i += CDATA_CLOSE.length;
143
+ continue main;
144
+ }
145
+
146
+ i += 1;
147
+ }
148
+ }
149
+
150
+ // skip comments
151
+ if (html.substr(i, COMMENT_OPEN.length) === COMMENT_OPEN) {
152
+ i += COMMENT_OPEN.length;
153
+ while (i < html.length) {
154
+ if (html.substr(i, COMMENT_CLOSE.length) === COMMENT_CLOSE) {
155
+ i += COMMENT_CLOSE.length;
156
+ continue main;
157
+ }
158
+
159
+ i += 1;
160
+ }
161
+ }
162
+ }
163
+
164
+ // parse opening tags
165
+ const start = ++i;
166
+ if (TAG_OPEN.test(html[start])) {
167
+ while (i < html.length) {
168
+ if (!TAG_CHAR.test(html[i])) {
169
+ break;
170
+ }
171
+
172
+ i += 1;
173
+ }
174
+
175
+ const tag = html.slice(start, i).toUpperCase();
176
+
177
+ if (tag === 'SCRIPT' || tag === 'STYLE') {
178
+ while (i < html.length) {
179
+ if (
180
+ html[i] === '<' &&
181
+ html[i + 1] === '/' &&
182
+ html.substr(i + 2, tag.length).toUpperCase() === tag
183
+ ) {
184
+ continue main;
185
+ }
186
+
187
+ i += 1;
188
+ }
189
+ }
190
+
191
+ let rel = '';
192
+ let href = '';
193
+
194
+ while (i < html.length) {
195
+ const start = i;
133
196
 
134
- /** @param {string} attrs */
135
- function get_srcset_urls(attrs) {
136
- const results = [];
137
- // Note that the srcset allows any ASCII whitespace, including newlines.
138
- const match = /([\s'"]|^)srcset\s*=\s*(?:"(.*?)"|'(.*?)'|([^\s>]*))/s.exec(attrs);
139
- if (match) {
140
- const attr_content = match[1] || match[2] || match[3];
141
- // Parse the content of the srcset attribute.
142
- // The regexp is modelled after the srcset specs (https://html.spec.whatwg.org/multipage/images.html#srcset-attribute)
143
- // and should cover most reasonable cases.
144
- const regex = /\s*([^\s,]\S+[^\s,])\s*((?:\d+w)|(?:-?\d+(?:\.\d+)?(?:[eE]-?\d+)?x))?/gm;
145
- let sub_matches;
146
- while ((sub_matches = regex.exec(attr_content))) {
147
- results.push(sub_matches[1]);
197
+ const char = html[start];
198
+ if (char === '>') break;
199
+
200
+ if (ATTRIBUTE_NAME.test(char)) {
201
+ i += 1;
202
+
203
+ while (i < html.length) {
204
+ if (!ATTRIBUTE_NAME.test(html[i])) {
205
+ break;
206
+ }
207
+
208
+ i += 1;
209
+ }
210
+
211
+ const name = html.slice(start, i).toLowerCase();
212
+
213
+ while (WHITESPACE.test(html[i])) i += 1;
214
+
215
+ if (html[i] === '=') {
216
+ i += 1;
217
+ while (WHITESPACE.test(html[i])) i += 1;
218
+
219
+ let value;
220
+
221
+ if (html[i] === "'" || html[i] === '"') {
222
+ const quote = html[i++];
223
+
224
+ const start = i;
225
+ let escaped = false;
226
+
227
+ while (i < html.length) {
228
+ if (!escaped) {
229
+ const char = html[i];
230
+
231
+ if (html[i] === quote) {
232
+ break;
233
+ }
234
+
235
+ if (char === '\\') {
236
+ escaped = true;
237
+ }
238
+ }
239
+
240
+ i += 1;
241
+ }
242
+
243
+ value = html.slice(start, i);
244
+ } else {
245
+ const start = i;
246
+ while (html[i] !== '>' && !WHITESPACE.test(html[i])) i += 1;
247
+ value = html.slice(start, i);
248
+
249
+ i -= 1;
250
+ }
251
+
252
+ if (name === 'rel') {
253
+ rel = value;
254
+ } else if (name === 'href') {
255
+ href = value;
256
+ } else if (name === 'src') {
257
+ hrefs.push(value);
258
+ } else if (name === 'srcset') {
259
+ const candidates = [];
260
+ let insideURL = true;
261
+ value = value.trim();
262
+ for (let i = 0; i < value.length; i++) {
263
+ if (value[i] === ',' && (!insideURL || (insideURL && value[i + 1] === ' '))) {
264
+ candidates.push(value.slice(0, i));
265
+ value = value.substring(i + 1).trim();
266
+ i = 0;
267
+ insideURL = true;
268
+ } else if (value[i] === ' ') {
269
+ insideURL = false;
270
+ }
271
+ }
272
+ candidates.push(value);
273
+ for (const candidate of candidates) {
274
+ const src = candidate.split(WHITESPACE)[0];
275
+ hrefs.push(src);
276
+ }
277
+ }
278
+ }
279
+ }
280
+
281
+ i += 1;
282
+ }
283
+
284
+ if (href && !EXTERNAL.test(rel)) {
285
+ hrefs.push(href);
286
+ }
287
+ }
148
288
  }
289
+
290
+ i += 1;
149
291
  }
150
- return results;
292
+
293
+ return hrefs;
151
294
  }
152
295
 
296
+ /**
297
+ * @typedef {import('types/config').PrerenderErrorHandler} PrerenderErrorHandler
298
+ * @typedef {import('types/config').PrerenderOnErrorValue} OnError
299
+ * @typedef {import('types/internal').Logger} Logger
300
+ */
301
+
153
302
  /** @type {(errorDetails: Parameters<PrerenderErrorHandler>[0] ) => string} */
154
303
  function errorDetailsToString({ status, path, referrer, referenceType }) {
155
304
  return `${status} ${path}${referrer ? ` (${referenceType} from ${referrer})` : ''}`;
@@ -357,36 +506,8 @@ async function prerender({ cwd, out, log, config, build_data, fallback, all }) {
357
506
  });
358
507
 
359
508
  if (is_html && config.kit.prerender.crawl) {
360
- const cleaned = clean_html(/** @type {string} */ (rendered.body));
361
-
362
- let match;
363
- const pattern = /<(a|img|link|source)\s+([\s\S]+?)>/gm;
364
-
365
- const hrefs = [];
366
-
367
- while ((match = pattern.exec(cleaned))) {
368
- const element = match[1];
369
- const attrs = match[2];
370
-
371
- if (element === 'a' || element === 'link') {
372
- if (is_rel_external(attrs)) continue;
373
-
374
- let href = get_href(attrs);
375
- if (!href) continue;
376
-
377
- const i = href.indexOf('#');
378
- href = i < 0 ? href : href.substring(0, i);
379
- hrefs.push(href);
380
- } else {
381
- if (element === 'img') {
382
- hrefs.push(get_src(attrs));
383
- }
384
- hrefs.push(...get_srcset_urls(attrs));
385
- }
386
- }
387
-
388
- for (const href of hrefs) {
389
- if (!href) continue;
509
+ for (const href of crawl(/** @type {string} */ (rendered.body))) {
510
+ if (href.startsWith('data:')) continue;
390
511
 
391
512
  const resolved = resolve$1(path, href);
392
513
  if (!is_root_relative(resolved)) continue;
package/dist/cli.js CHANGED
@@ -479,6 +479,23 @@ const options = object(
479
479
 
480
480
  hydrate: boolean(true),
481
481
 
482
+ inlineStyleThreshold: number(0),
483
+
484
+ methodOverride: object({
485
+ parameter: string('_method'),
486
+ allowed: validate([], (input, keypath) => {
487
+ if (!Array.isArray(input) || !input.every((method) => typeof method === 'string')) {
488
+ throw new Error(`${keypath} must be an array of strings`);
489
+ }
490
+
491
+ if (input.map((i) => i.toUpperCase()).includes('GET')) {
492
+ throw new Error(`${keypath} cannot contain "GET"`);
493
+ }
494
+
495
+ return input;
496
+ })
497
+ }),
498
+
482
499
  package: object({
483
500
  dir: string('package'),
484
501
  // excludes all .d.ts and filename starting with _
@@ -853,7 +870,7 @@ async function launch(port, https) {
853
870
  exec(`${cmd} ${https ? 'https' : 'http'}://localhost:${port}`);
854
871
  }
855
872
 
856
- const prog = sade('svelte-kit').version('1.0.0-next.222');
873
+ const prog = sade('svelte-kit').version('1.0.0-next.227');
857
874
 
858
875
  prog
859
876
  .command('dev')
@@ -1005,7 +1022,7 @@ async function check_port(port) {
1005
1022
  function welcome({ port, host, https, open, loose, allow, cwd }) {
1006
1023
  if (open) launch(port, https);
1007
1024
 
1008
- console.log($.bold().cyan(`\n SvelteKit v${'1.0.0-next.222'}\n`));
1025
+ console.log($.bold().cyan(`\n SvelteKit v${'1.0.0-next.227'}\n`));
1009
1026
 
1010
1027
  const protocol = https ? 'https:' : 'http:';
1011
1028
  const exposed = typeof host !== 'undefined' && host !== 'localhost' && host !== '127.0.0.1';