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 {}
+}