@plumeria/eslint-plugin 11.2.0 → 12.0.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 CHANGED
@@ -12,7 +12,7 @@ The `plugin:@plumeria/recommended` config enables the following:
12
12
  - `@plumeria/no-destructure`: **error**
13
13
  - `@plumeria/no-inline-object`: **error**
14
14
  - `@plumeria/no-inner-call`: **error**
15
- - `@plumeria/no-invalid-selector-nesting`: **error**
15
+ - `@plumeria/no-invalid-selector`: **error**
16
16
  - `@plumeria/no-mixed-styling-props`: **error**
17
17
  - `@plumeria/no-unknown-css-properties`: **error**
18
18
  - `@plumeria/no-unused-keys`: **warn**
@@ -21,9 +21,9 @@ The `plugin:@plumeria/recommended` config enables the following:
21
21
  - `@plumeria/validate-values`: **warn**
22
22
 
23
23
  ```js
24
- import { plumeria } from '@plumeria/eslint-plugin';
24
+ import plumeria from '@plumeria/eslint-plugin';
25
25
 
26
- export default [plumeria.flatConfigs.recommended];
26
+ export default [plumeria.configs.recommended];
27
27
  ```
28
28
 
29
29
  ## Rules
@@ -48,9 +48,10 @@ Disallow passing inline object to `styleName` and `css.use()`. Only compiled sty
48
48
 
49
49
  Disallow calling APIs inside functions.
50
50
 
51
- ### no-invalid-selector-nesting
51
+ ### no-invalid-selector
52
52
 
53
- Disallow invalid selector nesting inside `css.create()`. (e.g. Pseudo -> Query, Query -> Query)
53
+ Disallow invalid selector inside `css.create()` and `css.keyframes()` and `css.viewTransition()`.
54
+ `create()` example: (Pseudo -> Query, Query -> Query)
54
55
 
55
56
  ### no-mixed-styling-props
56
57
 
