diff --git a/.changeset/silent-lamps-chew.md b/.changeset/silent-lamps-chew.md new file mode 100644 index 000000000..19e4e9ee4 --- /dev/null +++ b/.changeset/silent-lamps-chew.md @@ -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. diff --git a/.changeset/silent-lamps-chew2.md b/.changeset/silent-lamps-chew2.md new file mode 100644 index 000000000..7e7a9dfa2 --- /dev/null +++ b/.changeset/silent-lamps-chew2.md @@ -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. diff --git a/README.md b/README.md index 4ba4c5a2c..9a6c25600 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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. diff --git a/docs/user-guide.md b/docs/user-guide.md index 0a48c998c..0818976de 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -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. @@ -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. diff --git a/packages/eslint-plugin-svelte/src/rules/no-unused-class-name.ts b/packages/eslint-plugin-svelte/src/rules/no-unused-class-name.ts index 57f795725..9d183c850 100644 --- a/packages/eslint-plugin-svelte/src/rules/no-unused-class-name.ts +++ b/packages/eslint-plugin-svelte/src/rules/no-unused-class-name.ts @@ -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({ diff --git a/packages/eslint-plugin-svelte/src/types.ts b/packages/eslint-plugin-svelte/src/types.ts index ab313fbb6..a04eb81f8 100644 --- a/packages/eslint-plugin-svelte/src/types.ts +++ b/packages/eslint-plugin-svelte/src/types.ts @@ -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 { @@ -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 ``. + */ + 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; + tsNodeToESTreeNodeMap?: ReadonlyMap; + hasFullTypeInformation?: boolean; // Old typescript-eslint + [key: string]: unknown; + }; scopeManager: ScopeManager; visitorKeys: ESLintSourceCode.VisitorKeys; diff --git a/packages/eslint-plugin-svelte/src/utils/svelte-kit.ts b/packages/eslint-plugin-svelte/src/utils/svelte-kit.ts index c42be3c56..04a1b390b 100644 --- a/packages/eslint-plugin-svelte/src/utils/svelte-kit.ts +++ b/packages/eslint-plugin-svelte/src/utils/svelte-kit.ts @@ -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; @@ -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); diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/invalid/svelte-config-from-parser-options/+test01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/invalid/svelte-config-from-parser-options/+test01-errors.yaml new file mode 100644 index 000000000..fd8227f1f --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/invalid/svelte-config-from-parser-options/+test01-errors.yaml @@ -0,0 +1,5 @@ +- message: disallow exporting load functions in `*.svelte` module in SvelteKit + page components. + line: 2 + column: 18 + suggestions: null diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/invalid/svelte-config-from-parser-options/+test01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/invalid/svelte-config-from-parser-options/+test01-input.svelte new file mode 100644 index 000000000..86add8e41 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/invalid/svelte-config-from-parser-options/+test01-input.svelte @@ -0,0 +1,3 @@ + diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/invalid/svelte-config-from-parser-options/_config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/invalid/svelte-config-from-parser-options/_config.json new file mode 100644 index 000000000..775099633 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/invalid/svelte-config-from-parser-options/_config.json @@ -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" + } + } + } + } + } +} diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/invalid/svelte-config/+test01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/invalid/svelte-config/+test01-errors.yaml new file mode 100644 index 000000000..fd8227f1f --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/invalid/svelte-config/+test01-errors.yaml @@ -0,0 +1,5 @@ +- message: disallow exporting load functions in `*.svelte` module in SvelteKit + page components. + line: 2 + column: 18 + suggestions: null diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/invalid/svelte-config/+test01-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/invalid/svelte-config/+test01-input.svelte new file mode 100644 index 000000000..86add8e41 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/invalid/svelte-config/+test01-input.svelte @@ -0,0 +1,3 @@ + diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/invalid/svelte-config/svelte.config.js b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/invalid/svelte-config/svelte.config.js new file mode 100644 index 000000000..8314528ec --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/invalid/svelte-config/svelte.config.js @@ -0,0 +1,8 @@ +export default { + kit: { + files: { + routes: + 'tests/fixtures/rules/no-export-load-in-svelte-module-in-kit-pages/invalid/svelte-config' + } + } +}; diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config-from-parser-options/+test001-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config-from-parser-options/+test001-errors.yaml new file mode 100644 index 000000000..56a75c0ec --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config-from-parser-options/+test001-errors.yaml @@ -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 diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config-from-parser-options/+test001-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config-from-parser-options/+test001-input.svelte new file mode 100644 index 000000000..6928abbd7 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config-from-parser-options/+test001-input.svelte @@ -0,0 +1,8 @@ + + +{foo}, {bar} diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config-from-parser-options/_config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config-from-parser-options/_config.json new file mode 100644 index 000000000..c58ee3c00 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config-from-parser-options/_config.json @@ -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" + } + } + } + } + } +} diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config/+test001-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config/+test001-errors.yaml new file mode 100644 index 000000000..56a75c0ec --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config/+test001-errors.yaml @@ -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 diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config/+test001-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config/+test001-input.svelte new file mode 100644 index 000000000..6928abbd7 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config/+test001-input.svelte @@ -0,0 +1,8 @@ + + +{foo}, {bar} diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config/svelte.config.js b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config/svelte.config.js new file mode 100644 index 000000000..6153da28b --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config/svelte.config.js @@ -0,0 +1,7 @@ +export default { + kit: { + files: { + routes: 'tests/fixtures/rules/valid-prop-names-in-kit-pages/invalid/svelte-config' + } + } +};