@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 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/style-name-requires-import`: **error**
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
- ### style-name-requires-import
68
+ ### props-require-import
33
69
 
34
- Disallow styleName prop in files without a @plumeria/core import.
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 `styleName` with `className` or `style`. `styleName` can handle both `className` and `style`.
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
- - Formats a line into a multi-line.
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 style_name_requires_import_1 = require("./rules/style-name-requires-import");
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
- 'style-name-requires-import': style_name_requires_import_1.styleNameRequiresImport,
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/style-name-requires-import': 'error',
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 styleName is present',
9
+ description: 'Disallow className and style props when the styling prop is present',
9
10
  },
10
11
  messages: {
11
- noMixedStylingProps: '"styleName" handles both "className" and "style". Avoid mixing them.',
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 hasStyleName = attributes.some((attr) => attr.type === 'JSXAttribute' &&
22
+ const hasStyleProp = attributes.some((attr) => attr.type === 'JSXAttribute' &&
20
23
  attr.name.type === 'JSXIdentifier' &&
21
- attr.name.name === 'styleName');
22
- if (hasStyleName) {
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 === 'className' || attr.name.name === 'style')) {
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
  }
@@ -0,0 +1,2 @@
1
+ import type { Rule } from 'eslint';
2
+ export declare const propsRequireImport: Rule.RuleModule;
@@ -1,21 +1,23 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.styleNameRequiresImport = void 0;
4
- exports.styleNameRequiresImport = {
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 styleName prop in files without a @plumeria/core import',
9
+ description: 'Disallow the styling prop in files without a @plumeria/core import',
9
10
  },
10
11
  fixable: 'code',
11
12
  messages: {
12
- styleNameError: 'styleName requires importing "@plumeria/core".',
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 styleNameNodes = [];
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 === 'styleName') {
28
- styleNameNodes.push(node);
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
- styleNameNodes.forEach((node, index) => {
35
+ stylePropNodes.forEach((node, index) => {
34
36
  context.report({
35
37
  node,
36
- messageId: 'styleNameError',
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
+ }
@@ -316,7 +316,16 @@ const validData = {
316
316
  columnSpan: ['none', 'all'],
317
317
  columnWidth: ['auto', 'max-content', 'min-content'],
318
318
  columns: [],
319
- contain: ['none', 'strict', 'content'],
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/style-name-requires-import": "error",
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumeria/eslint-plugin",
3
- "version": "16.5.0",
3
+ "version": "17.0.1",
4
4
  "description": "Plumeria ESLint plugin",
5
5
  "author": "Refirst 11",
6
6
  "license": "MIT",
@@ -1,2 +0,0 @@
1
- import type { Rule } from 'eslint';
2
- export declare const styleNameRequiresImport: Rule.RuleModule;