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