diff --git a/.changeset/nasty-garlics-divide.md b/.changeset/nasty-garlics-divide.md new file mode 100644 index 000000000..3bae24dde --- /dev/null +++ b/.changeset/nasty-garlics-divide.md @@ -0,0 +1,5 @@ +--- +"eslint-plugin-svelte": patch +--- + +fix: false positive for `customElement="..."` in `svelte/valid-compile` diff --git a/package.json b/package.json index bc8dbb329..e3fe3d74b 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "postcss-load-config": "^3.1.4", "postcss-safe-parser": "^6.0.0", "postcss-selector-parser": "^6.0.11", + "semver": "^7.5.3", "svelte-eslint-parser": "^0.31.0" }, "devDependencies": { @@ -110,6 +111,7 @@ "@types/node": "^18.11.0", "@types/postcss-safe-parser": "^5.0.1", "@types/prismjs": "^1.26.0", + "@types/semver": "^7.5.0", "@types/stylus": "^0.48.38", "@typescript-eslint/eslint-plugin": "^5.59.5", "@typescript-eslint/parser": "^5.59.5", @@ -158,7 +160,6 @@ "prismjs": "^1.25.0", "rimraf": "^5.0.0", "sass": "^1.51.0", - "semver": "^7.3.5", "simple-git-hooks": "^2.8.0", "source-map-js": "^1.0.2", "stylelint": "^15.0.0", diff --git a/src/shared/svelte-compile-warns/index.ts b/src/shared/svelte-compile-warns/index.ts index 05afc854a..89331133f 100644 --- a/src/shared/svelte-compile-warns/index.ts +++ b/src/shared/svelte-compile-warns/index.ts @@ -21,6 +21,7 @@ import { extractLeadingComments } from "./extract-leading-comments" import { getLangValue } from "../../utils/ast-utils" import path from "path" import fs from "fs" +import semver from "semver" type WarningTargetNode = | (AST.SvelteProgram & ASTNodeWithParent) @@ -446,6 +447,9 @@ function getWarningsFromCode(code: string): { try { const result = compiler.compile(code, { generate: false, + ...(semver.satisfies(compiler.VERSION, ">=4.0.0-0") + ? { customElement: true } + : {}), }) return { warnings: result.warnings as Warning[], kind: "warn" } diff --git a/tests/fixtures/rules/valid-compile/valid/svelte-options-custom-element-input.svelte b/tests/fixtures/rules/valid-compile/valid/svelte-options-custom-element-input.svelte new file mode 100644 index 000000000..22e1c0127 --- /dev/null +++ b/tests/fixtures/rules/valid-compile/valid/svelte-options-custom-element-input.svelte @@ -0,0 +1 @@ + diff --git a/tests/fixtures/rules/valid-compile/valid/svelte-options-custom-element-requirements.json b/tests/fixtures/rules/valid-compile/valid/svelte-options-custom-element-requirements.json new file mode 100644 index 000000000..b2cb8e736 --- /dev/null +++ b/tests/fixtures/rules/valid-compile/valid/svelte-options-custom-element-requirements.json @@ -0,0 +1,3 @@ +{ + "svelte": ">=4.0.0-0" +} diff --git a/tests/utils/utils.ts b/tests/utils/utils.ts index 04e6e2560..a99e20eea 100644 --- a/tests/utils/utils.ts +++ b/tests/utils/utils.ts @@ -8,6 +8,7 @@ import * as typescriptESLintParser from "@typescript-eslint/parser" import plugin = require("../../src/index") import { applyFixes } from "./source-code-fixer" import { parse as parseYaml, stringify as stringifyYaml } from "yaml" +import semver from "semver" /** * Prevents leading spaces in a multiline template literal from appearing in the resulting string @@ -86,7 +87,27 @@ export function loadTestCases( const validFixtureRoot = path.resolve(rootDir, `./valid/`) const invalidFixtureRoot = path.resolve(rootDir, `./invalid/`) - const filter = options?.filter ?? (() => true) + const fileNameFilter = options?.filter ?? (() => true) + + function filter(inputFile: string) { + if (!fileNameFilter(inputFile)) { + return false + } + const requirements = getRequirements(inputFile) + if ( + Object.entries(requirements).some(([pkgName, pkgVersion]) => { + const pkg = + pkgName === "node" + ? { version: process.version } + : // eslint-disable-next-line @typescript-eslint/no-require-imports -- test + require(`${pkgName}/package.json`) + return !semver.satisfies(pkg.version, pkgVersion) + }) + ) { + return false + } + return true + } const valid = listupInput(validFixtureRoot) .filter(filter) @@ -293,3 +314,17 @@ function getConfig(ruleName: string, inputFile: string) { { code, filename: inputFile }, ) } + +function getRequirements(inputFile: string): Record { + let requirementsFile: string = inputFile.replace( + /input\.[a-z]+$/u, + "requirements.json", + ) + if (!fs.existsSync(requirementsFile)) { + requirementsFile = path.join(path.dirname(inputFile), "_requirements.json") + } + if (fs.existsSync(requirementsFile)) { + return JSON.parse(fs.readFileSync(requirementsFile, "utf8")) + } + return {} +}