Skip to content

Commit

Permalink
chore: dedent (#9273)
Browse files Browse the repository at this point in the history
* create dedent utility

* recursive dedenting

* use `dedent` when creating env declarations

* use dedent in manifest generation

* dedent more stuff

* oops

* use dedent for virtual modules

* add comment

* Update packages/kit/src/core/sync/utils.js

Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com>

---------

Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com>
  • Loading branch information
Rich-Harris and benmccann authored Mar 2, 2023
1 parent 8095a63 commit 3124296
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 146 deletions.
27 changes: 18 additions & 9 deletions packages/kit/src/core/env.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { GENERATED_COMMENT } from '../constants.js';
import { dedent } from './sync/utils.js';
import { runtime_base } from './utils.js';

/**
Expand Down Expand Up @@ -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')}
}
`;
}

/**
Expand All @@ -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([
Expand Down
63 changes: 34 additions & 29 deletions packages/kit/src/core/generate_manifest/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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, '');
`;
}
47 changes: 42 additions & 5 deletions packages/kit/src/core/sync/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,46 @@ 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<TemplateStringsArray, { strings: string[], indents: string[] }>} */
const dedent_map = new WeakMap();

/**
* 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
*/
export function dedent(strings, ...values) {
let dedented = dedent_map.get(strings);

if (!dedented) {
const indentation = /** @type {RegExpExecArray} */ (/\n?([ \t]*)/.exec(strings[0]))[1];
const pattern = new RegExp(`^${indentation}`, 'gm');

dedented = {
strings: strings.map((str) => str.replace(pattern, '')),
indents: []
};

let current = '\n';

for (let i = 0; i < values.length; i += 1) {
const string = dedented.strings[i];
const match = /\n([ \t]*)$/.exec(string);

if (match) current = match[0];
dedented.indents[i] = current;
}

dedent_map.set(strings, dedented);
}

let str = dedented.strings[0];
for (let i = 0; i < values.length; i += 1) {
str += String(values[i]).replace(/\n/g, dedented.indents[i]) + dedented.strings[i + 1];
}

str = str.trim();

return str;
}
106 changes: 55 additions & 51 deletions packages/kit/src/core/sync/write_client_manifest.js
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -45,75 +45,79 @@ 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);

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';
export const nodes = [${nodes}];
export const nodes = [
${nodes}
];
export const server_loads = [${[...layouts_with_server_load].join(',')}];
Expand All @@ -126,7 +130,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
Expand Down
18 changes: 8 additions & 10 deletions packages/kit/src/core/sync/write_root.js
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -24,22 +24,20 @@ export function write_root(manifest_data, output) {
let pyramid = `<svelte:component this={constructors[${l}]} bind:this={components[${l}]} data={data_${l}} {form} />`;

while (l--) {
pyramid = `
pyramid = dedent`
{#if constructors[${l + 1}]}
<svelte:component this={constructors[${l}]} bind:this={components[${l}]} data={data_${l}}>
${pyramid.replace(/\n/g, '\n\t\t\t\t\t')}
${pyramid}
</svelte:component>
{:else}
<svelte:component this={constructors[${l}]} bind:this={components[${l}]} data={data_${l}} {form} />
{/if}
`
.replace(/^\t\t\t/gm, '')
.trim();
`;
}

write_if_changed(
`${output}/root.svelte`,
trim(`
dedent`
<!-- This file is generated by @sveltejs/kit — do not edit it! -->
<script>
import { setContext, afterUpdate, onMount } from 'svelte';
Expand All @@ -52,7 +50,7 @@ export function write_root(manifest_data, output) {
export let constructors;
export let components = [];
export let form;
${levels.map((l) => `export let data_${l} = null;`).join('\n\t\t\t\t')}
${levels.map((l) => `export let data_${l} = null;`).join('\n')}
if (!browser) {
setContext('__svelte__', stores);
Expand All @@ -78,7 +76,7 @@ export function write_root(manifest_data, output) {
});
</script>
${pyramid.replace(/\n/g, '\n\t\t\t')}
${pyramid}
{#if mounted}
<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">
Expand All @@ -87,6 +85,6 @@ export function write_root(manifest_data, output) {
{/if}
</div>
{/if}
`)
`
);
}
Loading

0 comments on commit 3124296

Please sign in to comment.