@plumeria/eslint-plugin 19.9.1 → 19.10.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
@@ -8,6 +8,7 @@ Below are the available rules and the recommended configuration.
8
8
  The `plugin:@plumeria/recommended` config enables the following:
9
9
 
10
10
  - `@plumeria/custom-props-require-import`: **error**
11
+ - `@plumeria/expand-border-shorthands`: **warn**
11
12
  - `@plumeria/no-combinator`: **error**
12
13
  - `@plumeria/no-destructure`: **error**
13
14
  - `@plumeria/no-inline-object`: **error**
@@ -82,6 +83,27 @@ the import.
82
83
 
83
84
  Accepts `{ styleProp }`; see [Configuring the styling prop](#configuring-the-styling-prop).
84
85
 
86
+ ### expand-border-shorthands
87
+
88
+ Expands a border shorthand that bundles a width, a style and a color —
89
+ `border`, `borderBlock`, `borderInline`, and the eight edge forms — into the
90
+ three declarations it stands for. Those bundles are the only properties left
91
+ that cross an axis shorthand without either containing the other, so expanding
92
+ them turns the last unrankable pairs into ordinary shorthand-to-longhand ones.
93
+
94
+ Fixable. A value it cannot split, such as `var(--edge)` or `inherit`, is
95
+ reported without a fix: leaving it silent would let the expanded declarations
96
+ elsewhere outrank it.
97
+
98
+ ```js
99
+ borderTop: '1px solid red'
100
+ // becomes
101
+ borderTopWidth: '1px', borderTopStyle: 'solid', borderTopColor: 'red'
102
+ ```
103
+
104
+ A shorthand resets what it omits, so `borderBlock: 'solid'` expands with
105
+ `medium` and `currentcolor` written out.
106
+
85
107
  ### no-combinator
86
108
 
87
109
  Disallow combinators `>`, `+`, `~` and descendant combinator (space) unless inside functional pseudo-classes.
@@ -176,8 +198,8 @@ Validates at-rules inside `css.create()`. It accepts `@media`, `@container`, `@s
176
198
 
177
199
  ## Optional rules
178
200
 
179
- These rules are not enabled by `plumeria.configs.recommended`. Enable only the
180
- policy or transformation that fits the project:
201
+ These rules are not enabled by `plumeria.configs.recommended`. Enable the
202
+ spelling policy that fits the project:
181
203
 
182
204
  ```js
183
205
  export default [
@@ -185,33 +207,11 @@ export default [
185
207
  {
186
208
  rules: {
187
209
  '@plumeria/no-logical-properties': 'warn',
188
- '@plumeria/expand-border-shorthands': 'warn',
189
210
  },
190
211
  },
191
212
  ];
192
213
  ```
193
214
 
194
- ### expand-border-shorthands
195
-
196
- Expands a border shorthand that bundles a width, a style and a color —
197
- `border`, `borderBlock`, `borderInline`, and the eight edge forms — into the
198
- three declarations it stands for. Those bundles are the only properties left
199
- that cross an axis shorthand without either containing the other, so expanding
200
- them turns the last unrankable pairs into ordinary shorthand-to-longhand ones.
201
-
202
- Fixable. A value it cannot split, such as `var(--edge)` or `inherit`, is
203
- reported without a fix: leaving it silent would let the expanded declarations
204
- elsewhere outrank it.
205
-
206
- ```js
207
- borderTop: '1px solid red'
208
- // becomes
209
- borderTopWidth: '1px', borderTopStyle: 'solid', borderTopColor: 'red'
210
- ```
211
-
212
- A shorthand resets what it omits, so `borderBlock: 'solid'` expands with
213
- `medium` and `currentcolor` written out.
214
-
215
215
  ### no-physical-properties / no-logical-properties
216
216
 
217
217
  Disallow one of the two names a property can carry, so a project writes edges
@@ -228,19 +228,24 @@ appears under one spelling only can never meet its other spelling on an element.
228
228
  A shorthand with no single counterpart, such as `borderBlockWidth`, is outside
229
229
  either rule and stays reported.
230
230
 
231
+ The bundler plugins turn the matching rule on in the build lint as well:
232
+ `withoutPhysicalProperties` runs `no-physical-properties`, and
233
+ `withoutLogicalProperties` runs `no-logical-properties`, `{ sizes }` included.
234
+
231
235
  ## CLI (plumerialint)
232
236
 
233
237
  This package provides a CLI command, `plumerialint`, as a convenient way
234
238
  to run Plumeria's custom ESLint rules.
235
239
 
236
- It uses `oxlint` internally for fast linting with code snippets in output.
240
+ It runs `oxlint`, which this package depends on, for fast linting with code
241
+ snippets in output, so oxlint needs no separate install.
237
242
 
238
243
  ### Installation
239
244
 
240
245
  ```bash
241
- npm install -D @plumeria/eslint-plugin oxlint
246
+ npm install -D @plumeria/eslint-plugin
242
247
  # or
243
- pnpm add -D @plumeria/eslint-plugin oxlint
248
+ pnpm add -D @plumeria/eslint-plugin
244
249
  ```
245
250
 
246
251
  ### Usage
@@ -252,16 +257,6 @@ plumerialint
252
257
  The process exits with a non-zero status code if any errors or warnings are found,
253
258
  making it suitable for use in CI and build pipelines.
254
259
 
255
- Example usage in `package.json`:
256
-
257
- ```json
258
- {
259
- "scripts": {
260
- "lint": "plumerialint"
261
- }
262
- }
263
- ```
264
-
265
260
  ### Aborting Builds on Lint Errors (Parallel Pipeline)
266
261
 
267
262
  You can run `plumerialint` in parallel with your build command (e.g. `next build` or `vite build`) using the `--` separator:
@@ -276,4 +271,4 @@ You can run `plumerialint` in parallel with your build command (e.g. `next build
276
271
 
277
272
  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.
278
273
 
279
- **Note:** `oxlint` is required as `plumerialint` uses it internally.
274
+ With `@plumeria/next-plugin` and `@plumeria/unplugin` (except on esbuild and Bun), this lint is integrated into the build by default, so it needs no setup. Pass `lint: false` to the plugin to build without it.
package/bin/oxlint.js CHANGED
@@ -4,6 +4,11 @@ const { spawn } = require('child_process');
4
4
  const process = require('process');
5
5
  const path = require('path');
6
6
  const oxlintConfig = path.join(__dirname, '..', 'oxlint.json');
7
+ const oxlintBin = path.join(
8
+ path.dirname(require.resolve('oxlint/package.json')),
9
+ 'bin',
10
+ 'oxlint',
11
+ );
7
12
 
8
13
  const doubleDashIndex = process.argv.indexOf('--');
9
14
  let oxlintExtraArgs = [];
@@ -20,22 +25,22 @@ if (doubleDashIndex !== -1) {
20
25
  oxlintExtraArgs = process.argv.slice(2);
21
26
  }
22
27
 
23
- const oxlintArgs = ['-c', oxlintConfig, '--deny-warnings', ...oxlintExtraArgs];
28
+ const oxlintArgs = [
29
+ oxlintBin,
30
+ '-c',
31
+ oxlintConfig,
32
+ '--deny-warnings',
33
+ '--no-error-on-unmatched-pattern',
34
+ ...oxlintExtraArgs,
35
+ ];
24
36
 
25
37
  function handleOxlintError(err) {
26
- if (err.code === 'ENOENT') {
27
- console.error('\n✖ oxlint is not installed.');
28
- console.error('➡︎ plumerialint uses oxlint.');
29
- console.error('✔ please install oxlint.\n');
30
- process.exit(1);
31
- } else {
32
- console.error('Error running oxlint:', err.message);
33
- process.exit(1);
34
- }
38
+ console.error('Error running oxlint:', err.message);
39
+ process.exit(1);
35
40
  }
36
41
 
37
42
  if (!buildCommand) {
38
- const child = spawn('oxlint', oxlintArgs, { stdio: 'inherit' });
43
+ const child = spawn(process.execPath, oxlintArgs, { stdio: 'inherit' });
39
44
  child.on('error', handleOxlintError);
40
45
  child.on('close', (code) => {
41
46
  process.exit(code || 0);
@@ -47,7 +52,7 @@ if (!buildCommand) {
47
52
  let buildCode = null;
48
53
  let aborted = false;
49
54
 
50
- const oxlintChild = spawn('oxlint', oxlintArgs, { stdio: 'inherit' });
55
+ const oxlintChild = spawn(process.execPath, oxlintArgs, { stdio: 'inherit' });
51
56
 
52
57
  const fullBuildCommand =
53
58
  buildArgs.length > 0
@@ -57,6 +62,7 @@ if (!buildCommand) {
57
62
  const buildChild = spawn(fullBuildCommand, {
58
63
  stdio: 'inherit',
59
64
  shell: true,
65
+ env: { ...process.env, PLUMERIA_LINT_GUARD: '1' },
60
66
  });
61
67
 
62
68
  function abort(exitCode, failedSource) {
@@ -0,0 +1,11 @@
1
+ export declare const GUARD_ENV = "PLUMERIA_LINT_GUARD";
2
+ type Spelling = boolean | {
3
+ sizes?: boolean;
4
+ } | undefined;
5
+ export interface SpellingOptions {
6
+ withoutLogicalProperties?: Spelling;
7
+ withoutPhysicalProperties?: Spelling;
8
+ }
9
+ export declare function spellingRules(options: SpellingOptions): Record<string, unknown>;
10
+ export declare function startLintGuard(rules?: Record<string, unknown>): boolean;
11
+ export {};
package/dist/guard.js ADDED
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.GUARD_ENV = void 0;
37
+ exports.spellingRules = spellingRules;
38
+ exports.startLintGuard = startLintGuard;
39
+ const child_process_1 = require("child_process");
40
+ const fs = __importStar(require("fs"));
41
+ const os = __importStar(require("os"));
42
+ const path = __importStar(require("path"));
43
+ exports.GUARD_ENV = 'PLUMERIA_LINT_GUARD';
44
+ const BASE_CONFIG = path.join(__dirname, '..', 'oxlint.json');
45
+ function spellingRule(spelling) {
46
+ if (!spelling)
47
+ return undefined;
48
+ return typeof spelling === 'object' && spelling.sizes
49
+ ? ['error', { sizes: true }]
50
+ : 'error';
51
+ }
52
+ function spellingRules(options) {
53
+ const rules = {};
54
+ const logical = spellingRule(options.withoutLogicalProperties);
55
+ const physical = spellingRule(options.withoutPhysicalProperties);
56
+ if (logical)
57
+ rules['@plumeria/no-logical-properties'] = logical;
58
+ if (physical)
59
+ rules['@plumeria/no-physical-properties'] = physical;
60
+ return rules;
61
+ }
62
+ function lintConfig(rules) {
63
+ if (Object.keys(rules).length === 0)
64
+ return BASE_CONFIG;
65
+ const file = path.join(os.tmpdir(), `plumeria-oxlint-${process.pid}.json`);
66
+ fs.writeFileSync(file, JSON.stringify({ extends: [BASE_CONFIG], rules }));
67
+ return file;
68
+ }
69
+ function startLintGuard(rules = {}) {
70
+ if (process.env[exports.GUARD_ENV])
71
+ return false;
72
+ process.env[exports.GUARD_ENV] = '1';
73
+ const config = lintConfig(rules);
74
+ const child = (0, child_process_1.spawn)(process.execPath, [
75
+ path.join(path.dirname(require.resolve('oxlint/package.json')), 'bin', 'oxlint'),
76
+ '-c',
77
+ config,
78
+ '--deny-warnings',
79
+ '--no-error-on-unmatched-pattern',
80
+ ], { stdio: 'inherit' });
81
+ const exit = process.exit.bind(process);
82
+ let lintCode = null;
83
+ let pendingCode = null;
84
+ child.on('error', (error) => {
85
+ console.error(`\n✖ [plumeria] Could not run oxlint: ${error.message}`);
86
+ exit(1);
87
+ });
88
+ child.on('close', (code) => {
89
+ if (config !== BASE_CONFIG)
90
+ fs.rmSync(config, { force: true });
91
+ lintCode = code ?? 1;
92
+ if (lintCode !== 0) {
93
+ console.error('\n✖ [plumeria] Linting failed. Aborting build...');
94
+ return exit(lintCode);
95
+ }
96
+ if (pendingCode !== null)
97
+ exit(pendingCode);
98
+ });
99
+ process.exit = ((code) => {
100
+ if (lintCode !== null)
101
+ return exit(code);
102
+ pendingCode = code;
103
+ });
104
+ return true;
105
+ }
package/dist/index.js CHANGED
@@ -50,6 +50,7 @@ const configs = {
50
50
  },
51
51
  rules: {
52
52
  '@plumeria/custom-props-require-import': 'error',
53
+ '@plumeria/expand-border-shorthands': 'warn',
53
54
  '@plumeria/no-combinator': 'error',
54
55
  '@plumeria/no-destructure': 'error',
55
56
  '@plumeria/no-inline-object': 'error',
package/oxlint.json CHANGED
@@ -6,10 +6,12 @@
6
6
  }
7
7
  ],
8
8
  "plugins": [],
9
+ "categories": { "correctness": "off" },
9
10
  "ignorePatterns": ["**/*.svelte", "**/*.vue"],
10
11
 
11
12
  "rules": {
12
13
  "@plumeria/custom-props-require-import": "error",
14
+ "@plumeria/expand-border-shorthands": "warn",
13
15
  "@plumeria/no-combinator": "error",
14
16
  "@plumeria/no-destructure": "error",
15
17
  "@plumeria/no-inline-object": "error",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumeria/eslint-plugin",
3
- "version": "19.9.1",
3
+ "version": "19.10.0",
4
4
  "description": "Plumeria ESLint plugin",
5
5
  "author": "Refirst 11",
6
6
  "license": "MIT",
@@ -29,6 +29,10 @@
29
29
  "types": "./dist/index.d.ts",
30
30
  "default": "./dist/index.js"
31
31
  },
32
+ "./guard": {
33
+ "types": "./dist/guard.d.ts",
34
+ "default": "./dist/guard.js"
35
+ },
32
36
  "./oxlint-plugin": "./oxlint-plugin.js"
33
37
  },
34
38
  "files": [
@@ -53,8 +57,9 @@
53
57
  },
54
58
  "dependencies": {
55
59
  "known-css-properties": "^0.37.0",
60
+ "oxlint": "1.87.0",
56
61
  "zss-engine": "2.9.1",
57
- "@plumeria/compiler": "19.9.1"
62
+ "@plumeria/compiler": "19.10.0"
58
63
  },
59
64
  "scripts": {
60
65
  "build": "rimraf dist && pnpm cjs",