Skip to content

feat: add no-export-load-in-svelte-module-in-kit-pages #281

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
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
bdd3580
feat: add no-export-load-in-svelte-module-in-kit-pages
baseballyama Oct 29, 2022
ab5e4d2
chore: add changeset
baseballyama Oct 29, 2022
c92a052
chore: add test
baseballyama Oct 29, 2022
07dbc08
fix: check devDependencies also
baseballyama Oct 29, 2022
08fc369
fix: adjust for test
baseballyama Oct 29, 2022
4cf5a14
feat: add config
baseballyama Oct 29, 2022
f8de8bc
chore: fix typo
baseballyama Oct 29, 2022
203bf8e
fix: bug
baseballyama Oct 30, 2022
a9fb99d
fix: bug
baseballyama Oct 30, 2022
59d3097
chore: uupdate docus
baseballyama Oct 30, 2022
4fc4c22
chore: update docs
baseballyama Oct 30, 2022
332c4bc
chore: add kit helper
baseballyama Oct 30, 2022
d5bdacd
fix: fix for demo
baseballyama Oct 30, 2022
f321a12
chore: adjust doc style
baseballyama Oct 30, 2022
dc451d3
Merge branch 'main' into feature/no-export-load-in-svelte-module-in-k…
baseballyama Oct 31, 2022
d0d21e7
chore: revert .eslintrc.js
baseballyama Oct 31, 2022
38c2690
chore: update uppercase -> lowercase
baseballyama Oct 31, 2022
2eb81df
fix: update recommended
baseballyama Oct 31, 2022
3137c67
fix: i did not know :exit
baseballyama Oct 31, 2022
5353b32
fix: project root path finding logic
baseballyama Oct 31, 2022
d68a1cf
chore: utilize
baseballyama Oct 31, 2022
20ad311
chore: yarn update
baseballyama Oct 31, 2022
7584b10
fix: remove needless logic
baseballyama Oct 31, 2022
cfaf58c
chore: simplify
baseballyama Oct 31, 2022
a903027
fix: false positive
baseballyama Nov 1, 2022
7d870c0
Merge branch 'main' into feature/no-export-load-in-svelte-module-in-k…
ota-meshi Nov 1, 2022
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
5 changes: 5 additions & 0 deletions .changeset/orange-months-sparkle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"eslint-plugin-svelte": minor
---

feat: add `no-export-load-in-svelte-module-in-kit-pages` rule
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,28 @@ module.exports = {
}
```

#### settings.kit

If you use SvelteKit with not default configuration, you need to set below configurations.
The schema is subset of SvelteKit's configuration.
Therefore please check [SvelteKit docs](https://kit.svelte.dev/docs/configuration) for more details.

e.g.

```js
module.exports = {
// ...
settings: {
kit: {
files: {
routes: "src/routes",
},
},
},
// ...
}
Comment on lines +228 to +238
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I choose that I added shared configuration for kit routes path.
I think over 95% of users use default settings src/routes.
So only a few users need to config it.

And for 5% users, we discussed that we will use espree but it may fail to get the path if they rely to env of some external information. So for now I didn't implement this logic.
#241 (comment)

