@plumeria/eslint-plugin 16.5.0 → 17.0.1
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 +47 -6
- package/dist/index.js +3 -3
- package/dist/rules/no-mixed-styling-props.js +11 -7
- package/dist/rules/props-require-import.d.ts +2 -0
- package/dist/rules/{style-name-requires-import.js → props-require-import.js} +13 -10
- package/dist/rules/validate-values.js +24 -0
- package/dist/util/style-prop.d.ts +12 -0
- package/dist/util/style-prop.js +22 -0
- package/dist/util/validData.js +10 -1
- package/oxlint.json +1 -1
- package/package.json +1 -1
- package/dist/rules/style-name-requires-import.d.ts +0 -2
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Below are the available rules and the recommended configuration.
|
|
|
7
7
|
|
|
8
8
|
The `plugin:@plumeria/recommended` config enables the following:
|
|
9
9
|
|
|
10
|
-
- `@plumeria/
|
|
10
|
+
- `@plumeria/props-require-import`: **error**
|
|
11
11
|
- `@plumeria/no-combinator`: **error**
|
|
12
12
|
- `@plumeria/no-destructure`: **error**
|
|
13
13
|
- `@plumeria/no-inline-object`: **error**
|
|
@@ -27,11 +27,49 @@ import plumeria from '@plumeria/eslint-plugin';
|
|
|
27
27
|
export default [plumeria.configs.recommended];
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
## Configuring the styling prop
|
|
31
|
+
|
|
32
|
+
Both `props-require-import` and `no-mixed-styling-props` need to know which JSX
|
|
33
|
+
prop carries styles. It is `styleName` unless you changed it, so most projects
|
|
34
|
+
configure nothing.
|
|
35
|
+
|
|
36
|
+
If you did rename it — via `styleProp` on `withPlumeria` or on the unplugin
|
|
37
|
+
options — tell the plugin the same name. ESLint cannot read it from your bundler
|
|
38
|
+
config, so set it once in `settings` and every rule picks it up:
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
import plumeria from '@plumeria/eslint-plugin';
|
|
42
|
+
|
|
43
|
+
export default [
|
|
44
|
+
plumeria.configs.recommended,
|
|
45
|
+
{
|
|
46
|
+
settings: {
|
|
47
|
+
plumeria: { styleProp: 'sx' },
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
A single rule can override that if you need it to:
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
{
|
|
57
|
+
rules: {
|
|
58
|
+
'@plumeria/no-mixed-styling-props': ['error', { styleProp: 'sx' }],
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The name must match whatever the loader or unplugin was given. If they disagree,
|
|
64
|
+
the lint rules report against a prop the compiler never transforms.
|
|
65
|
+
|
|
30
66
|
## Rules
|
|
31
67
|
|
|
32
|
-
###
|
|
68
|
+
### props-require-import
|
|
33
69
|
|
|
34
|
-
Disallow
|
|
70
|
+
Disallow the styling prop in files without a `@plumeria/core` import.
|
|
71
|
+
|
|
72
|
+
Accepts `{ styleProp }`; see [Configuring the styling prop](#configuring-the-styling-prop).
|
|
35
73
|
|
|
36
74
|
### no-combinator
|
|
37
75
|
|
|
@@ -56,7 +94,9 @@ Disallow invalid selector inside `css.create()` and `css.keyframes()` and `css.v
|
|
|
56
94
|
|
|
57
95
|
### no-mixed-styling-props
|
|
58
96
|
|
|
59
|
-
Disallow mixing
|
|
97
|
+
Disallow mixing the styling prop with `className` or `style`. `styleName` can handle both `className` and `style`.
|
|
98
|
+
|
|
99
|
+
Accepts `{ styleProp }`; see [Configuring the styling prop](#configuring-the-styling-prop).
|
|
60
100
|
|
|
61
101
|
### no-unknown-css-properties
|
|
62
102
|
|
|
@@ -71,8 +111,10 @@ Warns when object keys are defined but not used, mainly in component files.
|
|
|
71
111
|
Automatically sorts CSS properties in the recommended order for consistency and maintainability.
|
|
72
112
|
|
|
73
113
|
### format-properties
|
|
114
|
+
|
|
74
115
|
Automatically format for consistency and maintainability.
|
|
75
|
-
|
|
116
|
+
|
|
117
|
+
- Formats a line into a multi-line.
|
|
76
118
|
- Formats by filling in blank lines.
|
|
77
119
|
|
|
78
120
|
### validate-values
|
|
@@ -132,4 +174,3 @@ You can run `plumerialint` in parallel with your build command (e.g. `next build
|
|
|
132
174
|
If `plumerialint` detects any styling errors or warnings, it will print the diagnostics, kill the build process immediately, and exit with a non-zero code. This avoids compiling when styling validation fails.
|
|
133
175
|
|
|
134
176
|
**Note:** `oxlint` is required as `plumerialint` uses it internally.
|
|
135
|
-
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
const
|
|
2
|
+
const props_require_import_1 = require("./rules/props-require-import");
|
|
3
3
|
const no_combinator_1 = require("./rules/no-combinator");
|
|
4
4
|
const no_destructure_1 = require("./rules/no-destructure");
|
|
5
5
|
const no_inline_object_1 = require("./rules/no-inline-object");
|
|
@@ -13,7 +13,7 @@ const format_properties_1 = require("./rules/format-properties");
|
|
|
13
13
|
const validate_values_1 = require("./rules/validate-values");
|
|
14
14
|
const validate_pseudos_1 = require("./rules/validate-pseudos");
|
|
15
15
|
const rules = {
|
|
16
|
-
'
|
|
16
|
+
'props-require-import': props_require_import_1.propsRequireImport,
|
|
17
17
|
'no-combinator': no_combinator_1.noCombinator,
|
|
18
18
|
'no-destructure': no_destructure_1.noDestructure,
|
|
19
19
|
'no-inline-object': no_inline_object_1.noInlineObject,
|
|
@@ -35,7 +35,7 @@ const configs = {
|
|
|
35
35
|
},
|
|
36
36
|
},
|
|
37
37
|
rules: {
|
|
38
|
-
'@plumeria/
|
|
38
|
+
'@plumeria/props-require-import': 'error',
|
|
39
39
|
'@plumeria/no-combinator': 'error',
|
|
40
40
|
'@plumeria/no-destructure': 'error',
|
|
41
41
|
'@plumeria/no-inline-object': 'error',
|
|
@@ -1,32 +1,36 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.noMixedStylingProps = void 0;
|
|
4
|
+
const style_prop_1 = require("../util/style-prop");
|
|
4
5
|
exports.noMixedStylingProps = {
|
|
5
6
|
meta: {
|
|
6
7
|
type: 'problem',
|
|
7
8
|
docs: {
|
|
8
|
-
description: 'Disallow className and style props when
|
|
9
|
+
description: 'Disallow className and style props when the styling prop is present',
|
|
9
10
|
},
|
|
10
11
|
messages: {
|
|
11
|
-
noMixedStylingProps: '"
|
|
12
|
+
noMixedStylingProps: '"{{prop}}" handles both "className" and "style". Avoid mixing them.',
|
|
12
13
|
},
|
|
13
|
-
schema:
|
|
14
|
+
schema: style_prop_1.stylePropSchema,
|
|
14
15
|
},
|
|
15
16
|
create(context) {
|
|
17
|
+
const styleProp = (0, style_prop_1.resolveStyleProp)(context);
|
|
18
|
+
const mixedProps = ['className', 'style'].filter((name) => name !== styleProp);
|
|
16
19
|
return {
|
|
17
20
|
JSXOpeningElement(node) {
|
|
18
21
|
const attributes = node.attributes;
|
|
19
|
-
const
|
|
22
|
+
const hasStyleProp = attributes.some((attr) => attr.type === 'JSXAttribute' &&
|
|
20
23
|
attr.name.type === 'JSXIdentifier' &&
|
|
21
|
-
attr.name.name ===
|
|
22
|
-
if (
|
|
24
|
+
attr.name.name === styleProp);
|
|
25
|
+
if (hasStyleProp) {
|
|
23
26
|
for (const attr of attributes) {
|
|
24
27
|
if (attr.type === 'JSXAttribute' &&
|
|
25
28
|
attr.name.type === 'JSXIdentifier' &&
|
|
26
|
-
(attr.name.name
|
|
29
|
+
mixedProps.includes(attr.name.name)) {
|
|
27
30
|
context.report({
|
|
28
31
|
node: attr,
|
|
29
32
|
messageId: 'noMixedStylingProps',
|
|
33
|
+
data: { prop: styleProp },
|
|
30
34
|
});
|
|
31
35
|
}
|
|
32
36
|
}
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
|
|
3
|
+
exports.propsRequireImport = void 0;
|
|
4
|
+
const style_prop_1 = require("../util/style-prop");
|
|
5
|
+
exports.propsRequireImport = {
|
|
5
6
|
meta: {
|
|
6
7
|
type: 'problem',
|
|
7
8
|
docs: {
|
|
8
|
-
description: 'Disallow
|
|
9
|
+
description: 'Disallow the styling prop in files without a @plumeria/core import',
|
|
9
10
|
},
|
|
10
11
|
fixable: 'code',
|
|
11
12
|
messages: {
|
|
12
|
-
|
|
13
|
+
requiresImport: '{{prop}} requires importing "@plumeria/core".',
|
|
13
14
|
},
|
|
14
|
-
schema:
|
|
15
|
+
schema: style_prop_1.stylePropSchema,
|
|
15
16
|
},
|
|
16
17
|
create(context) {
|
|
18
|
+
const styleProp = (0, style_prop_1.resolveStyleProp)(context);
|
|
17
19
|
let hasPlumeriaImport = false;
|
|
18
|
-
const
|
|
20
|
+
const stylePropNodes = [];
|
|
19
21
|
return {
|
|
20
22
|
ImportDeclaration(node) {
|
|
21
23
|
const source = node.source.value;
|
|
@@ -24,16 +26,17 @@ exports.styleNameRequiresImport = {
|
|
|
24
26
|
}
|
|
25
27
|
},
|
|
26
28
|
JSXAttribute(node) {
|
|
27
|
-
if (node.name && node.name.name ===
|
|
28
|
-
|
|
29
|
+
if (node.name && node.name.name === styleProp) {
|
|
30
|
+
stylePropNodes.push(node);
|
|
29
31
|
}
|
|
30
32
|
},
|
|
31
33
|
'Program:exit'() {
|
|
32
34
|
if (!hasPlumeriaImport) {
|
|
33
|
-
|
|
35
|
+
stylePropNodes.forEach((node, index) => {
|
|
34
36
|
context.report({
|
|
35
37
|
node,
|
|
36
|
-
messageId: '
|
|
38
|
+
messageId: 'requiresImport',
|
|
39
|
+
data: { prop: styleProp },
|
|
37
40
|
fix(fixer) {
|
|
38
41
|
if (index === 0) {
|
|
39
42
|
return fixer.insertTextBefore(context.sourceCode.ast, 'import "@plumeria/core";\n');
|
|
@@ -520,6 +520,27 @@ function isValidTextDecorationLine(value) {
|
|
|
520
520
|
return false;
|
|
521
521
|
});
|
|
522
522
|
}
|
|
523
|
+
function isValidContain(value) {
|
|
524
|
+
const singleValues = ['none', 'strict', 'content'];
|
|
525
|
+
const sizeValues = ['size', 'inline-size'];
|
|
526
|
+
const featureValues = ['layout', 'style', 'paint'];
|
|
527
|
+
const usedValues = new Set();
|
|
528
|
+
const trimmedValue = value.trim();
|
|
529
|
+
if (value !== trimmedValue)
|
|
530
|
+
return false;
|
|
531
|
+
const tokens = trimmedValue.split(/\s+/);
|
|
532
|
+
return tokens.every((token) => {
|
|
533
|
+
if (token.startsWith('var(') && varRegex.test(token))
|
|
534
|
+
return true;
|
|
535
|
+
if (singleValues.includes(token))
|
|
536
|
+
return tokens.length === 1;
|
|
537
|
+
if (sizeValues.includes(token))
|
|
538
|
+
return !usedValues.has('size') && usedValues.add('size');
|
|
539
|
+
if (featureValues.includes(token))
|
|
540
|
+
return !usedValues.has(token) && usedValues.add(token);
|
|
541
|
+
return false;
|
|
542
|
+
});
|
|
543
|
+
}
|
|
523
544
|
function isValidFontVariantEastAsian(value) {
|
|
524
545
|
const fontVariantEastAsianRegex = new RegExp('^' +
|
|
525
546
|
`(?:jis78|jis83|jis90|jis04|simplified|traditional|full-width|proportional-width|ruby|${varString})` +
|
|
@@ -1297,6 +1318,9 @@ function getValidator(key) {
|
|
|
1297
1318
|
const r = new RegExp(`^(${urlString}|${gradientString}|${imageSetString}|${attrString}|${counterString}|${countersString}|${stringString})$`);
|
|
1298
1319
|
validator = (v) => r.test(v);
|
|
1299
1320
|
}
|
|
1321
|
+
else if (['contain'].includes(key)) {
|
|
1322
|
+
validator = isValidContain;
|
|
1323
|
+
}
|
|
1300
1324
|
else if (['columns'].includes(key)) {
|
|
1301
1325
|
const r = new RegExp(`^(?:auto\\s*(?:auto|${lvp}|${numberPattern})?|${numberPattern}\\s*(?:auto|${lvp}|${numberPattern})?|${lvp}\\s*(?:auto|${lvp}|${numberPattern})?)$`);
|
|
1302
1326
|
validator = (v) => r.test(v);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Rule } from 'eslint';
|
|
2
|
+
export declare const DEFAULT_STYLE_PROP = "styleName";
|
|
3
|
+
export declare const stylePropSchema: {
|
|
4
|
+
type: string;
|
|
5
|
+
properties: {
|
|
6
|
+
styleProp: {
|
|
7
|
+
type: string;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
additionalProperties: boolean;
|
|
11
|
+
}[];
|
|
12
|
+
export declare function resolveStyleProp(context: Rule.RuleContext): string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.stylePropSchema = exports.DEFAULT_STYLE_PROP = void 0;
|
|
4
|
+
exports.resolveStyleProp = resolveStyleProp;
|
|
5
|
+
exports.DEFAULT_STYLE_PROP = 'styleName';
|
|
6
|
+
exports.stylePropSchema = [
|
|
7
|
+
{
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
styleProp: { type: 'string' },
|
|
11
|
+
},
|
|
12
|
+
additionalProperties: false,
|
|
13
|
+
},
|
|
14
|
+
];
|
|
15
|
+
function resolveStyleProp(context) {
|
|
16
|
+
const fromRule = context.options?.[0]
|
|
17
|
+
?.styleProp;
|
|
18
|
+
if (fromRule)
|
|
19
|
+
return fromRule;
|
|
20
|
+
const fromSettings = context.settings?.plumeria?.styleProp;
|
|
21
|
+
return fromSettings ?? exports.DEFAULT_STYLE_PROP;
|
|
22
|
+
}
|
package/dist/util/validData.js
CHANGED
|
@@ -316,7 +316,16 @@ const validData = {
|
|
|
316
316
|
columnSpan: ['none', 'all'],
|
|
317
317
|
columnWidth: ['auto', 'max-content', 'min-content'],
|
|
318
318
|
columns: [],
|
|
319
|
-
contain: [
|
|
319
|
+
contain: [
|
|
320
|
+
'none',
|
|
321
|
+
'strict',
|
|
322
|
+
'content',
|
|
323
|
+
'size',
|
|
324
|
+
'inline-size',
|
|
325
|
+
'layout',
|
|
326
|
+
'style',
|
|
327
|
+
'paint',
|
|
328
|
+
],
|
|
320
329
|
containerType: ['size', 'inline-size', 'normal'],
|
|
321
330
|
content: [
|
|
322
331
|
'open-quote',
|
package/oxlint.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"ignorePatterns": ["**/*.svelte", "**/*.vue"],
|
|
10
10
|
|
|
11
11
|
"rules": {
|
|
12
|
-
"@plumeria/
|
|
12
|
+
"@plumeria/props-require-import": "error",
|
|
13
13
|
"@plumeria/no-combinator": "error",
|
|
14
14
|
"@plumeria/no-destructure": "error",
|
|
15
15
|
"@plumeria/no-inline-object": "error",
|
package/package.json
CHANGED