Skip to content

Commit 3124296

Browse files
chore: dedent (#9273)
* 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>
1 parent 8095a63 commit 3124296

File tree

7 files changed

+210
-146
lines changed

7 files changed

+210
-146
lines changed

packages/kit/src/core/env.js

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { GENERATED_COMMENT } from '../constants.js';
2+
import { dedent } from './sync/utils.js';
23
import { runtime_base } from './utils.js';
34

45
/**
@@ -50,10 +51,13 @@ export function create_dynamic_module(type, dev_values) {
5051
export function create_static_types(id, env) {
5152
const declarations = Object.keys(env[id])
5253
.filter((k) => valid_identifier.test(k))
53-
.map((k) => `\texport const ${k}: string;`)
54-
.join('\n');
54+
.map((k) => `export const ${k}: string;`);
5555

56-
return `declare module '$env/static/${id}' {\n${declarations}\n}`;
56+
return dedent`
57+
declare module '$env/static/${id}' {
58+
${declarations.join('\n')}
59+
}
60+
`;
5761
}
5862

5963
/**
@@ -65,19 +69,24 @@ export function create_static_types(id, env) {
6569
export function create_dynamic_types(id, env, prefix) {
6670
const properties = Object.keys(env[id])
6771
.filter((k) => valid_identifier.test(k))
68-
.map((k) => `\t\t${k}: string;`);
72+
.map((k) => `${k}: string;`);
6973

7074
const prefixed = `[key: \`${prefix}\${string}\`]`;
7175

7276
if (id === 'private') {
73-
properties.push(`\t\t${prefixed}: undefined;`);
74-
properties.push(`\t\t[key: string]: string | undefined;`);
77+
properties.push(`${prefixed}: undefined;`);
78+
properties.push(`[key: string]: string | undefined;`);
7579
} else {
76-
properties.push(`\t\t${prefixed}: string | undefined;`);
80+
properties.push(`${prefixed}: string | undefined;`);
7781
}
7882

79-
const declaration = `export const env: {\n${properties.join('\n')}\n\t}`;
80-
return `declare module '$env/dynamic/${id}' {\n\t${declaration}\n}`;
83+
return dedent`
84+
declare module '$env/dynamic/${id}' {
85+
export const env: {
86+
${properties.join('\n')}
87+
}
88+
}
89+
`;
8190
}
8291

8392
export const reserved = new Set([

packages/kit/src/core/generate_manifest/index.js

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { get_mime_lookup } from '../utils.js';
33
import { resolve_symlinks } from '../../exports/vite/build/utils.js';
44
import { compact } from '../../utils/array.js';
55
import { join_relative } from '../../utils/filesystem.js';
6+
import { dedent } from '../sync/utils.js';
67

78
/**
89
* 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 }) {
8081
// prettier-ignore
8182
// String representation of
8283
/** @type {import('types').SSRManifest} */
83-
return `{
84-
appDir: ${s(build_data.app_dir)},
85-
appPath: ${s(build_data.app_path)},
86-
assets: new Set(${s(assets)}),
87-
mimeTypes: ${s(get_mime_lookup(build_data.manifest_data))},
88-
_: {
89-
client: ${s(build_data.client)},
90-
nodes: [
91-
${(node_paths).map(loader).join(',\n\t\t\t\t')}
92-
],
93-
routes: [
94-
${routes.map(route => {
95-
route.params.forEach(param => {
96-
if (param.matcher) matchers.add(param.matcher);
97-
});
84+
return dedent`
85+
{
86+
appDir: ${s(build_data.app_dir)},
87+
appPath: ${s(build_data.app_path)},
88+
assets: new Set(${s(assets)}),
89+
mimeTypes: ${s(get_mime_lookup(build_data.manifest_data))},
90+
_: {
91+
client: ${s(build_data.client)},
92+
nodes: [
93+
${(node_paths).map(loader).join(',\n')}
94+
],
95+
routes: [
96+
${routes.map(route => {
97+
route.params.forEach(param => {
98+
if (param.matcher) matchers.add(param.matcher);
99+
});
98100
99-
if (!route.page && !route.endpoint) return;
101+
if (!route.page && !route.endpoint) return;
100102
101-
return `{
102-
id: ${s(route.id)},
103-
pattern: ${route.pattern},
104-
params: ${s(route.params)},
105-
page: ${route.page ? `{ layouts: ${get_nodes(route.page.layouts)}, errors: ${get_nodes(route.page.errors)}, leaf: ${reindexed.get(route.page.leaf)} }` : 'null'},
106-
endpoint: ${route.endpoint ? loader(join_relative(relative_path, resolve_symlinks(build_data.server_manifest, route.endpoint.file).chunk.file)) : 'null'}
107-
}`;
108-
}).filter(Boolean).join(',\n\t\t\t\t')}
109-
],
110-
matchers: async () => {
111-
${Array.from(matchers).map(type => `const { match: ${type} } = await import ('${(join_relative(relative_path, `/entries/matchers/${type}.js`))}')`).join('\n\t\t\t\t')}
112-
return { ${Array.from(matchers).join(', ')} };
103+
return dedent`
104+
{
105+
id: ${s(route.id)},
106+
pattern: ${route.pattern},
107+
params: ${s(route.params)},
108+
page: ${route.page ? `{ layouts: ${get_nodes(route.page.layouts)}, errors: ${get_nodes(route.page.errors)}, leaf: ${reindexed.get(route.page.leaf)} }` : 'null'},
109+
endpoint: ${route.endpoint ? loader(join_relative(relative_path, resolve_symlinks(build_data.server_manifest, route.endpoint.file).chunk.file)) : 'null'}
110+
}
111+
`;
112+
}).filter(Boolean).join(',\n')}
113+
],
114+
matchers: async () => {
115+
${Array.from(matchers).map(type => `const { match: ${type} } = await import ('${(join_relative(relative_path, `/entries/matchers/${type}.js`))}')`).join('\n')}
116+
return { ${Array.from(matchers).join(', ')} };
117+
}
113118
}
114119
}
115-
}`.replace(/^\t/gm, '');
120+
`;
116121
}

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

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,46 @@ export function write(file, code) {
2525
fs.writeFileSync(file, code);
2626
}
2727

