ui: ESLint config updates (#27700)
* chore: Spacing between sibling elements in html markup * chore: Formatting and linting rules
This commit is contained in:
+151
-3
@@ -12,6 +12,107 @@ import { fileURLToPath } from 'node:url';
|
||||
import ts from 'typescript-eslint';
|
||||
|
||||
const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url));
|
||||
// Require a blank line between sibling element-like nodes in a Svelte template
|
||||
// (elements, components, and the {#if} / {#each} / {#await} / {#snippet} /
|
||||
// {@render} blocks) that sit on separate lines at the same nesting level.
|
||||
// Whitespace between siblings is a whitespace-only SvelteText node; when it
|
||||
// holds a single newline (no blank line) the fix adds one, keeping the
|
||||
// indentation of the second sibling. Real text content (e.g. `foo\n\nbar`)
|
||||
// is left alone.
|
||||
const ELEMENT_LIKE_TYPES = new Set([
|
||||
'SvelteAwaitBlock',
|
||||
'SvelteComponent',
|
||||
'SvelteEachBlock',
|
||||
'SvelteElement',
|
||||
'SvelteIfBlock',
|
||||
'SvelteKeyBlock',
|
||||
'SvelteRenderTag',
|
||||
'SvelteSelf',
|
||||
'SvelteSnippetBlock'
|
||||
]);
|
||||
const paddingLineBetweenElements = {
|
||||
create(context) {
|
||||
// Check one list of template children. Each children array holds the
|
||||
// element-like nodes plus the whitespace/comment text between them.
|
||||
function checkChildren(children) {
|
||||
if (!Array.isArray(children)) return;
|
||||
|
||||
let lastElement = null;
|
||||
let lastWhitespace = null;
|
||||
|
||||
for (const child of children) {
|
||||
if (child.type === 'SvelteText' && /^\s*$/.test(child.value)) {
|
||||
lastWhitespace = child;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ELEMENT_LIKE_TYPES.has(child.type)) continue;
|
||||
|
||||
if (
|
||||
lastElement &&
|
||||
lastWhitespace &&
|
||||
child.loc.start.line - lastElement.loc.end.line === 1
|
||||
) {
|
||||
const textNode = lastWhitespace;
|
||||
|
||||
context.report({
|
||||
fix(fixer) {
|
||||
// Add a second newline so the two siblings are separated by a
|
||||
// blank line, keeping the trailing indentation.
|
||||
return fixer.replaceText(textNode, textNode.value.replace(/\n/, '\n\n'));
|
||||
},
|
||||
message: 'Expected a blank line between sibling elements.',
|
||||
node: child
|
||||
});
|
||||
}
|
||||
|
||||
lastElement = child;
|
||||
lastWhitespace = null;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
SvelteAwaitBlock(node) {
|
||||
checkChildren(node.children);
|
||||
checkChildren(node.then?.children);
|
||||
checkChildren(node.else?.children);
|
||||
},
|
||||
SvelteComponent(node) {
|
||||
checkChildren(node.children);
|
||||
},
|
||||
SvelteEachBlock(node) {
|
||||
checkChildren(node.children);
|
||||
checkChildren(node.else?.children);
|
||||
},
|
||||
SvelteElement(node) {
|
||||
checkChildren(node.children);
|
||||
},
|
||||
SvelteFragment(node) {
|
||||
checkChildren(node.children);
|
||||
},
|
||||
SvelteIfBlock(node) {
|
||||
checkChildren(node.children);
|
||||
checkChildren(node.else?.children);
|
||||
},
|
||||
SvelteKeyBlock(node) {
|
||||
checkChildren(node.children);
|
||||
},
|
||||
SvelteProgram(node) {
|
||||
checkChildren(node.children);
|
||||
},
|
||||
SvelteSnippetBlock(node) {
|
||||
checkChildren(node.children);
|
||||
}
|
||||
};
|
||||
},
|
||||
meta: {
|
||||
docs: { description: 'Require a blank line between sibling elements in a Svelte template.' },
|
||||
fixable: 'whitespace',
|
||||
schema: [],
|
||||
type: 'layout'
|
||||
}
|
||||
};
|
||||
// Require a blank line between consecutive class accessors (get/set). The core
|
||||
// `padding-line-between-statements` rule only handles statements, not class
|
||||
// members, so this is enforced with a small custom rule.
|
||||
@@ -66,7 +167,12 @@ export default ts.config(
|
||||
{
|
||||
languageOptions: { globals: { ...globals.browser, ...globals.node } },
|
||||
plugins: {
|
||||
local: { rules: { 'blank-line-between-accessors': blankLineBetweenAccessors } },
|
||||
local: {
|
||||
rules: {
|
||||
'blank-line-between-accessors': blankLineBetweenAccessors,
|
||||
'padding-line-between-elements': paddingLineBetweenElements
|
||||
}
|
||||
},
|
||||
perfectionist,
|
||||
'simple-import-sort': simpleImportSort
|
||||
},
|
||||
@@ -82,6 +188,8 @@ export default ts.config(
|
||||
'eol-last': 'error',
|
||||
// Enforce a blank line between consecutive get/set accessors
|
||||
'local/blank-line-between-accessors': 'error',
|
||||
// Require a blank line between sibling elements in a Svelte template
|
||||
'local/padding-line-between-elements': 'error',
|
||||
// typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects.
|
||||
// see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors
|
||||
'no-undef': 'off',
|
||||
@@ -156,9 +264,49 @@ export default ts.config(
|
||||
// grouping); Prettier normalizes comma spacing afterwards.
|
||||
'simple-import-sort/imports': ['error', { groups: [['.*']] }],
|
||||
'svelte/no-at-html-tags': 'off',
|
||||
|
||||
// This app uses hash-based routing (#/) where resolve() from $app/paths does not apply
|
||||
'svelte/no-navigation-without-resolve': 'off'
|
||||
'svelte/no-navigation-without-resolve': 'off',
|
||||
|
||||
// Sort HTML attributes alphabetically in the markup. The Svelte directives
|
||||
// (bind:/use:/animate:/style:/in:/out:/transition:/class:) sort first,
|
||||
// alphabetically among themselves, then all remaining attributes sort
|
||||
// alphabetically. The rule keeps spread attributes in place and does not cross
|
||||
// them. `this` stays first on <svelte:element> because Prettier forces it there
|
||||
// - reordering it alphabetically would fight the formatter.
|
||||
'svelte/sort-attributes': [
|
||||
'error',
|
||||
{
|
||||
order: [
|
||||
'this',
|
||||
{
|
||||
match: [
|
||||
'/^bind:/u',
|
||||
'/^use:/u',
|
||||
'/^animate:/u',
|
||||
'/^style:/u',
|
||||
'/^in:/u',
|
||||
'/^out:/u',
|
||||
'/^transition:/u',
|
||||
'/^class:/u'
|
||||
],
|
||||
sort: 'alphabetical'
|
||||
},
|
||||
{
|
||||
match: [
|
||||
'!/^bind:/u',
|
||||
'!/^use:/u',
|
||||
'!/^animate:/u',
|
||||
'!/^style:/u',
|
||||
'!/^in:/u',
|
||||
'!/^out:/u',
|
||||
'!/^transition:/u',
|
||||
'!/^class:/u'
|
||||
],
|
||||
sort: 'alphabetical'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user