Skip to content
Merged
5 changes: 5 additions & 0 deletions .changeset/purple-coats-vanish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"eslint-plugin-svelte": minor
---

feat: add `svelte/valid-prop-names-in-kit-pages` rule
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ These rules relate to possible syntax or logic errors in Svelte code:
| [svelte/no-unknown-style-directive-property](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-unknown-style-directive-property/) | disallow unknown `style:property` | :star: |
| [svelte/require-store-callbacks-use-set-param](https://ota-meshi.github.io/eslint-plugin-svelte/rules/require-store-callbacks-use-set-param/) | store callbacks must use `set` param | |
| [svelte/valid-compile](https://ota-meshi.github.io/eslint-plugin-svelte/rules/valid-compile/) | disallow warnings when compiling. | :star: |
| [svelte/valid-prop-names-in-kit-pages](https://ota-meshi.github.io/eslint-plugin-svelte/rules/valid-prop-names-in-kit-pages/) | disallow props other than data or errors in Svelte Kit page components. | |

## Security Vulnerability

Expand Down
1 change: 1 addition & 0 deletions docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ These rules relate to possible syntax or logic errors in Svelte code:
| [svelte/no-unknown-style-directive-property](./rules/no-unknown-style-directive-property.md) | disallow unknown `style:property` | :star: |
| [svelte/require-store-callbacks-use-set-param](./rules/require-store-callbacks-use-set-param.md) | store callbacks must use `set` param | |
| [svelte/valid-compile](./rules/valid-compile.md) | disallow warnings when compiling. | :star: |
| [svelte/valid-prop-names-in-kit-pages](./rules/valid-prop-names-in-kit-pages.md) | disallow props other than data or errors in Svelte Kit page components. | |

## Security Vulnerability

Expand Down
65 changes: 65 additions & 0 deletions docs/rules/valid-prop-names-in-kit-pages.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
pageClass: "rule-details"
sidebarDepth: 0
title: "svelte/valid-prop-names-in-kit-pages"
description: "disallow props other than data or errors in Svelte Kit page components."
---

# svelte/valid-prop-names-in-kit-pages

> disallow props other than data or errors in Svelte Kit page components.

- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> **_This rule has not been released yet._** </badge>

## :book: Rule Details

This rule reports unexpected exported variables at `<script>`.<br>
At SvelteKit v1.0.0-next.405, instead of having multiple props corresponding to the props returned from a load function, page components now have a single data prop.

<script>
const config = {settings: {
kit: {
files: {
routes: "",
},
},
},
}
</script>

<ESLintCodeBlock config="{config}">

<!--eslint-skip-->

```svelte
<script>
/* eslint svelte/valid-prop-names-in-kit-pages: "error" */
/** ✓ GOOD */
export let data
export let errors
// export let { data, errors } = { data: {}, errors: {} }

/** ✗ BAD */
export let foo
export let bar
export let { baz, qux } = data
export let { data: data2, errors: errors2 } = { data: {}, errors: {} }
</script>

{foo}, {bar}
```

</ESLintCodeBlock>

## :wrench: Options

Nothing. But if use are using not default routes folder, please set configuration according to the [user guide](../user-guide.md#settings-kit).

## :books: Further Reading

- [SvelteKit Migration Guide (v1.0.0-next.405)](https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292707)

## :mag: Implementation

- [Rule source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/src/rules/valid-prop-names-in-kit-pages.ts)
- [Test source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/tests/src/rules/valid-prop-names-in-kit-pages.ts)
83 changes: 83 additions & 0 deletions src/rules/valid-prop-names-in-kit-pages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import type { AST } from "svelte-eslint-parser"
import type * as ESTree from "estree"
import { createRule } from "../utils"
import { isKitPageComponent } from "../utils/svelte-kit"

const EXPECTED_PROP_NAMES = ["data", "errors"]

export default createRule("valid-prop-names-in-kit-pages", {
meta: {
docs: {
description:
"disallow props other than data or errors in Svelte Kit page components.",
category: "Possible Errors",
// TODO Switch to recommended in the major version.
recommended: false,
},
schema: [],
messages: {
unexpected:
"disallow props other than data or errors in Svelte Kit page components.",
},
type: "problem",
},
create(context) {
if (!isKitPageComponent(context)) return {}
let isScript = false
return {
// <script>
"Program > SvelteScriptElement > SvelteStartTag": (
node: AST.SvelteStartTag,
) => {
// except for <script context="module">
isScript = !node.attributes.some(
(a) =>
a.type === "SvelteAttribute" &&
a.key.name === "context" &&
a.value.some(
(v) => v.type === "SvelteLiteral" && v.value === "module",
),
)
},

// </script>
"Program > SvelteScriptElement:exit": () => {
isScript = false
},

"ExportNamedDeclaration > VariableDeclaration > VariableDeclarator": (
node: ESTree.VariableDeclarator,
) => {
if (!isScript) return

// export let foo
if (node.id.type === "Identifier") {
if (!EXPECTED_PROP_NAMES.includes(node.id.name)) {
context.report({
node,
loc: node.loc!,
messageId: "unexpected",
})
}
return
}

// export let { xxx, yyy } = zzz
if (node.id.type !== "ObjectPattern") return
for (const p of node.id.properties) {
if (
p.type === "Property" &&
p.value.type === "Identifier" &&
!EXPECTED_PROP_NAMES.includes(p.value.name)
) {
context.report({
node: p.value,
loc: p.value.loc!,
messageId: "unexpected",
})
}
}
},
}
},
})
2 changes: 2 additions & 0 deletions src/utils/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import sortAttributes from "../rules/sort-attributes"
import spacedHtmlComment from "../rules/spaced-html-comment"
import system from "../rules/system"
import validCompile from "../rules/valid-compile"
import validPropNamesInKitPages from "../rules/valid-prop-names-in-kit-pages"

export const rules = [
typescriptEslintNoUnnecessaryCondition,
Expand Down Expand Up @@ -87,4 +88,5 @@ export const rules = [
spacedHtmlComment,
system,
validCompile,
validPropNamesInKitPages,
] as RuleModule[]
7 changes: 6 additions & 1 deletion src/utils/svelte-kit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ export function isKitPageComponent(context: RuleContext): boolean {
"src/routes"
const filePath = context.getFilename()
const projectRootDir = getProjectRootDir(context.getFilename()) ?? ""
return filePath.startsWith(path.join(projectRootDir, routes))
const fileName = filePath.split("/").pop() || filePath
return (
filePath.startsWith(path.join(projectRootDir, routes)) &&
// MEMO: check only `+` and file extension for maintainability
Boolean(/^\+.+\.svelte$/.test(fileName))
)
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
- message: disallow props other than data or errors in Svelte Kit page components.
line: 2
column: 14
suggestions: null
- message: disallow props other than data or errors in Svelte Kit page components.
line: 3
column: 14
suggestions: null
- message: disallow props other than data or errors in Svelte Kit page components.
line: 4
column: 16
suggestions: null
- message: disallow props other than data or errors in Svelte Kit page components.
line: 4
column: 21
suggestions: null
- message: disallow props other than data or errors in Svelte Kit page components.
line: 5
column: 22
suggestions: null
- message: disallow props other than data or errors in Svelte Kit page components.
line: 5
column: 37
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script>
export let foo
export let bar
export let { baz, qux } = data
export let { data: data2, errors: errors2 } = { data: {}, errors: {} }
</script>

{foo}, {bar}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"settings": {
"svelte": {
"kit": {
"files": {
"routes": "tests/fixtures"
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<script>
export let data
export let errors
</script>

{data}, {errors}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script context="module">
export let data
export let errors
export let foo
export let bar
</script>

{data}, {errors}, {foo}, {bar}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script context="module">
export let { data, errors } = { data: {}, errors: {} }
</script>

{data}, {errors}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script context="module">
export let { data2: data, errors2: errors } = { data2: {}, errors2: {} }
</script>

{data}, {errors}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"settings": {
"svelte": {
"kit": {
"files": {
"routes": "tests/fixtures"
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script>
export let data
export let errors
export let foo
export let bar
</script>

{data}, {errors}, {foo}, {bar}
16 changes: 16 additions & 0 deletions tests/src/rules/valid-prop-names-in-kit-pages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { RuleTester } from "eslint"
import rule from "../../../src/rules/valid-prop-names-in-kit-pages"
import { loadTestCases } from "../../utils/utils"

const tester = new RuleTester({
parserOptions: {
ecmaVersion: 2020,
sourceType: "module",
},
})

tester.run(
"valid-prop-names-in-kit-pages",
rule as any,
loadTestCases("valid-prop-names-in-kit-pages"),
)