eslint-plugin-oxfmt 0.26.0 → 0.28.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 +75 -16
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +86 -1
- package/dts/rule-options.d.ts +27 -0
- package/package.json +12 -7
- package/workers/oxfmt.mjs +141 -6
package/README.md
CHANGED
|
@@ -24,22 +24,18 @@ Maintained in the [eslint-plugin-oxfmt monorepo](https://github.com/ntnyq/eslint
|
|
|
24
24
|
|
|
25
25
|
- **ESLint**: `^9.5.0 || ^10.0.0` (Only supports ESLint flat config)
|
|
26
26
|
- **Node.js**: `^22.13.0 || >=24`
|
|
27
|
-
- **oxfmt**: `>= 0.
|
|
28
|
-
|
|
29
|
-
Upgrading from oxfmt 0.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
formatting API; Markdown still uses Prettier, including frontmatter formatting.
|
|
40
|
-
The upstream fix for repeated CLI calls in one process does not affect this
|
|
41
|
-
plugin's worker, which uses the formatting API.
|
|
42
|
-
See the upstream [0.71.0 release notes](https://github.com/oxc-project/oxc/releases/tag/oxfmt_v0.71.0).
|
|
27
|
+
- **oxfmt**: `>= 0.73.0`
|
|
28
|
+
|
|
29
|
+
Upgrading from oxfmt 0.72.0 to 0.73.0 adds language `associations` and opt-in
|
|
30
|
+
Astro formatting. Install `@astrojs/compiler-rs` to use `astro: true` or an Astro
|
|
31
|
+
options object. No existing options were renamed or removed.
|
|
32
|
+
|
|
33
|
+
TOML now supports TOML 1.1 syntax and formats TOML frontmatter and Markdown code
|
|
34
|
+
fences. Review these outputs after upgrading. Upstream also fixes CSS-in-JS
|
|
35
|
+
interpolations, TypeScript instantiation parentheses, export wrapping, JSDoc
|
|
36
|
+
paragraphs, and nested Svelte embeds. Config merge precedence is unchanged, and
|
|
37
|
+
the loader already follows symlinked config files.
|
|
38
|
+
See the upstream [0.73.0 release notes](https://github.com/oxc-project/oxc/releases/tag/oxfmt_v0.73.0).
|
|
43
39
|
|
|
44
40
|
The monorepo migration aligns the Node.js requirement with `load-oxfmt-config`; Node.js 20 is no longer supported.
|
|
45
41
|
|
|
@@ -322,6 +318,7 @@ Tip: `jsdoc: true` is equivalent to enabling JSDoc with default settings.
|
|
|
322
318
|
| `sortImports` | `boolean \| object` | disabled | Experimental import sorting configuration |
|
|
323
319
|
| `sortPackageJson` | `boolean \| object` | `true` | Experimental package.json sorting (object form: `{ sortScripts?: boolean }`) |
|
|
324
320
|
| `sortTailwindcss` | `boolean \| object` | disabled | Experimental Tailwind CSS class sorting (enable with `{}` for defaults) |
|
|
321
|
+
| `astro` | `boolean \| object` | disabled | Enable/configure Astro formatting; requires `@astrojs/compiler-rs` |
|
|
325
322
|
| `svelte` | `boolean \| object` | disabled | Enable/configure `.svelte` formatting via `prettier-plugin-svelte` |
|
|
326
323
|
|
|
327
324
|
#### Import sorting (`sortImports`)
|
|
@@ -386,6 +383,68 @@ You can pass the Tailwind plugin options to control which attributes/functions a
|
|
|
386
383
|
|
|
387
384
|
Paths in `sortTailwindcss.config` and `sortTailwindcss.stylesheet` are resolved relative to the oxfmt config file when set in that file or its overrides. Paths set in ESLint rule options or rule-level overrides are resolved relative to ESLint `cwd`. Absolute paths are used directly.
|
|
388
385
|
|
|
386
|
+
## Language associations
|
|
387
|
+
|
|
388
|
+
Use `associations` in your oxfmt config or inline rule options to format files
|
|
389
|
+
as a language regardless of their extension:
|
|
390
|
+
|
|
391
|
+
```js
|
|
392
|
+
// eslint.config.mjs
|
|
393
|
+
import pluginOxfmt from 'eslint-plugin-oxfmt'
|
|
394
|
+
|
|
395
|
+
export default [
|
|
396
|
+
{
|
|
397
|
+
...pluginOxfmt.configs.recommended,
|
|
398
|
+
files: ['**/*.{html,wxml,conf}'],
|
|
399
|
+
rules: {
|
|
400
|
+
'oxfmt/oxfmt': [
|
|
401
|
+
'error',
|
|
402
|
+
{
|
|
403
|
+
associations: [
|
|
404
|
+
{
|
|
405
|
+
files: ['**/*.html'],
|
|
406
|
+
excludeFiles: ['index.html'],
|
|
407
|
+
language: 'angular-html',
|
|
408
|
+
},
|
|
409
|
+
{ files: ['**/*.wxml'], language: 'html' },
|
|
410
|
+
{ files: ['**/*.conf'], language: 'json' },
|
|
411
|
+
],
|
|
412
|
+
},
|
|
413
|
+
],
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
]
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
Each entry requires `files: string[]` and `language`; `excludeFiles: string[]`
|
|
420
|
+
is optional. Later matching entries win. Languages are `javascript`, `jsx`,
|
|
421
|
+
`typescript`, `tsx`, `json`, `jsonc`, `json5`, `css`, `scss`, `less`, `graphql`,
|
|
422
|
+
`yaml`, `markdown`, `toml`, `html`, `angular-html`, `vue`, `svelte`, `astro`,
|
|
423
|
+
`handlebars`, and `mdx`.
|
|
424
|
+
|
|
425
|
+
Config associations match paths relative to the resolved config directory.
|
|
426
|
+
Inline associations match relative to ESLint `cwd` and replace config
|
|
427
|
+
associations entirely; `associations: []` clears them. `useConfig: false` keeps
|
|
428
|
+
inline associations active. Ignores, EditorConfig, and overrides use the original
|
|
429
|
+
file path. Upstream's excluded lockfiles remain excluded. Associations do not
|
|
430
|
+
change ESLint file discovery or enable optional formatters automatically.
|
|
431
|
+
|
|
432
|
+
### Astro options
|
|
433
|
+
|
|
434
|
+
Install `@astrojs/compiler-rs` alongside this plugin, then set `astro: true`
|
|
435
|
+
or an object. The compiler is an optional peer dependency.
|
|
436
|
+
|
|
437
|
+
| Option | Type | Default | Description |
|
|
438
|
+
| ----------------------- | --------------------------- | ------- | -------------------------------------------------------------------------------------------------------- |
|
|
439
|
+
| `astro.allowShorthand` | `boolean` | unset | Normalize matching identifier attributes to shorthand or explicit form; unset preserves the written form |
|
|
440
|
+
| `astro.compressHTML` | `'jsx' \| 'html' \| 'none'` | `'jsx'` | Match Astro compiler whitespace collapsing |
|
|
441
|
+
| `astro.skipFrontmatter` | `boolean` | `false` | Preserve frontmatter without formatting |
|
|
442
|
+
|
|
443
|
+
`astro: false` disables Astro formatting, including in overrides. `astro: true`
|
|
444
|
+
resets inherited Astro options to defaults. With
|
|
445
|
+
`embeddedLanguageFormatting: 'off'`, Astro frontmatter is still formatted by
|
|
446
|
+
Prettier; use `astro.skipFrontmatter` to preserve it.
|
|
447
|
+
|
|
389
448
|
## File-specific overrides
|
|
390
449
|
|
|
391
450
|
Use `overrides` to apply different oxfmt options per file glob (later entries win on conflicts):
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Linter, Rule
|
|
1
|
+
import { Linter, Rule } from "eslint";
|
|
2
2
|
import "load-oxfmt-config";
|
|
3
3
|
import "oxfmt";
|
|
4
4
|
//#region src/types.d.ts
|
|
@@ -13,7 +13,7 @@ interface PluginOxfmt {
|
|
|
13
13
|
version: string;
|
|
14
14
|
};
|
|
15
15
|
rules: {
|
|
16
|
-
oxfmt: Rule
|
|
16
|
+
oxfmt: Rule.RuleModule;
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
//#endregion
|
package/dist/index.mjs
CHANGED
|
@@ -80,7 +80,7 @@ const configs = {
|
|
|
80
80
|
//#region src/meta.ts
|
|
81
81
|
const meta = {
|
|
82
82
|
name: "eslint-plugin-oxfmt",
|
|
83
|
-
version: "0.
|
|
83
|
+
version: "0.28.0"
|
|
84
84
|
};
|
|
85
85
|
//#endregion
|
|
86
86
|
//#region src/dir.ts
|
|
@@ -164,6 +164,43 @@ const oxfmtOptionsSchema = {
|
|
|
164
164
|
enum: ["always", "avoid"],
|
|
165
165
|
type: "string"
|
|
166
166
|
},
|
|
167
|
+
astro: {
|
|
168
|
+
anyOf: [{ type: "boolean" }, {
|
|
169
|
+
additionalProperties: false,
|
|
170
|
+
type: "object",
|
|
171
|
+
properties: {
|
|
172
|
+
allowShorthand: {
|
|
173
|
+
type: "boolean",
|
|
174
|
+
description: "Whether to normalize matching identifier attributes to shorthand or explicit form.\nWhen unset, the form that was written stays as-is.\n\n- Default: Unset"
|
|
175
|
+
},
|
|
176
|
+
compressHTML: {
|
|
177
|
+
description: "Mirror of Astro's `compressHTML` config.\nTells the formatter which whitespace the compiler will collapse.\n\n- Default: `\"jsx\"`",
|
|
178
|
+
oneOf: [
|
|
179
|
+
{
|
|
180
|
+
enum: ["jsx"],
|
|
181
|
+
type: "string",
|
|
182
|
+
description: "Whitespace runs containing a newline are dropped; same-line spaces are content."
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
description: "Browser HTML whitespace rules.",
|
|
186
|
+
enum: ["html"],
|
|
187
|
+
type: "string"
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
description: "No collapsing.",
|
|
191
|
+
enum: ["none"],
|
|
192
|
+
type: "string"
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
},
|
|
196
|
+
skipFrontmatter: {
|
|
197
|
+
type: "boolean",
|
|
198
|
+
description: "Whether to skip formatting the frontmatter.\n\n- Default: `false`"
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}],
|
|
202
|
+
description: "Options for `prettier-plugin-astro`.\n\nPass `true` or an object to enable `.astro` file formatting, `false` to disable (handy in overrides).\nSetting `true` resets to defaults, dropping options inherited from a parent scope.\n\nNOTE: `prettier-plugin-astro` requires the `@astrojs/compiler-rs` package at runtime.\nOxfmt does NOT bundle it, so install it in your project, otherwise formatting fails.\n\n- Languages: Astro\n- Default: Disabled"
|
|
203
|
+
},
|
|
167
204
|
bracketSameLine: {
|
|
168
205
|
description: `Put the > of a multi-line JSX element at the end of the last line\ninstead of being alone on the next line. (Default: false)`,
|
|
169
206
|
type: "boolean"
|
|
@@ -550,6 +587,54 @@ const oxfmtRuleSchema = {
|
|
|
550
587
|
properties: {
|
|
551
588
|
...oxfmtOptionsSchema.properties,
|
|
552
589
|
...oxfmtConfigSchema.properties,
|
|
590
|
+
associations: {
|
|
591
|
+
type: "array",
|
|
592
|
+
description: "Format matched files as the given language, instead of detecting it from the file name.\ne.g. Angular templates named `*.html`, or `*.wxml` files formatted as HTML.\nWhen a file matches multiple associations, the later one takes precedence (array order matters).\n\n- Default: `[]`",
|
|
593
|
+
items: {
|
|
594
|
+
additionalProperties: false,
|
|
595
|
+
required: ["files", "language"],
|
|
596
|
+
type: "object",
|
|
597
|
+
properties: {
|
|
598
|
+
excludeFiles: {
|
|
599
|
+
type: "array",
|
|
600
|
+
description: "Glob patterns excluded from this association, relative to the config directory or ESLint cwd for inline associations.",
|
|
601
|
+
items: { type: "string" }
|
|
602
|
+
},
|
|
603
|
+
files: {
|
|
604
|
+
type: "array",
|
|
605
|
+
description: "Glob patterns matched relative to the config directory, or ESLint cwd for inline associations.",
|
|
606
|
+
items: { type: "string" }
|
|
607
|
+
},
|
|
608
|
+
language: {
|
|
609
|
+
type: "string",
|
|
610
|
+
description: "The language to format matched files as, named by its Shiki id (as in Markdown code fences).",
|
|
611
|
+
enum: [
|
|
612
|
+
"javascript",
|
|
613
|
+
"jsx",
|
|
614
|
+
"typescript",
|
|
615
|
+
"tsx",
|
|
616
|
+
"json",
|
|
617
|
+
"jsonc",
|
|
618
|
+
"json5",
|
|
619
|
+
"css",
|
|
620
|
+
"scss",
|
|
621
|
+
"less",
|
|
622
|
+
"graphql",
|
|
623
|
+
"yaml",
|
|
624
|
+
"markdown",
|
|
625
|
+
"toml",
|
|
626
|
+
"html",
|
|
627
|
+
"angular-html",
|
|
628
|
+
"vue",
|
|
629
|
+
"svelte",
|
|
630
|
+
"astro",
|
|
631
|
+
"handlebars",
|
|
632
|
+
"mdx"
|
|
633
|
+
]
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
},
|
|
553
638
|
overrides: {
|
|
554
639
|
description: `File-specific overrides.\nWhen a file matches multiple overrides, the later override takes precedence (array order matters).\n\n- (Default: [])`,
|
|
555
640
|
type: "array",
|
package/dts/rule-options.d.ts
CHANGED
|
@@ -16,6 +16,15 @@ export type OxfmtOxfmt = []|[{
|
|
|
16
16
|
|
|
17
17
|
arrowParens?: ("always" | "avoid")
|
|
18
18
|
|
|
19
|
+
astro?: (boolean | {
|
|
20
|
+
|
|
21
|
+
allowShorthand?: boolean
|
|
22
|
+
|
|
23
|
+
compressHTML?: ("jsx" | "html" | "none")
|
|
24
|
+
|
|
25
|
+
skipFrontmatter?: boolean
|
|
26
|
+
})
|
|
27
|
+
|
|
19
28
|
bracketSameLine?: boolean
|
|
20
29
|
|
|
21
30
|
bracketSpacing?: boolean
|
|
@@ -162,6 +171,15 @@ export type OxfmtOxfmt = []|[{
|
|
|
162
171
|
|
|
163
172
|
withNodeModules?: boolean
|
|
164
173
|
|
|
174
|
+
associations?: {
|
|
175
|
+
|
|
176
|
+
excludeFiles?: string[]
|
|
177
|
+
|
|
178
|
+
files: string[]
|
|
179
|
+
|
|
180
|
+
language: ("javascript" | "jsx" | "typescript" | "tsx" | "json" | "jsonc" | "json5" | "css" | "scss" | "less" | "graphql" | "yaml" | "markdown" | "toml" | "html" | "angular-html" | "vue" | "svelte" | "astro" | "handlebars" | "mdx")
|
|
181
|
+
}[]
|
|
182
|
+
|
|
165
183
|
overrides?: {
|
|
166
184
|
|
|
167
185
|
excludeFiles?: string[]
|
|
@@ -172,6 +190,15 @@ export type OxfmtOxfmt = []|[{
|
|
|
172
190
|
|
|
173
191
|
arrowParens?: ("always" | "avoid")
|
|
174
192
|
|
|
193
|
+
astro?: (boolean | {
|
|
194
|
+
|
|
195
|
+
allowShorthand?: boolean
|
|
196
|
+
|
|
197
|
+
compressHTML?: ("jsx" | "html" | "none")
|
|
198
|
+
|
|
199
|
+
skipFrontmatter?: boolean
|
|
200
|
+
})
|
|
201
|
+
|
|
175
202
|
bracketSameLine?: boolean
|
|
176
203
|
|
|
177
204
|
bracketSpacing?: boolean
|
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.28.0",
|
|
5
5
|
"description": "An ESLint plugin for formatting code with oxfmt.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"eslint",
|
|
@@ -47,31 +47,36 @@
|
|
|
47
47
|
},
|
|
48
48
|
"sideEffects": false,
|
|
49
49
|
"peerDependencies": {
|
|
50
|
+
"@astrojs/compiler-rs": "*",
|
|
50
51
|
"eslint": "^9.5.0 || ^10.0.0",
|
|
51
|
-
"oxfmt": ">=0.
|
|
52
|
+
"oxfmt": ">=0.73.0",
|
|
52
53
|
"svelte": "*"
|
|
53
54
|
},
|
|
54
55
|
"peerDependenciesMeta": {
|
|
56
|
+
"@astrojs/compiler-rs": {
|
|
57
|
+
"optional": true
|
|
58
|
+
},
|
|
55
59
|
"svelte": {
|
|
56
60
|
"optional": true
|
|
57
61
|
}
|
|
58
62
|
},
|
|
59
63
|
"dependencies": {
|
|
60
64
|
"generate-differences": "^0.1.1",
|
|
61
|
-
"ignore": "^7.0.
|
|
62
|
-
"load-oxfmt-config": "^0.
|
|
65
|
+
"ignore": "^7.0.12",
|
|
66
|
+
"load-oxfmt-config": "^0.28.0",
|
|
63
67
|
"picomatch": "^4.0.7",
|
|
64
|
-
"synckit": "^0.
|
|
68
|
+
"synckit": "^0.12.3"
|
|
65
69
|
},
|
|
66
70
|
"devDependencies": {
|
|
71
|
+
"@astrojs/compiler-rs": "^0.5.1",
|
|
67
72
|
"@types/json-schema": "^7.0.15",
|
|
68
73
|
"eslint-parser-plain": "^0.1.1",
|
|
69
|
-
"eslint-plugin-oxfmt": "0.
|
|
74
|
+
"eslint-plugin-oxfmt": "0.28.0",
|
|
70
75
|
"eslint-typegen": "^2.3.1",
|
|
71
76
|
"eslint-vitest-rule-tester": "^3.2.0",
|
|
72
77
|
"jsonc-eslint-parser": "^3.3.0",
|
|
73
78
|
"show-invisibles": "^0.0.2",
|
|
74
|
-
"svelte": "^5.57.
|
|
79
|
+
"svelte": "^5.57.2",
|
|
75
80
|
"tinyglobby": "^0.2.17"
|
|
76
81
|
},
|
|
77
82
|
"engines": {
|
package/workers/oxfmt.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
-
import { dirname, extname, relative, resolve } from 'node:path'
|
|
3
|
+
import { basename, dirname, extname, join, relative, resolve } from 'node:path'
|
|
4
4
|
import ignore from 'ignore'
|
|
5
5
|
import { isOxfmtIgnored, loadOxfmtConfigForFile } from 'load-oxfmt-config'
|
|
6
6
|
import { format } from 'oxfmt'
|
|
@@ -96,6 +96,59 @@ const overrideMatcherCache = new Map()
|
|
|
96
96
|
*/
|
|
97
97
|
const ignoreMatcherCache = new Map()
|
|
98
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Filename suffixes selecting the same formatter as upstream Language routes.
|
|
101
|
+
* The format() API does not accept a language, unlike the CLI.
|
|
102
|
+
* @type {Record<import('oxfmt').Language, string>}
|
|
103
|
+
*/
|
|
104
|
+
const LANGUAGE_SUFFIXES = {
|
|
105
|
+
'angular-html': 'component.html',
|
|
106
|
+
astro: 'astro',
|
|
107
|
+
css: 'css',
|
|
108
|
+
graphql: 'graphql',
|
|
109
|
+
handlebars: 'hbs',
|
|
110
|
+
html: 'html',
|
|
111
|
+
javascript: 'js',
|
|
112
|
+
json: 'json',
|
|
113
|
+
json5: 'json5',
|
|
114
|
+
jsonc: 'jsonc',
|
|
115
|
+
jsx: 'jsx',
|
|
116
|
+
less: 'less',
|
|
117
|
+
markdown: 'md',
|
|
118
|
+
mdx: 'mdx',
|
|
119
|
+
scss: 'scss',
|
|
120
|
+
svelte: 'svelte',
|
|
121
|
+
toml: 'toml',
|
|
122
|
+
tsx: 'tsx',
|
|
123
|
+
typescript: 'ts',
|
|
124
|
+
vue: 'vue',
|
|
125
|
+
yaml: 'yaml',
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Upstream classify_as excludes these even when associations explicitly match.
|
|
130
|
+
* Keep this guard before substituting a formatter filename.
|
|
131
|
+
* @type {Set<string>}
|
|
132
|
+
*/
|
|
133
|
+
const ASSOCIATION_EXCLUDED_FILENAMES = new Set([
|
|
134
|
+
'bun.lock',
|
|
135
|
+
'Cargo.lock',
|
|
136
|
+
'composer.lock',
|
|
137
|
+
'deno.lock',
|
|
138
|
+
'flake.lock',
|
|
139
|
+
'Gopkg.lock',
|
|
140
|
+
'mcmod.info',
|
|
141
|
+
'MODULE.bazel.lock',
|
|
142
|
+
'package-lock.json',
|
|
143
|
+
'Package.resolved',
|
|
144
|
+
'pdm.lock',
|
|
145
|
+
'Pipfile.lock',
|
|
146
|
+
'pnpm-lock.yaml',
|
|
147
|
+
'poetry.lock',
|
|
148
|
+
'uv.lock',
|
|
149
|
+
'yarn.lock',
|
|
150
|
+
])
|
|
151
|
+
|
|
99
152
|
/**
|
|
100
153
|
* Apply override entries to a base options object.
|
|
101
154
|
* @param {string} relativePath - Relative file path used for matching.
|
|
@@ -215,8 +268,13 @@ async function formatViaOxfmt(filename, sourceText, options = {}) {
|
|
|
215
268
|
* Inline overrides separated from root options to preserve merge order.
|
|
216
269
|
* @type {OxfmtOptions}
|
|
217
270
|
*/
|
|
218
|
-
const {
|
|
219
|
-
|
|
271
|
+
const {
|
|
272
|
+
associations: ruleAssociations,
|
|
273
|
+
overrides: ruleOverrides,
|
|
274
|
+
...inlineOptionsWithoutOverrides
|
|
275
|
+
} = inlineFormatOptions
|
|
276
|
+
let associations = ruleAssociations
|
|
277
|
+
let associationBaseDir = inlineBaseDir
|
|
220
278
|
/**
|
|
221
279
|
* Effective formatter options with paths resolved against their source.
|
|
222
280
|
* @type {import('oxfmt').FormatConfig}
|
|
@@ -235,8 +293,16 @@ async function formatViaOxfmt(filename, sourceText, options = {}) {
|
|
|
235
293
|
filepath: filename,
|
|
236
294
|
useCache,
|
|
237
295
|
})
|
|
238
|
-
const {
|
|
296
|
+
const {
|
|
297
|
+
associations: configAssociations,
|
|
298
|
+
overrides: configOverrides,
|
|
299
|
+
...loadedConfig
|
|
300
|
+
} = loaded.config
|
|
239
301
|
overrideBaseDir = loaded.dirname ?? overrideBaseDir
|
|
302
|
+
if (ruleAssociations === undefined) {
|
|
303
|
+
associations = configAssociations
|
|
304
|
+
associationBaseDir = overrideBaseDir
|
|
305
|
+
}
|
|
240
306
|
|
|
241
307
|
finalOptions = {
|
|
242
308
|
...resolveTailwindPaths(loadedConfig, overrideBaseDir),
|
|
@@ -258,7 +324,27 @@ async function formatViaOxfmt(filename, sourceText, options = {}) {
|
|
|
258
324
|
inlineBaseDir,
|
|
259
325
|
)
|
|
260
326
|
|
|
261
|
-
|
|
327
|
+
const formatFilename = resolveAssociationFilename(
|
|
328
|
+
filename,
|
|
329
|
+
associations,
|
|
330
|
+
associationBaseDir,
|
|
331
|
+
)
|
|
332
|
+
if (
|
|
333
|
+
formatFilename !== filename &&
|
|
334
|
+
ASSOCIATION_EXCLUDED_FILENAMES.has(basename(filename))
|
|
335
|
+
) {
|
|
336
|
+
return {
|
|
337
|
+
code: sourceText,
|
|
338
|
+
ignored: true,
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
const result = await format(formatFilename, sourceText, finalOptions)
|
|
342
|
+
if (formatFilename !== filename) {
|
|
343
|
+
for (const error of result.errors) {
|
|
344
|
+
error.message = error.message.replaceAll(formatFilename, () => filename)
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return result
|
|
262
348
|
}
|
|
263
349
|
|
|
264
350
|
/**
|
|
@@ -354,6 +440,52 @@ function isStringArray(value) {
|
|
|
354
440
|
return Array.isArray(value) && value.every(item => typeof item === 'string')
|
|
355
441
|
}
|
|
356
442
|
|
|
443
|
+
/**
|
|
444
|
+
* Resolve the last matching association, validating entries even if unmatched.
|
|
445
|
+
* @param {string} filename - Original file path.
|
|
446
|
+
* @param {OxfmtOptions['associations']} associations - Ordered associations.
|
|
447
|
+
* @param {string} baseDir - Directory that owns the association patterns.
|
|
448
|
+
* @returns {string} Filename used only for selecting the formatter.
|
|
449
|
+
*/
|
|
450
|
+
function resolveAssociationFilename(filename, associations, baseDir) {
|
|
451
|
+
if (associations === undefined) {
|
|
452
|
+
return filename
|
|
453
|
+
}
|
|
454
|
+
if (!Array.isArray(associations)) {
|
|
455
|
+
throw new TypeError('oxfmt associations must be an array.')
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const relativePath = getRelativePath(baseDir, filename)
|
|
459
|
+
let formatFilename = filename
|
|
460
|
+
for (const association of associations) {
|
|
461
|
+
if (
|
|
462
|
+
!association ||
|
|
463
|
+
!isStringArray(association.files) ||
|
|
464
|
+
(association.excludeFiles !== undefined &&
|
|
465
|
+
!isStringArray(association.excludeFiles)) ||
|
|
466
|
+
!Object.hasOwn(LANGUAGE_SUFFIXES, association.language)
|
|
467
|
+
) {
|
|
468
|
+
throw new TypeError(
|
|
469
|
+
'Invalid oxfmt association: expected files, optional excludeFiles, and a supported language.',
|
|
470
|
+
)
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const matches = getCachedOverrideMatcher(association.files)(relativePath)
|
|
474
|
+
const excluded = association.excludeFiles
|
|
475
|
+
? getCachedOverrideMatcher(association.excludeFiles)(relativePath)
|
|
476
|
+
: false
|
|
477
|
+
if (matches && !excluded) {
|
|
478
|
+
// Use a neutral basename to avoid package.json sorting and .component.html
|
|
479
|
+
// detection overriding the explicitly selected language.
|
|
480
|
+
formatFilename = join(
|
|
481
|
+
dirname(filename),
|
|
482
|
+
`__oxfmt__.${LANGUAGE_SUFFIXES[association.language]}`,
|
|
483
|
+
)
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return formatFilename
|
|
487
|
+
}
|
|
488
|
+
|
|
357
489
|
/**
|
|
358
490
|
* Resolve Tailwind paths before merging options from different sources.
|
|
359
491
|
* @param {import('oxfmt').FormatConfig} options - Format options to normalize.
|
|
@@ -378,7 +510,10 @@ function resolveTailwindPaths(options, baseDir) {
|
|
|
378
510
|
resolved.stylesheet = resolve(baseDir, resolved.stylesheet)
|
|
379
511
|
}
|
|
380
512
|
|
|
381
|
-
return {
|
|
513
|
+
return {
|
|
514
|
+
...options,
|
|
515
|
+
sortTailwindcss: resolved,
|
|
516
|
+
}
|
|
382
517
|
}
|
|
383
518
|
|
|
384
519
|
/**
|