diff --git a/.changeset/itchy-dragons-boil.md b/.changeset/itchy-dragons-boil.md new file mode 100644 index 000000000..b87445b7a --- /dev/null +++ b/.changeset/itchy-dragons-boil.md @@ -0,0 +1,5 @@ +--- +'eslint-plugin-svelte': patch +--- + +chore: using svelte-eslint-parser for style selector parsing diff --git a/packages/eslint-plugin-svelte/package.json b/packages/eslint-plugin-svelte/package.json index 16564b1e8..2b4a8fcc8 100644 --- a/packages/eslint-plugin-svelte/package.json +++ b/packages/eslint-plugin-svelte/package.json @@ -64,7 +64,6 @@ "postcss": "^8.4.49", "postcss-load-config": "^3.1.4", "postcss-safe-parser": "^7.0.0", - "postcss-selector-parser": "^7.0.0", "semver": "^7.6.3", "svelte-eslint-parser": "^1.0.0-next.6" }, @@ -93,6 +92,7 @@ "less": "^4.2.1", "mocha": "^11.0.0", "postcss-nested": "^7.0.2", + "postcss-selector-parser": "^7.0.0", "sass": "^1.81.0", "source-map-js": "^1.2.1", "stylus": "^0.64.0", 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 21e8888ad..2a6b0adc4 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 @@ -10,8 +10,9 @@ import type { SvelteStyleDirective } from 'svelte-eslint-parser/lib/ast'; import type { AnyNode } from 'postcss'; -import { default as selectorParser, type Node as SelectorNode } from 'postcss-selector-parser'; +import type { Node as SelectorNode } from 'postcss-selector-parser'; import { getSourceCode } from '../utils/compat.js'; +import type { SourceCode } from '../types.js'; export default createRule('no-unused-class-name', { meta: { @@ -61,7 +62,9 @@ export default createRule('no-unused-class-name', { return; } const classesUsedInStyle = - styleContext.status === 'success' ? findClassesInPostCSSNode(styleContext.sourceAst) : []; + styleContext.status === 'success' + ? findClassesInPostCSSNode(styleContext.sourceAst, sourceCode.parserServices) + : []; for (const className in classesUsedInTemplate) { if (!allowedClassNames.includes(className) && !classesUsedInStyle.includes(className)) { context.report({ @@ -102,15 +105,17 @@ function findClassesInAttribute( /** * Extract all class names used in a PostCSS node. */ -function findClassesInPostCSSNode(node: AnyNode): string[] { +function findClassesInPostCSSNode( + node: AnyNode, + parserServices: SourceCode['parserServices'] +): string[] { if (node.type === 'rule') { - let classes = node.nodes.flatMap(findClassesInPostCSSNode); - const processor = selectorParser(); - classes = classes.concat(findClassesInSelector(processor.astSync(node.selector))); + let classes = node.nodes.flatMap((node) => findClassesInPostCSSNode(node, parserServices)); + classes = classes.concat(findClassesInSelector(parserServices.getStyleSelectorAST(node))); return classes; } if ((node.type === 'root' || node.type === 'atrule') && node.nodes !== undefined) { - return node.nodes.flatMap(findClassesInPostCSSNode); + return node.nodes.flatMap((node) => findClassesInPostCSSNode(node, parserServices)); } return []; } diff --git a/packages/eslint-plugin-svelte/src/types.ts b/packages/eslint-plugin-svelte/src/types.ts index 96a6abd9b..bc03997b0 100644 --- a/packages/eslint-plugin-svelte/src/types.ts +++ b/packages/eslint-plugin-svelte/src/types.ts @@ -3,8 +3,11 @@ import type { Linter, Rule, SourceCode as ESLintSourceCode } from 'eslint'; 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 { Rule as StyleRule, Node } from 'postcss'; +import type { Root as SelectorRoot, Node as SelectorNode } from 'postcss-selector-parser'; import type { ASTNode, ASTNodeWithParent, ASTNodeListener } from './types-for-node.js'; import type * as TS from 'typescript'; +import type { SourceLocation } from 'svelte-eslint-parser/lib/ast/common.js'; export type { ASTNode, ASTNodeWithParent, ASTNodeListener }; export interface RuleListener extends ASTNodeListener { @@ -207,6 +210,10 @@ export interface SourceCode { isSvelteScript?: boolean; getSvelteHtmlAst?: () => unknown; getStyleContext?: () => StyleContext; + getStyleSelectorAST: (rule: StyleRule) => SelectorRoot; + styleNodeLoc: (node: Node) => Partial; + styleNodeRange: (node: Node) => [number | undefined, number | undefined]; + styleSelectorNodeLoc: (node: SelectorNode) => Partial; svelteParseContext?: { /** * Whether to use Runes mode.