28-
/** @param {string} str */
29-
export function trim(str) {
30-
const indentation = /** @type {RegExpExecArray} */ (/\n?([ \t]*)/.exec(str))[1];
31-
const pattern = new RegExp(`^${indentation}`, 'gm');
32-
return str.replace(pattern, '').trim();
28+
/** @type {WeakMap<TemplateStringsArray, { strings: string[], indents: string[] }>} */
29+
const dedent_map = new WeakMap();
30+
31+
/**
32+
* Allows indenting template strings without the extra indentation ending up in the result.
33+
* Still allows indentation of lines relative to one another in the template string.
34+
* @param {TemplateStringsArray} strings
35+
* @param {any[]} values
36+
*/
37+
export function dedent(strings, ...values) {
38+
let dedented = dedent_map.get(strings);
39+
40+
if (!dedented) {
41+
const indentation = /** @type {RegExpExecArray} */ (/\n?([ \t]*)/.exec(strings[0]))[1];
42+
const pattern = new RegExp(`^${indentation}`, 'gm');
43+
44+
dedented = {
45+
strings: strings.map((str) => str.replace(pattern, '')),
46+
indents: []
47+
};
48+
49+
let current = '\n';
50+
51+
for (let i = 0; i < values.length; i += 1) {
52+
const string = dedented.strings[i];
53+
const match = /\n([ \t]*)$/.exec(string);
54+
55+
if (match) current = match[0];
56+
dedented.indents[i] = current;
57+
}
58+
59+
dedent_map.set(strings, dedented);
60+
}
61+
62+
let str = dedented.strings[0];
63+
for (let i = 0; i < values.length; i += 1) {
64+
str += String(values[i]).replace(/\n/g, dedented.indents[i]) + dedented.strings[i + 1];
65+
}
66+
67+
str = str.trim();
68+
69+
return str;
3370
}

packages/kit/src/core/sync/write_client_manifest.js

Lines changed: 55 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { relative_path, resolve_entry } from '../../utils/filesystem.js';
22
import { s } from '../../utils/misc.js';
3-
import { trim, write_if_changed } from './utils.js';
3+
import { dedent, write_if_changed } from './utils.js';
44

55
/**
66
* Writes the client manifest to disk. The manifest is used to power the router. It contains the
@@ -45,75 +45,79 @@ export function write_client_manifest(kit, manifest_data, output, metadata) {
4545
write_if_changed(`${output}/nodes/${i}.js`, generate_node(node));
4646
return `() => import('./nodes/${i}')`;
4747
})
48-
.join(',\n\t');
48+
.join(',\n');
4949

5050
const layouts_with_server_load = new Set();
5151

52-
const dictionary = `{
53-
${manifest_data.routes
54-
.map((route) => {
55-
if (route.page) {
56-
const errors = route.page.errors.slice(1).map((n) => n ?? '');
57-
const layouts = route.page.layouts.slice(1).map((n) => n ?? '');
58-
59-
while (layouts.at(-1) === '') layouts.pop();
60-
while (errors.at(-1) === '') errors.pop();
61-
62-
let leaf_has_server_load = false;
63-
if (route.leaf) {
64-
if (metadata) {
65-
const i = /** @type {number} */ (indices.get(route.leaf));
66-
67-
leaf_has_server_load = metadata[i].has_server_load;
68-
} else if (route.leaf.server) {
69-
leaf_has_server_load = true;
52+
const dictionary = dedent`
53+
{
54+
${manifest_data.routes
55+
.map((route) => {
56+
if (route.page) {
57+
const errors = route.page.errors.slice(1).map((n) => n ?? '');
58+
const layouts = route.page.layouts.slice(1).map((n) => n ?? '');
59+
60+
while (layouts.at(-1) === '') layouts.pop();
61+
while (errors.at(-1) === '') errors.pop();
62+
63+
let leaf_has_server_load = false;
64+
if (route.leaf) {
65+
if (metadata) {
66+
const i = /** @type {number} */ (indices.get(route.leaf));
67+
68+
leaf_has_server_load = metadata[i].has_server_load;
69+
} else if (route.leaf.server) {
70+
leaf_has_server_load = true;
71+
}
7072
}
71-
}
7273
73-
// Encode whether or not the route uses server data
74-
// using the ones' complement, to save space
75-
const array = [`${leaf_has_server_load ? '~' : ''}${route.page.leaf}`];
74+
// Encode whether or not the route uses server data
75+
// using the ones' complement, to save space
76+
const array = [`${leaf_has_server_load ? '~' : ''}${route.page.leaf}`];
7677
77-
// Encode whether or not the layout uses server data.
78-
// It's a different method compared to pages because layouts
79-
// are reused across pages, so we save space by doing it this way.
80-
route.page.layouts.forEach((layout) => {
81-
if (layout == undefined) return;
78+
// Encode whether or not the layout uses server data.
79+
// It's a different method compared to pages because layouts
80+
// are reused across pages, so we save space by doing it this way.
81+
route.page.layouts.forEach((layout) => {
82+
if (layout == undefined) return;
8283
83-
let layout_has_server_load = false;
84+
let layout_has_server_load = false;
8485
85-
if (metadata) {
86-
layout_has_server_load = metadata[layout].has_server_load;
87-
} else if (manifest_data.nodes[layout].server) {
88-
layout_has_server_load = true;
89-
}
86+
if (metadata) {
87+
layout_has_server_load = metadata[layout].has_server_load;
88+
} else if (manifest_data.nodes[layout].server) {
89+
layout_has_server_load = true;
90+
}
9091
91-
if (layout_has_server_load) {
92-
layouts_with_server_load.add(layout);
93-
}
94-
});
92+
if (layout_has_server_load) {
93+
layouts_with_server_load.add(layout);
94+
}
95+
});
9596
96-
// only include non-root layout/error nodes if they exist
97-
if (layouts.length > 0 || errors.length > 0) array.push(`[${layouts.join(',')}]`);
98-
if (errors.length > 0) array.push(`[${errors.join(',')}]`);
97+
// only include non-root layout/error nodes if they exist
98+
if (layouts.length > 0 || errors.length > 0) array.push(`[${layouts.join(',')}]`);
99+
if (errors.length > 0) array.push(`[${errors.join(',')}]`);
99100
100-
return `${s(route.id)}: [${array.join(',')}]`;
101-
}
102-
})
103-
.filter(Boolean)
104-
.join(',\n\t\t')}
105-
}`.replace(/^\t/gm, '');
101+
return `${s(route.id)}: [${array.join(',')}]`;
102+
}
103+
})
104+
.filter(Boolean)
105+
.join(',\n')}
106+
}
107+
`;
106108

107109
const hooks_file = resolve_entry(kit.files.hooks.client);
108110

109111
write_if_changed(
110112
`${output}/app.js`,
111-
trim(`
113+
dedent`
112114
${hooks_file ? `import * as client_hooks from '${relative_path(output, hooks_file)}';` : ''}
113115
114116
export { matchers } from './matchers.js';
115117
116-
export const nodes = [${nodes}];
118+
export const nodes = [
119+
${nodes}
120+
];
117121
118122
export const server_loads = [${[...layouts_with_server_load].join(',')}];
119123
@@ -126,7 +130,7 @@ export function write_client_manifest(kit, manifest_data, output, metadata) {
126130
};
127131
128132
export { default as root } from '../root.svelte';
129-
`)
133+
`
130134
);
131135

132136
// write matchers to a separate module so that we don't

packages/kit/src/core/sync/write_root.js

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { trim, write_if_changed } from './utils.js';
1+
import { dedent, write_if_changed } from './utils.js';
22

33
/**
44
* @param {import('types').ManifestData} manifest_data
@@ -24,22 +24,20 @@ export function write_root(manifest_data, output) {
2424
let pyramid = `<svelte:component this={constructors[${l}]} bind:this={components[${l}]} data={data_${l}} {form} />`;
2525

2626
while (l--) {
27-
pyramid = `
27+
pyramid = dedent`
2828
{#if constructors[${l + 1}]}
2929
<svelte:component this={constructors[${l}]} bind:this={components[${l}]} data={data_${l}}>
30-
${pyramid.replace(/\n/g, '\n\t\t\t\t\t')}
30+
${pyramid}
3131
</svelte:component>
3232
{:else}
3333
<svelte:component this={constructors[${l}]} bind:this={components[${l}]} data={data_${l}} {form} />
3434
{/if}
35-
`
36-
.replace(/^\t\t\t/gm, '')
37-
.trim();
35+
`;
3836
}
3937

4038
write_if_changed(
4139
`${output}/root.svelte`,
42-
trim(`
40+
dedent`
4341
<!-- This file is generated by @sveltejs/kit — do not edit it! -->
4442
<script>
4543
import { setContext, afterUpdate, onMount } from 'svelte';
@@ -52,7 +50,7 @@ export function write_root(manifest_data, output) {
5250
export let constructors;
5351
export let components = [];
5452
export let form;
55-
${levels.map((l) => `export let data_${l} = null;`).join('\n\t\t\t\t')}
53+
${levels.map((l) => `export let data_${l} = null;`).join('\n')}
5654
5755
if (!browser) {
5856
setContext('__svelte__', stores);
@@ -78,7 +76,7 @@ export function write_root(manifest_data, output) {
7876
});
7977
</script>
8078
81-
${pyramid.replace(/\n/g, '\n\t\t\t')}
79+
${pyramid}
8280
8381
{#if mounted}
8482
<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">
@@ -87,6 +85,6 @@ export function write_root(manifest_data, output) {
8785
{/if}
8886
</div>
8987
{/if}
90-
`)
88+
`
9189
);
9290
}

0 commit comments

Comments
 (0)