@plumeria/eslint-plugin 7.5.5 → 7.6.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
@@ -12,6 +12,7 @@ The `plugin:@plumeria/recommended` config enables the following:
12
12
  - `@plumeria/no-inner-call`: **error**
13
13
  - `@plumeria/no-unused-keys`: **warn**
14
14
  - `@plumeria/sort-properties`: **warn**
15
+ - `@plumeria/format-properties`: **warn**
15
16
  - `@plumeria/validate-values`: **warn**
16
17
 
17
18
  ```js
@@ -42,6 +43,11 @@ Warns when object keys are defined but not used, mainly in component files.
42
43
 
43
44
  Automatically sorts CSS properties in the recommended order for consistency and maintainability.
44
45
 
46
+ ### format-properties
47
+ Automatically format for consistency and maintainability.
48
+ - Formats a line into a multi-line.
49
+ - Formats by filling in blank lines.
50
+
45
51
  ### validate-values
46
52
 
47
53
  Validates CSS property values for correctness. Only standard CSS properties are checked; properties with string literal keys (e.g., computed or dynamic property names) are not validated.
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ const no_destructure_1 = require("./rules/no-destructure");
6
6
  const no_inner_call_1 = require("./rules/no-inner-call");
7
7
  const no_unused_keys_1 = require("./rules/no-unused-keys");
8
8
  const sort_properties_1 = require("./rules/sort-properties");
9
+ const format_properties_1 = require("./rules/format-properties");
9
10
  const validate_values_1 = require("./rules/validate-values");
10
11
  const rules = {
11
12
  'no-combinator': no_combinator_1.noCombinator,
@@ -13,6 +14,7 @@ const rules = {
13
14
  'no-inner-call': no_inner_call_1.noInnerCall,
14
15
  'no-unused-keys': no_unused_keys_1.noUnusedKeys,
15
16
  'sort-properties': sort_properties_1.sortProperties,
17
+ 'format-properties': format_properties_1.formatProperties,
16
18
  'validate-values': validate_values_1.validateValues,
17
19
  };
18
20
  const configs = {
@@ -24,6 +26,7 @@ const configs = {
24
26
  '@plumeria/no-inner-call': 'error',
25
27
  '@plumeria/no-unused-keys': 'warn',
26
28
  '@plumeria/sort-properties': 'warn',
29
+ '@plumeria/format-properties': 'warn',
27
30
  '@plumeria/validate-values': 'warn',
28
31
  },
29
32
  },
@@ -41,6 +44,7 @@ const flatConfigs = {
41
44
  '@plumeria/no-inner-call': 'error',
42
45
  '@plumeria/no-unused-keys': 'warn',
43
46
  '@plumeria/sort-properties': 'warn',
47
+ '@plumeria/format-properties': 'warn',
44
48
  '@plumeria/validate-values': 'warn',
45
49
  },
46
50
  },
@@ -0,0 +1,2 @@
1
+ import type { Rule } from 'eslint';
2
+ export declare const formatProperties: Rule.RuleModule;
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatProperties = void 0;
4
+ function getSourceCode(context) {
5
+ return context.sourceCode ?? context.getSourceCode();
6
+ }
7
+ exports.formatProperties = {
8
+ meta: {
9
+ type: 'layout',
10
+ docs: {
11
+ description: 'Format nested objects to have one property per line and remove empty lines',
12
+ },
13
+ fixable: 'whitespace',
14
+ schema: [],
15
+ messages: {
16
+ mustBeMultiline: 'Property object must be formatted with each property on its own line.',
17
+ noEmptyLines: 'No empty lines allowed between properties.',
18
+ },
19
+ },
20
+ create(context) {
21
+ const sourceCode = getSourceCode(context);
22
+ return {
23
+ ObjectExpression(node) {
24
+ if (!node.parent || node.parent.type !== 'Property')
25
+ return;
26
+ if (node.properties.length === 0)
27
+ return;
28
+ const properties = node.properties;
29
+ const parentLine = sourceCode.lines[node.parent.loc.start.line - 1];
30
+ const baseIndent = parentLine.match(/^(\s*)/)[1];
31
+ const innerIndent = baseIndent + ' ';
32
+ const countNewlines = (text) => (text.match(/\n/g) ?? []).length;
33
+ const fullText = sourceCode.getText();
34
+ const openBrace = sourceCode.getFirstToken(node);
35
+ const closeBrace = sourceCode.getLastToken(node);
36
+ let hasError = false;
37
+ let hasBlankLines = false;
38
+ const textBeforeFirst = fullText.slice(openBrace.range[1], properties[0].range[0]);
39
+ const newlinesBeforeFirst = countNewlines(textBeforeFirst);
40
+ if (newlinesBeforeFirst === 0)
41
+ hasError = true;
42
+ for (let i = 0; i < properties.length - 1; i++) {
43
+ const current = properties[i];
44
+ const next = properties[i + 1];
45
+ const comma = sourceCode.getTokenAfter(current);
46
+ const sliceStart = comma.range[1];
47
+ const between = fullText.slice(sliceStart, next.range[0]);
48
+ const newlines = countNewlines(between);
49
+ if (newlines === 0)
50
+ hasError = true;
51
+ if (newlines > 1)
52
+ hasBlankLines = true;
53
+ }
54
+ const lastProp = properties[properties.length - 1];
55
+ const lastComma = sourceCode.getTokenAfter(lastProp);
56
+ const lastSliceStart = lastComma?.value === ',' ? lastComma.range[1] : lastProp.range[1];
57
+ const textAfterLast = fullText.slice(lastSliceStart, closeBrace.range[0]);
58
+ const newlinesAfterLast = countNewlines(textAfterLast);
59
+ if (newlinesAfterLast === 0)
60
+ hasError = true;
61
+ if (!hasError && !hasBlankLines)
62
+ return;
63
+ const buildFixes = (fixer) => {
64
+ const fixes = [];
65
+ fixes.push(fixer.replaceTextRange([openBrace.range[1], properties[0].range[0]], `\n${innerIndent}`));
66
+ for (let i = 0; i < properties.length - 1; i++) {
67
+ const current = properties[i];
68
+ const next = properties[i + 1];
69
+ const comma = sourceCode.getTokenAfter(current);
70
+ if (comma.value === ',') {
71
+ fixes.push(fixer.replaceTextRange([comma.range[1], next.range[0]], `\n${innerIndent}`));
72
+ }
73
+ }
74
+ const lastComma = sourceCode.getTokenAfter(lastProp);
75
+ if (lastComma?.value === ',') {
76
+ fixes.push(fixer.replaceTextRange([lastComma.range[1], closeBrace.range[0]], `\n${baseIndent}`));
77
+ }
78
+ else {
79
+ fixes.push(fixer.replaceTextRange([lastProp.range[1], closeBrace.range[0]], `\n${baseIndent}`));
80
+ }
81
+ return fixes;
82
+ };
83
+ if (hasError) {
84
+ context.report({
85
+ node,
86
+ messageId: 'mustBeMultiline',
87
+ fix: buildFixes,
88
+ });
89
+ return;
90
+ }
91
+ let reported = false;
92
+ for (let i = 0; i < properties.length - 1; i++) {
93
+ const current = properties[i];
94
+ const next = properties[i + 1];
95
+ const comma = sourceCode.getTokenAfter(current);
96
+ const sliceStart = comma.range[1];
97
+ const between = fullText.slice(sliceStart, next.range[0]);
98
+ if (countNewlines(between) > 1) {
99
+ const blankLineNumber = current.loc.end.line + 1;
100
+ context.report({
101
+ loc: {
102
+ start: {
103
+ line: blankLineNumber,
104
+ column: 0,
105
+ },
106
+ end: {
107
+ line: blankLineNumber + 1,
108
+ column: 0,
109
+ },
110
+ },
111
+ messageId: 'noEmptyLines',
112
+ fix: reported ? null : buildFixes,
113
+ });
114
+ reported = true;
115
+ }
116
+ }
117
+ },
118
+ };
119
+ },
120
+ };
package/oxlint.json CHANGED
@@ -9,6 +9,7 @@
9
9
  "@plumeria/no-inner-call": "error",
10
10
  "@plumeria/no-unused-keys": "warn",
11
11
  "@plumeria/sort-properties": "warn",
12
+ "@plumeria/format-properties": "warn",
12
13
  "@plumeria/validate-values": "warn"
13
14
  }
14
15
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumeria/eslint-plugin",
3
- "version": "7.5.5",
3
+ "version": "7.6.1",
4
4
  "description": "Plumeria ESLint plugin",
5
5
  "author": "Refirst 11",
6
6
  "license": "MIT",