diff --git a/.changeset/rich-goats-notice.md b/.changeset/rich-goats-notice.md new file mode 100644 index 00000000..f22df8e2 --- /dev/null +++ b/.changeset/rich-goats-notice.md @@ -0,0 +1,5 @@ +--- +"eslint-plugin-vue-scoped-css": minor +--- + +feat: use eslint-compat-utils diff --git a/.eslintrc.js b/.eslintrc.js index 4bbc2a5b..e0dd1a0f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -23,6 +23,55 @@ module.exports = { "@typescript-eslint/ban-ts-ignore": "off", "eslint-comments/no-unused-disable": "error", "@typescript-eslint/no-non-null-assertion": "off", + // Repo rule + "@typescript-eslint/no-restricted-imports": [ + "error", + { + patterns: [ + { + group: ["/regexpp", "/regexpp/*"], + message: "Please use `@eslint-community/regexpp` instead.", + }, + { + group: ["/eslint-utils", "/eslint-utils/*"], + message: "Please use `@eslint-community/eslint-utils` instead.", + }, + ], + }, + ], + "no-restricted-properties": [ + "error", + { + object: "context", + property: "getSourceCode", + message: "Use src/utils/compat.ts", + }, + { + object: "context", + property: "getFilename", + message: "Use src/utils/compat.ts", + }, + { + object: "context", + property: "getPhysicalFilename", + message: "Use src/utils/compat.ts", + }, + { + object: "context", + property: "getCwd", + message: "Use src/utils/compat.ts", + }, + { + object: "context", + property: "getScope", + message: "Use src/utils/compat.ts", + }, + { + object: "context", + property: "parserServices", + message: "Use src/utils/compat.ts", + }, + ], }, overrides: [ { diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml new file mode 100644 index 00000000..0d340b26 --- /dev/null +++ b/.github/workflows/format.yml @@ -0,0 +1,31 @@ +name: 👔 Format + +on: + workflow_dispatch: null + +jobs: + format: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Setup node + uses: actions/setup-node@v4 + - name: Install deps + run: npm install + - name: Format + run: npm run eslint-fix + - name: Commit + run: | + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + + git add . + if [ -z "$(git status --porcelain)" ]; then + echo "no formatting changed" + exit 0 + fi + git commit -m "chore: format" + git push + echo "pushed formatting changes https://github.com/$GITHUB_REPOSITORY/commit/$(git rev-parse HEAD)" diff --git a/docs/.vuepress/components/demo/stylelint.config.js b/docs/.vuepress/components/demo/stylelint.config.js index f5cfec0b..2cce88d6 100644 --- a/docs/.vuepress/components/demo/stylelint.config.js +++ b/docs/.vuepress/components/demo/stylelint.config.js @@ -2,7 +2,6 @@ module.exports = { extends: ["stylelint-config-standard"], rules: { "no-descending-specificity": null, - indentation: null, "selector-max-empty-lines": null, "selector-type-no-unknown": null, "block-no-empty": null, diff --git a/docs/.vuepress/components/stylelint.config.js b/docs/.vuepress/components/stylelint.config.js index 0a8cbed3..bb0d836a 100644 --- a/docs/.vuepress/components/stylelint.config.js +++ b/docs/.vuepress/components/stylelint.config.js @@ -2,6 +2,5 @@ module.exports = { extends: ["stylelint-config-standard", "stylelint-config-recommended-vue"], rules: { "no-descending-specificity": null, - indentation: null, }, }; diff --git a/lib/rules/enforce-style-type.ts b/lib/rules/enforce-style-type.ts index 358be8e1..ccad4ec5 100644 --- a/lib/rules/enforce-style-type.ts +++ b/lib/rules/enforce-style-type.ts @@ -11,6 +11,7 @@ import { isValidStyleContext, getCommentDirectivesReporter, } from "../styles/context"; +import { getSourceCode } from "../utils/compat"; const styleTypesAttrs = ["scoped", "module"] as const; type StyleTypes = "plain" | (typeof styleTypesAttrs)[number]; @@ -64,8 +65,9 @@ export = { } const reporter = getCommentDirectivesReporter(context); + const sourceCode = getSourceCode(context); const tokenStore = - context.parserServices.getTemplateBodyTokenStore?.() as TokenStore; + sourceCode.parserServices.getTemplateBodyTokenStore?.() as TokenStore; const { options } = context; const allows: AllowsOption = options[0]?.allows ?? ["scoped"]; diff --git a/lib/rules/no-deprecated-deep-combinator.ts b/lib/rules/no-deprecated-deep-combinator.ts index aab8914a..a8aaee9e 100644 --- a/lib/rules/no-deprecated-deep-combinator.ts +++ b/lib/rules/no-deprecated-deep-combinator.ts @@ -7,6 +7,7 @@ import { import type { VCSSSelectorCombinator } from "../styles/ast"; import type { RuleContext, Range, RuleListener } from "../types"; import { isDeepCombinator } from "../styles/utils/selectors"; +import { getSourceCode } from "../utils/compat"; export = { meta: { @@ -45,7 +46,7 @@ export = { value: node.value.trim(), }, fix(fixer) { - const sourceCodeText = context.getSourceCode().text; + const sourceCodeText = getSourceCode(context).text; const range = [...node.range] as Range; let newText = "::v-deep"; if (sourceCodeText[range[0] - 1]?.trim()) { diff --git a/lib/rules/no-unused-keyframes.ts b/lib/rules/no-unused-keyframes.ts index d8a88bf6..0ee14606 100644 --- a/lib/rules/no-unused-keyframes.ts +++ b/lib/rules/no-unused-keyframes.ts @@ -7,6 +7,7 @@ import { getCommentDirectivesReporter, } from "../styles/context"; import { isValidStyleContext } from "../styles/context/style"; +import { getSourceCode } from "../utils/compat"; export = { meta: { @@ -42,7 +43,7 @@ export = { return {}; } const reporter = getCommentDirectivesReporter(context); - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); /** * Reports the given node diff --git a/lib/rules/require-scoped.ts b/lib/rules/require-scoped.ts index ad41c1ee..77b5bdc3 100644 --- a/lib/rules/require-scoped.ts +++ b/lib/rules/require-scoped.ts @@ -10,6 +10,7 @@ import { isValidStyleContext, getCommentDirectivesReporter, } from "../styles/context"; +import { getSourceCode } from "../utils/compat"; export = { meta: { @@ -40,8 +41,9 @@ export = { return {}; } const reporter = getCommentDirectivesReporter(context); + const sourceCode = getSourceCode(context); const tokenStore = - context.parserServices.getTemplateBodyTokenStore?.() as TokenStore; + sourceCode.parserServices.getTemplateBodyTokenStore?.() as TokenStore; /** * Reports the given node. diff --git a/lib/rules/require-v-deep-argument.ts b/lib/rules/require-v-deep-argument.ts index 89378587..a4de6c94 100644 --- a/lib/rules/require-v-deep-argument.ts +++ b/lib/rules/require-v-deep-argument.ts @@ -22,6 +22,7 @@ import { isVCSSDeclarationProperty, isVCSSComment, } from "../styles/utils/css-nodes"; +import { getSourceCode } from "../utils/compat"; export = { meta: { @@ -90,8 +91,7 @@ export = { nextNode.range[0], ]; if ( - context - .getSourceCode() + getSourceCode(context) .text.slice(...betweenRange) .trim() ) { diff --git a/lib/rules/v-deep-pseudo-style.ts b/lib/rules/v-deep-pseudo-style.ts index eeeb16ed..4888c4ee 100644 --- a/lib/rules/v-deep-pseudo-style.ts +++ b/lib/rules/v-deep-pseudo-style.ts @@ -10,6 +10,7 @@ import { isVDeepPseudo, isPseudoEmptyArguments, } from "../styles/utils/selectors"; +import { getSourceCode } from "../utils/compat"; export = { meta: { @@ -50,7 +51,7 @@ export = { loc: node.loc, messageId: expected === ":deep" ? "expectedDeep" : "expectedVDeep", fix(fixer) { - const nodeText = context.getSourceCode().text.slice(...node.range); + const nodeText = getSourceCode(context).text.slice(...node.range); return fixer.replaceTextRange( node.range, nodeText.replace( diff --git a/lib/rules/v-global-pseudo-style.ts b/lib/rules/v-global-pseudo-style.ts index 1a5e7d00..10b9584d 100644 --- a/lib/rules/v-global-pseudo-style.ts +++ b/lib/rules/v-global-pseudo-style.ts @@ -10,6 +10,7 @@ import { isVGlobalPseudo, isPseudoEmptyArguments, } from "../styles/utils/selectors"; +import { getSourceCode } from "../utils/compat"; export = { meta: { @@ -53,7 +54,7 @@ export = { messageId: expected === ":global" ? "expectedGlobal" : "expectedVGlobal", fix(fixer) { - const nodeText = context.getSourceCode().text.slice(...node.range); + const nodeText = getSourceCode(context).text.slice(...node.range); return fixer.replaceTextRange( node.range, nodeText.replace( diff --git a/lib/rules/v-slotted-pseudo-style.ts b/lib/rules/v-slotted-pseudo-style.ts index bd14aacf..0808e596 100644 --- a/lib/rules/v-slotted-pseudo-style.ts +++ b/lib/rules/v-slotted-pseudo-style.ts @@ -10,6 +10,7 @@ import { isVSlottedPseudo, isPseudoEmptyArguments, } from "../styles/utils/selectors"; +import { getSourceCode } from "../utils/compat"; export = { meta: { @@ -53,7 +54,7 @@ export = { messageId: expected === ":slotted" ? "expectedSlotted" : "expectedVSlotted", fix(fixer) { - const nodeText = context.getSourceCode().text.slice(...node.range); + const nodeText = getSourceCode(context).text.slice(...node.range); return fixer.replaceTextRange( node.range, nodeText.replace( diff --git a/lib/styles/context/index.ts b/lib/styles/context/index.ts index a1c6f3a9..604896a7 100644 --- a/lib/styles/context/index.ts +++ b/lib/styles/context/index.ts @@ -17,6 +17,7 @@ import { VueComponentContext, createVueComponentContext, } from "./vue-components"; +import { getSourceCode } from "../../utils/compat"; type CacheValue = { styles?: StyleContext[]; @@ -32,7 +33,7 @@ const CACHE = new WeakMap(); * Gets the cache. */ function getCache(context: RuleContext): CacheValue { - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); const { ast } = sourceCode; if (CACHE.has(ast)) { return CACHE.get(ast) as CacheValue; diff --git a/lib/styles/context/style/index.ts b/lib/styles/context/style/index.ts index f50bb590..6f52553b 100644 --- a/lib/styles/context/style/index.ts +++ b/lib/styles/context/style/index.ts @@ -7,6 +7,7 @@ import type { } from "../../../types"; import type { VCSSStyleSheet, VCSSNode, VCSSSelectorNode } from "../../ast"; import { isVCSSContainerNode, hasSelectorNodes } from "../../utils/css-nodes"; +import { getSourceCode } from "../../../utils/compat"; /** * Check whether the program has invalid EOF or not. @@ -18,16 +19,17 @@ function getInvalidEOFError( inDocumentFragment: boolean; error: AST.ParseError; } | null { - const node = context.getSourceCode().ast; + const node = getSourceCode(context).ast; const body = node.templateBody; let errors = body?.errors; let inDocumentFragment = false; if (errors == null) { + const sourceCode = getSourceCode(context); /* istanbul ignore if */ - if (!context.parserServices.getDocumentFragment) { + if (!sourceCode.parserServices.getDocumentFragment) { return null; } - const df = context.parserServices.getDocumentFragment(); + const df = sourceCode.parserServices.getDocumentFragment(); inDocumentFragment = true; errors = df?.errors; /* istanbul ignore if */ @@ -62,11 +64,11 @@ function getInvalidEOFError( */ function getStyleElements(context: RuleContext): AST.VElement[] { let document: AST.VDocumentFragment | null = null; - if (context.parserServices.getDocumentFragment) { + const sourceCode = getSourceCode(context); + if (sourceCode.parserServices.getDocumentFragment) { // vue-eslint-parser v7.0.0 - document = context.parserServices.getDocumentFragment(); + document = sourceCode.parserServices.getDocumentFragment(); } else { - const sourceCode = context.getSourceCode(); const { ast } = sourceCode; const templateBody = ast.templateBody as AST.ESLintProgram | undefined; /* istanbul ignore if */ @@ -189,7 +191,7 @@ export class StyleContextImpl { public readonly cssNode: VCSSStyleSheet | null; public constructor(style: AST.VElement, context: RuleContext) { - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); this.styleElement = style; this.sourceCode = sourceCode; diff --git a/lib/styles/context/vue-components/find-vue.ts b/lib/styles/context/vue-components/find-vue.ts index 5d61c562..32ec6e74 100644 --- a/lib/styles/context/vue-components/find-vue.ts +++ b/lib/styles/context/vue-components/find-vue.ts @@ -1,6 +1,7 @@ import { AST } from "vue-eslint-parser"; import type { ASTNode, RuleContext } from "../../../types"; import { unwrapTypesExpression } from "../../utils/nodes"; +import { getSourceCode } from "../../../utils/compat"; const traverseNodes = AST.traverseNodes; @@ -73,7 +74,7 @@ function findVueComponent( return cached.component; } - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); const componentComments = sourceCode .getAllComments() .filter((comment) => comment.value.includes("@vue/component")); diff --git a/lib/styles/context/vue-components/index.ts b/lib/styles/context/vue-components/index.ts index d7237c55..065f2ebf 100644 --- a/lib/styles/context/vue-components/index.ts +++ b/lib/styles/context/vue-components/index.ts @@ -4,6 +4,7 @@ import findVueComponent from "./find-vue"; import type { RuleContext, ASTNode, AST } from "../../../types"; import type { Template } from "../../template"; import { isDefined } from "../../../utils/utils"; +import { getSourceCode } from "../../../utils/compat"; const traverseNodes = vueAST.traverseNodes; @@ -288,7 +289,7 @@ function getClassesOperatedByClassList( ): (AST.ESLintExpression | AST.ESLintSpreadElement)[] { const results: (AST.ESLintExpression | AST.ESLintSpreadElement)[] = []; traverseNodes(vueNode, { - visitorKeys: context.getSourceCode().visitorKeys, + visitorKeys: getSourceCode(context).visitorKeys, enterNode(node) { if ( node.type !== "CallExpression" || @@ -351,7 +352,7 @@ function getReturnStatements( | AST.ESLintFunctionDeclaration )[] = []; traverseNodes(body, { - visitorKeys: context.getSourceCode().visitorKeys, + visitorKeys: getSourceCode(context).visitorKeys, enterNode(node) { if (skipNodes.length) { return; diff --git a/lib/styles/selectors/query/attribute-tracker.ts b/lib/styles/selectors/query/attribute-tracker.ts index dcd2d26c..36a08e21 100644 --- a/lib/styles/selectors/query/attribute-tracker.ts +++ b/lib/styles/selectors/query/attribute-tracker.ts @@ -47,6 +47,7 @@ export function getAttributeValueNodes( // empty or syntax error continue; } + if (expression.type === "VGenericExpression") continue; const expressions = getReferenceExpressions(expression, context); if (!expressions) { // Expressions not found. diff --git a/lib/styles/selectors/query/index.ts b/lib/styles/selectors/query/index.ts index 79e3f4d5..7b70e890 100644 --- a/lib/styles/selectors/query/index.ts +++ b/lib/styles/selectors/query/index.ts @@ -38,6 +38,7 @@ import { isVElement, isTransitionElement } from "../../../utils/templates"; import { isValidStyleContext } from "../../context/style"; import type { ReferenceExpressions } from "./reference-expression"; import { getReferenceExpressions } from "./reference-expression"; +import { getSourceCode } from "../../../utils/compat"; const TRANSITION_CLASS_BASES = [ "enter", @@ -130,7 +131,7 @@ class VueDocumentQueryContext extends QueryContext { public constructor(context: RuleContext, options: ParsedQueryOptions) { super(); - const sourceCode = context.getSourceCode(); + const sourceCode = getSourceCode(context); const { ast } = sourceCode; this.elements = ast.templateBody ? [...genDescendantElements([ast.templateBody])] diff --git a/lib/styles/selectors/query/reference-expression.ts b/lib/styles/selectors/query/reference-expression.ts index aafb00a4..41b83a23 100644 --- a/lib/styles/selectors/query/reference-expression.ts +++ b/lib/styles/selectors/query/reference-expression.ts @@ -1,5 +1,6 @@ import { getVueComponentContext } from "../../context"; import type { RuleContext, AST } from "../../../types"; +import { getSourceCode } from "../../../utils/compat"; export type ReferenceExpressions = | AST.ESLintExpression @@ -54,7 +55,7 @@ export function getReferenceExpressions( * Checks whether the given node within `