Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: dedent #9273

Merged
merged 9 commits into from
Mar 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
benmccann marked this conversation as resolved.
Show resolved Hide resolved
* @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