@plumeria/eslint-plugin 19.2.16 → 19.4.0
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 +10 -6
- package/dist/index.js +5 -5
- package/dist/rules/custom-props-require-import.d.ts +2 -0
- package/dist/rules/custom-props-require-import.js +185 -0
- package/dist/rules/no-invalid-selector.js +36 -49
- package/dist/rules/validate-at-rules.js +16 -29
- package/dist/rules/validate-pseudos.js +18 -33
- package/dist/util/staticValue.d.ts +2 -0
- package/dist/util/staticValue.js +183 -0
- package/oxlint.json +3 -3
- package/package.json +4 -3
- package/dist/rules/props-require-import.d.ts +0 -2
- package/dist/rules/props-require-import.js +0 -52
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/props-require-import`: **error**
|
|
10
|
+
- `@plumeria/custom-props-require-import`: **error**
|
|
11
11
|
- `@plumeria/no-combinator`: **error**
|
|
12
12
|
- `@plumeria/no-destructure`: **error**
|
|
13
13
|
- `@plumeria/no-inline-object`: **error**
|
|
@@ -20,8 +20,8 @@ The `plugin:@plumeria/recommended` config enables the following:
|
|
|
20
20
|
- `@plumeria/sort-properties`: **warn**
|
|
21
21
|
- `@plumeria/format-properties`: **warn**
|
|
22
22
|
- `@plumeria/validate-values`: **warn**
|
|
23
|
-
- `@plumeria/validate-pseudos`: **
|
|
24
|
-
- `@plumeria/validate-at-rules`: **
|
|
23
|
+
- `@plumeria/validate-pseudos`: **warn**
|
|
24
|
+
- `@plumeria/validate-at-rules`: **warn**
|
|
25
25
|
|
|
26
26
|
```js
|
|
27
27
|
import plumeria from '@plumeria/eslint-plugin';
|
|
@@ -31,7 +31,7 @@ export default [plumeria.configs.recommended];
|
|
|
31
31
|
|
|
32
32
|
## Configuring the styling prop
|
|
33
33
|
|
|
34
|
-
Both `props-require-import` and `no-mixed-styling-props` need to know which JSX
|
|
34
|
+
Both `custom-props-require-import` and `no-mixed-styling-props` need to know which JSX
|
|
35
35
|
prop carries styles. It is `classStyle` unless you changed it, so most projects
|
|
36
36
|
configure nothing.
|
|
37
37
|
|
|
@@ -70,9 +70,13 @@ the lint rules report against a prop the compiler never transforms.
|
|
|
70
70
|
The following rules are enabled by `plumeria.configs.recommended` with the
|
|
71
71
|
severity shown in [Recommended configuration](#recommended-configuration).
|
|
72
72
|
|
|
73
|
-
### props-require-import
|
|
73
|
+
### custom-props-require-import
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
Require the `@plumeria/core` import in a file that passes a `css.create` style to a
|
|
76
|
+
component through a prop other than the styling prop. The compiler cannot tell that
|
|
77
|
+
such a prop carries a style, so it compiles the file only when the file imports
|
|
78
|
+
`@plumeria/core`. A file that writes the styling prop itself is compiled without
|
|
79
|
+
the import.
|
|
76
80
|
|
|
77
81
|
Accepts `{ styleProp }`; see [Configuring the styling prop](#configuring-the-styling-prop).
|
|
78
82
|
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
const
|
|
2
|
+
const custom_props_require_import_1 = require("./rules/custom-props-require-import");
|
|
3
3
|
const expand_border_shorthands_1 = require("./rules/expand-border-shorthands");
|
|
4
4
|
const no_combinator_1 = require("./rules/no-combinator");
|
|
5
5
|
const no_destructure_1 = require("./rules/no-destructure");
|
|
@@ -18,7 +18,7 @@ const validate_values_1 = require("./rules/validate-values");
|
|
|
18
18
|
const validate_pseudos_1 = require("./rules/validate-pseudos");
|
|
19
19
|
const validate_at_rules_1 = require("./rules/validate-at-rules");
|
|
20
20
|
const rules = {
|
|
21
|
-
'props-require-import':
|
|
21
|
+
'custom-props-require-import': custom_props_require_import_1.customPropsRequireImport,
|
|
22
22
|
'expand-border-shorthands': expand_border_shorthands_1.expandBorderShorthands,
|
|
23
23
|
'no-combinator': no_combinator_1.noCombinator,
|
|
24
24
|
'no-destructure': no_destructure_1.noDestructure,
|
|
@@ -45,7 +45,7 @@ const configs = {
|
|
|
45
45
|
},
|
|
46
46
|
},
|
|
47
47
|
rules: {
|
|
48
|
-
'@plumeria/props-require-import': 'error',
|
|
48
|
+
'@plumeria/custom-props-require-import': 'error',
|
|
49
49
|
'@plumeria/no-combinator': 'error',
|
|
50
50
|
'@plumeria/no-destructure': 'error',
|
|
51
51
|
'@plumeria/no-inline-object': 'error',
|
|
@@ -58,8 +58,8 @@ const configs = {
|
|
|
58
58
|
'@plumeria/sort-properties': 'warn',
|
|
59
59
|
'@plumeria/format-properties': 'warn',
|
|
60
60
|
'@plumeria/validate-values': 'warn',
|
|
61
|
-
'@plumeria/validate-pseudos': '
|
|
62
|
-
'@plumeria/validate-at-rules': '
|
|
61
|
+
'@plumeria/validate-pseudos': 'warn',
|
|
62
|
+
'@plumeria/validate-at-rules': 'warn',
|
|
63
63
|
},
|
|
64
64
|
},
|
|
65
65
|
};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.customPropsRequireImport = void 0;
|
|
4
|
+
const utils_1 = require("@plumeria/utils");
|
|
5
|
+
const style_prop_1 = require("../util/style-prop");
|
|
6
|
+
const CORE = '@plumeria/core';
|
|
7
|
+
const styleRoots = (node, roots) => {
|
|
8
|
+
const expression = node;
|
|
9
|
+
if (!expression)
|
|
10
|
+
return roots;
|
|
11
|
+
switch (expression.type) {
|
|
12
|
+
case 'Identifier':
|
|
13
|
+
roots.push({ name: expression.name, member: null });
|
|
14
|
+
break;
|
|
15
|
+
case 'MemberExpression': {
|
|
16
|
+
let object = expression;
|
|
17
|
+
let member = null;
|
|
18
|
+
while (object.type === 'MemberExpression') {
|
|
19
|
+
member =
|
|
20
|
+
!object.computed && object.property.type === 'Identifier'
|
|
21
|
+
? object.property.name
|
|
22
|
+
: null;
|
|
23
|
+
object = object.object;
|
|
24
|
+
}
|
|
25
|
+
if (object.type === 'Identifier')
|
|
26
|
+
roots.push({ name: object.name, member });
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
case 'ArrayExpression':
|
|
30
|
+
expression.elements.forEach((element) => styleRoots(element, roots));
|
|
31
|
+
break;
|
|
32
|
+
case 'ConditionalExpression':
|
|
33
|
+
styleRoots(expression.consequent, roots);
|
|
34
|
+
styleRoots(expression.alternate, roots);
|
|
35
|
+
break;
|
|
36
|
+
case 'LogicalExpression':
|
|
37
|
+
styleRoots(expression.left, roots);
|
|
38
|
+
styleRoots(expression.right, roots);
|
|
39
|
+
break;
|
|
40
|
+
case 'ChainExpression':
|
|
41
|
+
case 'TSAsExpression':
|
|
42
|
+
case 'TSNonNullExpression':
|
|
43
|
+
case 'TSSatisfiesExpression':
|
|
44
|
+
styleRoots(expression.expression, roots);
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
return roots;
|
|
48
|
+
};
|
|
49
|
+
const SCAN_REUSE_MS = 1000;
|
|
50
|
+
const MAX_ALIAS_DEPTH = 4;
|
|
51
|
+
let recentScan;
|
|
52
|
+
const scanTables = (cwd, styleProp) => {
|
|
53
|
+
const key = `${cwd}\0${styleProp}`;
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
if (!recentScan ||
|
|
56
|
+
recentScan.key !== key ||
|
|
57
|
+
now - recentScan.at > SCAN_REUSE_MS) {
|
|
58
|
+
recentScan = { key, at: now, tables: (0, utils_1.scanAll)(cwd, styleProp) };
|
|
59
|
+
}
|
|
60
|
+
return recentScan.tables;
|
|
61
|
+
};
|
|
62
|
+
exports.customPropsRequireImport = {
|
|
63
|
+
meta: {
|
|
64
|
+
type: 'problem',
|
|
65
|
+
docs: {
|
|
66
|
+
description: 'Require the @plumeria/core import in files that pass a style through a prop other than the styling prop',
|
|
67
|
+
},
|
|
68
|
+
fixable: 'code',
|
|
69
|
+
messages: {
|
|
70
|
+
requiresImport: '{{prop}} passes a style from "{{source}}", so this file must import "@plumeria/core".',
|
|
71
|
+
},
|
|
72
|
+
schema: style_prop_1.stylePropSchema,
|
|
73
|
+
},
|
|
74
|
+
create(context) {
|
|
75
|
+
const styleProp = (0, style_prop_1.resolveStyleProp)(context);
|
|
76
|
+
const filename = context.filename;
|
|
77
|
+
const imports = new Map();
|
|
78
|
+
const candidates = [];
|
|
79
|
+
let hasPlumeriaImport = false;
|
|
80
|
+
const findVariable = (scope, name) => {
|
|
81
|
+
for (let current = scope; current; current = current.upper) {
|
|
82
|
+
const variable = current.set.get(name);
|
|
83
|
+
if (variable)
|
|
84
|
+
return variable;
|
|
85
|
+
}
|
|
86
|
+
return undefined;
|
|
87
|
+
};
|
|
88
|
+
const importedRoots = (roots, scope, depth) => roots.flatMap((root) => {
|
|
89
|
+
const definition = findVariable(scope, root.name)?.defs[0];
|
|
90
|
+
if (!definition)
|
|
91
|
+
return [];
|
|
92
|
+
if (definition.type === 'ImportBinding')
|
|
93
|
+
return [root];
|
|
94
|
+
const declarator = definition.node;
|
|
95
|
+
if (definition.type === 'Variable' &&
|
|
96
|
+
definition.parent?.kind === 'const' &&
|
|
97
|
+
declarator.init &&
|
|
98
|
+
depth < MAX_ALIAS_DEPTH) {
|
|
99
|
+
return importedRoots(styleRoots(declarator.init, []), context.sourceCode.getScope(definition.node), depth + 1);
|
|
100
|
+
}
|
|
101
|
+
return [];
|
|
102
|
+
});
|
|
103
|
+
const isStyle = (root) => {
|
|
104
|
+
const binding = imports.get(root.name);
|
|
105
|
+
const exportName = binding.importedName ?? root.member;
|
|
106
|
+
if (!exportName)
|
|
107
|
+
return null;
|
|
108
|
+
const actualPath = (0, utils_1.resolveImportPath)(binding.source, filename);
|
|
109
|
+
if (!actualPath)
|
|
110
|
+
return null;
|
|
111
|
+
const tables = scanTables(context.cwd, styleProp);
|
|
112
|
+
const resolved = (0, utils_1.resolveExport)(actualPath, exportName);
|
|
113
|
+
if (!resolved)
|
|
114
|
+
return null;
|
|
115
|
+
const key = `${resolved.filePath}-${resolved.localName}`;
|
|
116
|
+
return tables.createHashTable[key] !== undefined ? binding.source : null;
|
|
117
|
+
};
|
|
118
|
+
return {
|
|
119
|
+
ImportDeclaration(node) {
|
|
120
|
+
const source = node.source.value;
|
|
121
|
+
if (source.startsWith(CORE)) {
|
|
122
|
+
hasPlumeriaImport = true;
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
for (const specifier of node.specifiers) {
|
|
126
|
+
if (specifier.type === 'ImportNamespaceSpecifier') {
|
|
127
|
+
imports.set(specifier.local.name, { source, importedName: null });
|
|
128
|
+
}
|
|
129
|
+
else if (specifier.type === 'ImportDefaultSpecifier') {
|
|
130
|
+
imports.set(specifier.local.name, {
|
|
131
|
+
source,
|
|
132
|
+
importedName: 'default',
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
const imported = specifier.imported;
|
|
137
|
+
imports.set(specifier.local.name, {
|
|
138
|
+
source,
|
|
139
|
+
importedName: imported.name ?? String(imported.value),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
JSXAttribute(node) {
|
|
145
|
+
if (node.name.type !== 'JSXIdentifier')
|
|
146
|
+
return;
|
|
147
|
+
const prop = node.name.name;
|
|
148
|
+
if (prop === styleProp)
|
|
149
|
+
return;
|
|
150
|
+
if (node.value?.type !== 'JSXExpressionContainer')
|
|
151
|
+
return;
|
|
152
|
+
const roots = styleRoots(node.value.expression, []);
|
|
153
|
+
if (roots.length > 0)
|
|
154
|
+
candidates.push({
|
|
155
|
+
node,
|
|
156
|
+
prop,
|
|
157
|
+
roots,
|
|
158
|
+
scope: context.sourceCode.getScope(node),
|
|
159
|
+
});
|
|
160
|
+
},
|
|
161
|
+
'Program:exit'() {
|
|
162
|
+
if (hasPlumeriaImport || candidates.length === 0)
|
|
163
|
+
return;
|
|
164
|
+
let fixed = false;
|
|
165
|
+
for (const { node, prop, roots, scope } of candidates) {
|
|
166
|
+
const source = importedRoots(roots, scope, 0)
|
|
167
|
+
.map(isStyle)
|
|
168
|
+
.find((found) => found !== null);
|
|
169
|
+
if (!source)
|
|
170
|
+
continue;
|
|
171
|
+
const first = !fixed;
|
|
172
|
+
fixed = true;
|
|
173
|
+
context.report({
|
|
174
|
+
node,
|
|
175
|
+
messageId: 'requiresImport',
|
|
176
|
+
data: { prop, source },
|
|
177
|
+
fix: first
|
|
178
|
+
? (fixer) => fixer.insertTextBefore(context.sourceCode.ast, 'import "@plumeria/core";\n')
|
|
179
|
+
: null,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
},
|
|
185
|
+
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.noInvalidSelector = void 0;
|
|
4
|
-
const utils_1 = require("@typescript-eslint/utils");
|
|
5
4
|
const styleObject_1 = require("../util/styleObject");
|
|
5
|
+
const staticValue_1 = require("../util/staticValue");
|
|
6
6
|
exports.noInvalidSelector = {
|
|
7
7
|
meta: {
|
|
8
8
|
type: 'problem',
|
|
@@ -21,43 +21,34 @@ exports.noInvalidSelector = {
|
|
|
21
21
|
},
|
|
22
22
|
create(context) {
|
|
23
23
|
const plumeriaAliases = {};
|
|
24
|
-
const
|
|
25
|
-
const checker = parserServices?.program?.getTypeChecker();
|
|
24
|
+
const staticValue = (0, staticValue_1.staticValueResolver)(context);
|
|
26
25
|
function getSelectorType(node) {
|
|
27
|
-
if (node.type ===
|
|
28
|
-
typeof node.value === 'string') {
|
|
26
|
+
if (node.type === 'Literal' && typeof node.value === 'string') {
|
|
29
27
|
if (node.value.startsWith('@'))
|
|
30
28
|
return 'QUERY';
|
|
31
29
|
if (node.value.startsWith(':'))
|
|
32
30
|
return 'PSEUDO';
|
|
33
31
|
return 'PROPERTY';
|
|
34
32
|
}
|
|
35
|
-
if (node.type ===
|
|
36
|
-
!(node.parent?.type ===
|
|
37
|
-
node.parent.computed)) {
|
|
33
|
+
if (node.type === 'Identifier' &&
|
|
34
|
+
!(node.parent?.type === 'Property' && node.parent.computed)) {
|
|
38
35
|
return 'PROPERTY';
|
|
39
36
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (type.value.startsWith(':'))
|
|
48
|
-
return 'PSEUDO';
|
|
49
|
-
return 'PROPERTY';
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
catch (error) {
|
|
53
|
-
}
|
|
54
|
-
return 'UNKNOWN';
|
|
37
|
+
const value = staticValue(node);
|
|
38
|
+
if (typeof value === 'string') {
|
|
39
|
+
if (value.startsWith('@'))
|
|
40
|
+
return 'QUERY';
|
|
41
|
+
if (value.startsWith(':'))
|
|
42
|
+
return 'PSEUDO';
|
|
43
|
+
return 'PROPERTY';
|
|
55
44
|
}
|
|
45
|
+
if (value !== undefined)
|
|
46
|
+
return 'UNKNOWN';
|
|
56
47
|
return 'SKIP';
|
|
57
48
|
}
|
|
58
49
|
function checkNesting(node, parentType) {
|
|
59
50
|
for (const prop of node.properties) {
|
|
60
|
-
if (prop.type !==
|
|
51
|
+
if (prop.type !== 'Property')
|
|
61
52
|
continue;
|
|
62
53
|
const currentType = getSelectorType(prop.key);
|
|
63
54
|
if (currentType === 'SKIP')
|
|
@@ -83,14 +74,14 @@ exports.noInvalidSelector = {
|
|
|
83
74
|
messageId: 'noPseudoInsidePseudo',
|
|
84
75
|
});
|
|
85
76
|
}
|
|
86
|
-
if (prop.value.type ===
|
|
77
|
+
if (prop.value.type === 'ObjectExpression') {
|
|
87
78
|
checkNesting(prop.value, currentType === 'PROPERTY' ? parentType : currentType);
|
|
88
79
|
}
|
|
89
80
|
}
|
|
90
81
|
}
|
|
91
82
|
function checkOnlyProperties(node) {
|
|
92
83
|
for (const prop of node.properties) {
|
|
93
|
-
if (prop.type !==
|
|
84
|
+
if (prop.type !== 'Property')
|
|
94
85
|
continue;
|
|
95
86
|
const currentType = getSelectorType(prop.key);
|
|
96
87
|
if (currentType !== 'PROPERTY') {
|
|
@@ -99,24 +90,23 @@ exports.noInvalidSelector = {
|
|
|
99
90
|
messageId: 'invalidKeySelector',
|
|
100
91
|
});
|
|
101
92
|
}
|
|
102
|
-
if (prop.value.type ===
|
|
93
|
+
if (prop.value.type === 'ObjectExpression') {
|
|
103
94
|
checkOnlyProperties(prop.value);
|
|
104
95
|
}
|
|
105
96
|
}
|
|
106
97
|
}
|
|
107
98
|
function getKeyString(node) {
|
|
108
|
-
if (node.type ===
|
|
109
|
-
typeof node.value === 'string') {
|
|
99
|
+
if (node.type === 'Literal' && typeof node.value === 'string') {
|
|
110
100
|
return node.value;
|
|
111
101
|
}
|
|
112
|
-
if (node.type ===
|
|
102
|
+
if (node.type === 'Identifier') {
|
|
113
103
|
return node.name;
|
|
114
104
|
}
|
|
115
105
|
return null;
|
|
116
106
|
}
|
|
117
107
|
function checkKeyframesKeys(node) {
|
|
118
108
|
for (const prop of node.properties) {
|
|
119
|
-
if (prop.type !==
|
|
109
|
+
if (prop.type !== 'Property')
|
|
120
110
|
continue;
|
|
121
111
|
const key = getKeyString(prop.key);
|
|
122
112
|
if (key == null) {
|
|
@@ -125,7 +115,7 @@ exports.noInvalidSelector = {
|
|
|
125
115
|
messageId: 'invalidKeyframesKey',
|
|
126
116
|
});
|
|
127
117
|
}
|
|
128
|
-
if (prop.value.type ===
|
|
118
|
+
if (prop.value.type === 'ObjectExpression') {
|
|
129
119
|
checkOnlyProperties(prop.value);
|
|
130
120
|
}
|
|
131
121
|
if (key !== null &&
|
|
@@ -139,7 +129,7 @@ exports.noInvalidSelector = {
|
|
|
139
129
|
function checkViewTransitionKeys(node) {
|
|
140
130
|
const allowed = new Set(['group', 'imagePair', 'new', 'old']);
|
|
141
131
|
for (const prop of node.properties) {
|
|
142
|
-
if (prop.type !==
|
|
132
|
+
if (prop.type !== 'Property')
|
|
143
133
|
continue;
|
|
144
134
|
const key = getKeyString(prop.key);
|
|
145
135
|
if (key == null) {
|
|
@@ -148,7 +138,7 @@ exports.noInvalidSelector = {
|
|
|
148
138
|
messageId: 'invalidViewTransitionKey',
|
|
149
139
|
});
|
|
150
140
|
}
|
|
151
|
-
if (prop.value.type ===
|
|
141
|
+
if (prop.value.type === 'ObjectExpression') {
|
|
152
142
|
checkOnlyProperties(prop.value);
|
|
153
143
|
}
|
|
154
144
|
if (key !== null && !allowed.has(key)) {
|
|
@@ -163,13 +153,12 @@ exports.noInvalidSelector = {
|
|
|
163
153
|
ImportDeclaration(node) {
|
|
164
154
|
if (node.source.value === '@plumeria/core') {
|
|
165
155
|
node.specifiers.forEach((specifier) => {
|
|
166
|
-
if (specifier.type ===
|
|
167
|
-
|
|
168
|
-
specifier.type === utils_1.TSESTree.AST_NODE_TYPES.ImportDefaultSpecifier) {
|
|
156
|
+
if (specifier.type === 'ImportNamespaceSpecifier' ||
|
|
157
|
+
specifier.type === 'ImportDefaultSpecifier') {
|
|
169
158
|
plumeriaAliases[specifier.local.name] = 'NAMESPACE';
|
|
170
159
|
}
|
|
171
160
|
else {
|
|
172
|
-
const importedName = specifier.imported.type ===
|
|
161
|
+
const importedName = specifier.imported.type === 'Identifier'
|
|
173
162
|
? specifier.imported.name
|
|
174
163
|
: String(specifier.imported.value);
|
|
175
164
|
plumeriaAliases[specifier.local.name] = importedName;
|
|
@@ -181,10 +170,10 @@ exports.noInvalidSelector = {
|
|
|
181
170
|
let isCssCreate = false;
|
|
182
171
|
let isCssKeyframes = false;
|
|
183
172
|
let isCssViewTransition = false;
|
|
184
|
-
if (node.callee.type ===
|
|
185
|
-
if (node.callee.object.type ===
|
|
173
|
+
if (node.callee.type === 'MemberExpression') {
|
|
174
|
+
if (node.callee.object.type === 'Identifier' &&
|
|
186
175
|
plumeriaAliases[node.callee.object.name] === 'NAMESPACE') {
|
|
187
|
-
const propertyName = node.callee.property.type ===
|
|
176
|
+
const propertyName = node.callee.property.type === 'Identifier'
|
|
188
177
|
? node.callee.property.name
|
|
189
178
|
: null;
|
|
190
179
|
if (propertyName === 'create')
|
|
@@ -195,7 +184,7 @@ exports.noInvalidSelector = {
|
|
|
195
184
|
isCssViewTransition = true;
|
|
196
185
|
}
|
|
197
186
|
}
|
|
198
|
-
else if (node.callee.type ===
|
|
187
|
+
else if (node.callee.type === 'Identifier') {
|
|
199
188
|
const alias = plumeriaAliases[node.callee.name];
|
|
200
189
|
if (alias === 'create')
|
|
201
190
|
isCssCreate = true;
|
|
@@ -204,11 +193,10 @@ exports.noInvalidSelector = {
|
|
|
204
193
|
if (alias === 'viewTransition')
|
|
205
194
|
isCssViewTransition = true;
|
|
206
195
|
}
|
|
207
|
-
if (isCssCreate &&
|
|
208
|
-
node.arguments[0]?.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
|
|
196
|
+
if (isCssCreate && node.arguments[0]?.type === 'ObjectExpression') {
|
|
209
197
|
const styleObj = node.arguments[0];
|
|
210
198
|
styleObj.properties.forEach((prop) => {
|
|
211
|
-
if (prop.type ===
|
|
199
|
+
if (prop.type === 'Property') {
|
|
212
200
|
const currentType = getSelectorType(prop.key);
|
|
213
201
|
if (currentType !== 'SKIP' && currentType === 'UNKNOWN') {
|
|
214
202
|
context.report({
|
|
@@ -217,19 +205,18 @@ exports.noInvalidSelector = {
|
|
|
217
205
|
});
|
|
218
206
|
}
|
|
219
207
|
}
|
|
220
|
-
if (prop.type !==
|
|
208
|
+
if (prop.type !== 'Property')
|
|
221
209
|
return;
|
|
222
210
|
const style = (0, styleObject_1.styleObjectFromValue)(prop.value);
|
|
223
211
|
if (style)
|
|
224
212
|
checkNesting(style, 'CLASS');
|
|
225
213
|
});
|
|
226
214
|
}
|
|
227
|
-
if (isCssKeyframes &&
|
|
228
|
-
node.arguments[0]?.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
|
|
215
|
+
if (isCssKeyframes && node.arguments[0]?.type === 'ObjectExpression') {
|
|
229
216
|
checkKeyframesKeys(node.arguments[0]);
|
|
230
217
|
}
|
|
231
218
|
if (isCssViewTransition &&
|
|
232
|
-
node.arguments[0]?.type ===
|
|
219
|
+
node.arguments[0]?.type === 'ObjectExpression') {
|
|
233
220
|
checkViewTransitionKeys(node.arguments[0]);
|
|
234
221
|
}
|
|
235
222
|
},
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.validateAtRules = void 0;
|
|
4
4
|
exports.isValidAtRule = isValidAtRule;
|
|
5
|
-
const utils_1 = require("@typescript-eslint/utils");
|
|
6
5
|
const styleObject_1 = require("../util/styleObject");
|
|
6
|
+
const staticValue_1 = require("../util/staticValue");
|
|
7
7
|
const VALID_AT_RULES = [
|
|
8
8
|
'@media',
|
|
9
9
|
'@container',
|
|
@@ -34,28 +34,17 @@ exports.validateAtRules = {
|
|
|
34
34
|
},
|
|
35
35
|
create(context) {
|
|
36
36
|
const plumeriaAliases = {};
|
|
37
|
-
const
|
|
38
|
-
const checker = parserServices?.program?.getTypeChecker();
|
|
37
|
+
const staticValue = (0, staticValue_1.staticValueResolver)(context);
|
|
39
38
|
function getKeyString(node) {
|
|
40
|
-
if (node.type ===
|
|
41
|
-
typeof node.value === 'string') {
|
|
39
|
+
if (node.type === 'Literal' && typeof node.value === 'string') {
|
|
42
40
|
return node.value;
|
|
43
41
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
|
|
47
|
-
const type = checker.getTypeAtLocation(tsNode);
|
|
48
|
-
if (type.isStringLiteral())
|
|
49
|
-
return type.value;
|
|
50
|
-
}
|
|
51
|
-
catch (error) {
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return null;
|
|
42
|
+
const value = staticValue(node);
|
|
43
|
+
return typeof value === 'string' ? value : null;
|
|
55
44
|
}
|
|
56
45
|
function checkProperties(node) {
|
|
57
46
|
for (const prop of node.properties) {
|
|
58
|
-
if (prop.type !==
|
|
47
|
+
if (prop.type !== 'Property')
|
|
59
48
|
continue;
|
|
60
49
|
const key = getKeyString(prop.key);
|
|
61
50
|
if (key?.startsWith('@') && !isValidAtRule(key)) {
|
|
@@ -65,7 +54,7 @@ exports.validateAtRules = {
|
|
|
65
54
|
data: { rule: key },
|
|
66
55
|
});
|
|
67
56
|
}
|
|
68
|
-
if (prop.value.type ===
|
|
57
|
+
if (prop.value.type === 'ObjectExpression') {
|
|
69
58
|
checkProperties(prop.value);
|
|
70
59
|
}
|
|
71
60
|
}
|
|
@@ -75,13 +64,12 @@ exports.validateAtRules = {
|
|
|
75
64
|
if (node.source.value !== '@plumeria/core')
|
|
76
65
|
return;
|
|
77
66
|
node.specifiers.forEach((specifier) => {
|
|
78
|
-
if (specifier.type ===
|
|
79
|
-
|
|
80
|
-
specifier.type === utils_1.TSESTree.AST_NODE_TYPES.ImportDefaultSpecifier) {
|
|
67
|
+
if (specifier.type === 'ImportNamespaceSpecifier' ||
|
|
68
|
+
specifier.type === 'ImportDefaultSpecifier') {
|
|
81
69
|
plumeriaAliases[specifier.local.name] = 'NAMESPACE';
|
|
82
70
|
}
|
|
83
71
|
else {
|
|
84
|
-
const importedName = specifier.imported.type ===
|
|
72
|
+
const importedName = specifier.imported.type === 'Identifier'
|
|
85
73
|
? specifier.imported.name
|
|
86
74
|
: String(specifier.imported.value);
|
|
87
75
|
plumeriaAliases[specifier.local.name] = importedName;
|
|
@@ -90,23 +78,22 @@ exports.validateAtRules = {
|
|
|
90
78
|
},
|
|
91
79
|
CallExpression(node) {
|
|
92
80
|
let isCssCreate = false;
|
|
93
|
-
if (node.callee.type ===
|
|
94
|
-
node.callee.object.type ===
|
|
81
|
+
if (node.callee.type === 'MemberExpression' &&
|
|
82
|
+
node.callee.object.type === 'Identifier' &&
|
|
95
83
|
plumeriaAliases[node.callee.object.name] === 'NAMESPACE' &&
|
|
96
|
-
node.callee.property.type ===
|
|
84
|
+
node.callee.property.type === 'Identifier' &&
|
|
97
85
|
node.callee.property.name === 'create') {
|
|
98
86
|
isCssCreate = true;
|
|
99
87
|
}
|
|
100
|
-
else if (node.callee.type ===
|
|
88
|
+
else if (node.callee.type === 'Identifier' &&
|
|
101
89
|
plumeriaAliases[node.callee.name] === 'create') {
|
|
102
90
|
isCssCreate = true;
|
|
103
91
|
}
|
|
104
|
-
if (!isCssCreate ||
|
|
105
|
-
node.arguments[0]?.type !== utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
|
|
92
|
+
if (!isCssCreate || node.arguments[0]?.type !== 'ObjectExpression') {
|
|
106
93
|
return;
|
|
107
94
|
}
|
|
108
95
|
for (const prop of node.arguments[0].properties) {
|
|
109
|
-
if (prop.type !==
|
|
96
|
+
if (prop.type !== 'Property')
|
|
110
97
|
continue;
|
|
111
98
|
const style = (0, styleObject_1.styleObjectFromValue)(prop.value);
|
|
112
99
|
if (style)
|
|
@@ -3,8 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.validatePseudos = void 0;
|
|
4
4
|
exports.splitChainedPseudos = splitChainedPseudos;
|
|
5
5
|
exports.isValidPseudo = isValidPseudo;
|
|
6
|
-
const utils_1 = require("@typescript-eslint/utils");
|
|
7
6
|
const styleObject_1 = require("../util/styleObject");
|
|
7
|
+
const staticValue_1 = require("../util/staticValue");
|
|
8
8
|
const VALID_STATIC_PSEUDOS = new Set([
|
|
9
9
|
':hover',
|
|
10
10
|
':active',
|
|
@@ -147,34 +147,21 @@ exports.validatePseudos = {
|
|
|
147
147
|
},
|
|
148
148
|
create(context) {
|
|
149
149
|
const plumeriaAliases = {};
|
|
150
|
-
const
|
|
151
|
-
const checker = parserServices?.program?.getTypeChecker();
|
|
150
|
+
const staticValue = (0, staticValue_1.staticValueResolver)(context);
|
|
152
151
|
function getSelectorString(node) {
|
|
153
|
-
if (node.type ===
|
|
154
|
-
typeof node.value === 'string') {
|
|
152
|
+
if (node.type === 'Literal' && typeof node.value === 'string') {
|
|
155
153
|
return node.value;
|
|
156
154
|
}
|
|
157
|
-
if (node.type ===
|
|
158
|
-
!(node.parent?.type ===
|
|
159
|
-
node.parent.computed)) {
|
|
155
|
+
if (node.type === 'Identifier' &&
|
|
156
|
+
!(node.parent?.type === 'Property' && node.parent.computed)) {
|
|
160
157
|
return node.name;
|
|
161
158
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);
|
|
165
|
-
const type = checker.getTypeAtLocation(tsNode);
|
|
166
|
-
if (type.isStringLiteral()) {
|
|
167
|
-
return type.value;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
catch (error) {
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
return null;
|
|
159
|
+
const value = staticValue(node);
|
|
160
|
+
return typeof value === 'string' ? value : null;
|
|
174
161
|
}
|
|
175
162
|
function checkProperties(node) {
|
|
176
163
|
for (const prop of node.properties) {
|
|
177
|
-
if (prop.type !==
|
|
164
|
+
if (prop.type !== 'Property')
|
|
178
165
|
continue;
|
|
179
166
|
const selectorString = getSelectorString(prop.key);
|
|
180
167
|
if (selectorString !== null && selectorString.startsWith(':')) {
|
|
@@ -192,7 +179,7 @@ exports.validatePseudos = {
|
|
|
192
179
|
}
|
|
193
180
|
}
|
|
194
181
|
}
|
|
195
|
-
if (prop.value.type ===
|
|
182
|
+
if (prop.value.type === 'ObjectExpression') {
|
|
196
183
|
checkProperties(prop.value);
|
|
197
184
|
}
|
|
198
185
|
}
|
|
@@ -201,13 +188,12 @@ exports.validatePseudos = {
|
|
|
201
188
|
ImportDeclaration(node) {
|
|
202
189
|
if (node.source.value === '@plumeria/core') {
|
|
203
190
|
node.specifiers.forEach((specifier) => {
|
|
204
|
-
if (specifier.type ===
|
|
205
|
-
|
|
206
|
-
specifier.type === utils_1.TSESTree.AST_NODE_TYPES.ImportDefaultSpecifier) {
|
|
191
|
+
if (specifier.type === 'ImportNamespaceSpecifier' ||
|
|
192
|
+
specifier.type === 'ImportDefaultSpecifier') {
|
|
207
193
|
plumeriaAliases[specifier.local.name] = 'NAMESPACE';
|
|
208
194
|
}
|
|
209
195
|
else {
|
|
210
|
-
const importedName = specifier.imported.type ===
|
|
196
|
+
const importedName = specifier.imported.type === 'Identifier'
|
|
211
197
|
? specifier.imported.name
|
|
212
198
|
: String(specifier.imported.value);
|
|
213
199
|
plumeriaAliases[specifier.local.name] = importedName;
|
|
@@ -217,26 +203,25 @@ exports.validatePseudos = {
|
|
|
217
203
|
},
|
|
218
204
|
CallExpression(node) {
|
|
219
205
|
let isCssCreate = false;
|
|
220
|
-
if (node.callee.type ===
|
|
221
|
-
if (node.callee.object.type ===
|
|
206
|
+
if (node.callee.type === 'MemberExpression') {
|
|
207
|
+
if (node.callee.object.type === 'Identifier' &&
|
|
222
208
|
plumeriaAliases[node.callee.object.name] === 'NAMESPACE') {
|
|
223
|
-
const propertyName = node.callee.property.type ===
|
|
209
|
+
const propertyName = node.callee.property.type === 'Identifier'
|
|
224
210
|
? node.callee.property.name
|
|
225
211
|
: null;
|
|
226
212
|
if (propertyName === 'create')
|
|
227
213
|
isCssCreate = true;
|
|
228
214
|
}
|
|
229
215
|
}
|
|
230
|
-
else if (node.callee.type ===
|
|
216
|
+
else if (node.callee.type === 'Identifier') {
|
|
231
217
|
const alias = plumeriaAliases[node.callee.name];
|
|
232
218
|
if (alias === 'create')
|
|
233
219
|
isCssCreate = true;
|
|
234
220
|
}
|
|
235
|
-
if (isCssCreate &&
|
|
236
|
-
node.arguments[0]?.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
|
|
221
|
+
if (isCssCreate && node.arguments[0]?.type === 'ObjectExpression') {
|
|
237
222
|
const styleObj = node.arguments[0];
|
|
238
223
|
styleObj.properties.forEach((prop) => {
|
|
239
|
-
if (prop.type !==
|
|
224
|
+
if (prop.type !== 'Property')
|
|
240
225
|
return;
|
|
241
226
|
const style = (0, styleObject_1.styleObjectFromValue)(prop.value);
|
|
242
227
|
if (style)
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.staticValueResolver = staticValueResolver;
|
|
4
|
+
const utils_1 = require("@plumeria/utils");
|
|
5
|
+
const CORE = '@plumeria/core';
|
|
6
|
+
const unwrap = (node) => {
|
|
7
|
+
while (node &&
|
|
8
|
+
(node.type === 'TSAsExpression' ||
|
|
9
|
+
node.type === 'TSSatisfiesExpression' ||
|
|
10
|
+
node.type === 'TSNonNullExpression' ||
|
|
11
|
+
node.type === 'TSTypeAssertion'))
|
|
12
|
+
node = node.expression;
|
|
13
|
+
return node;
|
|
14
|
+
};
|
|
15
|
+
const findVariable = (scope, name) => {
|
|
16
|
+
for (let current = scope; current; current = current.upper) {
|
|
17
|
+
const variable = current.set.get(name);
|
|
18
|
+
if (variable)
|
|
19
|
+
return variable;
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
};
|
|
23
|
+
const importedNameOf = (specifier) => {
|
|
24
|
+
if (specifier.type === 'ImportNamespaceSpecifier')
|
|
25
|
+
return null;
|
|
26
|
+
if (specifier.type === 'ImportDefaultSpecifier')
|
|
27
|
+
return 'default';
|
|
28
|
+
return specifier.imported.type === 'Identifier'
|
|
29
|
+
? specifier.imported.name
|
|
30
|
+
: String(specifier.imported.value);
|
|
31
|
+
};
|
|
32
|
+
function staticValueResolver(context) {
|
|
33
|
+
const { sourceCode, filename } = context;
|
|
34
|
+
const visiting = new Set();
|
|
35
|
+
const bindingOf = (node) => {
|
|
36
|
+
const definition = findVariable(sourceCode.getScope(node), node.name)
|
|
37
|
+
?.defs[0];
|
|
38
|
+
if (!definition)
|
|
39
|
+
return undefined;
|
|
40
|
+
if (definition.type === 'Variable' &&
|
|
41
|
+
definition.parent.kind === 'const' &&
|
|
42
|
+
definition.node.init) {
|
|
43
|
+
return { kind: 'value', node: definition.node.init };
|
|
44
|
+
}
|
|
45
|
+
if (definition.type === 'ImportBinding') {
|
|
46
|
+
return {
|
|
47
|
+
kind: 'import',
|
|
48
|
+
source: String(definition.parent.source.value),
|
|
49
|
+
importedName: importedNameOf(definition.node),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
};
|
|
54
|
+
const importedValue = (source, exportName) => {
|
|
55
|
+
const actualPath = (0, utils_1.resolveImportPath)(source, filename);
|
|
56
|
+
if (!actualPath)
|
|
57
|
+
return undefined;
|
|
58
|
+
return (0, utils_1.resolveExportValue)(actualPath, exportName);
|
|
59
|
+
};
|
|
60
|
+
const isCreateStatic = (callee) => {
|
|
61
|
+
callee = unwrap(callee);
|
|
62
|
+
if (callee.type === 'Identifier') {
|
|
63
|
+
const binding = bindingOf(callee);
|
|
64
|
+
return (binding?.kind === 'import' &&
|
|
65
|
+
binding.source === CORE &&
|
|
66
|
+
binding.importedName === 'createStatic');
|
|
67
|
+
}
|
|
68
|
+
if (callee.type === 'MemberExpression' &&
|
|
69
|
+
!callee.computed &&
|
|
70
|
+
callee.property.type === 'Identifier' &&
|
|
71
|
+
callee.property.name === 'createStatic' &&
|
|
72
|
+
callee.object.type === 'Identifier') {
|
|
73
|
+
const binding = bindingOf(callee.object);
|
|
74
|
+
return (binding?.kind === 'import' &&
|
|
75
|
+
binding.source === CORE &&
|
|
76
|
+
(binding.importedName === null || binding.importedName === 'default'));
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
};
|
|
80
|
+
const memberKey = (node) => {
|
|
81
|
+
if (!node.computed && node.property.type === 'Identifier') {
|
|
82
|
+
return node.property.name;
|
|
83
|
+
}
|
|
84
|
+
const key = resolve(node.property);
|
|
85
|
+
return typeof key === 'string' ? key : undefined;
|
|
86
|
+
};
|
|
87
|
+
const resolve = (node) => {
|
|
88
|
+
node = unwrap(node);
|
|
89
|
+
if (!node || visiting.has(node))
|
|
90
|
+
return undefined;
|
|
91
|
+
visiting.add(node);
|
|
92
|
+
try {
|
|
93
|
+
switch (node.type) {
|
|
94
|
+
case 'Literal':
|
|
95
|
+
return typeof node.value === 'string' ||
|
|
96
|
+
typeof node.value === 'number' ||
|
|
97
|
+
typeof node.value === 'boolean'
|
|
98
|
+
? node.value
|
|
99
|
+
: undefined;
|
|
100
|
+
case 'BinaryExpression': {
|
|
101
|
+
const left = resolve(node.left);
|
|
102
|
+
const right = resolve(node.right);
|
|
103
|
+
if (left === undefined || right === undefined)
|
|
104
|
+
return undefined;
|
|
105
|
+
if (node.operator === '+') {
|
|
106
|
+
return typeof left === 'object' || typeof right === 'object'
|
|
107
|
+
? undefined
|
|
108
|
+
: left + right;
|
|
109
|
+
}
|
|
110
|
+
if (typeof left !== 'number' || typeof right !== 'number')
|
|
111
|
+
return undefined;
|
|
112
|
+
switch (node.operator) {
|
|
113
|
+
case '-':
|
|
114
|
+
return left - right;
|
|
115
|
+
case '*':
|
|
116
|
+
return left * right;
|
|
117
|
+
case '/':
|
|
118
|
+
return left / right;
|
|
119
|
+
case '%':
|
|
120
|
+
return left % right;
|
|
121
|
+
case '**':
|
|
122
|
+
return left ** right;
|
|
123
|
+
default:
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
case 'TemplateLiteral':
|
|
128
|
+
return node.expressions.length === 0
|
|
129
|
+
? node.quasis[0].value.cooked
|
|
130
|
+
: undefined;
|
|
131
|
+
case 'Identifier': {
|
|
132
|
+
const binding = bindingOf(node);
|
|
133
|
+
if (binding?.kind === 'value')
|
|
134
|
+
return resolve(binding.node);
|
|
135
|
+
if (binding?.kind === 'import' && binding.importedName) {
|
|
136
|
+
return importedValue(binding.source, binding.importedName);
|
|
137
|
+
}
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
case 'MemberExpression': {
|
|
141
|
+
const key = memberKey(node);
|
|
142
|
+
if (key === undefined)
|
|
143
|
+
return undefined;
|
|
144
|
+
const object = unwrap(node.object);
|
|
145
|
+
if (object.type === 'Identifier') {
|
|
146
|
+
const binding = bindingOf(object);
|
|
147
|
+
if (binding?.kind === 'import' && !binding.importedName) {
|
|
148
|
+
return importedValue(binding.source, key);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const value = resolve(object);
|
|
152
|
+
return value && typeof value === 'object' && Object.hasOwn(value, key)
|
|
153
|
+
? value[key]
|
|
154
|
+
: undefined;
|
|
155
|
+
}
|
|
156
|
+
case 'ObjectExpression': {
|
|
157
|
+
const value = Object.create(null);
|
|
158
|
+
for (const property of node.properties) {
|
|
159
|
+
if (property.type !== 'Property' || property.kind !== 'init')
|
|
160
|
+
continue;
|
|
161
|
+
const key = !property.computed && property.key.type === 'Identifier'
|
|
162
|
+
? property.key.name
|
|
163
|
+
: resolve(property.key);
|
|
164
|
+
if (typeof key !== 'string')
|
|
165
|
+
continue;
|
|
166
|
+
value[key] = resolve(property.value);
|
|
167
|
+
}
|
|
168
|
+
return value;
|
|
169
|
+
}
|
|
170
|
+
case 'CallExpression':
|
|
171
|
+
return isCreateStatic(node.callee)
|
|
172
|
+
? resolve(node.arguments[0])
|
|
173
|
+
: undefined;
|
|
174
|
+
default:
|
|
175
|
+
return undefined;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
finally {
|
|
179
|
+
visiting.delete(node);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
return (node) => resolve(node);
|
|
183
|
+
}
|
package/oxlint.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"ignorePatterns": ["**/*.svelte", "**/*.vue"],
|
|
10
10
|
|
|
11
11
|
"rules": {
|
|
12
|
-
"@plumeria/props-require-import": "error",
|
|
12
|
+
"@plumeria/custom-props-require-import": "error",
|
|
13
13
|
"@plumeria/no-combinator": "error",
|
|
14
14
|
"@plumeria/no-destructure": "error",
|
|
15
15
|
"@plumeria/no-inline-object": "error",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"@plumeria/sort-properties": "warn",
|
|
23
23
|
"@plumeria/format-properties": "warn",
|
|
24
24
|
"@plumeria/validate-values": "warn",
|
|
25
|
-
"@plumeria/validate-pseudos": "
|
|
26
|
-
"@plumeria/validate-at-rules": "
|
|
25
|
+
"@plumeria/validate-pseudos": "warn",
|
|
26
|
+
"@plumeria/validate-at-rules": "warn"
|
|
27
27
|
}
|
|
28
28
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plumeria/eslint-plugin",
|
|
3
|
-
"version": "19.
|
|
3
|
+
"version": "19.4.0",
|
|
4
4
|
"description": "Plumeria ESLint plugin",
|
|
5
5
|
"author": "Refirst 11",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"@types/estree": "^1.0.9",
|
|
46
46
|
"@types/estree-jsx": "^1.0.5",
|
|
47
47
|
"@typescript-eslint/parser": "^8.60.0",
|
|
48
|
+
"@typescript-eslint/utils": "^8.60.0",
|
|
48
49
|
"eslint": "^10.4.0"
|
|
49
50
|
},
|
|
50
51
|
"publishConfig": {
|
|
@@ -52,9 +53,9 @@
|
|
|
52
53
|
"provenance": true
|
|
53
54
|
},
|
|
54
55
|
"dependencies": {
|
|
55
|
-
"@typescript-eslint/utils": "^8.60.0",
|
|
56
56
|
"known-css-properties": "^0.37.0",
|
|
57
|
-
"zss-engine": "2.8.0"
|
|
57
|
+
"zss-engine": "2.8.0",
|
|
58
|
+
"@plumeria/utils": "19.4.0"
|
|
58
59
|
},
|
|
59
60
|
"scripts": {
|
|
60
61
|
"build": "rimraf dist && pnpm cjs",
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.propsRequireImport = void 0;
|
|
4
|
-
const style_prop_1 = require("../util/style-prop");
|
|
5
|
-
exports.propsRequireImport = {
|
|
6
|
-
meta: {
|
|
7
|
-
type: 'problem',
|
|
8
|
-
docs: {
|
|
9
|
-
description: 'Disallow the styling prop in files without a @plumeria/core import',
|
|
10
|
-
},
|
|
11
|
-
fixable: 'code',
|
|
12
|
-
messages: {
|
|
13
|
-
requiresImport: '{{prop}} requires importing "@plumeria/core".',
|
|
14
|
-
},
|
|
15
|
-
schema: style_prop_1.stylePropSchema,
|
|
16
|
-
},
|
|
17
|
-
create(context) {
|
|
18
|
-
const styleProp = (0, style_prop_1.resolveStyleProp)(context);
|
|
19
|
-
let hasPlumeriaImport = false;
|
|
20
|
-
const stylePropNodes = [];
|
|
21
|
-
return {
|
|
22
|
-
ImportDeclaration(node) {
|
|
23
|
-
const source = node.source.value;
|
|
24
|
-
if (typeof source === 'string' && source.startsWith('@plumeria/core')) {
|
|
25
|
-
hasPlumeriaImport = true;
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
JSXAttribute(node) {
|
|
29
|
-
if (node.name && node.name.name === styleProp) {
|
|
30
|
-
stylePropNodes.push(node);
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
'Program:exit'() {
|
|
34
|
-
if (!hasPlumeriaImport) {
|
|
35
|
-
stylePropNodes.forEach((node, index) => {
|
|
36
|
-
context.report({
|
|
37
|
-
node,
|
|
38
|
-
messageId: 'requiresImport',
|
|
39
|
-
data: { prop: styleProp },
|
|
40
|
-
fix(fixer) {
|
|
41
|
-
if (index === 0) {
|
|
42
|
-
return fixer.insertTextBefore(context.sourceCode.ast, 'import "@plumeria/core";\n');
|
|
43
|
-
}
|
|
44
|
-
return null;
|
|
45
|
-
},
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
};
|
|
51
|
-
},
|
|
52
|
-
};
|