diff --git a/README.md b/README.md index 3315bd3ba..db731dd26 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ npm install --save-dev svelte eslint eslint-plugin-svelte globals > **Requirements:** > > - ESLint v8.57.1, v9.0.0, and above -> - Node.js v18.20.4, v20.18.0, v22.10.0, and above +> - Node.js v18.18.0, v20.9.0, v21.1.0 and above ## Usage diff --git a/docs/rules/no-unused-props.md b/docs/rules/no-unused-props.md index c375aa430..fffe3864f 100644 --- a/docs/rules/no-unused-props.md +++ b/docs/rules/no-unused-props.md @@ -159,14 +159,17 @@ Note: Properties of class types are not checked for usage, as they might be used "svelte/no-unused-props": ["error", { // Whether to check properties from imported types "checkImportedTypes": false, + // Patterns to ignore when checking property types + "ignoreTypePatterns": [], // Patterns to ignore when checking for unused props - "ignorePatterns": [] + "ignorePropertyPatterns": [], }] } ``` -- `checkImportedTypes` ... Controls whether to check properties from imported types. Default is `false`. -- `ignorePatterns` ... Patterns to ignore when checking for unused props. Default is an empty array. +- `checkImportedTypes` ... Controls whether to check properties from types defined in external files. Default is `false`, meaning the rule only checks types defined within the component file itself. When set to `true`, the rule will also check properties from imported and extended types. +- `ignoreTypePatterns` ... Regular expression patterns for type names to exclude from checks. Default is `[]` (no exclusions). Most useful when `checkImportedTypes` is `true`, allowing you to exclude specific imported types (like utility types or third-party types) from being checked. +- `ignorePropertyPatterns` ... Regular expression patterns for property names to exclude from unused checks. Default is `[]` (no exclusions). Most useful when `checkImportedTypes` is `true`, allowing you to ignore specific properties from external types that shouldn't trigger warnings. Examples: @@ -187,8 +190,26 @@ Examples: ```svelte <!-- ✓ Good Examples --> <script lang="ts"> - /* eslint svelte/no-unused-props: ["error", { "ignorePatterns": ["^_"] }] */ - // Ignore properties starting with underscore + /* eslint svelte/no-unused-props: ["error", { "ignoreTypePatterns": ["/^Internal/"] }] */ + // Ignore properties from types matching the pattern + interface InternalConfig { + secretKey: string; + debugMode: boolean; + } + interface Props { + config: InternalConfig; + value: number; + } + let { config, value }: Props = $props(); + console.log(value, config.secretKey); +</script> +``` + +```svelte +<!-- ✓ Good Examples --> +<script lang="ts"> + /* eslint svelte/no-unused-props: ["error", { "ignorePropertyPatterns": ["/^_/"] }] */ + // Ignore properties with names matching the pattern interface Props { _internal: string; value: number; diff --git a/docs/user-guide.md b/docs/user-guide.md index 0eba2e2bd..babec87fa 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -13,7 +13,7 @@ npm install --save-dev svelte eslint eslint-plugin-svelte globals > **Requirements:** > > - ESLint v8.57.1, v9.0.0, and above -> - Node.js v18.20.4, v20.18.0, v22.10.0, and above +> - Node.js v18.18.0, v20.9.0, v21.1.0 and above ## Usage diff --git a/package.json b/package.json index 4c02f05d2..254352fc5 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "@typescript-eslint/parser": "^8.16.0", "c8": "^10.1.2", "env-cmd": "^10.1.0", - "eslint": "~9.22.0", + "eslint": "~9.23.0", "eslint-config-prettier": "^10.0.0", "eslint-formatter-friendly": "^7.0.0", "eslint-plugin-eslint-plugin": "^6.3.2", diff --git a/packages/eslint-plugin-svelte/CHANGELOG.md b/packages/eslint-plugin-svelte/CHANGELOG.md index 9aa040e0a..23a4fba9e 100644 --- a/packages/eslint-plugin-svelte/CHANGELOG.md +++ b/packages/eslint-plugin-svelte/CHANGELOG.md @@ -1,5 +1,41 @@ # eslint-plugin-svelte +## 3.3.3 + +### Patch Changes + +- [#1146](https://github.com/sveltejs/eslint-plugin-svelte/pull/1146) [`1233e46`](https://github.com/sveltejs/eslint-plugin-svelte/commit/1233e46413132c1e81dc69981be1533c5e6c6a4d) Thanks [@baseballyama](https://github.com/baseballyama)! - fix(no-unused-props): resolve false positives on props with default values or $bindable usage + +## 3.3.2 + +### Patch Changes + +- [#1143](https://github.com/sveltejs/eslint-plugin-svelte/pull/1143) [`138380d`](https://github.com/sveltejs/eslint-plugin-svelte/commit/138380deeec2ccd0189b70abf7d9a2cd6b608338) Thanks [@baseballyama](https://github.com/baseballyama)! - fix: stop reporting child properties in `no-unused-props` when the parent object itself is used + +## 3.3.1 + +### Patch Changes + +- [#1140](https://github.com/sveltejs/eslint-plugin-svelte/pull/1140) [`40806a4`](https://github.com/sveltejs/eslint-plugin-svelte/commit/40806a4b6342a7f2d1bd40d65df8dec5af666e1a) Thanks [@ota-meshi](https://github.com/ota-meshi)! - fix(no-useless-mustaches): Wrong auto-fix for quoted mustaches + +## 3.3.0 + +### Minor Changes + +- [#1132](https://github.com/sveltejs/eslint-plugin-svelte/pull/1132) [`30c1e5f`](https://github.com/sveltejs/eslint-plugin-svelte/commit/30c1e5fc6517cec7171ca42327699c3ad9ab6580) Thanks [@baseballyama](https://github.com/baseballyama)! - feat: add `ignorePropertyPatterns` property and rename `ignorePatterns` to `ignoreTypePatterns` in `no-unused-props` rule. The `ignorePatterns` option existed only for a few hours and is removed by this PR. Technically, this is a breaking change, but we’ll handle it as a minor release since very few users are likely affected. + +## 3.2.2 + +### Patch Changes + +- [#1135](https://github.com/sveltejs/eslint-plugin-svelte/pull/1135) [`1ff148a`](https://github.com/sveltejs/eslint-plugin-svelte/commit/1ff148a0bb24954aef59ce93b05a35913d83f2ee) Thanks [@baseballyama](https://github.com/baseballyama)! - fix: correct detection of externally defined types in `no-unused-props` rule + +## 3.2.1 + +### Patch Changes + +- [#1128](https://github.com/sveltejs/eslint-plugin-svelte/pull/1128) [`e94b59a`](https://github.com/sveltejs/eslint-plugin-svelte/commit/e94b59a35c914d868f89dd300b8d6b8090054976) Thanks [@baseballyama](https://github.com/baseballyama)! - fix: resolve false positives on nested objects in `no-unused-props` rule + ## 3.2.0 ### Minor Changes diff --git a/packages/eslint-plugin-svelte/package.json b/packages/eslint-plugin-svelte/package.json index 06a83ab12..236ec8e09 100644 --- a/packages/eslint-plugin-svelte/package.json +++ b/packages/eslint-plugin-svelte/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-svelte", - "version": "3.2.0", + "version": "3.3.3", "description": "ESLint plugin for Svelte using AST", "repository": "git+https://github.com/sveltejs/eslint-plugin-svelte.git", "homepage": "https://sveltejs.github.io/eslint-plugin-svelte", diff --git a/packages/eslint-plugin-svelte/src/meta.ts b/packages/eslint-plugin-svelte/src/meta.ts index 9de65a2c7..f3f181d74 100644 --- a/packages/eslint-plugin-svelte/src/meta.ts +++ b/packages/eslint-plugin-svelte/src/meta.ts @@ -2,4 +2,4 @@ // This file has been automatically generated, // in order to update its content execute "pnpm run update" export const name = 'eslint-plugin-svelte'; -export const version = '3.2.0'; +export const version = '3.3.3'; diff --git a/packages/eslint-plugin-svelte/src/rule-types.ts b/packages/eslint-plugin-svelte/src/rule-types.ts index 86e6d65b8..19e30a345 100644 --- a/packages/eslint-plugin-svelte/src/rule-types.ts +++ b/packages/eslint-plugin-svelte/src/rule-types.ts @@ -535,7 +535,8 @@ type SvelteNoUnusedClassName = []|[{ // ----- svelte/no-unused-props ----- type SvelteNoUnusedProps = []|[{ checkImportedTypes?: boolean - ignorePatterns?: string[] + ignoreTypePatterns?: string[] + ignorePropertyPatterns?: string[] }] // ----- svelte/no-useless-mustaches ----- type SvelteNoUselessMustaches = []|[{ diff --git a/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts b/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts index 1588f3b32..2ea58fd87 100644 --- a/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts +++ b/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts @@ -8,6 +8,8 @@ import { getFilename } from '../utils/compat.js'; type PropertyPath = string[]; +let isRemovedWarningShown = false; + export default createRule('no-unused-props', { meta: { docs: { @@ -23,7 +25,14 @@ export default createRule('no-unused-props', { type: 'boolean', default: false }, - ignorePatterns: { + ignoreTypePatterns: { + type: 'array', + items: { + type: 'string' + }, + default: [] + }, + ignorePropertyPatterns: { type: 'array', items: { type: 'string' @@ -61,34 +70,55 @@ export default createRule('no-unused-props', { } const options = context.options[0] ?? {}; + + // TODO: Remove in v4 + // MEMO: `ignorePatterns` was a property that only existed from v3.2.0 to v3.2.2. + // From v3.3.0, it was replaced with `ignorePropertyPatterns` and `ignoreTypePatterns`. + if (options.ignorePatterns != null && !isRemovedWarningShown) { + console.warn( + 'eslint-plugin-svelte: The `ignorePatterns` option in the `no-unused-props` rule has been removed. Please use `ignorePropertyPatterns` or/and `ignoreTypePatterns` instead.' + ); + isRemovedWarningShown = true; + } + const checkImportedTypes = options.checkImportedTypes ?? false; - const ignorePatterns = (options.ignorePatterns ?? []).map((p: string | RegExp) => { + + const ignoreTypePatterns = (options.ignoreTypePatterns ?? []).map((p: string | RegExp) => { if (typeof p === 'string') { return toRegExp(p); } return p; }); - function shouldIgnore(name: string): boolean { - return ignorePatterns.some((pattern: RegExp) => pattern.test(name)); + const ignorePropertyPatterns = (options.ignorePropertyPatterns ?? []).map( + (p: string | RegExp) => { + if (typeof p === 'string') { + return toRegExp(p); + } + return p; + } + ); + + function shouldIgnoreProperty(name: string): boolean { + return ignorePropertyPatterns.some((pattern: RegExp) => pattern.test(name)); } function shouldIgnoreType(type: ts.Type): boolean { + function isMatched(name: string): boolean { + return ignoreTypePatterns.some((pattern: RegExp) => pattern.test(name)); + } + const typeStr = typeChecker.typeToString(type); const symbol = type.getSymbol(); const symbolName = symbol?.getName(); - return shouldIgnore(typeStr) || (symbolName ? shouldIgnore(symbolName) : false); + return isMatched(typeStr) || (symbolName ? isMatched(symbolName) : false); } - function isExternalType(type: ts.Type): boolean { - const symbol = type.getSymbol(); - if (!symbol) return false; - + function isInternalProperty(symbol: ts.Symbol): boolean { const declarations = symbol.getDeclarations(); if (!declarations || declarations.length === 0) return false; - const sourceFile = declarations[0].getSourceFile(); - return sourceFile.fileName !== fileName; + return declarations.every((decl) => decl.getSourceFile().fileName === fileName); } /** @@ -126,7 +156,12 @@ export default createRule('no-unused-props', { const paths: PropertyPath[] = []; for (const reference of variable.references) { - if ('identifier' in reference && reference.identifier.type === 'Identifier') { + if ( + 'identifier' in reference && + reference.identifier.type === 'Identifier' && + (reference.identifier.range[0] !== node.range[0] || + reference.identifier.range[1] !== node.range[1]) + ) { const referencePath = getPropertyPath(reference.identifier); paths.push(referencePath); } @@ -200,7 +235,6 @@ export default createRule('no-unused-props', { if (checkedTypes.has(typeStr)) return; checkedTypes.add(typeStr); if (shouldIgnoreType(type)) return; - if (!checkImportedTypes && isExternalType(type)) return; const properties = typeChecker.getPropertiesOfType(type); const baseTypes = type.getBaseTypes(); @@ -225,19 +259,28 @@ export default createRule('no-unused-props', { for (const prop of properties) { if (isBuiltInProperty(prop)) continue; + if (!checkImportedTypes && !isInternalProperty(prop)) continue; const propName = prop.getName(); + if (shouldIgnoreProperty(propName)) continue; + const currentPath = [...parentPath, propName]; const currentPathStr = [...parentPath, propName].join('.'); if (reportedProps.has(currentPathStr)) continue; const propType = typeChecker.getTypeOfSymbol(prop); - const isUsedInPath = usedPaths.some((path) => { - const usedPath = path.join('.'); - return usedPath === currentPathStr || usedPath.startsWith(`${currentPathStr}.`); + + const joinedUsedPaths = usedPaths.map((path) => path.join('.')); + const isUsedThisInPath = joinedUsedPaths.includes(currentPathStr); + const isUsedInPath = joinedUsedPaths.some((path) => { + return path.startsWith(`${currentPathStr}.`); }); + if (isUsedThisInPath && !isUsedInPath) { + continue; + } + const isUsedInProps = usedProps.has(propName); if (!isUsedInPath && !isUsedInProps) { @@ -250,10 +293,11 @@ export default createRule('no-unused-props', { parent: parentPath.join('.') } }); + continue; } - const isUsedNested = usedPaths.some((path) => { - return path.join('.').startsWith(`${currentPathStr}.`); + const isUsedNested = joinedUsedPaths.some((path) => { + return path.startsWith(`${currentPathStr}.`); }); if (isUsedNested || isUsedInProps) { @@ -292,6 +336,18 @@ export default createRule('no-unused-props', { return usedProps.size === 0; } + function normalizeUsedPaths(paths: PropertyPath[]): PropertyPath[] { + const normalized: PropertyPath[] = []; + for (const path of paths.sort((a, b) => a.length - b.length)) { + if (path.length === 0) continue; + if (normalized.some((p) => p.every((part, idx) => part === path[idx]))) { + continue; + } + normalized.push(path); + } + return normalized; + } + return { 'VariableDeclaration > VariableDeclarator': (node: TSESTree.VariableDeclarator) => { // Only check $props declarations @@ -313,13 +369,20 @@ export default createRule('no-unused-props', { if (node.id.type === 'ObjectPattern') { usedProps = getUsedPropertiesFromPattern(node.id); if (usedProps.size === 0) return; - const identifiers = node.id.properties - .filter((p): p is TSESTree.Property => p.type === 'Property') - .map((p) => p.value) - .filter((v): v is TSESTree.Identifier => v.type === 'Identifier'); + const identifiers: TSESTree.Identifier[] = []; + for (const p of node.id.properties) { + if (p.type !== 'Property') { + continue; + } + if (p.value.type === 'Identifier') { + identifiers.push(p.value); + } else if (p.value.type === 'AssignmentPattern' && p.value.left.type === 'Identifier') { + identifiers.push(p.value.left); + } + } for (const identifier of identifiers) { const paths = getUsedNestedPropertyNames(identifier); - usedPaths.push(...paths); + usedPaths.push(...paths.map((path) => [identifier.name, ...path])); } } else if (node.id.type === 'Identifier') { usedPaths = getUsedNestedPropertyNames(node.id); @@ -327,7 +390,7 @@ export default createRule('no-unused-props', { checkUnusedProperties( propType, - usedPaths, + normalizeUsedPaths(usedPaths), usedProps, node.id, [], diff --git a/packages/eslint-plugin-svelte/src/rules/no-useless-mustaches.ts b/packages/eslint-plugin-svelte/src/rules/no-useless-mustaches.ts index 05d3af28e..d8da0cc11 100644 --- a/packages/eslint-plugin-svelte/src/rules/no-useless-mustaches.ts +++ b/packages/eslint-plugin-svelte/src/rules/no-useless-mustaches.ts @@ -142,14 +142,21 @@ export default createRule('no-useless-mustaches', { node.parent.key.range[1], node.parent.value[0].range[0] ); - if (!div.endsWith('"') && !div.endsWith("'")) { + const quote = div.endsWith('"') ? 'quot' : div.endsWith("'") ? 'apos' : null; + if (!quote) { return [ fixer.insertTextBefore(node.parent.value[0], '"'), fixer.replaceText(node, unescaped.replace(/"/gu, '"')), fixer.insertTextAfter(node.parent.value[node.parent.value.length - 1], '"') ]; } - return fixer.replaceText(node, unescaped); + + return fixer.replaceText( + node, + quote === 'quot' + ? unescaped.replace(/"/gu, '"') + : unescaped.replace(/'/gu, ''') + ); } return fixer.replaceText(node, unescaped.replace(/</gu, '<').replace(/>/gu, '>')); } diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/custom-config-combination-config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/custom-config-combination-config.json new file mode 100644 index 000000000..57ebeb292 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/custom-config-combination-config.json @@ -0,0 +1,9 @@ +{ + "options": [ + { + "checkImportedTypes": true, + "ignoreTypePatterns": ["BaseProps"], + "ignorePropertyPatterns": ["/^(_|baz)/"] + } + ] +} diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/custom-config-combination-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/custom-config-combination-errors.yaml new file mode 100644 index 000000000..52e74674f --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/custom-config-combination-errors.yaml @@ -0,0 +1,12 @@ +- message: "'base' is an unused Props property." + line: 10 + column: 6 + suggestions: null +- message: "'bar' in 'my_foo' is an unused property." + line: 10 + column: 6 + suggestions: null +- message: "'qux' is an unused Props property." + line: 10 + column: 6 + suggestions: null diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/custom-config-combination-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/custom-config-combination-input.svelte new file mode 100644 index 000000000..c0558eed7 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/custom-config-combination-input.svelte @@ -0,0 +1,12 @@ +<script lang="ts"> + import type { BaseProps, FooDTO } from './shared-types'; + interface Props { + base: BaseProps; + my_foo: FooDTO; + _my_bar: string; + baz: string; + qux: string; + } + let { my_foo }: Props = $props(); + console.log(my_foo.foo); +</script> diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignore-external-type-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignore-external-type-errors.yaml new file mode 100644 index 000000000..502c15458 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignore-external-type-errors.yaml @@ -0,0 +1,4 @@ +- message: "'child2' is an unused Props property." + line: 9 + column: 6 + suggestions: null diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignore-external-type-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignore-external-type-input.svelte new file mode 100644 index 000000000..2d26bd2d9 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignore-external-type-input.svelte @@ -0,0 +1,10 @@ +<script lang="ts"> + import type { FooDTO } from './shared-types'; + + interface Props extends FooDTO { + child1: string; + child2: number; + } + + let { foo, child1 }: Props = $props(); +</script> diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignore-property-patterns-custom-config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignore-property-patterns-custom-config.json new file mode 100644 index 000000000..65233c1d9 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignore-property-patterns-custom-config.json @@ -0,0 +1,7 @@ +{ + "options": [ + { + "ignorePropertyPatterns": ["^foo$"] + } + ] +} diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignore-property-patterns-custom-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignore-property-patterns-custom-errors.yaml new file mode 100644 index 000000000..32c659188 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignore-property-patterns-custom-errors.yaml @@ -0,0 +1,8 @@ +- message: "'foo' is an unused Props property." + line: 8 + column: 8 + suggestions: null +- message: "'_foo' is an unused Props property." + line: 8 + column: 8 + suggestions: null diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignore-property-patterns-custom-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignore-property-patterns-custom-input.svelte new file mode 100644 index 000000000..7bee93044 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignore-property-patterns-custom-input.svelte @@ -0,0 +1,9 @@ +<script lang="ts"> + interface Props { + foo: string; + _foo: string; + bar: string; + } + + const { bar }: Props = $props(); +</script> diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-pattern-partial-config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-pattern-partial-config.json deleted file mode 100644 index c049eb81c..000000000 --- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-pattern-partial-config.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "options": [ - { - "ignorePatterns": [".*DTO$"] - } - ] -} diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-type-patterns-custom-config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-type-patterns-custom-config.json new file mode 100644 index 000000000..ee109aa43 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-type-patterns-custom-config.json @@ -0,0 +1,7 @@ +{ + "options": [ + { + "ignoreTypePatterns": [".*DTO$"] + } + ] +} diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-pattern-partial-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-type-patterns-custom-errors.yaml similarity index 100% rename from packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-pattern-partial-errors.yaml rename to packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-type-patterns-custom-errors.yaml diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-pattern-partial-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-type-patterns-custom-input.svelte similarity index 100% rename from packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-pattern-partial-input.svelte rename to packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/ignored-type-patterns-custom-input.svelte diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/imported-type-check-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/imported-type-check-errors.yaml index 2f341476f..278805b2d 100644 --- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/imported-type-check-errors.yaml +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/imported-type-check-errors.yaml @@ -1,6 +1,4 @@ - message: "'name' is an unused Props property." line: 6 column: 6 - endLine: 6 - endColumn: 20 suggestions: null diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/shared-types.ts b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/shared-types.ts index a60e61ef1..e33a3a24e 100644 --- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/shared-types.ts +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/invalid/shared-types.ts @@ -1,3 +1,14 @@ export interface BaseProps { name: string; } + +export interface FooDTO { + foo: string; + bar: number; + baz: BazDTO; +} + +interface BazDTO { + qux: string; + quux: number; +} diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/bindable-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/bindable-input.svelte new file mode 100644 index 000000000..ef2c5e990 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/bindable-input.svelte @@ -0,0 +1,9 @@ +<script lang="ts"> + interface Props { + selected: { value: string }; + } + + let { selected = $bindable() }: Props = $props(); +</script> + +<input type="text" bind:value={selected.value} /> diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/custom-config-combination-config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/custom-config-combination-config.json new file mode 100644 index 000000000..57ebeb292 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/custom-config-combination-config.json @@ -0,0 +1,9 @@ +{ + "options": [ + { + "checkImportedTypes": true, + "ignoreTypePatterns": ["BaseProps"], + "ignorePropertyPatterns": ["/^(_|baz)/"] + } + ] +} diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/custom-config-combination-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/custom-config-combination-input.svelte new file mode 100644 index 000000000..c34bc850e --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/custom-config-combination-input.svelte @@ -0,0 +1,11 @@ +<script lang="ts"> + import type { BaseProps, FooDTO } from './shared-types'; + interface Props { + base: BaseProps; + my_foo: FooDTO; + _my_bar: string; + baz: string; + } + let { base, my_foo }: Props = $props(); + console.log(base.age, my_foo.foo, my_foo.bar); +</script> diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/default-value-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/default-value-input.svelte new file mode 100644 index 000000000..48d6887f4 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/default-value-input.svelte @@ -0,0 +1,14 @@ +<script lang="ts"> + interface Props { + newTaskAttributes: { attribute: number; attribute2: string }; + } + + const { newTaskAttributes = { attribute: 0, attribute2: '' } }: Props = $props(); +</script> + +<div class="col-span-full"> + <div> + {newTaskAttributes.attribute} + {newTaskAttributes.attribute2} + </div> +</div> diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignore-external-type-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignore-external-type-input.svelte new file mode 100644 index 000000000..920dece77 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignore-external-type-input.svelte @@ -0,0 +1,10 @@ +<script lang="ts"> + import type { FooDTO } from './shared-types'; + + interface Props extends FooDTO { + child1: string; + child2: number; + } + + let { foo, child1, child2 }: Props = $props(); +</script> diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignore-property-patterns-custom-config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignore-property-patterns-custom-config.json new file mode 100644 index 000000000..c22dc1654 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignore-property-patterns-custom-config.json @@ -0,0 +1,7 @@ +{ + "options": [ + { + "ignorePropertyPatterns": ["/^[#$@_~]/"] + } + ] +} diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignore-property-patterns-custom-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignore-property-patterns-custom-input.svelte new file mode 100644 index 000000000..1dc0ca14d --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignore-property-patterns-custom-input.svelte @@ -0,0 +1,13 @@ +<script lang="ts"> + interface Props { + _internal: string; + $store: boolean; + '@decorator': string; + '#private': number; + '~tilde': boolean; + normalUsed: string; + } + + const { normalUsed }: Props = $props(); + console.log(normalUsed); +</script> diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignore-property-patterns-default-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignore-property-patterns-default-input.svelte new file mode 100644 index 000000000..5694cb2d7 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignore-property-patterns-default-input.svelte @@ -0,0 +1,8 @@ +<script lang="ts"> + interface Props { + foo: string; + bar: string; + } + + const { foo, bar }: Props = $props(); +</script> diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignored-conditional-type-config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignored-conditional-type-config.json deleted file mode 100644 index 73df77c94..000000000 --- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignored-conditional-type-config.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "options": [ - { - "ignorePatterns": ["^Conditional"] - } - ] -} diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignored-type-patterns-custom-config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignored-type-patterns-custom-config.json new file mode 100644 index 000000000..3a5c1de84 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignored-type-patterns-custom-config.json @@ -0,0 +1,7 @@ +{ + "options": [ + { + "ignoreTypePatterns": ["/^Conditional/"] + } + ] +} diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignored-conditional-type-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignored-type-patterns-custom-input.svelte similarity index 100% rename from packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignored-conditional-type-input.svelte rename to packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignored-type-patterns-custom-input.svelte diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignored-type-patterns-custom2-config.json b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignored-type-patterns-custom2-config.json new file mode 100644 index 000000000..347ef2b85 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignored-type-patterns-custom2-config.json @@ -0,0 +1,7 @@ +{ + "options": [ + { + "ignoreTypePatterns": ["/^Internal/"] + } + ] +} diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignored-type-patterns-custom2-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignored-type-patterns-custom2-input.svelte new file mode 100644 index 000000000..0f465ea7d --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ignored-type-patterns-custom2-input.svelte @@ -0,0 +1,12 @@ +<script lang="ts"> + interface InternalConfig { + secretKey: string; + debugMode: boolean; + } + interface Props { + config: InternalConfig; + value: number; + } + let { config, value }: Props = $props(); + console.log(value, config.secretKey); +</script> diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/nested-props2-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/nested-props2-input.svelte new file mode 100644 index 000000000..3fb8790fd --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/nested-props2-input.svelte @@ -0,0 +1,14 @@ +<script lang="ts"> + import GenericPopout from './GenericPopout.svelte'; + + let { + position + }: { + position: { + x: number; + y: number; + }; + } = $props(); +</script> + +<GenericPopout {position}>Test</GenericPopout> diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/nested-props3-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/nested-props3-input.svelte new file mode 100644 index 000000000..7090054f8 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/nested-props3-input.svelte @@ -0,0 +1,16 @@ +<script lang="ts"> + import GenericPopout from './GenericPopout.svelte'; + + let { + wrapper + }: { + wrapper: { + position: { + x: number; + y: number; + }; + }; + } = $props(); +</script> + +<GenericPopout position={wrapper.position}>Test</GenericPopout> diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/nested-props4-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/nested-props4-input.svelte new file mode 100644 index 000000000..15bdb1277 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/nested-props4-input.svelte @@ -0,0 +1,17 @@ +<script lang="ts"> + import GenericPopout from './GenericPopout.svelte'; + + let { + wrapper + }: { + wrapper: { + position: { + x: number; + y: number; + }; + }; + } = $props(); +</script> + +<GenericPopout x={wrapper.position.x}>Test</GenericPopout> +<GenericPopout position={wrapper.position}>Test</GenericPopout> diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/shared-types.ts b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/shared-types.ts index a60e61ef1..76d6b33c5 100644 --- a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/shared-types.ts +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/shared-types.ts @@ -1,3 +1,15 @@ export interface BaseProps { name: string; + age: number; +} + +export interface FooDTO { + foo: string; + bar: number; + baz: BazDTO; +} + +interface BazDTO { + qux: string; + quux: number; } diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ts-basic-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ts-basic-input.svelte new file mode 100644 index 000000000..d2c509f6a --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/ts-basic-input.svelte @@ -0,0 +1,15 @@ +<script lang="ts"> + // fix: https://github.com/sveltejs/eslint-plugin-svelte/issues/1028#issuecomment-2728101827 + type Prop = { + value: string; + value2: string; + }; + + interface Props { + myObjectProp: Prop; + } + + const { myObjectProp }: Props = $props(); +</script> + +<p>{myObjectProp.value} {myObjectProp.value2}</p> diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-mustaches/invalid/quote-test02-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-mustaches/invalid/quote-test02-errors.yaml new file mode 100644 index 000000000..ee6ce5e3e --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-mustaches/invalid/quote-test02-errors.yaml @@ -0,0 +1,16 @@ +- message: Unexpected mustache interpolation with a string literal value. + line: 2 + column: 17 + suggestions: null +- message: Unexpected mustache interpolation with a string literal value. + line: 4 + column: 17 + suggestions: null +- message: Unexpected mustache interpolation with a string literal value. + line: 6 + column: 17 + suggestions: null +- message: Unexpected mustache interpolation with a string literal value. + line: 8 + column: 17 + suggestions: null diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-mustaches/invalid/quote-test02-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-mustaches/invalid/quote-test02-input.svelte new file mode 100644 index 000000000..43dbb27d9 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-mustaches/invalid/quote-test02-input.svelte @@ -0,0 +1,8 @@ +<!--prettier-ignore--> +<div data-text="{'\'"'}" /> +<!--prettier-ignore--> +<div data-text="{"'\""}" /> +<!--prettier-ignore--> +<div data-text='{'\'"'}' /> +<!--prettier-ignore--> +<div data-text='{"'\""}' /> diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-mustaches/invalid/quote-test02-output.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-mustaches/invalid/quote-test02-output.svelte new file mode 100644 index 000000000..fd9b14743 --- /dev/null +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-useless-mustaches/invalid/quote-test02-output.svelte @@ -0,0 +1,8 @@ +<!--prettier-ignore--> +<div data-text="'"" /> +<!--prettier-ignore--> +<div data-text="'"" /> +<!--prettier-ignore--> +<div data-text=''"' /> +<!--prettier-ignore--> +<div data-text=''"' />