```

### Running ESLint from the command line

If you want to run `eslint` from the command line, make sure you include the `.svelte` extension using [the `--ext` option](https://eslint.org/docs/user-guide/configuring#specifying-file-extensions-to-lint) or a glob pattern, because ESLint targets only `.js` files by default.
Expand Down Expand Up @@ -266,6 +288,7 @@ These rules relate to possible syntax or logic errors in Svelte code:
| [svelte/no-dupe-else-if-blocks](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-dupe-else-if-blocks/) | disallow duplicate conditions in `{#if}` / `{:else if}` chains | :star: |
| [svelte/no-dupe-style-properties](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-dupe-style-properties/) | disallow duplicate style properties | :star: |
| [svelte/no-dynamic-slot-name](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-dynamic-slot-name/) | disallow dynamic slot name | :star::wrench: |
| [svelte/no-export-load-in-svelte-module-in-kit-pages](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-export-load-in-svelte-module-in-kit-pages/) | disallow exporting load functions in `*.svelte` module in Svelte Kit page components. | |
| [svelte/no-not-function-handler](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-not-function-handler/) | disallow use of not function in event handler | :star: |
| [svelte/no-object-in-text-mustaches](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-object-in-text-mustaches/) | disallow objects in text mustache interpolation | :star: |
| [svelte/no-shorthand-style-property-overrides](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-shorthand-style-property-overrides/) | disallow shorthand style properties that override related longhand properties | :star: |
Expand Down
2 changes: 2 additions & 0 deletions docs-svelte-kit/src/lib/components/ESLintCodeBlock.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
let tsParser = null

let code = ""
export let config = {}
export let rules = {}
export let fix = false
export let language = "svelte"
Expand Down Expand Up @@ -75,6 +76,7 @@
browser: true,
es2021: true,
},
...config,
}}
{language}
{options}
Expand Down
1 change: 1 addition & 0 deletions docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ These rules relate to possible syntax or logic errors in Svelte code:
| [svelte/no-dupe-else-if-blocks](./rules/no-dupe-else-if-blocks.md) | disallow duplicate conditions in `{#if}` / `{:else if}` chains | :star: |
| [svelte/no-dupe-style-properties](./rules/no-dupe-style-properties.md) | disallow duplicate style properties | :star: |
| [svelte/no-dynamic-slot-name](./rules/no-dynamic-slot-name.md) | disallow dynamic slot name | :star::wrench: |
| [svelte/no-export-load-in-svelte-module-in-kit-pages](./rules/no-export-load-in-svelte-module-in-kit-pages.md) | disallow exporting load functions in `*.svelte` module in Svelte Kit page components. | |
| [svelte/no-not-function-handler](./rules/no-not-function-handler.md) | disallow use of not function in event handler | :star: |
| [svelte/no-object-in-text-mustaches](./rules/no-object-in-text-mustaches.md) | disallow objects in text mustache interpolation | :star: |
| [svelte/no-shorthand-style-property-overrides](./rules/no-shorthand-style-property-overrides.md) | disallow shorthand style properties that override related longhand properties | :star: |
Expand Down
61 changes: 61 additions & 0 deletions docs/rules/no-export-load-in-svelte-module-in-kit-pages.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
pageClass: "rule-details"
sidebarDepth: 0
title: "svelte/no-export-load-in-svelte-module-in-kit-pages"
description: "disallow exporting load functions in `*.svelte` module in Svelte Kit page components."
---

# svelte/no-export-load-in-svelte-module-in-kit-pages

> disallow exporting load functions in `*.svelte` module 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 `load` function at `<script context="module">`.
At SvelteKit v1.0.0-next.405, `load` function has been moved into a separate file — `+page.js` for pages, `+layout.js` for layouts.
And the API has changed.

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

<ESLintCodeBlock config="{config}">

<!--eslint-skip-->

```svelte
<script context="module">
/* eslint svelte/no-export-load-in-svelte-module-in-kit-pages: "error" */
/* ✓ GOOD */
export function foo() {}
export function bar() {}
/* ✗ BAD */
export function load() {}
// export const load = () => {}
</script>
```

</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-3292693)

## :mag: Implementation

