Skip to content

feat: add config option for foreign elements in svelte/html-self-closing rule #841

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/rotten-news-swim.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'eslint-plugin-svelte': minor
---

feat: add config option for foreign elements in `svelte/html-self-closing` rule
2 changes: 2 additions & 0 deletions docs/rules/html-self-closing.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ config object:
{
"void": "always", // or "never" or "ignore"
"normal": "always", // or "never" or "ignore"
"foreign": "always", // or "never" or "ignore"
"component": "always", // or "never" or "ignore"
"svelte": "always" // or "never" or "ignore"
}
Expand All @@ -86,6 +87,7 @@ presets:
config object:

- `void` (`"always"` in default preset)... Style of HTML void elements
- `foreign` (`"always"` in default preset)... Style of foreign elements (SVG and MathML)
- `component` (`"always"` in default preset)... Style of svelte components
- `svelte` (`"always"` in default preset)... Style of svelte special elements (`<svelte:head>`, `<svelte:self>`)
- `normal` (`"always"` in default preset)... Style of other elements
Expand Down
1 change: 1 addition & 0 deletions packages/eslint-plugin-svelte/src/rule-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ type SvelteHtmlQuotes = []|[{
type SvelteHtmlSelfClosing = []|[({
void?: ("never" | "always" | "ignore")
normal?: ("never" | "always" | "ignore")
foreign?: ("never" | "always" | "ignore")
component?: ("never" | "always" | "ignore")
svelte?: ("never" | "always" | "ignore")
} | ("all" | "html" | "none"))]
Expand Down
12 changes: 10 additions & 2 deletions packages/eslint-plugin-svelte/src/rules/html-self-closing.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import type { AST } from 'svelte-eslint-parser';
import { createRule } from '../utils';
import { getNodeName, isVoidHtmlElement } from '../utils/ast-utils';
import { getNodeName, isVoidHtmlElement, isForeignElement } from '../utils/ast-utils';
import { getSourceCode } from '../utils/compat';

const TYPE_MESSAGES = {
normal: 'HTML elements',
void: 'HTML void elements',
foreign: 'foreign (SVG or MathML) elements',
component: 'Svelte custom components',
svelte: 'Svelte special elements'
};

type ElementTypes = 'normal' | 'void' | 'component' | 'svelte';
type ElementTypes = 'normal' | 'void' | 'foreign' | 'component' | 'svelte';

export default createRule('html-self-closing', {
meta: {
Expand All @@ -37,6 +38,9 @@ export default createRule('html-self-closing', {
normal: {
enum: ['never', 'always', 'ignore']
},
foreign: {
enum: ['never', 'always', 'ignore']
},
component: {
enum: ['never', 'always', 'ignore']
},
Expand All @@ -57,6 +61,7 @@ export default createRule('html-self-closing', {
let options = {
void: 'always',
normal: 'always',
foreign: 'always',
component: 'always',
svelte: 'always'
};
Expand All @@ -67,6 +72,7 @@ export default createRule('html-self-closing', {
options = {
void: 'never',
normal: 'never',
foreign: 'never',
component: 'never',
svelte: 'never'
};
Expand All @@ -75,6 +81,7 @@ export default createRule('html-self-closing', {
options = {
void: 'always',
normal: 'never',
foreign: 'always',
component: 'never',
svelte: 'always'
};
Expand All @@ -101,6 +108,7 @@ export default createRule('html-self-closing', {
if (node.kind === 'component') return 'component';
if (node.kind === 'special') return 'svelte';
if (isVoidHtmlElement(node)) return 'void';
if (isForeignElement(node)) return 'foreign';
return 'normal';
}

Expand Down
9 changes: 8 additions & 1 deletion packages/eslint-plugin-svelte/src/utils/ast-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { TSESTree } from '@typescript-eslint/types';
import type { Scope, Variable } from '@typescript-eslint/scope-manager';
import type { AST as SvAST } from 'svelte-eslint-parser';
import * as eslintUtils from '@eslint-community/eslint-utils';
import voidElements from './void-elements';
import { voidElements, svgElements, mathmlElements } from './element-types';
import { getSourceCode } from './compat';

/**
Expand Down Expand Up @@ -560,6 +560,13 @@ export function isVoidHtmlElement(node: SvAST.SvelteElement): boolean {
return voidElements.includes(getNodeName(node));
}

/**
* Returns true if element is known foreign (SVG or MathML) element
*/
export function isForeignElement(node: SvAST.SvelteElement): boolean {
return svgElements.includes(getNodeName(node)) || mathmlElements.includes(getNodeName(node));
}

/** Checks whether the given identifier node is used as an expression. */
export function isExpressionIdentifier(node: TSESTree.Identifier): boolean {
const parent = node.parent;
Expand Down
140 changes: 140 additions & 0 deletions packages/eslint-plugin-svelte/src/utils/element-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
export const voidElements = [
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved from removed void-elemets.ts file. It's actually different that the list that Svelte uses (https://github.com/sveltejs/svelte/blob/c32a91891fc647998f738371a012fcba97d56e50/packages/svelte/src/utils.js#L16-L33).

'area',
'base',
'br',
'col',
'embed',
'hr',
'img',
'input',
'keygen',
'link',
'menuitem',
'meta',
'param',
'source',
'track',
'wbr'
];

export const svgElements = [
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'altGlyph',
'altGlyphDef',
'altGlyphItem',
'animate',
'animateColor',
'animateMotion',
'animateTransform',
'circle',
'clipPath',
'color-profile',
'cursor',
'defs',
'desc',
'discard',
'ellipse',
'feBlend',
'feColorMatrix',
'feComponentTransfer',
'feComposite',
'feConvolveMatrix',
'feDiffuseLighting',
'feDisplacementMap',
'feDistantLight',
'feDropShadow',
'feFlood',
'feFuncA',
'feFuncB',
'feFuncG',
'feFuncR',
'feGaussianBlur',
'feImage',
'feMerge',
'feMergeNode',
'feMorphology',
'feOffset',
'fePointLight',
'feSpecularLighting',
'feSpotLight',
'feTile',
'feTurbulence',
'filter',
'font',
'font-face',
'font-face-format',
'font-face-name',
'font-face-src',
'font-face-uri',
'foreignObject',
'g',
'glyph',
'glyphRef',
'hatch',
'hatchpath',
'hkern',
'image',
'line',
'linearGradient',
'marker',
'mask',
'mesh',
'meshgradient',
'meshpatch',
'meshrow',
'metadata',
'missing-glyph',
'mpath',
'path',
'pattern',
'polygon',
'polyline',
'radialGradient',
'rect',
'set',
'solidcolor',
'stop',
'svg',
'switch',
'symbol',
'text',
'textPath',
'tref',
'tspan',
'unknown',
'use',
'view',
'vkern'
];

export const mathmlElements = [
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'annotation',
'annotation-xml',
'maction',
'math',
'merror',
'mfrac',
'mi',
'mmultiscripts',
'mn',
'mo',
'mover',
'mpadded',
'mphantom',
'mprescripts',
'mroot',
'mrow',
'ms',
'mspace',
'msqrt',
'mstyle',
'msub',
'msubsup',
'msup',
'mtable',
'mtd',
'mtext',
'mtr',
'munder',
'munderover',
'semantics'
];
20 changes: 0 additions & 20 deletions packages/eslint-plugin-svelte/src/utils/void-elements.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"options": [
{
"foreign": "never"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
- message: Disallow self-closing on foreign (SVG or MathML) elements.
line: 2
column: 12
suggestions: null
- message: Disallow self-closing on foreign (SVG or MathML) elements.
line: 3
column: 13
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<!-- prettier-ignore -->
<svg><path /></svg>
<math><msup /></math>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<!-- prettier-ignore -->
<svg><path ></path></svg>
<math><msup ></msup></math>
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@
line: 5
column: 18
suggestions: null
- message: Require self-closing on foreign (SVG or MathML) elements.
line: 6
column: 13
suggestions: null
- message: Require self-closing on foreign (SVG or MathML) elements.
line: 7
column: 14
suggestions: null
- message: Require self-closing on Svelte special elements.
line: 8
line: 10
column: 13
suggestions: null
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
<div />
<img>
<TestComponent />
<svg><path></path></svg>
<math><msup></msup></math>
</div>
<!-- prettier-ignore -->
<svelte:head></svelte:head>
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
<div ></div>
<img/>
<TestComponent ></TestComponent>
<svg><path/></svg>
<math><msup/></math>
</div>
<!-- prettier-ignore -->
<svelte:head/>
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@
line: 5
column: 8
suggestions: null
- message: Disallow self-closing on foreign (SVG or MathML) elements.
line: 6
column: 14
suggestions: null
- message: Disallow self-closing on foreign (SVG or MathML) elements.
line: 7
column: 15
suggestions: null
- message: Disallow self-closing on Svelte special elements.
line: 8
line: 10
column: 14
suggestions: null
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
<div />
<TestComponent />
<img />
<svg><path /></svg>
<math><msup /></math>
</div>
<!-- prettier-ignore -->
<svelte:head />
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
<div ></div>
<TestComponent ></TestComponent>
<img >
<svg><path ></path></svg>
<math><msup ></msup></math>
</div>
<!-- prettier-ignore -->
<svelte:head ></svelte:head>
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<div />
<div>hello</div>
<img />
<svg><path /></svg>
<math><msup /></math>
{#if true}
<svelte:self />
{/if}
Expand Down