From 851a1cc9247591592ea946dcba3b6a1df8c45d92 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 1 Mar 2023 16:42:52 -0500 Subject: [PATCH 1/9] create dedent utility --- packages/kit/src/core/sync/utils.js | 38 ++++++++++++++++--- .../src/core/sync/write_client_manifest.js | 6 +-- packages/kit/src/core/sync/write_root.js | 6 +-- 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/packages/kit/src/core/sync/utils.js b/packages/kit/src/core/sync/utils.js index f475e4b98a7f..e2bf047ddd12 100644 --- a/packages/kit/src/core/sync/utils.js +++ b/packages/kit/src/core/sync/utils.js @@ -25,9 +25,37 @@ export function write(file, code) { fs.writeFileSync(file, code); } -/** @param {string} str */ -export function trim(str) { - const indentation = /** @type {RegExpExecArray} */ (/\n?([ \t]*)/.exec(str))[1]; - const pattern = new RegExp(`^${indentation}`, 'gm'); - return str.replace(pattern, '').trim(); +/** @type {WeakMap str.replace(pattern, '')) + }; + + dedent_map.set(strings, dedented); + } + + let str = dedented.strings[0]; + for (let i = 0; i < values.length; i += 1) { + str += values[i] + dedented.strings[i + 1]; + } + + str = str.trim(); + + const result = /** @type {string} */ ({ + toString: () => str + }); + + return result; } diff --git a/packages/kit/src/core/sync/write_client_manifest.js b/packages/kit/src/core/sync/write_client_manifest.js index 36158be6ad25..ed7769f8fe90 100644 --- a/packages/kit/src/core/sync/write_client_manifest.js +++ b/packages/kit/src/core/sync/write_client_manifest.js @@ -1,6 +1,6 @@ import { relative_path, resolve_entry } from '../../utils/filesystem.js'; import { s } from '../../utils/misc.js'; -import { trim, write_if_changed } from './utils.js'; +import { dedent, write_if_changed } from './utils.js'; /** * Writes the client manifest to disk. The manifest is used to power the router. It contains the @@ -108,7 +108,7 @@ export function write_client_manifest(kit, manifest_data, output, metadata) { write_if_changed( `${output}/app.js`, - trim(` + dedent` ${hooks_file ? `import * as client_hooks from '${relative_path(output, hooks_file)}';` : ''} export { matchers } from './matchers.js'; @@ -126,7 +126,7 @@ export function write_client_manifest(kit, manifest_data, output, metadata) { }; export { default as root } from '../root.svelte'; - `) + ` ); // write matchers to a separate module so that we don't diff --git a/packages/kit/src/core/sync/write_root.js b/packages/kit/src/core/sync/write_root.js index a82e2d00770f..c4b663b0fab5 100644 --- a/packages/kit/src/core/sync/write_root.js +++ b/packages/kit/src/core/sync/write_root.js @@ -1,4 +1,4 @@ -import { trim, write_if_changed } from './utils.js'; +import { dedent, write_if_changed } from './utils.js'; /** * @param {import('types').ManifestData} manifest_data @@ -39,7 +39,7 @@ export function write_root(manifest_data, output) { write_if_changed( `${output}/root.svelte`, - trim(` + dedent` - ${pyramid.replace(/\n/g, '\n\t\t\t')} + ${pyramid} {#if mounted}
From db29ed06d6fc85a612acacc463c74df8e0203948 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 1 Mar 2023 17:16:02 -0500 Subject: [PATCH 3/9] use `dedent` when creating env declarations --- packages/kit/src/core/env.js | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/kit/src/core/env.js b/packages/kit/src/core/env.js index a51c5948993b..835a3f78dbf1 100644 --- a/packages/kit/src/core/env.js +++ b/packages/kit/src/core/env.js @@ -1,4 +1,5 @@ import { GENERATED_COMMENT } from '../constants.js'; +import { dedent } from './sync/utils.js'; import { runtime_base } from './utils.js'; /** @@ -50,10 +51,13 @@ export function create_dynamic_module(type, dev_values) { export function create_static_types(id, env) { const declarations = Object.keys(env[id]) .filter((k) => valid_identifier.test(k)) - .map((k) => `\texport const ${k}: string;`) - .join('\n'); + .map((k) => `export const ${k}: string;`); - return `declare module '$env/static/${id}' {\n${declarations}\n}`; + return dedent` + declare module '$env/static/${id}' { + ${declarations.join('\n')} + } + `; } /** @@ -65,19 +69,24 @@ export function create_static_types(id, env) { export function create_dynamic_types(id, env, prefix) { const properties = Object.keys(env[id]) .filter((k) => valid_identifier.test(k)) - .map((k) => `\t\t${k}: string;`); + .map((k) => `${k}: string;`); const prefixed = `[key: \`${prefix}\${string}\`]`; if (id === 'private') { - properties.push(`\t\t${prefixed}: undefined;`); - properties.push(`\t\t[key: string]: string | undefined;`); + properties.push(`${prefixed}: undefined;`); + properties.push(`[key: string]: string | undefined;`); } else { - properties.push(`\t\t${prefixed}: string | undefined;`); + properties.push(`${prefixed}: string | undefined;`); } - const declaration = `export const env: {\n${properties.join('\n')}\n\t}`; - return `declare module '$env/dynamic/${id}' {\n\t${declaration}\n}`; + return dedent` + declare module '$env/dynamic/${id}' { + export const env: { + ${properties.join('\n')} + } + } + `; } export const reserved = new Set([ From 2c67c898d6834c2279b9f1e951cf79b2609bb237 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 1 Mar 2023 17:21:06 -0500 Subject: [PATCH 4/9] use dedent in manifest generation --- .../kit/src/core/generate_manifest/index.js | 63 ++++++++++--------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/packages/kit/src/core/generate_manifest/index.js b/packages/kit/src/core/generate_manifest/index.js index 2e59d145113d..32410c28c0db 100644 --- a/packages/kit/src/core/generate_manifest/index.js +++ b/packages/kit/src/core/generate_manifest/index.js @@ -3,6 +3,7 @@ import { get_mime_lookup } from '../utils.js'; import { resolve_symlinks } from '../../exports/vite/build/utils.js'; import { compact } from '../../utils/array.js'; import { join_relative } from '../../utils/filesystem.js'; +import { dedent } from '../sync/utils.js'; /** * Generates the data used to write the server-side manifest.js file. This data is used in the Vite @@ -80,37 +81,41 @@ export function generate_manifest({ build_data, relative_path, routes }) { // prettier-ignore // String representation of /** @type {import('types').SSRManifest} */ - return `{ - appDir: ${s(build_data.app_dir)}, - appPath: ${s(build_data.app_path)}, - assets: new Set(${s(assets)}), - mimeTypes: ${s(get_mime_lookup(build_data.manifest_data))}, - _: { - client: ${s(build_data.client)}, - nodes: [ - ${(node_paths).map(loader).join(',\n\t\t\t\t')} - ], - routes: [ - ${routes.map(route => { - route.params.forEach(param => { - if (param.matcher) matchers.add(param.matcher); - }); + return dedent` + { + appDir: ${s(build_data.app_dir)}, + appPath: ${s(build_data.app_path)}, + assets: new Set(${s(assets)}), + mimeTypes: ${s(get_mime_lookup(build_data.manifest_data))}, + _: { + client: ${s(build_data.client)}, + nodes: [ + ${(node_paths).map(loader).join(',\n')} + ], + routes: [ + ${routes.map(route => { + route.params.forEach(param => { + if (param.matcher) matchers.add(param.matcher); + }); - if (!route.page && !route.endpoint) return; + if (!route.page && !route.endpoint) return; - return `{ - id: ${s(route.id)}, - pattern: ${route.pattern}, - params: ${s(route.params)}, - page: ${route.page ? `{ layouts: ${get_nodes(route.page.layouts)}, errors: ${get_nodes(route.page.errors)}, leaf: ${reindexed.get(route.page.leaf)} }` : 'null'}, - endpoint: ${route.endpoint ? loader(join_relative(relative_path, resolve_symlinks(build_data.server_manifest, route.endpoint.file).chunk.file)) : 'null'} - }`; - }).filter(Boolean).join(',\n\t\t\t\t')} - ], - matchers: async () => { - ${Array.from(matchers).map(type => `const { match: ${type} } = await import ('${(join_relative(relative_path, `/entries/matchers/${type}.js`))}')`).join('\n\t\t\t\t')} - return { ${Array.from(matchers).join(', ')} }; + return dedent` + { + id: ${s(route.id)}, + pattern: ${route.pattern}, + params: ${s(route.params)}, + page: ${route.page ? `{ layouts: ${get_nodes(route.page.layouts)}, errors: ${get_nodes(route.page.errors)}, leaf: ${reindexed.get(route.page.leaf)} }` : 'null'}, + endpoint: ${route.endpoint ? loader(join_relative(relative_path, resolve_symlinks(build_data.server_manifest, route.endpoint.file).chunk.file)) : 'null'} + } + `; + }).filter(Boolean).join(',\n')} + ], + matchers: async () => { + ${Array.from(matchers).map(type => `const { match: ${type} } = await import ('${(join_relative(relative_path, `/entries/matchers/${type}.js`))}')`).join('\n')} + return { ${Array.from(matchers).join(', ')} }; + } } } - }`.replace(/^\t/gm, ''); + `; } From bfd2883442f0520fe2c195ac4d5c0fd188bddc3f Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 1 Mar 2023 17:25:53 -0500 Subject: [PATCH 5/9] dedent more stuff --- .../src/core/sync/write_client_manifest.js | 100 +++++++++--------- .../vite/build/build_service_worker.js | 11 +- packages/kit/src/exports/vite/index.js | 27 ++--- 3 files changed, 71 insertions(+), 67 deletions(-) diff --git a/packages/kit/src/core/sync/write_client_manifest.js b/packages/kit/src/core/sync/write_client_manifest.js index ed7769f8fe90..df98c8cd7fa2 100644 --- a/packages/kit/src/core/sync/write_client_manifest.js +++ b/packages/kit/src/core/sync/write_client_manifest.js @@ -45,64 +45,66 @@ export function write_client_manifest(kit, manifest_data, output, metadata) { write_if_changed(`${output}/nodes/${i}.js`, generate_node(node)); return `() => import('./nodes/${i}')`; }) - .join(',\n\t'); + .join(',\n'); const layouts_with_server_load = new Set(); - const dictionary = `{ - ${manifest_data.routes - .map((route) => { - if (route.page) { - const errors = route.page.errors.slice(1).map((n) => n ?? ''); - const layouts = route.page.layouts.slice(1).map((n) => n ?? ''); - - while (layouts.at(-1) === '') layouts.pop(); - while (errors.at(-1) === '') errors.pop(); - - let leaf_has_server_load = false; - if (route.leaf) { - if (metadata) { - const i = /** @type {number} */ (indices.get(route.leaf)); - - leaf_has_server_load = metadata[i].has_server_load; - } else if (route.leaf.server) { - leaf_has_server_load = true; + const dictionary = dedent` + { + ${manifest_data.routes + .map((route) => { + if (route.page) { + const errors = route.page.errors.slice(1).map((n) => n ?? ''); + const layouts = route.page.layouts.slice(1).map((n) => n ?? ''); + + while (layouts.at(-1) === '') layouts.pop(); + while (errors.at(-1) === '') errors.pop(); + + let leaf_has_server_load = false; + if (route.leaf) { + if (metadata) { + const i = /** @type {number} */ (indices.get(route.leaf)); + + leaf_has_server_load = metadata[i].has_server_load; + } else if (route.leaf.server) { + leaf_has_server_load = true; + } } - } - // Encode whether or not the route uses server data - // using the ones' complement, to save space - const array = [`${leaf_has_server_load ? '~' : ''}${route.page.leaf}`]; + // Encode whether or not the route uses server data + // using the ones' complement, to save space + const array = [`${leaf_has_server_load ? '~' : ''}${route.page.leaf}`]; - // Encode whether or not the layout uses server data. - // It's a different method compared to pages because layouts - // are reused across pages, so we save space by doing it this way. - route.page.layouts.forEach((layout) => { - if (layout == undefined) return; + // Encode whether or not the layout uses server data. + // It's a different method compared to pages because layouts + // are reused across pages, so we save space by doing it this way. + route.page.layouts.forEach((layout) => { + if (layout == undefined) return; - let layout_has_server_load = false; + let layout_has_server_load = false; - if (metadata) { - layout_has_server_load = metadata[layout].has_server_load; - } else if (manifest_data.nodes[layout].server) { - layout_has_server_load = true; - } + if (metadata) { + layout_has_server_load = metadata[layout].has_server_load; + } else if (manifest_data.nodes[layout].server) { + layout_has_server_load = true; + } - if (layout_has_server_load) { - layouts_with_server_load.add(layout); - } - }); + if (layout_has_server_load) { + layouts_with_server_load.add(layout); + } + }); - // only include non-root layout/error nodes if they exist - if (layouts.length > 0 || errors.length > 0) array.push(`[${layouts.join(',')}]`); - if (errors.length > 0) array.push(`[${errors.join(',')}]`); + // only include non-root layout/error nodes if they exist + if (layouts.length > 0 || errors.length > 0) array.push(`[${layouts.join(',')}]`); + if (errors.length > 0) array.push(`[${errors.join(',')}]`); - return `${s(route.id)}: [${array.join(',')}]`; - } - }) - .filter(Boolean) - .join(',\n\t\t')} - }`.replace(/^\t/gm, ''); + return `${s(route.id)}: [${array.join(',')}]`; + } + }) + .filter(Boolean) + .join(',\n')} + } + `; const hooks_file = resolve_entry(kit.files.hooks.client); @@ -113,7 +115,9 @@ export function write_client_manifest(kit, manifest_data, output, metadata) { export { matchers } from './matchers.js'; - export const nodes = [${nodes}]; + export const nodes = [ + ${nodes} + ]; export const server_loads = [${[...layouts_with_server_load].join(',')}]; diff --git a/packages/kit/src/exports/vite/build/build_service_worker.js b/packages/kit/src/exports/vite/build/build_service_worker.js index 2fd8ec2f3052..285ce62eba51 100644 --- a/packages/kit/src/exports/vite/build/build_service_worker.js +++ b/packages/kit/src/exports/vite/build/build_service_worker.js @@ -1,5 +1,6 @@ import fs from 'node:fs'; import * as vite from 'vite'; +import { dedent } from '../../../core/sync/utils.js'; import { s } from '../../../utils/misc.js'; import { get_config_aliases } from '../utils.js'; import { assets_base } from './utils.js'; @@ -34,28 +35,26 @@ export async function build_service_worker( fs.writeFileSync( service_worker, - ` + dedent` export const build = [ ${Array.from(build) .map((file) => `${s(`${kit.paths.base}/${file}`)}`) - .join(',\n\t\t\t\t')} + .join(',\n')} ]; export const files = [ ${manifest_data.assets .filter((asset) => kit.serviceWorker.files(asset.file)) .map((asset) => `${s(`${kit.paths.base}/${asset.file}`)}`) - .join(',\n\t\t\t\t')} + .join(',\n')} ]; export const prerendered = [ - ${prerendered.paths.map((path) => s(path)).join(',\n\t\t\t\t')} + ${prerendered.paths.map((path) => s(path)).join(',\n')} ]; export const version = ${s(kit.version.name)}; ` - .replace(/^\t{3}/gm, '') - .trim() ); await vite.build({ diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 067f71cb6339..ec16a8eb5707 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -24,6 +24,7 @@ import prerender from '../../core/postbuild/prerender.js'; import analyse from '../../core/postbuild/analyse.js'; import { s } from '../../utils/misc.js'; import { hash } from '../../runtime/hash.js'; +import { dedent } from '../../core/sync/utils.js'; export { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; @@ -863,18 +864,18 @@ function find_overridden_config(config, resolved_config, enforced_config, path, /** * @param {import('types').ValidatedConfig} config */ -const create_service_worker_module = (config) => ` -if (typeof self === 'undefined' || self instanceof ServiceWorkerGlobalScope === false) { - throw new Error('This module can only be imported inside a service worker'); -} +const create_service_worker_module = (config) => dedent` + if (typeof self === 'undefined' || self instanceof ServiceWorkerGlobalScope === false) { + throw new Error('This module can only be imported inside a service worker'); + } -export const build = []; -export const files = [ - ${create_assets(config) - .filter((asset) => config.kit.serviceWorker.files(asset.file)) - .map((asset) => `${s(`${config.kit.paths.base}/${asset.file}`)}`) - .join(',\n\t\t\t\t')} -]; -export const prerendered = []; -export const version = ${s(config.kit.version.name)}; + export const build = []; + export const files = [ + ${create_assets(config) + .filter((asset) => config.kit.serviceWorker.files(asset.file)) + .map((asset) => `${s(`${config.kit.paths.base}/${asset.file}`)}`) + .join(',\n')} + ]; + export const prerendered = []; + export const version = ${s(config.kit.version.name)}; `; From 9ce6ae8771de077c747ba018401ac4197141c326 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 1 Mar 2023 19:55:30 -0500 Subject: [PATCH 6/9] oops --- packages/kit/src/core/sync/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kit/src/core/sync/utils.js b/packages/kit/src/core/sync/utils.js index 00b296fbeb99..a1eb68d42359 100644 --- a/packages/kit/src/core/sync/utils.js +++ b/packages/kit/src/core/sync/utils.js @@ -25,7 +25,7 @@ export function write(file, code) { fs.writeFileSync(file, code); } -/** @type {WeakMap} */ const dedent_map = new WeakMap(); /** From 15c16b9328cba26f6d8aa5c14e80d58bec52ce86 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 1 Mar 2023 21:55:51 -0500 Subject: [PATCH 7/9] use dedent for virtual modules --- packages/kit/src/exports/vite/index.js | 57 +++++++++++++++----------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index ec16a8eb5707..569064bb9a98 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -355,13 +355,16 @@ function kit({ svelte_config }) { switch (id) { case '\0$env/static/private': return create_static_module('$env/static/private', env.private); + case '\0$env/static/public': return create_static_module('$env/static/public', env.public); + case '\0$env/dynamic/private': return create_dynamic_module( 'private', vite_config_env.command === 'serve' ? env.private : undefined ); + case '\0$env/dynamic/public': // populate `$env/dynamic/public` from `window` if (browser) { @@ -372,6 +375,7 @@ function kit({ svelte_config }) { 'public', vite_config_env.command === 'serve' ? env.public : undefined ); + case '\0$service-worker': return create_service_worker_module(svelte_config); @@ -384,40 +388,47 @@ function kit({ svelte_config }) { // for the sake of things like Vitest which may import this module // outside the context of a page if (browser) { - return `export const base = ${global}?.base ?? ${s(base)}; -export const assets = ${global}?.assets ?? ${assets ? s(assets) : 'base'};`; + return dedent` + export const base = ${global}?.base ?? ${s(base)}; + export const assets = ${global}?.assets ?? ${assets ? s(assets) : 'base'}; + `; } - return `export let base = ${s(base)}; -export let assets = ${assets ? s(assets) : 'base'}; + return dedent` + export let base = ${s(base)}; + export let assets = ${assets ? s(assets) : 'base'}; -export const relative = ${svelte_config.kit.paths.relative}; + export const relative = ${svelte_config.kit.paths.relative}; -const initial = { base, assets }; + const initial = { base, assets }; -export function override(paths) { - base = paths.base; - assets = paths.assets; -} + export function override(paths) { + base = paths.base; + assets = paths.assets; + } -export function reset() { - base = initial.base; - assets = initial.assets; -} + export function reset() { + base = initial.base; + assets = initial.assets; + } -/** @param {string} path */ -export function set_assets(path) { - assets = initial.assets = path; -}`; + /** @param {string} path */ + export function set_assets(path) { + assets = initial.assets = path; + } + `; case '\0__sveltekit/environment': const { version } = svelte_config.kit; - return `export const version = ${s(version.name)}; -export let building = false; -export function set_building() { - building = true; -}`; + return dedent` + export const version = ${s(version.name)}; + export let building = false; + + export function set_building() { + building = true; + } + `; } } }; From 63223fb0e4d6738cc7e5cb66daa8991a931b1287 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 2 Mar 2023 14:43:36 -0500 Subject: [PATCH 8/9] add comment --- packages/kit/src/core/sync/utils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/kit/src/core/sync/utils.js b/packages/kit/src/core/sync/utils.js index a1eb68d42359..4dd614e69d85 100644 --- a/packages/kit/src/core/sync/utils.js +++ b/packages/kit/src/core/sync/utils.js @@ -29,6 +29,7 @@ export function write(file, code) { const dedent_map = new WeakMap(); /** + * Removes leading whitespace from each line of the input string * @param {TemplateStringsArray} strings * @param {any[]} values */ From 7090d40b9d952c30bd891ffa21977a0d2acde712 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 2 Mar 2023 17:04:05 -0500 Subject: [PATCH 9/9] Update packages/kit/src/core/sync/utils.js Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> --- packages/kit/src/core/sync/utils.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/kit/src/core/sync/utils.js b/packages/kit/src/core/sync/utils.js index 4dd614e69d85..0e0181366f9d 100644 --- a/packages/kit/src/core/sync/utils.js +++ b/packages/kit/src/core/sync/utils.js @@ -29,7 +29,8 @@ export function write(file, code) { const dedent_map = new WeakMap(); /** - * Removes leading whitespace from each line of the input string + * Allows indenting template strings without the extra indentation ending up in the result. + * Still allows indentation of lines relative to one another in the template string. * @param {TemplateStringsArray} strings * @param {any[]} values */