eslint-plugin-oxfmt 0.5.1 → 0.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 +50 -7
- package/dist/index.d.mts +3 -2
- package/dist/index.mjs +56 -4
- package/dts/rule-options.d.ts +15 -0
- package/package.json +10 -10
- package/workers/oxfmt.mjs +271 -420
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
- 🔧 **Auto-fix** - Automatically format code on save or via ESLint's fix command
|
|
14
14
|
- 🎯 **ESLint Integration** - Seamlessly integrates with ESLint v9+ flat config
|
|
15
15
|
- 📦 **Zero Config** - Works out of the box with sensible defaults
|
|
16
|
-
- 🧩 **Config File Discovery** -
|
|
16
|
+
- 🧩 **Config File Discovery** - Auto-discovers `.oxfmtrc.json`, `.oxfmtrc.jsonc`, and `oxfmt.config.ts`
|
|
17
17
|
- 📝 **EditorConfig Integration** - Respects a subset of `.editorconfig` options via [oxfmt's strategy](https://oxc.rs/docs/guide/usage/formatter/config#editorconfig)
|
|
18
18
|
- 🎨 **Highly Configurable** - Supports all oxfmt formatting options
|
|
19
19
|
- 🌐 **Multi-language Support** - JavaScript, TypeScript, JSX, TSX and [more](https://oxc.rs/docs/guide/usage/formatter.html#supported-languages)
|
|
@@ -71,6 +71,20 @@ export default [
|
|
|
71
71
|
]
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
+
For CLI-like ignore/config behavior, use `cliParity`:
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
// eslint.config.mjs
|
|
78
|
+
import pluginOxfmt from 'eslint-plugin-oxfmt'
|
|
79
|
+
|
|
80
|
+
export default [
|
|
81
|
+
{
|
|
82
|
+
...pluginOxfmt.configs.cliParity,
|
|
83
|
+
files: ['**/*.{js,ts,mjs,cjs,jsx,tsx,json,jsonc,yaml,yml}'],
|
|
84
|
+
},
|
|
85
|
+
]
|
|
86
|
+
```
|
|
87
|
+
|
|
74
88
|
### Custom Configuration
|
|
75
89
|
|
|
76
90
|
You can customize the formatting options by configuring the rule:
|
|
@@ -157,10 +171,15 @@ All options are optional and default to sensible values.
|
|
|
157
171
|
|
|
158
172
|
### Plugin Options
|
|
159
173
|
|
|
160
|
-
| Option
|
|
161
|
-
|
|
|
162
|
-
| `useConfig`
|
|
163
|
-
| `configPath`
|
|
174
|
+
| Option | Type | Default | Description |
|
|
175
|
+
| ---------------------------- | ------------------------------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
176
|
+
| `useConfig` | `boolean` | `true` | Load `.oxfmtrc.json`, `.oxfmtrc.jsonc`, or `oxfmt.config.*` via `load-oxfmt-config`. Set to `false` to rely only on inline options. |
|
|
177
|
+
| `configPath` | `string` | — | Custom path to an oxfmt config file. Relative paths resolve from ESLint `cwd`; absolute paths are used as-is. Explicit `configPath` accepts `.json`, `.jsonc`, `.ts`, `.mts`, `.cts`, `.js`, `.mjs`, `.cjs`. |
|
|
178
|
+
| `editorconfig` | `boolean \| { onlyCwd?: boolean; cwd?: string }` | `true` | Control `.editorconfig` loading. Use `false` to disable, or object form for advanced resolution strategy. |
|
|
179
|
+
| `ignorePath` | `string \| string[]` | — | Ignore file path(s) for CLI-style ignore resolution (same role as CLI `--ignore-path`). |
|
|
180
|
+
| `withNodeModules` | `boolean` | `false` | Include files under `node_modules` during ignore checks. |
|
|
181
|
+
| `disableNestedConfig` | `boolean` | `false` | Disable nested config discovery and resolve config from `cwd` / `configPath` only. |
|
|
182
|
+
| `respectOxfmtDefaultIgnores` | `boolean` | `true` | Respect oxfmt default ignores (`.gitignore`, `.prettierignore`, default ignored directories, default ignored lockfiles). |
|
|
164
183
|
|
|
165
184
|
> Note: `cwd` is taken from ESLint automatically; you usually do not need to set it manually.
|
|
166
185
|
> `.editorconfig` merge behavior follows oxfmt's documented strategy: https://oxc.rs/docs/guide/usage/formatter/config#editorconfig
|
|
@@ -171,10 +190,15 @@ When `useConfig` is `true`, the plugin loads config using `load-oxfmt-config`.
|
|
|
171
190
|
|
|
172
191
|
- Config discovery order (from `cwd`, walking upward): `.oxfmtrc.json` → `.oxfmtrc.jsonc` → `oxfmt.config.ts`
|
|
173
192
|
- `.editorconfig` support: nearest `.editorconfig` (including section overrides) is merged into the final options
|
|
193
|
+
- Set `editorconfig: false` to disable `.editorconfig` merging
|
|
194
|
+
- Set `editorconfig: { onlyCwd: true }` to read only the current `cwd`'s `.editorconfig` (no upward traversal)
|
|
195
|
+
- Set `editorconfig: { cwd: '/path/to/base' }` to customize editorconfig resolution base directory
|
|
174
196
|
- `configPath` overrides discovery and directly targets the specified config file
|
|
197
|
+
- `configPath` supports explicit file paths with extensions: `.json`, `.jsonc`, `.ts`, `.mts`, `.cts`, `.js`, `.mjs`, `.cjs`
|
|
175
198
|
- ESLint rule options generally take highest priority because inline rule options are merged after loaded config.
|
|
176
|
-
-
|
|
177
|
-
-
|
|
199
|
+
- Rule-level `ignorePatterns` are resolved relative to ESLint `cwd`; config-level `ignorePatterns` are resolved relative to the resolved config file directory.
|
|
200
|
+
- When `useConfig` is `true`, config `overrides` are applied first and rule-level `overrides` are appended after them (later entries win on conflicts).
|
|
201
|
+
- When `useConfig` is `false`, config discovery and config `ignorePatterns` are skipped, while global ignores still apply when `respectOxfmtDefaultIgnores` is enabled.
|
|
178
202
|
|
|
179
203
|
For detailed behavior, see:
|
|
180
204
|
|
|
@@ -381,12 +405,31 @@ This plugin provides a single rule that formats your code using oxfmt.
|
|
|
381
405
|
- Fixable: Yes (automatically applies formatting)
|
|
382
406
|
- Type: Layout
|
|
383
407
|
|
|
408
|
+
## CLI parity mode
|
|
409
|
+
|
|
410
|
+
`oxfmt/cli-parity` tries to match `oxfmt` CLI behavior for files processed by ESLint.
|
|
411
|
+
|
|
412
|
+
It respects:
|
|
413
|
+
|
|
414
|
+
- `.oxfmtrc.json`
|
|
415
|
+
- `.oxfmtrc.jsonc`
|
|
416
|
+
- `oxfmt.config.*`
|
|
417
|
+
- `.editorconfig`
|
|
418
|
+
- `ignorePatterns`
|
|
419
|
+
- `.gitignore`
|
|
420
|
+
- `.prettierignore`
|
|
421
|
+
- default ignored directories
|
|
422
|
+
- default ignored lockfiles
|
|
423
|
+
|
|
424
|
+
Note: ESLint still controls file discovery. Files excluded by ESLint will never reach this rule.
|
|
425
|
+
|
|
384
426
|
## Integration
|
|
385
427
|
|
|
386
428
|
### Parser Compatibility
|
|
387
429
|
|
|
388
430
|
- `recommended`: forces `eslint-parser-plain` for matched files
|
|
389
431
|
- `recommendedWithoutParser`: parser-agnostic (safe to compose with language-specific parsers)
|
|
432
|
+
- `cliParity`: parser-agnostic preset tuned for CLI-like config/ignore behavior
|
|
390
433
|
|
|
391
434
|
When composing shareable configs, prefer `recommendedWithoutParser` if parser ownership belongs to another preset.
|
|
392
435
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as _$eslint from "eslint";
|
|
2
2
|
import { Linter, Rule } from "eslint";
|
|
3
|
-
|
|
4
3
|
//#region src/types.d.ts
|
|
5
4
|
interface PluginOxfmt {
|
|
6
5
|
configs: {
|
|
6
|
+
cliParity: Linter.Config<Linter.RulesRecord>;
|
|
7
7
|
recommended: Linter.Config<Linter.RulesRecord>;
|
|
8
8
|
recommendedWithoutParser: Linter.Config<Linter.RulesRecord>;
|
|
9
9
|
};
|
|
@@ -33,9 +33,10 @@ declare const parserPlain: Linter.Parser;
|
|
|
33
33
|
//#region src/configs.d.ts
|
|
34
34
|
declare const recommendedWithoutParser: Linter.Config<Linter.RulesRecord>;
|
|
35
35
|
declare const recommended: Linter.Config<Linter.RulesRecord>;
|
|
36
|
+
declare const cliParity: Linter.Config<Linter.RulesRecord>;
|
|
36
37
|
declare const configs: PluginOxfmt['configs'];
|
|
37
38
|
//#endregion
|
|
38
39
|
//#region src/index.d.ts
|
|
39
40
|
declare const plugin: PluginOxfmt;
|
|
40
41
|
//#endregion
|
|
41
|
-
export { configs, plugin as default, plugin, meta, parserPlain, recommended, recommendedWithoutParser, rules };
|
|
42
|
+
export { cliParity, configs, plugin as default, plugin, meta, parserPlain, recommended, recommendedWithoutParser, rules };
|
package/dist/index.mjs
CHANGED
|
@@ -60,7 +60,18 @@ const recommended = {
|
|
|
60
60
|
name: "oxfmt/recommended",
|
|
61
61
|
languageOptions: { parser: parserPlain }
|
|
62
62
|
};
|
|
63
|
+
const cliParity = {
|
|
64
|
+
...recommendedWithoutParser,
|
|
65
|
+
name: "oxfmt/cli-parity",
|
|
66
|
+
rules: { "oxfmt/oxfmt": ["error", {
|
|
67
|
+
disableNestedConfig: false,
|
|
68
|
+
respectOxfmtDefaultIgnores: true,
|
|
69
|
+
useConfig: true,
|
|
70
|
+
withNodeModules: false
|
|
71
|
+
}] }
|
|
72
|
+
};
|
|
63
73
|
const configs = {
|
|
74
|
+
cliParity,
|
|
64
75
|
recommended,
|
|
65
76
|
recommendedWithoutParser
|
|
66
77
|
};
|
|
@@ -68,7 +79,7 @@ const configs = {
|
|
|
68
79
|
//#region src/meta.ts
|
|
69
80
|
const meta = {
|
|
70
81
|
name: "eslint-plugin-oxfmt",
|
|
71
|
-
version: "0.
|
|
82
|
+
version: "0.6.1"
|
|
72
83
|
};
|
|
73
84
|
//#endregion
|
|
74
85
|
//#region src/dir.ts
|
|
@@ -184,7 +195,7 @@ const oxfmtOptionsSchema = {
|
|
|
184
195
|
type: "string"
|
|
185
196
|
},
|
|
186
197
|
ignorePatterns: {
|
|
187
|
-
description: `Ignore files matching these glob patterns.
|
|
198
|
+
description: `Ignore files matching these glob patterns. Rule-level ignorePatterns are resolved relative to ESLint cwd. Config ignorePatterns are resolved relative to the config file directory.`,
|
|
188
199
|
type: "array",
|
|
189
200
|
items: { type: "string" }
|
|
190
201
|
},
|
|
@@ -458,8 +469,48 @@ const oxfmtConfigSchema = {
|
|
|
458
469
|
description: `Path to Oxfmt configuration file.\nIf you provide an absolute path, Oxfmt will use it directly.\n If not provided, Oxfmt will search for configuration files starting from the current working directory upwards.\n\n- (Default: undefined)`,
|
|
459
470
|
type: "string"
|
|
460
471
|
},
|
|
472
|
+
disableNestedConfig: {
|
|
473
|
+
default: false,
|
|
474
|
+
description: `Disable nested config lookup and resolve from cwd/configPath only. (Default: false)`,
|
|
475
|
+
type: "boolean"
|
|
476
|
+
},
|
|
477
|
+
editorconfig: {
|
|
478
|
+
description: `Control .editorconfig reading.\n- true: enable with default behavior.\n- false: disable .editorconfig reading.\n- object: enable with advanced options (onlyCwd/cwd).\n\n- (Default: true)`,
|
|
479
|
+
oneOf: [{ type: "boolean" }, {
|
|
480
|
+
additionalProperties: false,
|
|
481
|
+
type: "object",
|
|
482
|
+
properties: {
|
|
483
|
+
cwd: {
|
|
484
|
+
description: `Override directory used as the start point for .editorconfig resolution.`,
|
|
485
|
+
type: "string"
|
|
486
|
+
},
|
|
487
|
+
onlyCwd: {
|
|
488
|
+
description: `When true, only read .editorconfig from cwd (no upward traversal).`,
|
|
489
|
+
type: "boolean"
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}]
|
|
493
|
+
},
|
|
494
|
+
ignorePath: {
|
|
495
|
+
description: `Path(s) to ignore files used for CLI-style ignore resolution. Accepts a single path or multiple paths. (Default: undefined)`,
|
|
496
|
+
oneOf: [{ type: "string" }, {
|
|
497
|
+
type: "array",
|
|
498
|
+
items: { type: "string" }
|
|
499
|
+
}]
|
|
500
|
+
},
|
|
501
|
+
respectOxfmtDefaultIgnores: {
|
|
502
|
+
default: true,
|
|
503
|
+
description: `Whether to respect oxfmt default ignores (.gitignore, .prettierignore, lockfiles, default directories). (Default: true)`,
|
|
504
|
+
type: "boolean"
|
|
505
|
+
},
|
|
461
506
|
useConfig: {
|
|
462
|
-
|
|
507
|
+
default: true,
|
|
508
|
+
description: `Whether to load Oxfmt configuration file. (Default: true)`,
|
|
509
|
+
type: "boolean"
|
|
510
|
+
},
|
|
511
|
+
withNodeModules: {
|
|
512
|
+
default: false,
|
|
513
|
+
description: `Whether to include files inside node_modules during ignore resolution. (Default: false)`,
|
|
463
514
|
type: "boolean"
|
|
464
515
|
}
|
|
465
516
|
}
|
|
@@ -528,6 +579,7 @@ const rules = { oxfmt: {
|
|
|
528
579
|
...context.options?.[0],
|
|
529
580
|
cwd: context.cwd
|
|
530
581
|
});
|
|
582
|
+
if (formatResult.ignored) return;
|
|
531
583
|
if (formatResult.errors?.length) for (const error of formatResult.errors) {
|
|
532
584
|
const label = error.labels?.[0];
|
|
533
585
|
if (label) {
|
|
@@ -582,4 +634,4 @@ const plugin = {
|
|
|
582
634
|
rules
|
|
583
635
|
};
|
|
584
636
|
//#endregion
|
|
585
|
-
export { configs, plugin as default, plugin, meta, parserPlain, recommended, recommendedWithoutParser, rules };
|
|
637
|
+
export { cliParity, configs, plugin as default, plugin, meta, parserPlain, recommended, recommendedWithoutParser, rules };
|
package/dts/rule-options.d.ts
CHANGED
|
@@ -134,8 +134,23 @@ export type OxfmtOxfmt = []|[{
|
|
|
134
134
|
|
|
135
135
|
configPath?: string
|
|
136
136
|
|
|
137
|
+
disableNestedConfig?: boolean
|
|
138
|
+
|
|
139
|
+
editorconfig?: (boolean | {
|
|
140
|
+
|
|
141
|
+
cwd?: string
|
|
142
|
+
|
|
143
|
+
onlyCwd?: boolean
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
ignorePath?: (string | string[])
|
|
147
|
+
|
|
148
|
+
respectOxfmtDefaultIgnores?: boolean
|
|
149
|
+
|
|
137
150
|
useConfig?: boolean
|
|
138
151
|
|
|
152
|
+
withNodeModules?: boolean
|
|
153
|
+
|
|
139
154
|
overrides?: {
|
|
140
155
|
|
|
141
156
|
excludeFiles?: string[]
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-oxfmt",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.6.1",
|
|
5
5
|
"description": "An ESLint plugin for formatting code with oxfmt.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"eslint",
|
|
@@ -48,17 +48,17 @@
|
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"generate-differences": "^0.1.1",
|
|
51
|
-
"load-oxfmt-config": "^0.
|
|
51
|
+
"load-oxfmt-config": "^0.7.2",
|
|
52
52
|
"picomatch": "^4.0.4",
|
|
53
53
|
"synckit": "^0.11.12"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@ntnyq/eslint-config": "^6.1.
|
|
56
|
+
"@ntnyq/eslint-config": "^6.1.3",
|
|
57
57
|
"@types/json-schema": "^7.0.15",
|
|
58
|
-
"@types/node": "^25.6.
|
|
59
|
-
"@typescript/native-preview": "^7.0.0-dev.
|
|
60
|
-
"bumpp": "^11.0
|
|
61
|
-
"eslint": "^10.
|
|
58
|
+
"@types/node": "^25.6.2",
|
|
59
|
+
"@typescript/native-preview": "^7.0.0-dev.20260508.1",
|
|
60
|
+
"bumpp": "^11.1.0",
|
|
61
|
+
"eslint": "^10.3.0",
|
|
62
62
|
"eslint-parser-plain": "^0.1.1",
|
|
63
63
|
"eslint-typegen": "^2.3.1",
|
|
64
64
|
"eslint-vitest-rule-tester": "^3.1.0",
|
|
@@ -66,14 +66,14 @@
|
|
|
66
66
|
"jsonc-eslint-parser": "^3.1.0",
|
|
67
67
|
"nano-staged": "^1.0.2",
|
|
68
68
|
"npm-run-all2": "^8.0.4",
|
|
69
|
-
"oxfmt": "^0.
|
|
69
|
+
"oxfmt": "^0.48.0",
|
|
70
70
|
"show-invisibles": "^0.0.2",
|
|
71
71
|
"tinyglobby": "^0.2.16",
|
|
72
|
-
"tsdown": "^0.
|
|
72
|
+
"tsdown": "^0.22.0",
|
|
73
73
|
"tsx": "^4.21.0",
|
|
74
74
|
"typescript": "^6.0.3",
|
|
75
75
|
"vitest": "^4.1.5",
|
|
76
|
-
"eslint-plugin-oxfmt": "0.
|
|
76
|
+
"eslint-plugin-oxfmt": "0.6.1"
|
|
77
77
|
},
|
|
78
78
|
"engines": {
|
|
79
79
|
"node": "^20.19.0 || >=22.12.0"
|
package/workers/oxfmt.mjs
CHANGED
|
@@ -1,508 +1,359 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
-
import { dirname,
|
|
4
|
-
import {
|
|
3
|
+
import { dirname, relative } from 'node:path'
|
|
4
|
+
import { isOxfmtIgnored, loadOxfmtConfigResult } from 'load-oxfmt-config'
|
|
5
5
|
import { format } from 'oxfmt'
|
|
6
6
|
import picomatch from 'picomatch'
|
|
7
7
|
import { runAsWorker } from 'synckit'
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* @typedef {object}
|
|
11
|
-
* @property {
|
|
12
|
-
* @property {string}
|
|
13
|
-
* @property {string} [
|
|
10
|
+
* @typedef {object} PluginOnlyOptions
|
|
11
|
+
* @property {string} [cwd] - Base working directory used for path resolution.
|
|
12
|
+
* @property {string} [configPath] - Explicit path to oxfmt config file.
|
|
13
|
+
* @property {string | string[]} [ignorePath] - One or more ignore file paths.
|
|
14
|
+
* @property {boolean} [withNodeModules] - Whether node_modules should be included in ignore checks.
|
|
15
|
+
* @property {boolean} [disableNestedConfig] - Disable per-file nested config lookup.
|
|
16
|
+
* @property {boolean} [useCache] - Reuse caches for config/ignore resolution.
|
|
17
|
+
* @property {boolean} [useConfig] - Whether config discovery/loading is enabled.
|
|
18
|
+
* @property {boolean} [respectOxfmtDefaultIgnores] - Whether CLI-like default ignores should apply.
|
|
19
|
+
* @property {import('load-oxfmt-config').LoadOxfmtConfigOptions['editorconfig']} [editorconfig] - EditorConfig loading strategy.
|
|
14
20
|
*/
|
|
15
21
|
|
|
16
22
|
/**
|
|
17
|
-
* @typedef {import('load-oxfmt-config').
|
|
23
|
+
* @typedef {PluginOnlyOptions & import('load-oxfmt-config').LoadOxfmtConfigOptions & import('oxfmt').FormatConfig} PluginOptions
|
|
18
24
|
*/
|
|
25
|
+
|
|
19
26
|
/**
|
|
20
|
-
* @typedef {
|
|
27
|
+
* @typedef {object} WorkerIgnoredResult
|
|
28
|
+
* @property {true} ignored - Indicates formatting was skipped due to ignore rules.
|
|
29
|
+
* @property {import('load-oxfmt-config').IsOxfmtIgnoredResult['reason']} [reason] - Ignore reason from resolution step.
|
|
30
|
+
* @property {string} code - Unchanged source code.
|
|
31
|
+
* @property {never} [errors] - Not present for ignored results.
|
|
21
32
|
*/
|
|
22
33
|
|
|
23
34
|
/**
|
|
24
|
-
* @typedef {object}
|
|
25
|
-
* @property {
|
|
26
|
-
* @property {string
|
|
27
|
-
* @property {
|
|
28
|
-
* @property {Override[] | undefined} overrides Rule-level override entries.
|
|
29
|
-
* @property {boolean} useConfig Whether config loading is enabled.
|
|
30
|
-
* @property {import('oxfmt').FormatConfig} formatOptions Pure formatter options passed to oxfmt.
|
|
35
|
+
* @typedef {object} WorkerFormattedResult
|
|
36
|
+
* @property {false} [ignored] - False or undefined when formatting was attempted.
|
|
37
|
+
* @property {string} code - Formatted source code.
|
|
38
|
+
* @property {unknown[]} [errors] - Optional formatter errors.
|
|
31
39
|
*/
|
|
32
40
|
|
|
33
41
|
/**
|
|
34
|
-
* @typedef {
|
|
35
|
-
* @property {import('oxfmt').FormatConfig} formatOptions Resolved base formatter options.
|
|
36
|
-
* @property {string[] | undefined} ignorePatterns Resolved ignore patterns from config loading.
|
|
37
|
-
* @property {Override[] | undefined} overrides Resolved overrides from config loading.
|
|
38
|
-
* @property {string} configDir Directory of the resolved config file, used as base for config-derived glob patterns.
|
|
42
|
+
* @typedef {WorkerIgnoredResult | WorkerFormattedResult} WorkerFormatResult
|
|
39
43
|
*/
|
|
40
44
|
|
|
41
|
-
const MAX_CACHE_SIZE = 1000
|
|
42
|
-
|
|
43
45
|
/**
|
|
44
|
-
*
|
|
45
|
-
* @
|
|
46
|
+
* @typedef {object} SplitOptionsResult
|
|
47
|
+
* @property {PluginOnlyOptions} pluginOptions - Plugin orchestration options.
|
|
48
|
+
* @property {import('oxfmt').FormatConfig} formatOptions - Pure oxfmt options.
|
|
46
49
|
*/
|
|
47
|
-
function evictCache(
|
|
48
|
-
/** @type {Map<string, unknown>} */
|
|
49
|
-
cache,
|
|
50
|
-
) {
|
|
51
|
-
if (cache.size <= MAX_CACHE_SIZE) {
|
|
52
|
-
return
|
|
53
|
-
}
|
|
54
|
-
const keysToDelete = [...cache.keys()].slice(0, cache.size - MAX_CACHE_SIZE)
|
|
55
|
-
for (const key of keysToDelete) {
|
|
56
|
-
cache.delete(key)
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
50
|
|
|
60
51
|
/**
|
|
61
|
-
*
|
|
62
|
-
* @param key - The key of the property being processed
|
|
63
|
-
* @param value - The value of the property being processed
|
|
64
|
-
* @returns - The value to be serialized, with object keys sorted if it's an object
|
|
52
|
+
* @typedef {import('load-oxfmt-config').OxfmtConfigOverride} OxfmtConfigOverride
|
|
65
53
|
*/
|
|
66
|
-
function stableReplacer(
|
|
67
|
-
/** @type {string} */
|
|
68
|
-
key,
|
|
69
|
-
/** @type {unknown} */
|
|
70
|
-
value,
|
|
71
|
-
) {
|
|
72
|
-
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
73
|
-
return Object.keys(value)
|
|
74
|
-
.sort()
|
|
75
|
-
.reduce((sorted, k) => {
|
|
76
|
-
sorted[k] = /** @type {Record<string, unknown>} */ (value)[k]
|
|
77
|
-
return sorted
|
|
78
|
-
}, /** @type {Record<string, unknown>} */ ({}))
|
|
79
|
-
}
|
|
80
|
-
return value
|
|
81
|
-
}
|
|
82
54
|
|
|
83
|
-
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
55
|
+
const MAX_CACHE_SIZE = 200
|
|
56
|
+
const PLUGIN_ONLY_OPTIONS = new Set([
|
|
57
|
+
'configPath',
|
|
58
|
+
'cwd',
|
|
59
|
+
'disableNestedConfig',
|
|
60
|
+
'editorconfig',
|
|
61
|
+
'ignorePath',
|
|
62
|
+
'respectOxfmtDefaultIgnores',
|
|
63
|
+
'useCache',
|
|
64
|
+
'useConfig',
|
|
65
|
+
'withNodeModules',
|
|
66
|
+
])
|
|
67
|
+
/** @type {Map<string, import('picomatch').Matcher>} */
|
|
68
|
+
const matcherCache = new Map()
|
|
91
69
|
|
|
92
70
|
/**
|
|
93
|
-
* Apply
|
|
94
|
-
* @param relativePath -
|
|
95
|
-
* @param baseOptions - Base format options
|
|
96
|
-
* @param [
|
|
97
|
-
* @returns
|
|
71
|
+
* Apply override entries to a base options object.
|
|
72
|
+
* @param {string} relativePath - Relative file path used for matching.
|
|
73
|
+
* @param {import('oxfmt').FormatConfig} baseOptions - Base format options.
|
|
74
|
+
* @param {OxfmtConfigOverride[] | undefined} overrides - Override entries.
|
|
75
|
+
* @returns {import('oxfmt').FormatConfig} Options after override merge.
|
|
98
76
|
*/
|
|
99
|
-
function applyOverrides(
|
|
100
|
-
|
|
101
|
-
relativePath,
|
|
102
|
-
/** @type {import('oxfmt').FormatConfig} */
|
|
103
|
-
baseOptions,
|
|
104
|
-
/** @type {Override[] | undefined} */
|
|
105
|
-
overrides,
|
|
106
|
-
) {
|
|
107
|
-
if (!overrides || overrides.length === 0) {
|
|
77
|
+
function applyOverrides(relativePath, baseOptions, overrides) {
|
|
78
|
+
if (!overrides?.length) {
|
|
108
79
|
return baseOptions
|
|
109
80
|
}
|
|
110
81
|
|
|
111
|
-
let
|
|
112
|
-
let hasOverrides = false
|
|
113
|
-
|
|
114
|
-
// Apply overrides in order (later overrides take precedence)
|
|
82
|
+
let merged = baseOptions
|
|
115
83
|
for (const override of overrides) {
|
|
116
|
-
|
|
84
|
+
if (!override?.files?.length) {
|
|
85
|
+
continue
|
|
86
|
+
}
|
|
117
87
|
|
|
118
|
-
|
|
119
|
-
const matches =
|
|
88
|
+
const fileMatcher = getCachedMatcher(override.files)
|
|
89
|
+
const matches = !!fileMatcher && fileMatcher(relativePath)
|
|
120
90
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
: false
|
|
91
|
+
const excludeMatcher = override.excludeFiles?.length
|
|
92
|
+
? getCachedMatcher(override.excludeFiles)
|
|
93
|
+
: undefined
|
|
94
|
+
const excluded = excludeMatcher ? excludeMatcher(relativePath) : false
|
|
126
95
|
|
|
127
|
-
if (matches && !excluded &&
|
|
128
|
-
|
|
129
|
-
|
|
96
|
+
if (matches && !excluded && override.options) {
|
|
97
|
+
merged = {
|
|
98
|
+
...merged,
|
|
99
|
+
...override.options,
|
|
100
|
+
}
|
|
130
101
|
}
|
|
131
102
|
}
|
|
132
103
|
|
|
133
|
-
return
|
|
104
|
+
return merged
|
|
134
105
|
}
|
|
135
106
|
|
|
136
107
|
/**
|
|
137
|
-
*
|
|
138
|
-
* @param
|
|
139
|
-
* @
|
|
108
|
+
* Format source text via oxfmt with CLI-parity ignore orchestration.
|
|
109
|
+
* @param {string} filename - File path passed from ESLint.
|
|
110
|
+
* @param {string} sourceText - Source text to format.
|
|
111
|
+
* @param {PluginOptions} [options] - Worker options.
|
|
112
|
+
* @returns {Promise<WorkerFormatResult>} Format result.
|
|
140
113
|
*/
|
|
141
|
-
function
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
)
|
|
145
|
-
const key = Array.isArray(patterns) ? patterns.join('\0') : patterns
|
|
146
|
-
const cached = picomatchCache.get(key)
|
|
147
|
-
if (cached) {
|
|
148
|
-
return cached
|
|
149
|
-
}
|
|
150
|
-
const matcher = picomatch(patterns)
|
|
151
|
-
picomatchCache.set(key, matcher)
|
|
152
|
-
evictCache(picomatchCache)
|
|
153
|
-
return matcher
|
|
154
|
-
}
|
|
114
|
+
async function formatViaOxfmt(filename, sourceText, options = {}) {
|
|
115
|
+
const { formatOptions: inlineFormatOptions, pluginOptions } =
|
|
116
|
+
splitOptions(options)
|
|
117
|
+
validatePluginOptions(pluginOptions)
|
|
155
118
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
* - Absolute paths are returned as-is.
|
|
159
|
-
* - Relative paths are resolved from cwd.
|
|
160
|
-
*
|
|
161
|
-
* @param cwd - Current working directory from ESLint context.
|
|
162
|
-
* @param [configPath] - Optional user-provided config path.
|
|
163
|
-
* @returns Absolute config path when provided, otherwise undefined.
|
|
164
|
-
*/
|
|
165
|
-
function getConfigPathForFile(
|
|
166
|
-
/** @type {string} */
|
|
167
|
-
cwd,
|
|
168
|
-
/** @type {string | undefined} */
|
|
169
|
-
configPath,
|
|
170
|
-
) {
|
|
171
|
-
if (!configPath) {
|
|
172
|
-
return undefined
|
|
173
|
-
}
|
|
119
|
+
const cwd = pluginOptions.cwd
|
|
120
|
+
const useConfig = pluginOptions.useConfig !== false
|
|
174
121
|
|
|
175
|
-
|
|
176
|
-
|
|
122
|
+
const ruleIgnorePatterns = isStringArray(inlineFormatOptions.ignorePatterns)
|
|
123
|
+
? inlineFormatOptions.ignorePatterns
|
|
124
|
+
: undefined
|
|
177
125
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
function getMergedOptionsCacheKey(
|
|
188
|
-
/** @type {import('oxfmt').FormatConfig} */
|
|
189
|
-
baseOptions,
|
|
190
|
-
/** @type {string | undefined} */
|
|
191
|
-
relativePath,
|
|
192
|
-
/** @type {Override[] | undefined} */
|
|
193
|
-
overrides,
|
|
194
|
-
) {
|
|
195
|
-
const hasOverrides = !!(overrides && overrides.length > 0)
|
|
196
|
-
|
|
197
|
-
return JSON.stringify(
|
|
198
|
-
{
|
|
199
|
-
baseOptions,
|
|
200
|
-
overrides: hasOverrides ? overrides : undefined,
|
|
201
|
-
relativePath: hasOverrides ? relativePath : undefined,
|
|
202
|
-
},
|
|
203
|
-
stableReplacer,
|
|
204
|
-
)
|
|
205
|
-
}
|
|
126
|
+
if (ruleIgnorePatterns?.length && cwd) {
|
|
127
|
+
const ruleRelativePath = getRelativePath(cwd, filename)
|
|
128
|
+
if (shouldIgnoreFile(ruleRelativePath, ruleIgnorePatterns)) {
|
|
129
|
+
return {
|
|
130
|
+
code: sourceText,
|
|
131
|
+
ignored: true,
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
206
135
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
136
|
+
if (pluginOptions.respectOxfmtDefaultIgnores !== false && cwd) {
|
|
137
|
+
/** @type {import('load-oxfmt-config').IsOxfmtIgnoredOptions} */
|
|
138
|
+
const ignoredOptions = {
|
|
139
|
+
configPath: pluginOptions.configPath,
|
|
140
|
+
cwd,
|
|
141
|
+
disableNestedConfig: pluginOptions.disableNestedConfig,
|
|
142
|
+
filepath: filename,
|
|
143
|
+
ignorePath: pluginOptions.ignorePath,
|
|
144
|
+
includeConfigIgnorePatterns: useConfig,
|
|
145
|
+
loadConfigForIgnorePatterns: useConfig,
|
|
146
|
+
useCache: pluginOptions.useCache,
|
|
147
|
+
withNodeModules: pluginOptions.withNodeModules,
|
|
148
|
+
}
|
|
149
|
+
const ignored = await isOxfmtIgnored(ignoredOptions)
|
|
150
|
+
|
|
151
|
+
if (ignored.ignored) {
|
|
152
|
+
if (
|
|
153
|
+
ruleIgnorePatterns?.length &&
|
|
154
|
+
ignored.reason === 'config-ignore-patterns'
|
|
155
|
+
) {
|
|
156
|
+
// Rule-level ignorePatterns should take precedence over config ignorePatterns.
|
|
157
|
+
} else {
|
|
158
|
+
return {
|
|
159
|
+
code: sourceText,
|
|
160
|
+
ignored: true,
|
|
161
|
+
reason: ignored.reason,
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
221
166
|
|
|
222
|
-
/**
|
|
223
|
-
|
|
224
|
-
* The key includes file directory and all resolution inputs.
|
|
225
|
-
*
|
|
226
|
-
* @param filename - Current file path.
|
|
227
|
-
* @param cwd - Current working directory from ESLint context.
|
|
228
|
-
* @param configPath - Optional user-provided config path.
|
|
229
|
-
* @param useConfig - Whether config file loading is enabled.
|
|
230
|
-
* @param formatOptions - Rule-level format options.
|
|
231
|
-
* @returns Serialized cache key.
|
|
232
|
-
*/
|
|
233
|
-
function getResolvedBaseOptionsCacheKey(
|
|
167
|
+
/** @type {OxfmtConfigOverride[] | undefined} */
|
|
168
|
+
let effectiveOverrides
|
|
234
169
|
/** @type {string} */
|
|
235
|
-
filename
|
|
236
|
-
/** @type {string} */
|
|
237
|
-
cwd,
|
|
238
|
-
/** @type {string | undefined} */
|
|
239
|
-
configPath,
|
|
240
|
-
/** @type {boolean} */
|
|
241
|
-
useConfig,
|
|
170
|
+
let overrideBaseDir = cwd ?? dirname(filename)
|
|
242
171
|
/** @type {import('oxfmt').FormatConfig} */
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
172
|
+
let finalOptions
|
|
173
|
+
|
|
174
|
+
if (useConfig) {
|
|
175
|
+
const configResolutionCwd = pluginOptions.configPath
|
|
176
|
+
? cwd
|
|
177
|
+
: pluginOptions.disableNestedConfig
|
|
178
|
+
? cwd
|
|
179
|
+
: dirname(filename)
|
|
180
|
+
|
|
181
|
+
const loaded = await loadOxfmtConfigResult({
|
|
182
|
+
configPath: pluginOptions.configPath,
|
|
183
|
+
cwd: configResolutionCwd,
|
|
184
|
+
editorconfig: pluginOptions.editorconfig,
|
|
185
|
+
useCache: pluginOptions.useCache,
|
|
186
|
+
})
|
|
187
|
+
const { overrides: configOverrides, ...loadedConfig } = loaded.config
|
|
188
|
+
const { overrides: ruleOverrides, ...inlineOptionsWithoutOverrides } =
|
|
189
|
+
inlineFormatOptions
|
|
190
|
+
effectiveOverrides = [
|
|
191
|
+
...(configOverrides ?? []),
|
|
192
|
+
...(Array.isArray(ruleOverrides) ? ruleOverrides : []),
|
|
193
|
+
]
|
|
194
|
+
overrideBaseDir = loaded.dirname ?? overrideBaseDir
|
|
195
|
+
|
|
196
|
+
finalOptions = {
|
|
197
|
+
...loadedConfig,
|
|
198
|
+
...inlineOptionsWithoutOverrides,
|
|
199
|
+
}
|
|
200
|
+
} else {
|
|
201
|
+
const { overrides: ruleOverrides, ...inlineOptionsWithoutOverrides } =
|
|
202
|
+
inlineFormatOptions
|
|
203
|
+
effectiveOverrides = Array.isArray(ruleOverrides)
|
|
204
|
+
? ruleOverrides
|
|
205
|
+
: undefined
|
|
206
|
+
finalOptions = {
|
|
207
|
+
...inlineOptionsWithoutOverrides,
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const overrideRelativePath = getRelativePath(overrideBaseDir, filename)
|
|
212
|
+
finalOptions = applyOverrides(
|
|
213
|
+
overrideRelativePath,
|
|
214
|
+
finalOptions,
|
|
215
|
+
effectiveOverrides,
|
|
254
216
|
)
|
|
217
|
+
|
|
218
|
+
return format(filename, sourceText, finalOptions)
|
|
255
219
|
}
|
|
256
220
|
|
|
257
221
|
/**
|
|
258
|
-
*
|
|
259
|
-
* @param
|
|
260
|
-
* @returns
|
|
222
|
+
* Get or create a cached picomatch matcher.
|
|
223
|
+
* @param {string[]} patterns - Glob patterns.
|
|
224
|
+
* @returns {import('picomatch').Matcher} Compiled matcher.
|
|
261
225
|
*/
|
|
262
|
-
function
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
) {
|
|
266
|
-
|
|
267
|
-
throw new TypeError('oxfmt worker expected an options object.')
|
|
226
|
+
function getCachedMatcher(patterns) {
|
|
227
|
+
const key = patterns.join('\0')
|
|
228
|
+
const cached = matcherCache.get(key)
|
|
229
|
+
if (cached) {
|
|
230
|
+
return cached
|
|
268
231
|
}
|
|
269
232
|
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
overrides,
|
|
275
|
-
useConfig = true,
|
|
276
|
-
...formatOptions
|
|
277
|
-
} = options
|
|
278
|
-
|
|
279
|
-
if (typeof cwd !== 'string' || cwd.length === 0) {
|
|
280
|
-
throw new TypeError('oxfmt worker requires a non-empty "cwd" option.')
|
|
281
|
-
}
|
|
282
|
-
if (configPath != null && typeof configPath !== 'string') {
|
|
283
|
-
throw new TypeError(
|
|
284
|
-
'oxfmt worker requires "configPath" to be a string when provided.',
|
|
285
|
-
)
|
|
286
|
-
}
|
|
287
|
-
if (ignorePatterns != null && !isStringArray(ignorePatterns)) {
|
|
288
|
-
throw new TypeError(
|
|
289
|
-
'oxfmt worker requires "ignorePatterns" to be an array of strings when provided.',
|
|
290
|
-
)
|
|
291
|
-
}
|
|
292
|
-
if (overrides != null && !Array.isArray(overrides)) {
|
|
293
|
-
throw new TypeError(
|
|
294
|
-
'oxfmt worker requires "overrides" to be an array when provided.',
|
|
295
|
-
)
|
|
296
|
-
}
|
|
297
|
-
if (typeof useConfig !== 'boolean') {
|
|
298
|
-
throw new TypeError(
|
|
299
|
-
'oxfmt worker requires "useConfig" to be a boolean when provided.',
|
|
300
|
-
)
|
|
301
|
-
}
|
|
233
|
+
const matcher = picomatch(patterns)
|
|
234
|
+
setCacheEntry(matcherCache, key, matcher)
|
|
235
|
+
return matcher
|
|
236
|
+
}
|
|
302
237
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
238
|
+
/**
|
|
239
|
+
* Normalize a file path relative to the provided base directory.
|
|
240
|
+
* @param {string} baseDir - Base directory used for glob evaluation.
|
|
241
|
+
* @param {string} filename - Absolute file path.
|
|
242
|
+
* @returns {string} Relative path using forward slashes.
|
|
243
|
+
*/
|
|
244
|
+
function getRelativePath(baseDir, filename) {
|
|
245
|
+
return relative(baseDir, filename).replace(/\\/g, '/')
|
|
311
246
|
}
|
|
312
247
|
|
|
313
248
|
/**
|
|
314
249
|
* Check whether a value is an array of strings.
|
|
315
|
-
* @param value - Value to validate
|
|
316
|
-
* @returns
|
|
250
|
+
* @param {unknown} value - Value to validate.
|
|
251
|
+
* @returns {value is string[]} Whether the value is a string array.
|
|
317
252
|
*/
|
|
318
|
-
function isStringArray(
|
|
319
|
-
/** @type {unknown} */
|
|
320
|
-
value,
|
|
321
|
-
) {
|
|
253
|
+
function isStringArray(value) {
|
|
322
254
|
return Array.isArray(value) && value.every(item => typeof item === 'string')
|
|
323
255
|
}
|
|
324
256
|
|
|
325
257
|
/**
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
* @param
|
|
329
|
-
* @param
|
|
330
|
-
* @param
|
|
331
|
-
* @param useConfig - Whether config file loading is enabled.
|
|
332
|
-
* @param formatOptions - Rule-level format options.
|
|
333
|
-
* @returns Base options after config loading and inline option merge.
|
|
258
|
+
* Store a value in a FIFO cache map with a bounded size.
|
|
259
|
+
* @template T Cache value type.
|
|
260
|
+
* @param {Map<string, T>} cache - Cache map.
|
|
261
|
+
* @param {string} key - Cache key.
|
|
262
|
+
* @param {T} value - Value to store.
|
|
334
263
|
*/
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
configPath,
|
|
342
|
-
/** @type {boolean} */
|
|
343
|
-
useConfig,
|
|
344
|
-
/** @type {import('oxfmt').FormatConfig} */
|
|
345
|
-
formatOptions,
|
|
346
|
-
) {
|
|
347
|
-
const cacheKey = getResolvedBaseOptionsCacheKey(
|
|
348
|
-
filename,
|
|
349
|
-
cwd,
|
|
350
|
-
configPath,
|
|
351
|
-
useConfig,
|
|
352
|
-
formatOptions,
|
|
353
|
-
)
|
|
354
|
-
|
|
355
|
-
const cachedTask = resolvedBaseOptionsCache.get(cacheKey)
|
|
356
|
-
if (cachedTask) {
|
|
357
|
-
return cachedTask
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/** @type {Promise<ResolvedBaseOptions>} */
|
|
361
|
-
const task = (async () => {
|
|
362
|
-
const resolveFromDir = dirname(filename)
|
|
363
|
-
|
|
364
|
-
if (!useConfig) {
|
|
365
|
-
return {
|
|
366
|
-
configDir: cwd,
|
|
367
|
-
ignorePatterns: undefined,
|
|
368
|
-
overrides: undefined,
|
|
369
|
-
formatOptions: {
|
|
370
|
-
...formatOptions,
|
|
371
|
-
},
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
const resolvedConfigPath = getConfigPathForFile(cwd, configPath)
|
|
376
|
-
const resolvedPath = await resolveOxfmtrcPath(
|
|
377
|
-
resolveFromDir,
|
|
378
|
-
resolvedConfigPath,
|
|
379
|
-
)
|
|
380
|
-
const configDir = resolvedPath ? dirname(resolvedPath) : cwd
|
|
381
|
-
const configOptions = await loadOxfmtConfig({
|
|
382
|
-
configPath: resolvedConfigPath,
|
|
383
|
-
cwd: resolveFromDir,
|
|
384
|
-
})
|
|
385
|
-
const { ignorePatterns, overrides, ...loadedFormatOptions } =
|
|
386
|
-
configOptions ?? {}
|
|
387
|
-
|
|
388
|
-
return {
|
|
389
|
-
configDir,
|
|
390
|
-
ignorePatterns,
|
|
391
|
-
overrides,
|
|
392
|
-
formatOptions: {
|
|
393
|
-
...loadedFormatOptions,
|
|
394
|
-
...formatOptions,
|
|
395
|
-
},
|
|
264
|
+
function setCacheEntry(cache, key, value) {
|
|
265
|
+
cache.set(key, value)
|
|
266
|
+
if (cache.size > MAX_CACHE_SIZE) {
|
|
267
|
+
const oldestKey = cache.keys().next().value
|
|
268
|
+
if (oldestKey != null) {
|
|
269
|
+
cache.delete(oldestKey)
|
|
396
270
|
}
|
|
397
|
-
})()
|
|
398
|
-
|
|
399
|
-
resolvedBaseOptionsCache.set(cacheKey, task)
|
|
400
|
-
evictCache(resolvedBaseOptionsCache)
|
|
401
|
-
|
|
402
|
-
try {
|
|
403
|
-
return await task
|
|
404
|
-
} catch (err) {
|
|
405
|
-
resolvedBaseOptionsCache.delete(cacheKey)
|
|
406
|
-
throw err
|
|
407
271
|
}
|
|
408
272
|
}
|
|
409
273
|
|
|
410
274
|
/**
|
|
411
|
-
* Check if a file should be ignored
|
|
412
|
-
* @param relativePath -
|
|
413
|
-
* @param [
|
|
414
|
-
* @returns
|
|
275
|
+
* Check if a file should be ignored by provided glob patterns.
|
|
276
|
+
* @param {string} relativePath - Relative path against pattern base.
|
|
277
|
+
* @param {string[] | undefined} ignorePatterns - Glob patterns.
|
|
278
|
+
* @returns {boolean} Whether file is ignored.
|
|
415
279
|
*/
|
|
416
|
-
function shouldIgnoreFile(
|
|
417
|
-
|
|
418
|
-
relativePath,
|
|
419
|
-
/** @type {string[] | undefined} */ ignorePatterns,
|
|
420
|
-
) {
|
|
421
|
-
if (!ignorePatterns || ignorePatterns.length === 0) {
|
|
280
|
+
function shouldIgnoreFile(relativePath, ignorePatterns) {
|
|
281
|
+
if (!ignorePatterns?.length) {
|
|
422
282
|
return false
|
|
423
283
|
}
|
|
424
284
|
|
|
425
|
-
|
|
426
|
-
return
|
|
285
|
+
const matcher = getCachedMatcher(ignorePatterns)
|
|
286
|
+
return !!matcher && matcher(relativePath)
|
|
427
287
|
}
|
|
428
288
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
configPath,
|
|
446
|
-
cwd,
|
|
447
|
-
formatOptions,
|
|
448
|
-
ignorePatterns,
|
|
449
|
-
overrides,
|
|
450
|
-
useConfig,
|
|
451
|
-
} = getWorkerOptions(options)
|
|
452
|
-
|
|
453
|
-
const {
|
|
454
|
-
configDir,
|
|
455
|
-
formatOptions: baseFormatOptions,
|
|
456
|
-
ignorePatterns: baseIgnorePatterns,
|
|
457
|
-
overrides: baseOverrides,
|
|
458
|
-
} = await resolveBaseOptions(
|
|
459
|
-
filename,
|
|
460
|
-
cwd,
|
|
461
|
-
configPath,
|
|
462
|
-
useConfig,
|
|
463
|
-
formatOptions,
|
|
464
|
-
)
|
|
465
|
-
const effectiveIgnorePatterns = ignorePatterns ?? baseIgnorePatterns
|
|
466
|
-
const ignoreBase = ignorePatterns == null ? configDir : cwd
|
|
467
|
-
const ignoreRelativePath = effectiveIgnorePatterns?.length
|
|
468
|
-
? getRelativePath(ignoreBase, filename)
|
|
469
|
-
: undefined
|
|
470
|
-
|
|
471
|
-
if (
|
|
472
|
-
ignoreRelativePath &&
|
|
473
|
-
shouldIgnoreFile(ignoreRelativePath, effectiveIgnorePatterns)
|
|
474
|
-
) {
|
|
475
|
-
return { code: sourceText }
|
|
289
|
+
/**
|
|
290
|
+
* Split worker options into plugin orchestration options and pure format options.
|
|
291
|
+
* @param {PluginOptions} [options] - Raw worker options.
|
|
292
|
+
* @returns {SplitOptionsResult} Split option buckets.
|
|
293
|
+
*/
|
|
294
|
+
function splitOptions(options = {}) {
|
|
295
|
+
/** @type {Record<string, unknown>} */
|
|
296
|
+
const pluginOptions = {}
|
|
297
|
+
/** @type {Record<string, unknown>} */
|
|
298
|
+
const formatOptions = {}
|
|
299
|
+
|
|
300
|
+
for (const [key, value] of Object.entries(options)) {
|
|
301
|
+
if (PLUGIN_ONLY_OPTIONS.has(key)) {
|
|
302
|
+
pluginOptions[key] = value
|
|
303
|
+
} else {
|
|
304
|
+
formatOptions[key] = value
|
|
476
305
|
}
|
|
306
|
+
}
|
|
477
307
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
const mergedOptionsCacheKey = getMergedOptionsCacheKey(
|
|
484
|
-
baseFormatOptions,
|
|
485
|
-
overrideRelativePath,
|
|
486
|
-
effectiveOverrides,
|
|
487
|
-
)
|
|
308
|
+
return {
|
|
309
|
+
formatOptions: /** @type {import('oxfmt').FormatConfig} */ (formatOptions),
|
|
310
|
+
pluginOptions: /** @type {PluginOnlyOptions} */ (pluginOptions),
|
|
311
|
+
}
|
|
312
|
+
}
|
|
488
313
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
314
|
+
/**
|
|
315
|
+
* Validate plugin-only options before dispatching to helper libraries.
|
|
316
|
+
* @param {PluginOnlyOptions} pluginOptions - Plugin-only options.
|
|
317
|
+
*/
|
|
318
|
+
function validatePluginOptions(pluginOptions) {
|
|
319
|
+
/** @type {('cwd' | 'configPath')[]} */
|
|
320
|
+
const stringKeys = ['configPath', 'cwd']
|
|
321
|
+
/** @type {('disableNestedConfig' | 'respectOxfmtDefaultIgnores' | 'useConfig' | 'useCache' | 'withNodeModules')[]} */
|
|
322
|
+
const booleanKeys = [
|
|
323
|
+
'disableNestedConfig',
|
|
324
|
+
'respectOxfmtDefaultIgnores',
|
|
325
|
+
'useCache',
|
|
326
|
+
'useConfig',
|
|
327
|
+
'withNodeModules',
|
|
328
|
+
]
|
|
329
|
+
|
|
330
|
+
for (const key of stringKeys) {
|
|
331
|
+
const value = pluginOptions[key]
|
|
332
|
+
if (value != null && typeof value !== 'string') {
|
|
333
|
+
throw new TypeError(
|
|
334
|
+
`oxfmt worker requires "${key}" to be a string when provided.`,
|
|
335
|
+
)
|
|
492
336
|
}
|
|
337
|
+
}
|
|
493
338
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
effectiveOverrides,
|
|
339
|
+
for (const key of booleanKeys) {
|
|
340
|
+
const value = pluginOptions[key]
|
|
341
|
+
if (value != null && typeof value !== 'boolean') {
|
|
342
|
+
throw new TypeError(
|
|
343
|
+
`oxfmt worker requires "${key}" to be a boolean when provided.`,
|
|
500
344
|
)
|
|
501
345
|
}
|
|
346
|
+
}
|
|
502
347
|
|
|
503
|
-
|
|
504
|
-
|
|
348
|
+
if (
|
|
349
|
+
pluginOptions.ignorePath != null &&
|
|
350
|
+
typeof pluginOptions.ignorePath !== 'string' &&
|
|
351
|
+
!isStringArray(pluginOptions.ignorePath)
|
|
352
|
+
) {
|
|
353
|
+
throw new TypeError(
|
|
354
|
+
'oxfmt worker requires "ignorePath" to be a string or string array when provided.',
|
|
355
|
+
)
|
|
356
|
+
}
|
|
357
|
+
}
|
|
505
358
|
|
|
506
|
-
|
|
507
|
-
},
|
|
508
|
-
)
|
|
359
|
+
runAsWorker(formatViaOxfmt)
|