Skip to content

Commit

Permalink
feat: add no-export-load-in-svelte-module-in-kit-pages (#281)
Browse files Browse the repository at this point in the history
* feat: add no-export-load-in-svelte-module-in-kit-pages

* chore: add changeset

* chore: add test

* fix: check devDependencies also

* fix: adjust for test

* feat: add config

* chore: fix typo

* fix: bug

* fix: bug

* chore: uupdate docus

* chore: update docs

* chore: add kit helper

* fix: fix for demo

* chore: adjust doc style

* chore: revert .eslintrc.js

* chore: update uppercase -> lowercase

* fix: update recommended

* fix: i did not know :exit

* fix: project root path finding logic

* chore: utilize

* chore: yarn update

* fix: remove needless logic

* chore: simplify

* fix: false positive

Co-authored-by: Yosuke Ota <otameshiyo23@gmail.com>
  • Loading branch information
baseballyama and ota-meshi committed Nov 1, 2022
1 parent 1240968 commit 8da870f
Show file tree
Hide file tree
Showing 33 changed files with 481 additions and 1 deletion.
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",
},
},
},
// ...
}
```

### 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

0 comments on commit 8da870f

Please sign in to comment.