kensington-eslint-plugin 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -470,7 +470,7 @@ Auto-fixable when the group's members are contiguous in the source. Non-contiguo
|
|
|
470
470
|
|
|
471
471
|
### `prefer-array-for-multiline-content`
|
|
472
472
|
|
|
473
|
-
Mirrors what `html-to-kensington` emits: when a tag's content
|
|
473
|
+
Mirrors what `html-to-kensington` emits: when a tag's content occupies its own line(s) — separated from both the opening and closing paren — it goes in an array, even when it's the only item. The array form makes line-by-line edits easier (no need to add `[ ]` when adding a sibling).
|
|
474
474
|
|
|
475
475
|
```js
|
|
476
476
|
// Bad
|
|
@@ -482,6 +482,12 @@ t.div({ class: 'x' },
|
|
|
482
482
|
t.div({ class: 'x' }, [
|
|
483
483
|
t.p('only'),
|
|
484
484
|
]);
|
|
485
|
+
|
|
486
|
+
// Also good — content trails on the closing-paren line, no array needed
|
|
487
|
+
t.a({
|
|
488
|
+
href: 'https://example.com',
|
|
489
|
+
target: '_blank',
|
|
490
|
+
}, 'VS Code');
|
|
485
491
|
```
|
|
486
492
|
|
|
487
493
|
Auto-fixable. Single-line calls (`t.div({…}, t.p('inner'))`) are left alone.
|
package/package.json
CHANGED
package/rules/_utils.js
CHANGED
|
@@ -89,6 +89,28 @@ export function getObjectNames(context) {
|
|
|
89
89
|
return context.options[0]?.objectNames ?? context.settings?.kensington?.objectNames;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
// Default set of attribute namespaces — kebab prefixes whose hyphenated
|
|
93
|
+
// children form an unbounded, namespaced family (data-*, aria-*). Mirrors the
|
|
94
|
+
// Kensington `additionalNamespaces` defaults.
|
|
95
|
+
export const DEFAULT_NAMESPACES = ['data', 'aria'];
|
|
96
|
+
|
|
97
|
+
// Standard rule schema entry for the `namespaces` option.
|
|
98
|
+
export const namespacesSchema = {
|
|
99
|
+
type: 'array',
|
|
100
|
+
items: { type: 'string' },
|
|
101
|
+
uniqueItems: true,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// Resolve `namespaces` from per-rule options first, then from plugin-level
|
|
105
|
+
// settings (`settings.kensington.namespaces`), then from `DEFAULT_NAMESPACES`.
|
|
106
|
+
export function getNamespaces(context) {
|
|
107
|
+
return (
|
|
108
|
+
context.options[0]?.namespaces
|
|
109
|
+
?? context.settings?.kensington?.namespaces
|
|
110
|
+
?? DEFAULT_NAMESPACES
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
92
114
|
// Returns the property key as a string, or null if computed/unrecognised.
|
|
93
115
|
export function getPropertyKey(prop) {
|
|
94
116
|
if (prop.type !== 'Property') { return null; }
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
// Tag content
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// Tag content that occupies its own line(s) — separated from both the call's
|
|
2
|
+
// opening paren and its closing paren — must be wrapped in an array, even when
|
|
3
|
+
// it's the only item. Mirrors what html-to-kensington emits when a tag's
|
|
4
|
+
// content can't fit on a single line. Content that trails on the closing-paren
|
|
5
|
+
// line stays bare.
|
|
4
6
|
|
|
5
7
|
import { isTagCall, getObjectNames, objectNamesSchema } from './_utils.js';
|
|
6
8
|
|
|
@@ -48,15 +50,18 @@ export default {
|
|
|
48
50
|
if (content.type === 'SpreadElement') { return; }
|
|
49
51
|
|
|
50
52
|
// The "opening line" is where the open paren sits — same line as the
|
|
51
|
-
// callee's last token. Content
|
|
53
|
+
// callee's last token. Content that touches either the opening-paren
|
|
54
|
+
// line or the closing-paren line stays bare.
|
|
52
55
|
const openParenLine = node.callee.loc.end.line;
|
|
53
56
|
if (content.loc.start.line <= openParenLine) { return; }
|
|
54
57
|
|
|
58
|
+
const closeParen = sourceCode.getLastToken(node);
|
|
59
|
+
if (closeParen && closeParen.value === ')' && content.loc.end.line >= closeParen.loc.start.line) { return; }
|
|
60
|
+
|
|
55
61
|
context.report({
|
|
56
62
|
node: content,
|
|
57
63
|
messageId: 'wrapInArray',
|
|
58
64
|
*fix(fixer) {
|
|
59
|
-
const closeParen = sourceCode.getLastToken(node);
|
|
60
65
|
const tokenAfterContent = sourceCode.getTokenAfter(content);
|
|
61
66
|
const hasTrailingComma = tokenAfterContent
|
|
62
67
|
&& tokenAfterContent.value === ','
|
|
@@ -1,29 +1,37 @@
|
|
|
1
|
-
import { getTagAttrsObject, getPropertyKey, normalizeAttrName, kebabToCamel, isValidIdentifier, getObjectNames, objectNamesSchema } from './_utils.js';
|
|
1
|
+
import { getTagAttrsObject, getPropertyKey, normalizeAttrName, kebabToCamel, isValidIdentifier, getObjectNames, getNamespaces, objectNamesSchema, namespacesSchema } from './_utils.js';
|
|
2
2
|
|
|
3
|
-
// When two or more attribute keys share the same
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// When two or more attribute keys share the same namespace prefix (data-*,
|
|
4
|
+
// aria-*, hx-*, etc.), prefer the nested object form. The default namespace
|
|
5
|
+
// list is ['data', 'aria']; override via the `namespaces` option or
|
|
6
|
+
// plugin-level `settings.kensington.namespaces`. The rule does NOT fire for
|
|
7
|
+
// multi-word HTML/SVG attribute names that happen to share a word (e.g.
|
|
8
|
+
// strokeWidth + strokeLinecap are separate attributes, not a "stroke
|
|
9
|
+
// namespace").
|
|
6
10
|
|
|
7
11
|
export default {
|
|
8
12
|
meta: {
|
|
9
13
|
type: 'suggestion',
|
|
10
14
|
fixable: 'code',
|
|
11
15
|
docs: {
|
|
12
|
-
description: 'prefer nested object form for attributes sharing a
|
|
16
|
+
description: 'prefer nested object form for attributes sharing a namespace prefix',
|
|
13
17
|
},
|
|
14
18
|
schema: [{
|
|
15
19
|
type: 'object',
|
|
16
|
-
properties: {
|
|
20
|
+
properties: {
|
|
21
|
+
objectNames: objectNamesSchema,
|
|
22
|
+
namespaces: namespacesSchema,
|
|
23
|
+
},
|
|
17
24
|
additionalProperties: false,
|
|
18
25
|
}],
|
|
19
26
|
messages: {
|
|
20
|
-
preferNested: 'Attributes sharing the `{{prefix}}-`
|
|
27
|
+
preferNested: 'Attributes sharing the `{{prefix}}-` namespace should be nested under a single `{{prefix}}` key.',
|
|
21
28
|
},
|
|
22
29
|
},
|
|
23
30
|
|
|
24
31
|
create(context) {
|
|
25
32
|
const sourceCode = context.sourceCode;
|
|
26
33
|
const objectNames = getObjectNames(context);
|
|
34
|
+
const namespaces = new Set(getNamespaces(context));
|
|
27
35
|
|
|
28
36
|
return {
|
|
29
37
|
CallExpression(node) {
|
|
@@ -40,6 +48,7 @@ export default {
|
|
|
40
48
|
if (hy === -1) { continue; }
|
|
41
49
|
const prefix = kebab.slice(0, hy);
|
|
42
50
|
if (!isValidIdentifier(prefix)) { continue; }
|
|
51
|
+
if (!namespaces.has(prefix)) { continue; }
|
|
43
52
|
if (!groups.has(prefix)) { groups.set(prefix, []); }
|
|
44
53
|
groups.get(prefix).push({ prop, remainder: kebab.slice(hy + 1) });
|
|
45
54
|
}
|