Skip to content

Commit

Permalink
feat: improve svelte/valid-prop-names-in-kit-pages and `svelte/no-e…
Browse files Browse the repository at this point in the history
…xport-load-in-svelte-module-in-kit-pages` to use `svelte.config.js` data (#794)

This PR improves the two rules to use `svelte.config.js` data provided
by the parser.

- `svelte/valid-prop-names-in-kit-pages`
- `svelte/no-export-load-in-svelte-module-in-kit-pages`
  • Loading branch information
ota-meshi committed Jun 17, 2024
1 parent cb722bc commit 7894f82
Show file tree
Hide file tree
Showing 19 changed files with 237 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/silent-lamps-chew.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'eslint-plugin-svelte': minor
---

feat: improve `svelte/valid-prop-names-in-kit-pages` to use `svelte.config.js` data from the parser.
5 changes: 5 additions & 0 deletions .changeset/silent-lamps-chew2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'eslint-plugin-svelte': minor
---

feat: improve `svelte/no-export-load-in-svelte-module-in-kit-pages` to use `svelte.config.js` data from the parser.
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,36 @@ for some context.

:::

#### Specify `svelte.config.js`

If you are using `eslint.config.js`, we recommend that you import and specify `svelte.config.js`.
By specifying it, some rules of `eslint-plugin-svelte` will read it and try to behave well for you by default.
Some Svelte configurations will be statically loaded from `svelte.config.js` even if you don't specify it, but you need to specify it to make it work better.

Example **eslint.config.js**:

```js
import eslintPluginSvelte from 'eslint-plugin-svelte';
import svelteConfig from './svelte.config.js';
export default [
...eslintPluginSvelte.configs['flat/recommended'],
{
files: [
'**/*.svelte',
'*.svelte'
// Add more files if you need.
// '**/*.svelte.ts', '*.svelte.ts', '**/*.svelte.js', '*.svelte.js',
],
languageOptions: {
parserOptions: {
// Specify the `svelte.config.js`.
svelteConfig
}
}
}
];
```

#### settings.svelte

You can change the behavior of this plugin with some settings.
Expand Down Expand Up @@ -274,6 +304,12 @@ Specifies options for Svelte compile. Effects rules that use Svelte compile. The

#### settings.svelte.kit

::: warning

Even if you don't specify `settings.svelte.kit`, the rules will try to load information from `svelte.config.js`, so specify `settings.svelte.kit` if the default doesn't work.

:::

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.
Expand Down
36 changes: 36 additions & 0 deletions docs/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,36 @@ for some context.

:::

#### Specify `svelte.config.js`

If you are using `eslint.config.js`, we recommend that you import and specify `svelte.config.js`.
By specifying it, some rules of `eslint-plugin-svelte` will read it and try to behave well for you by default.
Some Svelte configurations will be statically loaded from `svelte.config.js` even if you don't specify it, but you need to specify it to make it work better.

Example **eslint.config.js**:

```js
import eslintPluginSvelte from 'eslint-plugin-svelte';
import svelteConfig from './svelte.config.js';
export default [
...eslintPluginSvelte.configs['flat/recommended'],
{
files: [
'**/*.svelte',
'*.svelte'
// Add more files if you need.
// '**/*.svelte.ts', '*.svelte.ts', '**/*.svelte.js', '*.svelte.js',
],
languageOptions: {
parserOptions: {
// Specify the `svelte.config.js`.
svelteConfig
}
}
}
];
```

#### settings.svelte

You can change the behavior of this plugin with some settings.
Expand Down Expand Up @@ -225,6 +255,12 @@ Specifies options for Svelte compile. Effects rules that use Svelte compile. The

#### settings.svelte.kit

::: warning

Even if you don't specify `settings.svelte.kit`, the rules will try to load information from `svelte.config.js`, so specify `settings.svelte.kit` if the default doesn't work.

:::

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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ export default createRule('no-unused-class-name', {
}
},
'Program:exit'() {
const styleContext = sourceCode.parserServices.getStyleContext();
if (['parse-error', 'unknown-lang'].includes(styleContext.status)) {
const styleContext = sourceCode.parserServices.getStyleContext!();
if (styleContext.status === 'parse-error' || styleContext.status === 'unknown-lang') {
return;
}
const classesUsedInStyle =
styleContext.sourceAst != null ? findClassesInPostCSSNode(styleContext.sourceAst) : [];
styleContext.status === 'success' ? findClassesInPostCSSNode(styleContext.sourceAst) : [];
for (const className in classesUsedInTemplate) {
if (!allowedClassNames.includes(className) && !classesUsedInStyle.includes(className)) {
context.report({
Expand Down
27 changes: 25 additions & 2 deletions packages/eslint-plugin-svelte/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { JSONSchema4 } from 'json-schema';
import type { Linter, Rule, SourceCode as ESLintSourceCode } from 'eslint';
import type { AST } from 'svelte-eslint-parser';
import type { AST, StyleContext, SvelteConfig } from 'svelte-eslint-parser';
import type { TSESTree } from '@typescript-eslint/types';
import type { ScopeManager, Scope, Variable } from '@typescript-eslint/scope-manager';
import type { ASTNode, ASTNodeWithParent, ASTNodeListener } from './types-for-node';
import type * as TS from 'typescript';

export type { ASTNode, ASTNodeWithParent, ASTNodeListener };
export interface RuleListener extends ASTNodeListener {
Expand Down Expand Up @@ -201,7 +202,29 @@ export interface SourceCode {
ast: AST.SvelteProgram;
lines: string[];
hasBOM: boolean;
parserServices: ESLintSourceCode.ParserServices;
parserServices: {
isSvelte?: boolean;
isSvelteScript?: boolean;
getSvelteHtmlAst?: () => unknown;
getStyleContext?: () => StyleContext;
svelteParseContext?: {
/**
* Whether to use Runes mode.
* May be `true` if the user is using Svelte v5.
* Resolved from `svelte.config.js` or `parserOptions`, but may be overridden by `<svelte:options>`.
*/
runes?: boolean;
/** The version of "svelte/compiler". */
compilerVersion?: string;
/** The result of static analysis of `svelte.config.js`. */
svelteConfig?: SvelteConfig | null;
};
program?: TS.Program;
esTreeNodeToTSNodeMap?: ReadonlyMap<unknown, TS.Node>;
tsNodeToESTreeNodeMap?: ReadonlyMap<TS.Node, ASTNode>;
hasFullTypeInformation?: boolean; // Old typescript-eslint
[key: string]: unknown;
};
scopeManager: ScopeManager;
visitorKeys: ESLintSourceCode.VisitorKeys;

Expand Down
8 changes: 6 additions & 2 deletions packages/eslint-plugin-svelte/src/utils/svelte-kit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { RuleContext } from '../types';
import fs from 'fs';
import path from 'path';
import { getPackageJson } from './get-package-json';
import { getFilename } from './compat';
import { getFilename, getSourceCode } from './compat';

const isRunOnBrowser = !fs.readFileSync;

Expand All @@ -19,7 +19,11 @@ export function isKitPageComponent(context: RuleContext): boolean {
// Hack: if it runs on browser, it regards as SvelteKit project.
if (isRunOnBrowser) return true;
if (!hasSvelteKit(getFilename(context))) return false;
const routes = context.settings?.svelte?.kit?.files?.routes?.replace(/^\//, '') ?? 'src/routes';
const routes =
(
context.settings?.svelte?.kit?.files?.routes ??
getSourceCode(context).parserServices.svelteParseContext?.svelteConfig?.kit?.files?.routes
)?.replace(/^\//, '') ?? 'src/routes';
const filePath = getFilename(context);
const projectRootDir = getProjectRootDir(getFilename(context)) ?? '';
const fileName = path.basename(filePath);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- message: disallow exporting load functions in `*.svelte` module in SvelteKit
page components.
line: 2
column: 18
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<script context="module">
export function load() {}
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"languageOptions": {
"parserOptions": {
"svelteConfig": {
"kit": {
"files": {
"routes": "tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/invalid/svelte-config-from-parser-options"
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- message: disallow exporting load functions in `*.svelte` module in SvelteKit
page components.
line: 2
column: 18
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<script context="module">
export function load() {}
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default {
kit: {
files: {
routes:
'tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/invalid/svelte-config'
}
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
- message: disallow props other than data or errors in SvelteKit page components.
line: 2
column: 13
suggestions: null
- message: disallow props other than data or errors in SvelteKit page components.
line: 3
column: 13
suggestions: null
- message: disallow props other than data or errors in SvelteKit page components.
line: 4
column: 15
suggestions: null
- message: disallow props other than data or errors in SvelteKit page components.
line: 4
column: 20
suggestions: null
- message: disallow props other than data or errors in SvelteKit page components.
line: 5
column: 21
suggestions: null
- message: disallow props other than data or errors in SvelteKit page components.
line: 5
column: 36
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,13 @@
{
"languageOptions": {
"parserOptions": {
"svelteConfig": {
"kit": {
"files": {
"routes": "tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config-from-parser-options"
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
- message: disallow props other than data or errors in SvelteKit page components.
line: 2
column: 13
suggestions: null
- message: disallow props other than data or errors in SvelteKit page components.
line: 3
column: 13
suggestions: null
- message: disallow props other than data or errors in SvelteKit page components.
line: 4
column: 15
suggestions: null
- message: disallow props other than data or errors in SvelteKit page components.
line: 4
column: 20
suggestions: null
- message: disallow props other than data or errors in SvelteKit page components.
line: 5
column: 21
suggestions: null
- message: disallow props other than data or errors in SvelteKit page components.
line: 5
column: 36
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,7 @@
export default {
kit: {
files: {
routes: 'tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config'
}
}
};

0 comments on commit 7894f82

Please sign in to comment.