diff --git a/.changeset/slow-spiders-agree.md b/.changeset/slow-spiders-agree.md new file mode 100644 index 000000000000..48d86996609f --- /dev/null +++ b/.changeset/slow-spiders-agree.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +Provide helpful error message on invalid named layout reference diff --git a/documentation/docs/04-advanced-routing.md b/documentation/docs/04-advanced-routing.md index 4541b15f834f..8c82a8be0ec2 100644 --- a/documentation/docs/04-advanced-routing.md +++ b/documentation/docs/04-advanced-routing.md @@ -144,6 +144,8 @@ Some parts of your app might need something other than the default layout. For t

I am inside +layout-foo

``` +> Named layout should only be referenced from Svelte files + Named layouts are very powerful, but it can take a minute to get your head round them. Don't worry if this doesn't make sense all at once. #### Scoping diff --git a/packages/kit/src/core/sync/create_manifest_data/index.js b/packages/kit/src/core/sync/create_manifest_data/index.js index 9234a5e3f439..3cba3936d5b4 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.js @@ -67,14 +67,7 @@ export default function create_manifest_data({ if (file[0] !== '+') return; // not a route file - const item = analyze(file, config.extensions, config.kit.moduleExtensions); - - if (!item) { - throw new Error( - `Files and directories prefixed with + are reserved (saw ${project_relative})` - ); - } - + const item = analyze(project_relative, file, config.extensions, config.kit.moduleExtensions); const id = segments.join('/'); if (/\]\[/.test(id)) { @@ -275,19 +268,22 @@ export default function create_manifest_data({ } /** + * @param {string} project_relative * @param {string} file * @param {string[]} component_extensions * @param {string[]} module_extensions - * @returns {import('./types').RouteFile | null} + * @returns {import('./types').RouteFile} */ -function analyze(file, component_extensions, module_extensions) { +function analyze(project_relative, file, component_extensions, module_extensions) { const component_extension = component_extensions.find((ext) => file.endsWith(ext)); if (component_extension) { const name = file.slice(0, -component_extension.length); const pattern = /^\+(?:(page(?:@([a-zA-Z0-9_-]+))?)|(layout(?:-([a-zA-Z0-9_-]+))?(?:@([a-zA-Z0-9_-]+))?)|(error))$/; const match = pattern.exec(name); - if (!match) return null; + if (!match) { + throw new Error(`Files prefixed with + are reserved (saw ${project_relative})`); + } return { kind: 'component', @@ -302,21 +298,29 @@ function analyze(file, component_extensions, module_extensions) { const module_extension = module_extensions.find((ext) => file.endsWith(ext)); if (module_extension) { const name = file.slice(0, -module_extension.length); - const pattern = /^\+(?:(server)|(page(\.server)?)|(layout(?:-([a-zA-Z0-9_-]+))?(\.server)?))$/; + const pattern = + /^\+(?:(server)|(page(?:@([a-zA-Z0-9_-]+))?(\.server)?)|(layout(?:-([a-zA-Z0-9_-]+))?(?:@([a-zA-Z0-9_-]+))?(\.server)?))$/; const match = pattern.exec(name); - if (!match) return null; + if (!match) { + throw new Error(`Files prefixed with + are reserved (saw ${project_relative})`); + } else if (match[3] || match[7]) { + throw new Error( + // prettier-ignore + `Only Svelte files can reference named layouts. Remove '@${match[3] || match[7]}' from ${file} (at ${project_relative})` + ); + } - const kind = !!(match[1] || match[3] || match[6]) ? 'server' : 'shared'; + const kind = !!(match[1] || match[4] || match[8]) ? 'server' : 'shared'; return { kind, is_page: !!match[2], - is_layout: !!match[4], - declares_layout: match[5] + is_layout: !!match[5], + declares_layout: match[6] }; } - return null; + throw new Error(`Files and directories prefixed with + are reserved (saw ${project_relative})`); } /** diff --git a/packages/kit/src/core/sync/create_manifest_data/index.spec.js b/packages/kit/src/core/sync/create_manifest_data/index.spec.js index a841e923d4a1..f60559ec4d15 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.spec.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.spec.js @@ -566,6 +566,13 @@ test('errors on layout named default', () => { ); }); +test('errors on invalid named layout reference', () => { + assert.throws( + () => create('samples/invalid-named-layout-reference'), + /Only Svelte files can reference named layouts. Remove '@named' from \+page@named.js \(at samples\/invalid-named-layout-reference\/\+page@named.js\)/ + ); +}); + test('errors on duplicate layout definition', () => { assert.throws( () => create('samples/duplicate-layout'), diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/invalid-named-layout-reference/+layout-named.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/invalid-named-layout-reference/+layout-named.svelte new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/invalid-named-layout-reference/+page@named.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/invalid-named-layout-reference/+page@named.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/invalid-named-layout-reference/+page@named.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/invalid-named-layout-reference/+page@named.svelte new file mode 100644 index 000000000000..e69de29bb2d1