diff --git a/.changeset/yellow-icons-explode.md b/.changeset/yellow-icons-explode.md new file mode 100644 index 000000000..bf5ff3569 --- /dev/null +++ b/.changeset/yellow-icons-explode.md @@ -0,0 +1,5 @@ +--- +"eslint-plugin-svelte": minor +--- + +feat: add support for `{@snippet}` and `{@render}` in mustache-spacing rule diff --git a/.eslintignore b/.eslintignore index 108621b22..8c0a0b948 100644 --- a/.eslintignore +++ b/.eslintignore @@ -18,6 +18,8 @@ /tests/fixtures/rules/prefer-style-directive /tests/fixtures/rules/@typescript-eslint /tests/fixtures/rules/valid-compile/valid/svelte3-options-custom-element-input.svelte +/tests/fixtures/rules/mustache-spacing/valid/always/snippet-render01-input.svelte +/tests/fixtures/rules/mustache-spacing/invalid/snippet-render01-input.svelte /.svelte-kit /svelte.config-dist.js /build diff --git a/src/rules/mustache-spacing.ts b/src/rules/mustache-spacing.ts index 1b1c971e5..e48c9ea3f 100644 --- a/src/rules/mustache-spacing.ts +++ b/src/rules/mustache-spacing.ts @@ -217,6 +217,16 @@ export default createRule('mustache-spacing', { true ); }, + SvelteRenderTag(node) { + const mustacheTokens = getMustacheTokens(node, sourceCode); + verifyBraces( + mustacheTokens.openToken, + mustacheTokens.closeToken, + options.tags.openingBrace, + options.tags.closingBrace, + true + ); + }, SvelteIfBlock(node) { const openBlockOpeningToken = sourceCode.getFirstToken(node); const openBlockClosingToken = sourceCode.getTokenAfter(node.expression, { @@ -292,7 +302,7 @@ export default createRule('mustache-spacing', { false ); }, - SvelteKeyBlock(node: AST.SvelteEachBlock | AST.SvelteKeyBlock) { + SvelteKeyBlock(node: AST.SvelteKeyBlock) { const openBlockOpeningToken = sourceCode.getFirstToken(node); const openBlockClosingToken = sourceCode.getTokenAfter(node.expression, { includeComments: false, @@ -387,6 +397,32 @@ export default createRule('mustache-spacing', { openBlockClosingToken === sourceCode.getTokenAfter(openBlockLast) ) ); + }, + SvelteSnippetBlock(node) { + const openBlockOpeningToken = sourceCode.getFirstToken(node); + const openBlockClosingToken = sourceCode.getTokenAfter(node.context || node.id, { + includeComments: false, + filter: isClosingBraceToken + })!; + verifyBraces( + openBlockOpeningToken, + openBlockClosingToken, + options.tags.openingBrace, + options.tags.closingBrace, + true + ); + const closeBlockClosingToken = sourceCode.getLastToken(node); + const closeBlockOpeningToken = sourceCode.getTokenBefore(closeBlockClosingToken, { + includeComments: false, + filter: isOpeningBraceToken + })!; + verifyBraces( + closeBlockOpeningToken, + closeBlockClosingToken, + options.tags.openingBrace, + options.tags.closingBrace, + false + ); } }; } diff --git a/src/utils/ast-utils.ts b/src/utils/ast-utils.ts index d05395908..c347bb2c9 100644 --- a/src/utils/ast-utils.ts +++ b/src/utils/ast-utils.ts @@ -352,7 +352,8 @@ export function getMustacheTokens( | SvAST.SvelteMustacheTag | SvAST.SvelteShorthandAttribute | SvAST.SvelteSpreadAttribute - | SvAST.SvelteDebugTag, + | SvAST.SvelteDebugTag + | SvAST.SvelteRenderTag, sourceCode: SourceCode ): { openToken: SvAST.Token; @@ -365,7 +366,8 @@ export function getMustacheTokens( | SvAST.SvelteMustacheTag | SvAST.SvelteShorthandAttribute | SvAST.SvelteSpreadAttribute - | SvAST.SvelteDebugTag, + | SvAST.SvelteDebugTag + | SvAST.SvelteRenderTag, sourceCode: SourceCode ): { openToken: SvAST.Token; @@ -379,18 +381,14 @@ export function getMustacheTokens( | SvAST.SvelteMustacheTag | SvAST.SvelteShorthandAttribute | SvAST.SvelteSpreadAttribute - | SvAST.SvelteDebugTag, + | SvAST.SvelteDebugTag + | SvAST.SvelteRenderTag, sourceCode: SourceCode ): { openToken: SvAST.Token; closeToken: SvAST.Token; } | null { - if ( - node.type === 'SvelteMustacheTag' || - node.type === 'SvelteShorthandAttribute' || - node.type === 'SvelteSpreadAttribute' || - node.type === 'SvelteDebugTag' - ) { + if (isWrappedInBraces(node)) { const openToken = sourceCode.getFirstToken(node); const closeToken = sourceCode.getLastToken(node); return { @@ -433,6 +431,30 @@ export function getMustacheTokens( }; } +function isWrappedInBraces( + node: + | SvAST.SvelteDirective + | SvAST.SvelteSpecialDirective + | SvAST.SvelteMustacheTag + | SvAST.SvelteShorthandAttribute + | SvAST.SvelteSpreadAttribute + | SvAST.SvelteDebugTag + | SvAST.SvelteRenderTag +): node is + | SvAST.SvelteMustacheTag + | SvAST.SvelteShorthandAttribute + | SvAST.SvelteSpreadAttribute + | SvAST.SvelteDebugTag + | SvAST.SvelteRenderTag { + return ( + node.type === 'SvelteMustacheTag' || + node.type === 'SvelteShorthandAttribute' || + node.type === 'SvelteSpreadAttribute' || + node.type === 'SvelteDebugTag' || + node.type === 'SvelteRenderTag' + ); +} + /** Get attribute key text */ export function getAttributeKeyText( node: diff --git a/tests/fixtures/rules/mustache-spacing/invalid/always-after-expression/snippet-render01-errors.yaml b/tests/fixtures/rules/mustache-spacing/invalid/always-after-expression/snippet-render01-errors.yaml new file mode 100644 index 000000000..5270244ea --- /dev/null +++ b/tests/fixtures/rules/mustache-spacing/invalid/always-after-expression/snippet-render01-errors.yaml @@ -0,0 +1,8 @@ +- message: Expected 1 space before '}', but not found. + line: 2 + column: 16 + suggestions: null +- message: Expected 1 space before '}', but not found. + line: 4 + column: 15 + suggestions: null diff --git a/tests/fixtures/rules/mustache-spacing/invalid/always-after-expression/snippet-render01-input.svelte b/tests/fixtures/rules/mustache-spacing/invalid/always-after-expression/snippet-render01-input.svelte new file mode 100644 index 000000000..04cc8421b --- /dev/null +++ b/tests/fixtures/rules/mustache-spacing/invalid/always-after-expression/snippet-render01-input.svelte @@ -0,0 +1,4 @@ + +{#snippet foo()} +{/snippet} +{@render foo()} diff --git a/tests/fixtures/rules/mustache-spacing/invalid/always-after-expression/snippet-render01-output.svelte b/tests/fixtures/rules/mustache-spacing/invalid/always-after-expression/snippet-render01-output.svelte new file mode 100644 index 000000000..981b40eb5 --- /dev/null +++ b/tests/fixtures/rules/mustache-spacing/invalid/always-after-expression/snippet-render01-output.svelte @@ -0,0 +1,4 @@ + +{#snippet foo() } +{/snippet} +{@render foo() } diff --git a/tests/fixtures/rules/mustache-spacing/invalid/always-after-expression/snippet-render01-requirements.json b/tests/fixtures/rules/mustache-spacing/invalid/always-after-expression/snippet-render01-requirements.json new file mode 100644 index 000000000..0192b1098 --- /dev/null +++ b/tests/fixtures/rules/mustache-spacing/invalid/always-after-expression/snippet-render01-requirements.json @@ -0,0 +1,3 @@ +{ + "svelte": ">=5.0.0-0" +} diff --git a/tests/fixtures/rules/mustache-spacing/invalid/always/snippet-render01-errors.yaml b/tests/fixtures/rules/mustache-spacing/invalid/always/snippet-render01-errors.yaml new file mode 100644 index 000000000..0eb38c3cd --- /dev/null +++ b/tests/fixtures/rules/mustache-spacing/invalid/always/snippet-render01-errors.yaml @@ -0,0 +1,24 @@ +- message: Expected 1 space after '{', but not found. + line: 2 + column: 1 + suggestions: null +- message: Expected 1 space before '}', but not found. + line: 2 + column: 16 + suggestions: null +- message: Expected 1 space after '{', but not found. + line: 3 + column: 1 + suggestions: null +- message: Expected 1 space before '}', but not found. + line: 3 + column: 10 + suggestions: null +- message: Expected 1 space after '{', but not found. + line: 4 + column: 1 + suggestions: null +- message: Expected 1 space before '}', but not found. + line: 4 + column: 15 + suggestions: null diff --git a/tests/fixtures/rules/mustache-spacing/invalid/always/snippet-render01-input.svelte b/tests/fixtures/rules/mustache-spacing/invalid/always/snippet-render01-input.svelte new file mode 100644 index 000000000..04cc8421b --- /dev/null +++ b/tests/fixtures/rules/mustache-spacing/invalid/always/snippet-render01-input.svelte @@ -0,0 +1,4 @@ + +{#snippet foo()} +{/snippet} +{@render foo()} diff --git a/tests/fixtures/rules/mustache-spacing/invalid/always/snippet-render01-output.svelte b/tests/fixtures/rules/mustache-spacing/invalid/always/snippet-render01-output.svelte new file mode 100644 index 000000000..59b726019 --- /dev/null +++ b/tests/fixtures/rules/mustache-spacing/invalid/always/snippet-render01-output.svelte @@ -0,0 +1,4 @@ + +{ #snippet foo() } +{ /snippet } +{ @render foo() } diff --git a/tests/fixtures/rules/mustache-spacing/invalid/always/snippet-render01-requirements.json b/tests/fixtures/rules/mustache-spacing/invalid/always/snippet-render01-requirements.json new file mode 100644 index 000000000..0192b1098 --- /dev/null +++ b/tests/fixtures/rules/mustache-spacing/invalid/always/snippet-render01-requirements.json @@ -0,0 +1,3 @@ +{ + "svelte": ">=5.0.0-0" +} diff --git a/tests/fixtures/rules/mustache-spacing/invalid/snippet-render01-errors.yaml b/tests/fixtures/rules/mustache-spacing/invalid/snippet-render01-errors.yaml new file mode 100644 index 000000000..d14b8bb3f --- /dev/null +++ b/tests/fixtures/rules/mustache-spacing/invalid/snippet-render01-errors.yaml @@ -0,0 +1,24 @@ +- message: Expected no space after '{', but found. + line: 2 + column: 3 + suggestions: null +- message: Expected no space before '}', but found. + line: 2 + column: 17 + suggestions: null +- message: Expected no space after '{', but found. + line: 3 + column: 1 + suggestions: null +- message: Expected no space before '}', but found. + line: 3 + column: 11 + suggestions: null +- message: Expected no space after '{', but found. + line: 4 + column: 1 + suggestions: null +- message: Expected no space before '}', but found. + line: 4 + column: 16 + suggestions: null diff --git a/tests/fixtures/rules/mustache-spacing/invalid/snippet-render01-input.svelte b/tests/fixtures/rules/mustache-spacing/invalid/snippet-render01-input.svelte new file mode 100644 index 000000000..59b726019 --- /dev/null +++ b/tests/fixtures/rules/mustache-spacing/invalid/snippet-render01-input.svelte @@ -0,0 +1,4 @@ + +{ #snippet foo() } +{ /snippet } +{ @render foo() } diff --git a/tests/fixtures/rules/mustache-spacing/invalid/snippet-render01-output.svelte b/tests/fixtures/rules/mustache-spacing/invalid/snippet-render01-output.svelte new file mode 100644 index 000000000..04cc8421b --- /dev/null +++ b/tests/fixtures/rules/mustache-spacing/invalid/snippet-render01-output.svelte @@ -0,0 +1,4 @@ + +{#snippet foo()} +{/snippet} +{@render foo()} diff --git a/tests/fixtures/rules/mustache-spacing/invalid/snippet-render01-requirements.json b/tests/fixtures/rules/mustache-spacing/invalid/snippet-render01-requirements.json new file mode 100644 index 000000000..269d5cfe2 --- /dev/null +++ b/tests/fixtures/rules/mustache-spacing/invalid/snippet-render01-requirements.json @@ -0,0 +1,4 @@ +{ + "FIXME": "It seems to return the wrong location in Svelte v5.", + "svelte": "<=0.0.0 >=5.0.0-0" +} diff --git a/tests/fixtures/rules/mustache-spacing/valid/always/snippet-render01-input.svelte b/tests/fixtures/rules/mustache-spacing/valid/always/snippet-render01-input.svelte new file mode 100644 index 000000000..59b726019 --- /dev/null +++ b/tests/fixtures/rules/mustache-spacing/valid/always/snippet-render01-input.svelte @@ -0,0 +1,4 @@ + +{ #snippet foo() } +{ /snippet } +{ @render foo() } diff --git a/tests/fixtures/rules/mustache-spacing/valid/always/snippet-render01-requirements.json b/tests/fixtures/rules/mustache-spacing/valid/always/snippet-render01-requirements.json new file mode 100644 index 000000000..0192b1098 --- /dev/null +++ b/tests/fixtures/rules/mustache-spacing/valid/always/snippet-render01-requirements.json @@ -0,0 +1,3 @@ +{ + "svelte": ">=5.0.0-0" +} diff --git a/tests/fixtures/rules/mustache-spacing/valid/snippet-render01-input.svelte b/tests/fixtures/rules/mustache-spacing/valid/snippet-render01-input.svelte new file mode 100644 index 000000000..04cc8421b --- /dev/null +++ b/tests/fixtures/rules/mustache-spacing/valid/snippet-render01-input.svelte @@ -0,0 +1,4 @@ + +{#snippet foo()} +{/snippet} +{@render foo()} diff --git a/tests/fixtures/rules/mustache-spacing/valid/snippet-render01-requirements.json b/tests/fixtures/rules/mustache-spacing/valid/snippet-render01-requirements.json new file mode 100644 index 000000000..0192b1098 --- /dev/null +++ b/tests/fixtures/rules/mustache-spacing/valid/snippet-render01-requirements.json @@ -0,0 +1,3 @@ +{ + "svelte": ">=5.0.0-0" +}