Skip to content

feat: no-not-data-props-in-kit-pages #283

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

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"),
)