package/dist/index.d.ts CHANGED
@@ -1,12 +1,8 @@
1
- import type { ESLint, Linter, Rule } from 'eslint';
2
- type PlumeriaPlugin = ESLint.Plugin & {
1
+ import type { Linter, Rule } from 'eslint';
2
+ declare const plugin: {
3
3
  rules: Record<string, Rule.RuleModule>;
4
4
  configs: {
5
- recommended: Linter.LegacyConfig;
6
- };
7
- flatConfigs: {
8
5
  recommended: Linter.Config;
9
6
  };
10
7
  };
11
- export declare const plumeria: PlumeriaPlugin;
12
- export {};
8
+ export = plugin;
package/dist/index.js CHANGED
@@ -1,12 +1,10 @@
1
1
  "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.plumeria = void 0;
4
2
  const style_name_requires_import_1 = require("./rules/style-name-requires-import");
5
3
  const no_combinator_1 = require("./rules/no-combinator");
6
4
  const no_destructure_1 = require("./rules/no-destructure");
7
5
  const no_inline_object_1 = require("./rules/no-inline-object");
8
6
  const no_inner_call_1 = require("./rules/no-inner-call");
9
- const no_invalid_selector_nesting_1 = require("./rules/no-invalid-selector-nesting");
7
+ const no_invalid_selector_1 = require("./rules/no-invalid-selector");
10
8
  const no_mixed_styling_props_1 = require("./rules/no-mixed-styling-props");
11
9
  const no_unknown_css_properties_1 = require("./rules/no-unknown-css-properties");
12
10
  const no_unused_keys_1 = require("./rules/no-unused-keys");
@@ -19,7 +17,7 @@ const rules = {
19
17
  'no-destructure': no_destructure_1.noDestructure,
20
18
  'no-inline-object': no_inline_object_1.noInlineObject,
21
19
  'no-inner-call': no_inner_call_1.noInnerCall,
22
- 'no-invalid-selector-nesting': no_invalid_selector_nesting_1.noInvalidSelectorNesting,
20
+ 'no-invalid-selector': no_invalid_selector_1.noInvalidSelector,
23
21
  'no-mixed-styling-props': no_mixed_styling_props_1.noMixedStylingProps,
24
22
  'no-unknown-css-properties': no_unknown_css_properties_1.noUnknownCssProperties,
25
23
  'no-unused-keys': no_unused_keys_1.noUnusedKeys,
@@ -28,25 +26,6 @@ const rules = {
28
26
  'validate-values': validate_values_1.validateValues,
29
27
  };
30
28
  const configs = {
31
- recommended: {
32
- plugins: ['@plumeria'],
33
- rules: {
34
- '@plumeria/style-name-requires-import': 'error',
35
- '@plumeria/no-combinator': 'error',
36
- '@plumeria/no-destructure': 'error',
37
- '@plumeria/no-inline-object': 'error',
38
- '@plumeria/no-inner-call': 'error',
39
- '@plumeria/no-invalid-selector-nesting': 'error',
40
- '@plumeria/no-mixed-styling-props': 'error',
41
- '@plumeria/no-unknown-css-properties': 'error',
42
- '@plumeria/no-unused-keys': 'warn',
43
- '@plumeria/sort-properties': 'warn',
44
- '@plumeria/format-properties': 'warn',
45
- '@plumeria/validate-values': 'warn',
46
- },
47
- },
48
- };
49
- const flatConfigs = {
50
29
  recommended: {
51
30
  plugins: {
52
31
  '@plumeria': {
@@ -59,7 +38,7 @@ const flatConfigs = {
59
38
  '@plumeria/no-destructure': 'error',
60
39
  '@plumeria/no-inline-object': 'error',
61
40
  '@plumeria/no-inner-call': 'error',
62
- '@plumeria/no-invalid-selector-nesting': 'error',
41
+ '@plumeria/no-invalid-selector': 'error',
63
42
  '@plumeria/no-mixed-styling-props': 'error',
64
43
  '@plumeria/no-unknown-css-properties': 'error',
65
44
  '@plumeria/no-unused-keys': 'warn',
@@ -69,8 +48,8 @@ const flatConfigs = {
69
48
  },
70
49
  },
71
50
  };
72
- exports.plumeria = {
51
+ const plugin = {
73
52
  rules,
74
53
  configs,
75
- flatConfigs,
76
54
  };
55
+ module.exports = plugin;
@@ -1,9 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.formatProperties = void 0;
4
- function getSourceCode(context) {
5
- return context.sourceCode ?? context.getSourceCode();
6
- }
7
4
  exports.formatProperties = {
8
5
  meta: {
9
6
  type: 'layout',
@@ -19,7 +16,7 @@ exports.formatProperties = {
19
16
  },
20
17
  create(context) {
21
18
  const plumeriaAliases = {};
22
- const sourceCode = getSourceCode(context);
19
+ const sourceCode = context.sourceCode;
23
20
  function checkStyleObject(node) {
24
21
  if (node.type !== 'ObjectExpression' || node.properties.length === 0)
25
22
  return;
@@ -201,7 +201,7 @@ exports.noCombinator = {
201
201
  text.includes('\t') ||
202
202
  text.includes('\n')) {
203
203
  if (!isCombinatorAllowed(text)) {
204
- let found = '';
204
+ let found;
205
205
  if (text.includes('>'))
206
206
  found = '>';
207
207
  else if (text.includes('+'))
@@ -0,0 +1,2 @@
1
+ import { Rule } from 'eslint';
2
+ export declare const noInvalidSelector: Rule.RuleModule;
@@ -1,17 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noInvalidSelectorNesting = void 0;
3
+ exports.noInvalidSelector = void 0;
4
4
  const utils_1 = require("@typescript-eslint/utils");
5
- exports.noInvalidSelectorNesting = {
5
+ exports.noInvalidSelector = {
6
6
  meta: {
7
7
  type: 'problem',
8
8
  docs: {
9
9
  description: 'Disallow invalid selector nesting (e.g. Pseudo -> Query, Query -> Query) based on Plumeria rules.',
10
10
  },
11
11
  messages: {
12
+ invalidKeySelector: 'Invalid key selector.',
12
13
  noQueryInsidePseudo: 'Media/Container queries cannot be nested inside pseudo-selectors.',
13
14
  noQueryInsideQuery: 'Media/Container queries cannot be nested inside other queries.',
14
15
  noPseudoInsidePseudo: 'Pseudo-selectors cannot be nested inside other pseudo-selectors.',
16
+ invalidKeyframesKey: 'Keyframes keys must be "from", "to", or a percentage value (e.g. "0%", "50%", "100%").',
17
+ invalidViewTransitionKey: 'ViewTransition keys must be one of: "group", "imagePair", "new", "old".',
15
18
  },
16
19
  schema: [],
17
20
  },
@@ -42,25 +45,28 @@ exports.noInvalidSelectorNesting = {
42
45
  return 'QUERY';
43
46
  if (type.value.startsWith(':'))
44
47
  return 'PSEUDO';
45
- }
46
- if (type.isUnion()) {
47
- const types = type.types;
48
- if (types.every((t) => t.isStringLiteral() && t.value.startsWith('@')))
49
- return 'QUERY';
50
- if (types.every((t) => t.isStringLiteral() && t.value.startsWith(':')))
51
- return 'PSEUDO';
48
+ return 'PROPERTY';
52
49
  }
53
50
  }
54
- catch (e) {
51
+ catch (error) {
55
52
  }
53
+ return 'UNKNOWN';
56
54
  }
57
- return 'UNKNOWN';
55
+ return 'SKIP';
58
56
  }
59
57
  function checkNesting(node, parentType) {
60
58
  for (const prop of node.properties) {
61
59
  if (prop.type !== utils_1.TSESTree.AST_NODE_TYPES.Property)
62
60
  continue;
63
61
  const currentType = getSelectorType(prop.key);
62
+ if (currentType === 'SKIP')
63
+ continue;
64
+ if (currentType === 'UNKNOWN') {
65
+ context.report({
66
+ node: prop.key,
67
+ messageId: 'invalidKeySelector',
68
+ });
69
+ }
64
70
  if (parentType === 'PSEUDO' && currentType === 'QUERY') {
65
71
  context.report({
66
72
  node: prop.key,
@@ -81,6 +87,77 @@ exports.noInvalidSelectorNesting = {
81
87
  }
82
88
  }
83
89
  }
90
+ function checkOnlyProperties(node) {
91
+ for (const prop of node.properties) {
92
+ if (prop.type !== utils_1.TSESTree.AST_NODE_TYPES.Property)
93
+ continue;
94
+ const currentType = getSelectorType(prop.key);
95
+ if (currentType !== 'PROPERTY') {
96
+ context.report({
97
+ node: prop.key,
98
+ messageId: 'invalidKeySelector',
99
+ });
100
+ }
101
+ if (prop.value.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
102
+ checkOnlyProperties(prop.value);
103
+ }
104
+ }
105
+ }
106
+ function getKeyString(node) {
107
+ if (node.type === utils_1.TSESTree.AST_NODE_TYPES.Literal &&
108
+ typeof node.value === 'string') {
109
+ return node.value;
110
+ }
111
+ if (node.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier) {
112
+ return node.name;
113
+ }
114
+ return null;
115
+ }
116
+ function checkKeyframesKeys(node) {
117
+ for (const prop of node.properties) {
118
+ if (prop.type !== utils_1.TSESTree.AST_NODE_TYPES.Property)
119
+ continue;
120
+ const key = getKeyString(prop.key);
121
+ if (key == null) {
122
+ context.report({
123
+ node: prop.key,
124
+ messageId: 'invalidKeyframesKey',
125
+ });
126
+ }
127
+ if (prop.value.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
128
+ checkOnlyProperties(prop.value);
129
+ }
130
+ if (key !== null &&
131
+ key !== 'from' &&
132
+ key !== 'to' &&
133
+ !/^\d+(\.\d+)?%$/.test(key)) {
134
+ context.report({ node: prop.key, messageId: 'invalidKeyframesKey' });
135
+ }
136
+ }
137
+ }
138
+ function checkViewTransitionKeys(node) {
139
+ const allowed = new Set(['group', 'imagePair', 'new', 'old']);
140
+ for (const prop of node.properties) {
141
+ if (prop.type !== utils_1.TSESTree.AST_NODE_TYPES.Property)
142
+ continue;
143
+ const key = getKeyString(prop.key);
144
+ if (key == null) {
145
+ context.report({
146
+ node: prop.key,
147
+ messageId: 'invalidViewTransitionKey',
148
+ });
149
+ }
150
+ if (prop.value.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
151
+ checkOnlyProperties(prop.value);
152
+ }
153
+ if (key !== null && !allowed.has(key)) {
154
+ context.report({
155
+ node: prop.key,
156
+ messageId: 'invalidViewTransitionKey',
157
+ });
158
+ }
159
+ }
160
+ }
84
161
  return {
85
162
  ImportDeclaration(node) {
86
163
  if (node.source.value === '@plumeria/core') {
@@ -101,6 +178,8 @@ exports.noInvalidSelectorNesting = {
101
178
  },
102
179
  CallExpression(node) {
103
180
  let isCssCreate = false;
181
+ let isCssKeyframes = false;
182
+ let isCssViewTransition = false;
104
183
  if (node.callee.type === utils_1.TSESTree.AST_NODE_TYPES.MemberExpression) {
105
184
  if (node.callee.object.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier &&
106
185
  plumeriaAliases[node.callee.object.name] === 'NAMESPACE') {
@@ -109,23 +188,48 @@ exports.noInvalidSelectorNesting = {
109
188
  : null;
110
189
  if (propertyName === 'create')
111
190
  isCssCreate = true;
191
+ if (propertyName === 'keyframes')
192
+ isCssKeyframes = true;
193
+ if (propertyName === 'viewTransition')
194
+ isCssViewTransition = true;
112
195
  }
113
196
  }
114
197
  else if (node.callee.type === utils_1.TSESTree.AST_NODE_TYPES.Identifier) {
115
198
  const alias = plumeriaAliases[node.callee.name];
116
199
  if (alias === 'create')
117
200
  isCssCreate = true;
201
+ if (alias === 'keyframes')
202
+ isCssKeyframes = true;
203
+ if (alias === 'viewTransition')
204
+ isCssViewTransition = true;
118
205
  }
119
206
  if (isCssCreate &&
120
207
  node.arguments[0]?.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
121
208
  const styleObj = node.arguments[0];
122
209
  styleObj.properties.forEach((prop) => {
210
+ if (prop.type === utils_1.TSESTree.AST_NODE_TYPES.Property) {
211
+ const currentType = getSelectorType(prop.key);
212
+ if (currentType !== 'SKIP' && currentType === 'UNKNOWN') {
213
+ context.report({
214
+ node: prop.key,
215
+ messageId: 'invalidKeySelector',
216
+ });
217
+ }
218
+ }
123
219
  if (prop.type === utils_1.TSESTree.AST_NODE_TYPES.Property &&
124
220
  prop.value.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
125
221
  checkNesting(prop.value, 'CLASS');
126
222
  }
127
223
  });
128
224
  }
225
+ if (isCssKeyframes &&
226
+ node.arguments[0]?.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
227
+ checkKeyframesKeys(node.arguments[0]);
228
+ }
229
+ if (isCssViewTransition &&
230
+ node.arguments[0]?.type === utils_1.TSESTree.AST_NODE_TYPES.ObjectExpression) {
231
+ checkViewTransitionKeys(node.arguments[0]);
232
+ }
129
233
  },
130
234
  };
131
235
  },
@@ -1,9 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.noUnusedKeys = void 0;
4
- function getFilename(context) {
5
- return context.getFilename ? context.getFilename() : context.filename;
6
- }
7
4
  function getRootObject(node) {
8
5
  if (node.type === 'Identifier') {
9
6
  return node;
@@ -25,7 +22,7 @@ exports.noUnusedKeys = {
25
22
  schema: [],
26
23
  },
27
24
  create(context) {
28
- const filename = getFilename(context);
25
+ const filename = context.filename;
29
26
  if (filename.endsWith('.ts')) {
30
27
  return {};
31
28
  }
@@ -2,9 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.sortProperties = void 0;
4
4
  const propertyGroups_1 = require("../util/propertyGroups");
5
- function getSourceCode(context) {
6
- return context.getSourceCode ? context.getSourceCode() : context.sourceCode;
7
- }
8
5
  function getPropertyName(property) {
9
6
  if (property.key.type === 'Identifier') {
10
7
  return property.key.name;
@@ -88,7 +85,7 @@ exports.sortProperties = {
88
85
  create(context) {
89
86
  const plumeriaAliases = {};
90
87
  function checkStyleObject(node) {
91
- const sourceCode = getSourceCode(context);
88
+ const sourceCode = context.sourceCode;
92
89
  const properties = node.properties.filter((prop) => ('key' in prop && !!prop.key) || prop.type === 'SpreadElement');
93
90
  const chunks = [];
94
91
  let currentChunk = [];
package/oxlint-plugin.js CHANGED
@@ -1,4 +1,4 @@
1
- const { plumeria } = require('./dist/index.js');
1
+ const plumeria = require('./dist/index.js');
2
2
 
3
3
  module.exports = {
4
4
  meta: { name: '@plumeria' },
package/oxlint.json CHANGED
@@ -1,12 +1,22 @@
1
1
  {
2
- "jsPlugins": ["./oxlint-plugin.js"],
2
+ "jsPlugins": [
3
+ {
4
+ "name": "@plumeria",
5
+ "specifier": "@plumeria/eslint-plugin/oxlint-plugin"
6
+ }
7
+ ],
3
8
  "plugins": [],
4
9
  "ignorePatterns": ["**/*.svelte", "**/*.vue"],
5
10
 
6
11
  "rules": {
12
+ "@plumeria/style-name-requires-import": "error",
7
13
  "@plumeria/no-combinator": "error",
8
14
  "@plumeria/no-destructure": "error",
15
+ "@plumeria/no-inline-object": "error",
9
16
  "@plumeria/no-inner-call": "error",
17
+ "@plumeria/no-invalid-selector": "error",
18
+ "@plumeria/no-mixed-styling-props": "error",
19
+ "@plumeria/no-unknown-css-properties": "error",
10
20
  "@plumeria/no-unused-keys": "warn",
11
21
  "@plumeria/sort-properties": "warn",
12
22
  "@plumeria/format-properties": "warn",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumeria/eslint-plugin",
3
- "version": "11.2.0",
3
+ "version": "12.0.0",
4
4
  "description": "Plumeria ESLint plugin",
5
5
  "author": "Refirst 11",
6
6
  "license": "MIT",
@@ -25,6 +25,13 @@
25
25
  "sideEffects": false,
26
26
  "main": "dist/index.js",
27
27
  "types": "dist/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "default": "./dist/index.js"
32
+ },
33
+ "./oxlint-plugin": "./oxlint-plugin.js"
34
+ },
28
35
  "files": [
29
36
  "dist/",
30
37
  "oxlint-plugin.js",
@@ -35,16 +42,16 @@
35
42
  },
36
43
  "devDependencies": {
37
44
  "@types/eslint": "^9.6.1",
38
- "@types/estree": "^1.0.8",
39
- "@typescript-eslint/parser": "^8.59.2",
40
- "eslint": "^9.39.0"
45
+ "@types/estree": "^1.0.9",
46
+ "@typescript-eslint/parser": "^8.59.3",
47
+ "eslint": "^10.4.0"
41
48
  },
42
49
  "publishConfig": {
43
50
  "access": "public",
44
51
  "provenance": true
45
52
  },
46
53
  "dependencies": {
47
- "@typescript-eslint/utils": "^8.59.2",
54
+ "@typescript-eslint/utils": "^8.59.3",
48
55
  "known-css-properties": "^0.37.0"
49
56
  },
50
57
  "scripts": {
@@ -1,2 +0,0 @@
1
- import { Rule } from 'eslint';
2
- export declare const noInvalidSelectorNesting: Rule.RuleModule;