- [Rule source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/src/rules/no-export-load-in-svelte-module-in-kit-pages.ts)
- [Test source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/tests/src/rules/no-export-load-in-svelte-module-in-kit-pages.ts)
22 changes: 22 additions & 0 deletions docs/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,28 @@ module.exports = {
}
```

#### settings.kit

If you use SvelteKit with not default configuration, you need to set below configurations.
The schema is subset of SvelteKit's configuration.
Therefore please check [SvelteKit docs](https://kit.svelte.dev/docs/configuration) for more details.

e.g.

```js
module.exports = {
// ...
settings: {
kit: {
files: {
routes: "src/routes",
},
},
},
// ...
}
```

### Running ESLint from the command line

If you want to run `eslint` from the command line, make sure you include the `.svelte` extension using [the `--ext` option](https://eslint.org/docs/user-guide/configuring#specifying-file-extensions-to-lint) or a glob pattern, because ESLint targets only `.js` files by default.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@
"access": "public"
},
"typeCoverage": {
"atLeast": 98.72,
"atLeast": 98.69,
"cache": true,
"detail": true,
"ignoreAsAssertion": true,
Expand Down
51 changes: 51 additions & 0 deletions src/rules/no-export-load-in-svelte-module-in-kit-pages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type * as ESTree from "estree"
import { createRule } from "../utils"
import { isKitPageComponent } from "../utils/svelte-kit"

export default createRule("no-export-load-in-svelte-module-in-kit-pages", {
meta: {
docs: {
description:
"disallow exporting load functions in `*.svelte` module in Svelte Kit page components.",
category: "Possible Errors",
// TODO Switch to recommended in the major version.
recommended: false,
},
schema: [],
messages: {
unexpected:
"disallow exporting load functions in `*.svelte` module in Svelte Kit page components.",
},
type: "problem",
},
create(context) {
if (!isKitPageComponent(context)) {
return {}
}
let isModule = false
return {
// <script context="module">
[`Program > SvelteScriptElement > SvelteStartTag > SvelteAttribute[key.name="context"] > SvelteLiteral[value="module"]`]:
() => {
isModule = true
},

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

// export function load() {}
// export const load = () => {}
[`:matches(ExportNamedDeclaration > FunctionDeclaration, ExportNamedDeclaration > VariableDeclaration > VariableDeclarator) > Identifier.id[name="load"]`]:
(node: ESTree.Identifier) => {
if (!isModule) return {}
return context.report({
node,
loc: node.loc!,
messageId: "unexpected",
})
},
}
},
})
5 changes: 5 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ export type RuleContext = {
postcss?: false | { configFilePath?: unknown }
}
}
["kit"]?: {
files?: {
routes?: string
}
}
}
parserPath: string
parserOptions: Linter.ParserOptions
Expand Down
63 changes: 63 additions & 0 deletions src/utils/cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* Simple cache manager.
*
* refer: https://github.com/mysticatea/eslint-plugin-node/blob/f45c6149be7235c0f7422d1179c25726afeecd83/lib/util/cache.js
*/

const SKIP_TIME = 5000

type CacheValue<T> = {
expire: number
value: T
}

/**
* The cache will dispose of each value if the value has not been accessed
* during 5 seconds.
* @returns getter and setter ofr the cache.
*/
export function createCache<T>(): {
get: (key: string) => T | null
set: (key: string, value: T) => void
} {
const map: Map<string, CacheValue<T>> = new Map()

/**
* Get the cached value of the given key.
* @param key The key to get.
* @returns The cached value or null.
*/
function get(key: string): T | null {
const entry = map.get(key)
const now = Date.now()

if (entry) {
if (entry.expire > now) {
entry.expire = now + SKIP_TIME
return entry.value
}
map.delete(key)
}
return null
}

/**
* Set the value of the given key.
* @param key The key to set.
* @param value The value to set.
* @returns
*/
function set(key: string, value: T): void {
const entry = map.get(key)
const expire = Date.now() + SKIP_TIME

if (entry) {
entry.value = value
entry.expire = expire
} else {
map.set(key, { value, expire })
}
}

return { get, set }
}
77 changes: 77 additions & 0 deletions src/utils/get-package-json.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* refer: https://github.com/mysticatea/eslint-plugin-node/blob/f45c6149be7235c0f7422d1179c25726afeecd83/lib/util/get-package-json.js
*/

import fs from "fs"
import path from "path"
import { createCache } from "./cache"

type PackageJson = Record<string, any> & { filePath: string }

const isRunOnBrowser = !fs.readFileSync
const cache = createCache<PackageJson | null>()

/**
* Reads the `package.json` data in a given path.
*
* Don't cache the data.
*
* @param dir The path to a directory to read.
* @returns The read `package.json` data, or null.
*/
function readPackageJson(dir: string): PackageJson | null {
if (isRunOnBrowser) return null
const filePath = path.join(dir, "package.json")
try {
const text = fs.readFileSync(filePath, "utf8")
const data = JSON.parse(text)

if (typeof data === "object" && data !== null) {
data.filePath = filePath
return data
}
} catch (_err) {
// do nothing.
}

return null
}

/**
* Gets a `package.json` data.
* The data is cached if found, then it's used after.
* @param startPath A file path to lookup.
* @returns A found `package.json` data or `null`.
* This object have additional property `filePath`.
*/
export function getPackageJson(startPath = "a.js"): PackageJson | null {
if (isRunOnBrowser) return null
const startDir = path.dirname(path.resolve(startPath))
let dir = startDir
let prevDir = ""
let data = null

do {
data = cache.get(dir)
if (data) {
if (dir !== startDir) {
cache.set(startDir, data)
}
return data
}

data = readPackageJson(dir)
if (data) {
cache.set(dir, data)
cache.set(startDir, data)
return data
}

// Go to next.
prevDir = dir
dir = path.resolve(dir, "..")
} while (dir !== prevDir)

cache.set(startDir, null)
return null
}
2 changes: 2 additions & 0 deletions src/utils/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import noAtHtmlTags from "../rules/no-at-html-tags"
import noDupeElseIfBlocks from "../rules/no-dupe-else-if-blocks"
import noDupeStyleProperties from "../rules/no-dupe-style-properties"
import noDynamicSlotName from "../rules/no-dynamic-slot-name"
import noExportLoadInSvelteModuleInKitPages from "../rules/no-export-load-in-svelte-module-in-kit-pages"
import noExtraReactiveCurlies from "../rules/no-extra-reactive-curlies"
import noInnerDeclarations from "../rules/no-inner-declarations"
import noNotFunctionHandler from "../rules/no-not-function-handler"
Expand Down Expand Up @@ -59,6 +60,7 @@ export const rules = [
noDupeElseIfBlocks,
noDupeStyleProperties,
noDynamicSlotName,
noExportLoadInSvelteModuleInKitPages,
noExtraReactiveCurlies,
noInnerDeclarations,
noNotFunctionHandler,
Expand Down
Loading