load-oxfmt-config 0.4.0 → 0.5.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
@@ -12,6 +12,7 @@
12
12
  - 🔍 **Auto-discovery** - Automatically searches for config files in current and parent directories
13
13
  - 📦 **Multiple formats** - Supports `.oxfmtrc.json`, `.oxfmtrc.jsonc`, and `oxfmt.config.ts`
14
14
  - 🧩 **EditorConfig fallback** - Merges supported `.editorconfig` fields into the returned oxfmt config result
15
+ - 🚫 **Ignore resolution** - Resolves ignore status with oxfmt CLI-like global + config-scoped semantics
15
16
  - ⚡ **Built-in caching** - Caches both file resolution and parsed configs for optimal performance
16
17
  - 🎯 **TypeScript support** - Fully typed with comprehensive type definitions
17
18
  - 🛠️ **Flexible API** - Support explicit config paths or automatic discovery
@@ -37,22 +38,22 @@ pnpm add load-oxfmt-config
37
38
  Load config from current directory or parent directories:
38
39
 
39
40
  ```ts
40
- import { loadOxfmtConfig } from 'load-oxfmt-config'
41
+ import { loadOxfmtConfigResult } from 'load-oxfmt-config'
41
42
 
42
43
  // Automatically searches for oxfmt config files and the nearest .editorconfig
43
- const config = await loadOxfmtConfig()
44
- console.log(config) // { printWidth: 80, ... }
44
+ const result = await loadOxfmtConfigResult()
45
+ console.log(result.config) // { printWidth: 80, ... }
45
46
  ```
46
47
 
47
48
  ### Merge With `.editorconfig`
48
49
 
49
50
  ```ts
50
- import { loadOxfmtConfig } from 'load-oxfmt-config'
51
+ import { loadOxfmtConfigResult } from 'load-oxfmt-config'
51
52
 
52
- const config = await loadOxfmtConfig({ cwd: '/path/to/project' })
53
+ const result = await loadOxfmtConfigResult({ cwd: '/path/to/project' })
53
54
 
54
55
  // Returns one merged static config object
55
- console.log(config)
56
+ console.log(result.config)
56
57
  // {
57
58
  // tabWidth: 2,
58
59
  // printWidth: 100,
@@ -65,26 +66,52 @@ console.log(config)
65
66
  ### Specify Working Directory
66
67
 
67
68
  ```ts
68
- import { loadOxfmtConfig } from 'load-oxfmtConfig'
69
+ import { loadOxfmtConfigResult } from 'load-oxfmt-config'
69
70
 
70
- const config = await loadOxfmtConfig({
71
+ const result = await loadOxfmtConfigResult({
71
72
  cwd: '/path/to/project',
72
73
  })
73
74
  ```
74
75
 
76
+ ### Get Config Metadata
77
+
78
+ ```ts
79
+ import { loadOxfmtConfigResult } from 'load-oxfmt-config'
80
+
81
+ const result = await loadOxfmtConfigResult({ cwd: '/path/to/project' })
82
+
83
+ console.log(result.config) // merged oxfmt options
84
+ console.log(result.filepath) // /path/to/project/.oxfmtrc.json (or undefined)
85
+ console.log(result.dirname) // /path/to/project (or undefined)
86
+ ```
87
+
88
+ ### Resolve Ignore Status
89
+
90
+ ```ts
91
+ import { isOxfmtIgnored } from 'load-oxfmt-config'
92
+
93
+ const result = await isOxfmtIgnored({
94
+ cwd: '/path/to/project',
95
+ filepath: '/path/to/project/src/generated/foo.ts',
96
+ })
97
+
98
+ console.log(result)
99
+ // { ignored: true, reason: 'config-ignore-patterns' }
100
+ ```
101
+
75
102
  ### Explicit Config Path
76
103
 
77
104
  ```ts
78
- import { loadOxfmtConfig } from 'load-oxfmt-config'
105
+ import { loadOxfmtConfigResult } from 'load-oxfmt-config'
79
106
 
80
107
  // Relative path (resolved relative to cwd)
81
- const config = await loadOxfmtConfig({
108
+ const result = await loadOxfmtConfigResult({
82
109
  configPath: 'configs/.oxfmtrc.json',
83
110
  cwd: '/path/to/project',
84
111
  })
85
112
 
86
113
  // Absolute path
87
- const config = await loadOxfmtConfig({
114
+ const absoluteResult = await loadOxfmtConfigResult({
88
115
  configPath: '/absolute/path/to/.oxfmtrc.json',
89
116
  })
90
117
  ```
@@ -92,10 +119,10 @@ const config = await loadOxfmtConfig({
92
119
  ### Disable `.editorconfig`
93
120
 
94
121
  ```ts
95
- import { loadOxfmtConfig } from 'load-oxfmt-config'
122
+ import { loadOxfmtConfigResult } from 'load-oxfmt-config'
96
123
 
97
124
  // Skip .editorconfig reading entirely
98
- const config = await loadOxfmtConfig({
125
+ const result = await loadOxfmtConfigResult({
99
126
  editorconfig: false,
100
127
  })
101
128
  ```
@@ -103,10 +130,10 @@ const config = await loadOxfmtConfig({
103
130
  ### Limit `.editorconfig` to `cwd`
104
131
 
105
132
  ```ts
106
- import { loadOxfmtConfig } from 'load-oxfmt-config'
133
+ import { loadOxfmtConfigResult } from 'load-oxfmt-config'
107
134
 
108
135
  // Only look in the cwd directory itself, no upward traversal
109
- const config = await loadOxfmtConfig({
136
+ const result = await loadOxfmtConfigResult({
110
137
  editorconfig: { onlyCwd: true },
111
138
  })
112
139
  ```
@@ -114,10 +141,10 @@ const config = await loadOxfmtConfig({
114
141
  ### Override `.editorconfig` Search Directory
115
142
 
116
143
  ```ts
117
- import { loadOxfmtConfig } from 'load-oxfmt-config'
144
+ import { loadOxfmtConfigResult } from 'load-oxfmt-config'
118
145
 
119
146
  // Search .editorconfig from a custom directory instead of the config file's directory
120
- const config = await loadOxfmtConfig({
147
+ const result = await loadOxfmtConfigResult({
121
148
  editorconfig: {
122
149
  cwd: '/path/to/editorconfig-dir',
123
150
  },
@@ -127,14 +154,23 @@ const config = await loadOxfmtConfig({
127
154
  ### Disable Caching
128
155
 
129
156
  ```ts
130
- import { loadOxfmtConfig } from 'load-oxfmt-config'
157
+ import { loadOxfmtConfigResult } from 'load-oxfmt-config'
131
158
 
132
159
  // Force reload from disk, bypassing cache
133
- const config = await loadOxfmtConfig({
160
+ const result = await loadOxfmtConfigResult({
134
161
  useCache: false,
135
162
  })
136
163
  ```
137
164
 
165
+ ### Legacy API (Deprecated)
166
+
167
+ ```ts
168
+ import { loadOxfmtConfig } from 'load-oxfmt-config'
169
+
170
+ // Deprecated: prefer loadOxfmtConfigResult
171
+ const config = await loadOxfmtConfig({ cwd: '/path/to/project' })
172
+ ```
173
+
138
174
  ### Path Resolution Only
139
175
 
140
176
  ```ts
@@ -149,13 +185,29 @@ console.log(configPath) // '/path/to/.oxfmtrc.json' or undefined
149
185
 
150
186
  ### `loadOxfmtConfig(options?)`
151
187
 
188
+ > Deprecated. Prefer `loadOxfmtConfigResult(options?)`.
189
+
152
190
  Load and parse oxfmt configuration files, then merge supported `.editorconfig` fields into the returned result.
153
191
 
154
192
  **Parameters:**
155
193
 
156
194
  - `options` - Optional configuration object
157
195
 
158
- **Returns:** `Promise<FormatOptions>` - Parsed and merged oxfmt configuration object. Returns empty object `{}` if no supported config file is found.
196
+ **Returns:** `Promise<OxfmtOptions>` - Parsed and merged oxfmt configuration object. Returns empty object `{}` if no supported config file is found.
197
+
198
+ ### `loadOxfmtConfigResult(options?)`
199
+
200
+ Load and parse oxfmt configuration files, merge supported `.editorconfig` fields, and return metadata for the resolved config file.
201
+
202
+ **Parameters:**
203
+
204
+ - `options` - Optional configuration object (same as `loadOxfmtConfig`)
205
+
206
+ **Returns:** `Promise<LoadOxfmtConfigResult>`
207
+
208
+ - `config` - Parsed and merged oxfmt configuration object
209
+ - `filepath` - Resolved config absolute path (undefined when not found)
210
+ - `dirname` - Config directory (undefined when not found)
159
211
 
160
212
  **Throws:** Error if config file exists but cannot be parsed.
161
213
 
@@ -170,6 +222,23 @@ Resolve the absolute path to oxfmt config file.
170
222
 
171
223
  **Returns:** `Promise<string | undefined>` - Absolute path to config file, or `undefined` if not found.
172
224
 
225
+ ### `isOxfmtIgnored(options)`
226
+
227
+ Resolve whether a file should be ignored with oxfmt CLI-like semantics.
228
+
229
+ **Returns:** `Promise<IsOxfmtIgnoredResult>`
230
+
231
+ `ignorePath` accepts either a single path (`string`) or multiple paths (`string[]`).
232
+
233
+ - `ignored` - Whether the file is ignored
234
+ - `reason` - One of:
235
+ - `default-dir`
236
+ - `lockfile`
237
+ - `gitignore`
238
+ - `prettierignore`
239
+ - `ignore-path`
240
+ - `config-ignore-patterns`
241
+
173
242
  ## Options
174
243
 
175
244
  All options are optional.
@@ -229,9 +298,7 @@ When `configPath` is not provided, the loader automatically searches for config
229
298
  2. **Supported filenames:**
230
299
  - `.oxfmtrc.json`
231
300
  - `.oxfmtrc.jsonc`
232
-
233
- - `oxfmt.config.ts`
234
-
301
+ - `oxfmt.config.ts`
235
302
  3. **Stops when:**
236
303
  - A valid config file is found
237
304
  - Reaches the filesystem root
@@ -278,18 +345,47 @@ export default {
278
345
  }
279
346
  ```
280
347
 
348
+ When `configPath` is passed explicitly, extensions `.json`, `.jsonc`, `.ts`, `.mts`, `.cts`, `.js`, `.mjs`, and `.cjs` are supported. Extensionless paths are also accepted and parsed as JSON.
349
+
281
350
  ## `.editorconfig` Support
282
351
 
283
352
  The loader reads the nearest `.editorconfig` file and maps the subset of fields that oxfmt supports:
284
353
 
285
354
  - `end_of_line` → `endOfLine`
286
355
  - `indent_style` → `useTabs`
287
- - `indent_size` / `tab_width` `tabWidth`
356
+ - `indent_size` `tabWidth` when `indent_style = space`
357
+ - `tab_width` → `tabWidth`
288
358
  - `max_line_length` → `printWidth`
289
359
  - `insert_final_newline` → `insertFinalNewline`
290
360
  - `quote_type` → `singleQuote`
291
361
 
292
- Glob sections such as `[*.ts]` are converted into returned `overrides` entries.
362
+ Only `[*]` is treated as a global section to match oxfmt. Other sections such as `[**]` and `[*.ts]` are converted into returned `overrides` entries.
363
+
364
+ ## Ignore Strategy
365
+
366
+ `isOxfmtIgnored()` applies two layers:
367
+
368
+ 1. Global ignore
369
+ 2. Ignore patterns from the resolved oxfmt config (`ignorePatterns`)
370
+
371
+ Global ignore includes:
372
+
373
+ - Default ignored directories: `.git`, `.svn`, `.jj`, `node_modules`
374
+ - Default lockfiles: `package-lock.json`, `npm-shrinkwrap.json`, `pnpm-lock.yaml`, `yarn.lock`, `bun.lock`, `bun.lockb`
375
+ - Ignore files:
376
+ - If `ignorePath` is provided: use those files only (multiple supported)
377
+ - If `ignorePath` is not provided: use `.gitignore` and `.prettierignore` from `cwd`
378
+
379
+ Notes:
380
+
381
+ - `node_modules` can be included by passing `withNodeModules: true`.
382
+ - This package does not read parent `.gitignore` files or global gitignore settings.
383
+ - The default lockfile list mirrors oxfmt documentation intent (`package-lock.json`, `pnpm-lock.yaml`, etc.) and common ecosystem lockfiles. It is not guaranteed to be a complete internal oxfmt list.
384
+ - `ignorePatterns` are always interpreted relative to the resolved oxfmt config directory.
385
+ - Nested config behavior follows oxfmt semantics:
386
+ - default: nearest config from target file directory upward
387
+ - `disableNestedConfig: true`: resolve from `cwd` only
388
+ - `configPath`: also disables nested lookup (same intent as CLI `-c`)
293
389
 
294
390
  ## Precedence
295
391
 
@@ -304,17 +400,18 @@ This means explicit oxfmt config values always win over `.editorconfig` fallback
304
400
 
305
401
  ## Limitations
306
402
 
307
- `loadOxfmtConfig()` returns a static `OxfmtOptions` object. That means `.editorconfig` support is represented as a merged config shape, not as per-file runtime evaluation. In practice this works well for common root settings and section-based overrides, but it is not a full replacement for oxfmt's own file-by-file config resolution.
403
+ `loadOxfmtConfigResult()` (and the deprecated `loadOxfmtConfig()`) returns a static merged `OxfmtOptions` shape. That means `.editorconfig` support is represented as merged root + overrides config data, not as per-file runtime evaluation. In practice this works well for common root settings and section-based overrides, but it is not a full replacement for oxfmt's own file-by-file config resolution.
308
404
 
309
405
  ## Error Handling
310
406
 
311
407
  ```ts
312
- import { loadOxfmtConfig } from 'load-oxfmt-config'
408
+ import { loadOxfmtConfigResult } from 'load-oxfmt-config'
313
409
 
314
410
  try {
315
- const config = await loadOxfmtConfig()
411
+ const result = await loadOxfmtConfigResult()
412
+ console.log(result.config)
316
413
  } catch (error) {
317
- // Thrown when config file exists but contains invalid JSON
414
+ // Thrown when config file exists but contains invalid JSON/JSONC
318
415
  console.error('Failed to parse oxfmt config:', error.message)
319
416
  }
320
417
  ```
package/dist/index.d.mts CHANGED
@@ -77,35 +77,153 @@ interface OxfmtOptions extends FormatConfig {
77
77
  */
78
78
  overrides?: OxfmtConfigOverride[];
79
79
  }
80
+ /**
81
+ * Result object with metadata about resolved oxfmt config.
82
+ */
83
+ interface LoadOxfmtConfigResult {
84
+ /**
85
+ * Final merged config (oxfmt + optional editorconfig mapping)
86
+ */
87
+ config: OxfmtOptions;
88
+ /**
89
+ * Absolute path of resolved config file
90
+ */
91
+ filepath?: string;
92
+ /**
93
+ * Directory of resolved config file
94
+ */
95
+ dirname?: string;
96
+ }
97
+ /**
98
+ * Options for resolving whether a single file should be ignored.
99
+ */
100
+ interface IsOxfmtIgnoredOptions {
101
+ /**
102
+ * Current working directory.
103
+ * Also the base directory for default .gitignore/.prettierignore lookup.
104
+ */
105
+ cwd?: string;
106
+ /**
107
+ * File path to test.
108
+ */
109
+ filepath: string;
110
+ /**
111
+ * Explicit oxfmt config path.
112
+ * When provided, nested config lookup is disabled (same as oxfmt CLI -c).
113
+ */
114
+ configPath?: string;
115
+ /**
116
+ * Ignore files to use instead of cwd .gitignore/.prettierignore.
117
+ * Can be passed multiple times in CLI style.
118
+ */
119
+ ignorePath?: string | string[];
120
+ /**
121
+ * Whether node_modules should be included.
122
+ */
123
+ withNodeModules?: boolean;
124
+ /**
125
+ * Disable nested config lookup.
126
+ */
127
+ disableNestedConfig?: boolean;
128
+ /**
129
+ * Whether to use in-memory cache.
130
+ */
131
+ useCache?: boolean;
132
+ }
133
+ /**
134
+ * Ignore resolution result.
135
+ */
136
+ interface IsOxfmtIgnoredResult {
137
+ /**
138
+ * Whether the file should be ignored.
139
+ */
140
+ ignored: boolean;
141
+ /**
142
+ * Matched ignore source.
143
+ */
144
+ reason?: 'default-dir' | 'lockfile' | 'gitignore' | 'prettierignore' | 'ignore-path' | 'config-ignore-patterns';
145
+ }
80
146
  /**
81
147
  * @deprecated Use `OxfmtConfigOverride` instead
82
148
  */
83
149
  type FormatOptionOverride = OxfmtConfigOverride;
84
150
  //#endregion
85
- //#region src/core.d.ts
151
+ //#region src/config-file.d.ts
86
152
  /**
87
153
  * Resolve the oxfmt config file path.
88
- * - If `configPath` is provided, absolute paths are returned as-is; relative paths are joined to cwd.
89
- * - Otherwise, walk upward from cwd to find known filenames.
154
+ *
155
+ * - If `configPath` is provided, absolute paths are returned as-is;
156
+ * relative paths are joined to `cwd`.
157
+ * - Otherwise, walk upward from `cwd` to find known filenames.
90
158
  *
91
159
  * @param cwd - Starting directory for resolution.
92
- * @param configPath - Optional explicit path (absolute or relative to cwd).
93
- * @returns Absolute path to the config file, or undefined when not found.
160
+ * @param configPath - Optional explicit path.
161
+ * @returns Absolute path to config file, or undefined when not found.
162
+ *
163
+ * @example
164
+ * ```ts
165
+ * import { resolveOxfmtrcPath } from 'load-oxfmt-config'
166
+ *
167
+ * const path = await resolveOxfmtrcPath(process.cwd())
168
+ * ```
94
169
  */
95
170
  declare function resolveOxfmtrcPath(cwd: string, configPath?: string): Promise<string | undefined>;
171
+ //#endregion
172
+ //#region src/core.d.ts
96
173
  /**
97
- * Load oxfmt configuration: resolve the file path, then read and parse it.
98
- * Caching is enabled by default; pass `useCache: false` to force a re-read.
174
+ * Resolve config + editorconfig and return merged config with metadata.
99
175
  *
100
- * @param options - Optional loader settings (cwd, configPath, useCache).
101
- * @returns Parsed oxfmt OxfmtOptions or an empty object when missing.
102
- * @throws {Error} when the config file exists but cannot be parsed.
176
+ * @param options - Loader settings.
177
+ * @returns Merged config and optional resolved config metadata.
103
178
  *
104
179
  * @example
105
180
  * ```ts
106
- * const config = await loadOxfmtConfig({ cwd: '/project' })
181
+ * import { loadOxfmtConfigResult } from 'load-oxfmt-config'
182
+ *
183
+ * const result = await loadOxfmtConfigResult({ cwd: process.cwd() })
184
+ * console.log(result.config)
185
+ * ```
186
+ */
187
+ declare function loadOxfmtConfigResult(options?: Options): Promise<LoadOxfmtConfigResult>;
188
+ //#endregion
189
+ //#region src/ignore.d.ts
190
+ /**
191
+ * Resolve whether a file should be ignored using oxfmt-like CLI semantics.
192
+ *
193
+ * @param options - Ignore resolution options.
194
+ * @returns Ignore status with optional reason.
195
+ *
196
+ * @example
197
+ * ```ts
198
+ * import { isOxfmtIgnored } from 'load-oxfmt-config'
199
+ *
200
+ * const result = await isOxfmtIgnored({
201
+ * cwd: process.cwd(),
202
+ * filepath: 'src/generated/file.ts',
203
+ * })
204
+ * ```
205
+ */
206
+ declare function isOxfmtIgnored(options: IsOxfmtIgnoredOptions): Promise<IsOxfmtIgnoredResult>;
207
+ //#endregion
208
+ //#region src/legacy.d.ts
209
+ /**
210
+ * Legacy API that returns only the merged config object.
211
+ *
212
+ * Prefer using `loadOxfmtConfigResult` when you also need resolved config metadata.
213
+ *
214
+ * @deprecated Prefer `loadOxfmtConfigResult`.
215
+ *
216
+ * @param options - Loader options.
217
+ * @returns Parsed and merged oxfmt options.
218
+ *
219
+ * @example
220
+ * ```ts
221
+ * import { loadOxfmtConfig } from 'load-oxfmt-config'
222
+ *
223
+ * // Deprecated: prefer loadOxfmtConfigResult.
224
+ * const config = await loadOxfmtConfig({ cwd: process.cwd() })
107
225
  * ```
108
226
  */
109
227
  declare function loadOxfmtConfig(options?: Options): Promise<OxfmtOptions>;
110
228
  //#endregion
111
- export { EditorconfigOption, FormatOptionOverride, Options, OxfmtConfigOverride, OxfmtOptions, loadOxfmtConfig, resolveOxfmtrcPath };
229
+ export { EditorconfigOption, FormatOptionOverride, IsOxfmtIgnoredOptions, IsOxfmtIgnoredResult, LoadOxfmtConfigResult, Options, OxfmtConfigOverride, OxfmtOptions, isOxfmtIgnored, loadOxfmtConfig, loadOxfmtConfigResult, resolveOxfmtrcPath };
package/dist/index.mjs CHANGED
@@ -1,9 +1,11 @@
1
- import { readFile, stat } from "node:fs/promises";
2
- import { dirname, isAbsolute, join, relative } from "node:path";
1
+ import { basename, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
3
2
  import process from "node:process";
4
3
  import { interopDefault, isBoolean, isNumber, isObject } from "@ntnyq/utils";
5
- import { parse } from "jsonc-parser";
4
+ import { readFile, stat } from "node:fs/promises";
5
+ import { parse, printParseErrorCode } from "jsonc-parser";
6
6
  import { parseBuffer } from "editorconfig";
7
+ import ignore from "ignore";
8
+ import picomatch from "picomatch";
7
9
  //#region src/constants.ts
8
10
  /**
9
11
  * Supported configuration files for oxfmt.
@@ -15,13 +17,178 @@ const OXFMT_CONFIG_FILES = [
15
17
  "oxfmt.config.ts"
16
18
  ];
17
19
  /**
20
+ * Supported extensions for explicit config paths.
21
+ */
22
+ const OXFMT_EXPLICIT_CONFIG_EXTENSIONS = [
23
+ ".json",
24
+ ".jsonc",
25
+ ".ts",
26
+ ".mts",
27
+ ".cts",
28
+ ".js",
29
+ ".mjs",
30
+ ".cjs"
31
+ ];
32
+ /**
33
+ * Default ignored directories used by oxfmt CLI semantics.
34
+ */
35
+ const DEFAULT_IGNORED_DIRS = [
36
+ ".git",
37
+ ".svn",
38
+ ".jj",
39
+ "node_modules"
40
+ ];
41
+ /**
42
+ * Common lockfiles ignored by default.
43
+ *
44
+ * Note: oxfmt docs only list package-lock.json and pnpm-lock.yaml explicitly,
45
+ * then mention "etc.". This list mirrors common ecosystem lockfiles.
46
+ */
47
+ const DEFAULT_IGNORED_LOCKFILES = [
48
+ "package-lock.json",
49
+ "npm-shrinkwrap.json",
50
+ "pnpm-lock.yaml",
51
+ "yarn.lock",
52
+ "bun.lock",
53
+ "bun.lockb"
54
+ ];
55
+ /**
56
+ * Default ignore files loaded from cwd when --ignore-path is not provided.
57
+ */
58
+ const DEFAULT_IGNORE_FILES = [".gitignore", ".prettierignore"];
59
+ /**
18
60
  * Supported EditorConfig filename.
19
61
  */
20
62
  const EDITORCONFIG_FILE = ".editorconfig";
21
63
  /**
22
64
  * Sections that apply globally and can be merged into root-level oxfmt options.
23
65
  */
24
- const EDITORCONFIG_GLOBAL_SECTION_NAMES = ["*", "**"];
66
+ const EDITORCONFIG_GLOBAL_SECTION_NAMES = ["*"];
67
+ //#endregion
68
+ //#region src/config-file.ts
69
+ /**
70
+ * Build a cache key for path resolution (cwd + configPath).
71
+ *
72
+ * @param cwd - Current working directory.
73
+ * @param configPath - Optional config path.
74
+ * @returns Cache key for resolve cache.
75
+ */
76
+ function getResolveCacheKey(cwd, configPath) {
77
+ return `${cwd}::${configPath || ""}`;
78
+ }
79
+ /**
80
+ * Build a cache key for config content; prefixes missing entries.
81
+ *
82
+ * @param resolvedPath - Resolved oxfmt config path.
83
+ * @param editorconfigPath - Resolved editorconfig path.
84
+ * @param resolveKey - Resolution cache key.
85
+ * @returns Cache key for config content cache.
86
+ */
87
+ function getConfigCacheKey(resolvedPath, editorconfigPath, resolveKey) {
88
+ return `${resolvedPath || `missing-oxfmt:${resolveKey}`}::${editorconfigPath || `missing-editorconfig:${resolveKey}`}`;
89
+ }
90
+ /**
91
+ * Resolve the oxfmt config file path.
92
+ *
93
+ * - If `configPath` is provided, absolute paths are returned as-is;
94
+ * relative paths are joined to `cwd`.
95
+ * - Otherwise, walk upward from `cwd` to find known filenames.
96
+ *
97
+ * @param cwd - Starting directory for resolution.
98
+ * @param configPath - Optional explicit path.
99
+ * @returns Absolute path to config file, or undefined when not found.
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * import { resolveOxfmtrcPath } from 'load-oxfmt-config'
104
+ *
105
+ * const path = await resolveOxfmtrcPath(process.cwd())
106
+ * ```
107
+ */
108
+ async function resolveOxfmtrcPath(cwd, configPath) {
109
+ const resolvedCwd = resolve(cwd);
110
+ if (configPath) return isAbsolute(configPath) ? configPath : join(resolvedCwd, configPath);
111
+ let currentDir = resolvedCwd;
112
+ while (true) {
113
+ for (const filename of OXFMT_CONFIG_FILES) {
114
+ const configFilePath = join(currentDir, filename);
115
+ try {
116
+ if ((await stat(configFilePath)).isFile()) return configFilePath;
117
+ } catch {}
118
+ }
119
+ const parentDir = join(currentDir, "..");
120
+ if (parentDir === currentDir) break;
121
+ currentDir = parentDir;
122
+ }
123
+ }
124
+ /**
125
+ * Read and parse an oxfmt config file.
126
+ *
127
+ * @param resolvedPath - Absolute path to config file.
128
+ * @returns Parsed config object.
129
+ */
130
+ async function readConfigFromFile(resolvedPath) {
131
+ const extension = extname(resolvedPath);
132
+ if (extension === ".ts" || extension === ".mts" || extension === ".cts" || extension === ".js" || extension === ".mjs" || extension === ".cjs") {
133
+ const mod = await (await interopDefault(import("jiti")))(import.meta.url).import(resolvedPath);
134
+ return mod["default"] ?? mod;
135
+ }
136
+ const content = await readFile(resolvedPath, "utf8");
137
+ if (extension === ".jsonc") {
138
+ const parseErrors = [];
139
+ const parsed = parse(content, parseErrors);
140
+ if (parseErrors.length > 0) {
141
+ const firstError = parseErrors[0];
142
+ const errorCode = firstError === void 0 ? "Unknown" : printParseErrorCode(firstError.error);
143
+ throw new Error(`Invalid JSONC syntax: ${errorCode}`);
144
+ }
145
+ return parsed;
146
+ }
147
+ if (extension === ".json") return JSON.parse(content);
148
+ if (!extension) return JSON.parse(content);
149
+ if (!OXFMT_EXPLICIT_CONFIG_EXTENSIONS.includes(extension)) throw new Error(`Unsupported oxfmt config extension "${extension}" at ${resolvedPath}`);
150
+ return JSON.parse(content);
151
+ }
152
+ //#endregion
153
+ //#region src/utils.ts
154
+ /**
155
+ * Return a cached promise by key, creating and storing it on miss.
156
+ *
157
+ * If the promise rejects, the cache entry is removed so future calls can retry.
158
+ *
159
+ * @param cache - Map used to store inflight/resolved promises.
160
+ * @param key - Cache key.
161
+ * @param factory - Factory to create the promise when missing.
162
+ * @returns Cached or newly created promise.
163
+ */
164
+ function cachePromise(cache, key, factory) {
165
+ const cached = cache.get(key);
166
+ if (cached) return cached;
167
+ const task = factory().catch((error) => {
168
+ cache.delete(key);
169
+ throw error;
170
+ });
171
+ cache.set(key, task);
172
+ return task;
173
+ }
174
+ /**
175
+ * Normalize a filesystem path to POSIX-style separators.
176
+ *
177
+ * @param path - Original path.
178
+ * @returns Path using `/` as separator.
179
+ */
180
+ function toPosixPath(path) {
181
+ return path.replaceAll("\\", "/");
182
+ }
183
+ /**
184
+ * Split a path into non-empty segments.
185
+ *
186
+ * @param path - Original path.
187
+ * @returns Path segments.
188
+ */
189
+ function splitPathSegments(path) {
190
+ return path.split(/[\\/]+/u).filter(Boolean);
191
+ }
25
192
  //#endregion
26
193
  //#region src/editorconfig.ts
27
194
  /**
@@ -89,15 +256,6 @@ async function resolveEditorconfigPath(startDir, onlyCwd = false) {
89
256
  }
90
257
  }
91
258
  /**
92
- * Normalizes a file path to POSIX separators for glob compatibility.
93
- *
94
- * @param path - The path to normalize.
95
- * @returns The path with forward slashes.
96
- */
97
- function toPosixPath(path) {
98
- return path.replaceAll("\\", "/");
99
- }
100
- /**
101
259
  * Checks whether an EditorConfig section should be treated as a global section.
102
260
  *
103
261
  * @param sectionName - The section name parsed from .editorconfig.
@@ -139,12 +297,13 @@ function parseEditorconfigQuoteType(value) {
139
297
  * Resolves the effective tab width from an EditorConfig section.
140
298
  *
141
299
  * @param section - The parsed EditorConfig section body.
300
+ * @param useTabs - The resolved indent style from the same section, when available.
142
301
  * @returns The resolved tab width, or undefined when no numeric value is available.
143
302
  */
144
- function parseEditorconfigTabWidth(section) {
303
+ function parseEditorconfigTabWidth(section, useTabs) {
145
304
  const indentSize = section["indent_size"];
146
305
  const tabWidth = section["tab_width"];
147
- if (indentSize && indentSize !== "unset" && indentSize !== "tab") {
306
+ if (useTabs === false && indentSize && indentSize !== "unset" && indentSize !== "tab") {
148
307
  const parsedIndentSize = Number(indentSize);
149
308
  if (Number.isFinite(parsedIndentSize)) return parsedIndentSize;
150
309
  }
@@ -167,7 +326,7 @@ function mapEditorconfigSectionToOptions(section) {
167
326
  else if (section["indent_style"] === "space") options.useTabs = false;
168
327
  const singleQuote = parseEditorconfigQuoteType(section["quote_type"]);
169
328
  if (isBoolean(singleQuote)) options.singleQuote = singleQuote;
170
- const tabWidth = parseEditorconfigTabWidth(section);
329
+ const tabWidth = parseEditorconfigTabWidth(section, options.useTabs);
171
330
  if (isNumber(tabWidth)) options.tabWidth = tabWidth;
172
331
  if (section["max_line_length"] && section["max_line_length"] !== "unset") {
173
332
  const parsedPrintWidth = Number(section["max_line_length"]);
@@ -188,7 +347,7 @@ function mapEditorconfigSectionToOptions(section) {
188
347
  function rebaseEditorconfigPattern(pattern, editorconfigDir, anchorDir) {
189
348
  const relativePrefix = toPosixPath(relative(anchorDir, editorconfigDir));
190
349
  if (!relativePrefix || relativePrefix === ".") return pattern;
191
- return `${relativePrefix}/${pattern.replace(/^\//, "")}`;
350
+ return `${relativePrefix}/${pattern.replace(/^\//u, "")}`;
192
351
  }
193
352
  /**
194
353
  * Reads a .editorconfig file and converts it into root options and override entries.
@@ -225,129 +384,230 @@ async function readEditorconfigFromFile(editorconfigPath, anchorDir) {
225
384
  const resolveCache = /* @__PURE__ */ new Map();
226
385
  const configCache = /* @__PURE__ */ new Map();
227
386
  /**
228
- * Return a cached promise by key, creating and storing it on miss; failures clear the entry.
387
+ * Resolve config + editorconfig and return merged config with metadata.
229
388
  *
230
- * @param cache - Map used to store inflight/resolved promises.
231
- * @param key - Cache key.
232
- * @param factory - Factory to create the promise when missing.
233
- * @returns Cached or newly created promise.
389
+ * @param options - Loader settings.
390
+ * @returns Merged config and optional resolved config metadata.
391
+ *
392
+ * @example
393
+ * ```ts
394
+ * import { loadOxfmtConfigResult } from 'load-oxfmt-config'
395
+ *
396
+ * const result = await loadOxfmtConfigResult({ cwd: process.cwd() })
397
+ * console.log(result.config)
398
+ * ```
234
399
  */
235
- function cachePromise(cache, key, factory) {
236
- const cached = cache.get(key);
237
- if (cached) return cached;
238
- const task = factory().catch((error) => {
239
- cache.delete(key);
400
+ async function loadOxfmtConfigResult(options = {}) {
401
+ const useCache = options.useCache !== false;
402
+ const cwd = resolve(options.cwd || process.cwd());
403
+ const editorconfig = options.editorconfig ?? true;
404
+ const useEditorconfig = editorconfig !== false;
405
+ const isEditorconfigOptionsObject = useEditorconfig && isObject(editorconfig);
406
+ const onlyCwd = isEditorconfigOptionsObject ? editorconfig.onlyCwd ?? false : false;
407
+ const editorconfigCwd = isEditorconfigOptionsObject && editorconfig.cwd ? resolve(editorconfig.cwd) : void 0;
408
+ const resolveKey = getResolveCacheKey(cwd, options.configPath);
409
+ const editorconfigSearchDir = editorconfigCwd || getEditorconfigSearchDir(cwd, options.configPath);
410
+ const editorconfigResolveKey = editorconfigCwd ? getEditorconfigResolveCacheKey(`${editorconfigCwd}::${options.configPath || ""}`) : getEditorconfigResolveCacheKey(resolveKey);
411
+ const resolvedPath = useCache ? await cachePromise(resolveCache, resolveKey, () => resolveOxfmtrcPath(cwd, options.configPath)) : await resolveOxfmtrcPath(cwd, options.configPath);
412
+ const editorconfigPath = useEditorconfig ? await (useCache ? cachePromise(resolveCache, editorconfigResolveKey, () => resolveEditorconfigPath(editorconfigSearchDir, onlyCwd)) : resolveEditorconfigPath(editorconfigSearchDir, onlyCwd)) : void 0;
413
+ const anchorDir = dirname(resolvedPath || editorconfigPath || cwd);
414
+ const loadTask = async () => {
415
+ const oxfmtConfig = resolvedPath ? await readConfigFromFile(resolvedPath).catch((error) => {
416
+ throw new Error(`Failed to parse oxfmt configuration file at ${resolvedPath}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
417
+ }) : {};
418
+ if (!editorconfigPath) return oxfmtConfig;
419
+ const editorconfigData = await readEditorconfigFromFile(editorconfigPath, anchorDir);
420
+ const mergedConfig = mergeRootOptions(oxfmtConfig, editorconfigData.rootOptions);
421
+ const mergedOverrides = mergeOverrides(oxfmtConfig.overrides, editorconfigData.overrides);
422
+ if (!mergedOverrides) return mergedConfig;
423
+ return {
424
+ ...mergedConfig,
425
+ overrides: mergedOverrides
426
+ };
427
+ };
428
+ const hasNoConfigSources = !resolvedPath && !editorconfigPath;
429
+ return {
430
+ config: await (async () => {
431
+ if (hasNoConfigSources) return useCache ? await cachePromise(configCache, getConfigCacheKey(resolvedPath, editorconfigPath, resolveKey), () => Promise.resolve({})) : {};
432
+ return useCache ? await cachePromise(configCache, getConfigCacheKey(resolvedPath, editorconfigPath, resolveKey), loadTask) : await loadTask();
433
+ })(),
434
+ ...resolvedPath ? {
435
+ filepath: resolvedPath,
436
+ dirname: dirname(resolvedPath)
437
+ } : {}
438
+ };
439
+ }
440
+ //#endregion
441
+ //#region src/ignore.ts
442
+ const ignoreMatcherCache = /* @__PURE__ */ new Map();
443
+ /**
444
+ * Check whether a file is under oxfmt's default ignored directories.
445
+ *
446
+ * @param filepath - Absolute file path to test.
447
+ * @param options - Matching options.
448
+ * @returns True when the path should be ignored by default directory rules.
449
+ */
450
+ function isDefaultIgnoredDir(filepath, options) {
451
+ const directories = options.withNodeModules ? DEFAULT_IGNORED_DIRS.filter((dir) => dir !== "node_modules") : DEFAULT_IGNORED_DIRS;
452
+ const segments = splitPathSegments(dirname(filepath));
453
+ return directories.some((dir) => segments.includes(dir));
454
+ }
455
+ /**
456
+ * Check whether a file is a default ignored lockfile.
457
+ *
458
+ * @param filepath - Absolute file path.
459
+ * @returns True when the basename matches a default lockfile name.
460
+ */
461
+ function isLockfile(filepath) {
462
+ return DEFAULT_IGNORED_LOCKFILES.includes(basename(filepath));
463
+ }
464
+ /**
465
+ * Read and parse an ignore file into an `ignore` matcher.
466
+ *
467
+ * @param ignoreFilePath - Ignore file path.
468
+ * @param useCache - Whether to cache matcher instances.
469
+ * @returns Parsed matcher, or undefined when file does not exist.
470
+ */
471
+ function loadIgnoreMatcher(ignoreFilePath, useCache) {
472
+ const loadTask = () => readFile(ignoreFilePath, "utf8").then((content) => {
473
+ const ig = ignore();
474
+ ig.add(content);
475
+ return ig;
476
+ }).catch((error) => {
477
+ const code = error.code;
478
+ if (code === "ENOENT" || code === "ENOTDIR") return;
240
479
  throw error;
241
480
  });
242
- cache.set(key, task);
243
- return task;
481
+ if (!useCache) return loadTask();
482
+ return cachePromise(ignoreMatcherCache, ignoreFilePath, loadTask);
244
483
  }
245
484
  /**
246
- * Build a cache key for config content; prefixes missing entries with `missing:`.
485
+ * Build a POSIX relative path.
247
486
  *
248
- * @param resolvedPath - Resolved config path or undefined when missing.
249
- * @param editorconfigPath - Resolved editorconfig path or undefined when missing.
250
- * @param resolveKey - Key used for path resolution caching.
251
- * @returns Cache key for config content.
487
+ * @param from - Base directory.
488
+ * @param to - Target path.
489
+ * @returns POSIX relative path.
252
490
  */
253
- function getConfigCacheKey(resolvedPath, editorconfigPath, resolveKey) {
254
- return `${resolvedPath || `missing-oxfmt:${resolveKey}`}::${editorconfigPath || `missing-editorconfig:${resolveKey}`}`;
491
+ function relativeSafe(from, to) {
492
+ return toPosixPath(relative(from, to));
255
493
  }
256
494
  /**
257
- * Resolve the oxfmt config file path.
258
- * - If `configPath` is provided, absolute paths are returned as-is; relative paths are joined to cwd.
259
- * - Otherwise, walk upward from cwd to find known filenames.
495
+ * Match file path against one ignore file.
260
496
  *
261
- * @param cwd - Starting directory for resolution.
262
- * @param configPath - Optional explicit path (absolute or relative to cwd).
263
- * @returns Absolute path to the config file, or undefined when not found.
497
+ * @param filepath - Absolute file path.
498
+ * @param ignoreFilePath - Ignore file path.
499
+ * @param useCache - Whether to use matcher cache.
500
+ * @returns True when ignored by this file.
264
501
  */
265
- async function resolveOxfmtrcPath(cwd, configPath) {
266
- if (configPath) return isAbsolute(configPath) ? configPath : join(cwd, configPath);
267
- let currentDir = cwd;
268
- while (true) {
269
- for (const filename of OXFMT_CONFIG_FILES) {
270
- const configFilePath = join(currentDir, filename);
271
- try {
272
- if ((await stat(configFilePath)).isFile()) return configFilePath;
273
- } catch {}
274
- }
275
- const parentDir = join(currentDir, "..");
276
- if (parentDir === currentDir) break;
277
- currentDir = parentDir;
278
- }
502
+ async function matchIgnoreFile(filepath, ignoreFilePath, useCache) {
503
+ const matcher = await loadIgnoreMatcher(ignoreFilePath, useCache);
504
+ if (!matcher) return false;
505
+ const relativeToIgnore = relativeSafe(dirname(ignoreFilePath), filepath);
506
+ if (relativeToIgnore === ".." || relativeToIgnore.startsWith("../")) return false;
507
+ return matcher.ignores(relativeToIgnore);
279
508
  }
280
509
  /**
281
- * Build a cache key for path resolution (cwd + configPath).
510
+ * Resolve an ignore file path against cwd when needed.
282
511
  *
512
+ * @param path - Absolute or relative path.
283
513
  * @param cwd - Current working directory.
284
- * @param configPath - Optional config path.
285
- * @returns Cache key for resolve cache.
514
+ * @returns Absolute ignore file path.
286
515
  */
287
- function getResolveCacheKey(cwd, configPath) {
288
- return `${cwd}::${configPath || ""}`;
516
+ function resolveIgnoreFilePath(path, cwd) {
517
+ return isAbsolute(path) ? path : resolve(cwd, path);
289
518
  }
290
519
  /**
291
- * Read and parse config file, supporting JSON, JSONC, and TypeScript/JavaScript.
520
+ * Match `ignorePatterns` from config with support for negated patterns.
292
521
  *
293
- * @param resolvedPath - Absolute path to the config file.
294
- * @returns Parsed OxfmtOptions object.
522
+ * @param filepath - Absolute file path.
523
+ * @param configDir - Resolved config directory.
524
+ * @param patterns - Config ignore patterns.
525
+ * @returns True when patterns mark the file as ignored.
295
526
  */
296
- async function readConfigFromFile(resolvedPath) {
297
- if (resolvedPath.endsWith(".ts")) {
298
- const mod = await (await interopDefault(import("jiti")))(import.meta.url).import(resolvedPath);
299
- return mod["default"] ?? mod;
527
+ function matchConfigIgnorePatterns(filepath, configDir, patterns) {
528
+ const relativeFile = relativeSafe(configDir, filepath);
529
+ let ignored = false;
530
+ for (const rawPattern of patterns) {
531
+ if (!rawPattern) continue;
532
+ const isNegative = rawPattern.startsWith("!");
533
+ const pattern = isNegative ? rawPattern.slice(1) : rawPattern;
534
+ if (!pattern) continue;
535
+ if (picomatch(pattern, { dot: true })(relativeFile)) ignored = !isNegative;
300
536
  }
301
- const content = await readFile(resolvedPath, "utf8");
302
- if (resolvedPath.endsWith(".jsonc")) return parse(content);
303
- return JSON.parse(content);
537
+ return ignored;
304
538
  }
305
539
  /**
306
- * Load oxfmt configuration: resolve the file path, then read and parse it.
307
- * Caching is enabled by default; pass `useCache: false` to force a re-read.
540
+ * Resolve whether a file should be ignored using oxfmt-like CLI semantics.
308
541
  *
309
- * @param options - Optional loader settings (cwd, configPath, useCache).
310
- * @returns Parsed oxfmt OxfmtOptions or an empty object when missing.
311
- * @throws {Error} when the config file exists but cannot be parsed.
542
+ * @param options - Ignore resolution options.
543
+ * @returns Ignore status with optional reason.
312
544
  *
313
545
  * @example
314
546
  * ```ts
315
- * const config = await loadOxfmtConfig({ cwd: '/project' })
547
+ * import { isOxfmtIgnored } from 'load-oxfmt-config'
548
+ *
549
+ * const result = await isOxfmtIgnored({
550
+ * cwd: process.cwd(),
551
+ * filepath: 'src/generated/file.ts',
552
+ * })
316
553
  * ```
317
554
  */
318
- async function loadOxfmtConfig(options = {}) {
555
+ async function isOxfmtIgnored(options) {
319
556
  const useCache = options.useCache !== false;
320
- const cwd = options.cwd || process.cwd();
321
- const editorconfig = options.editorconfig ?? true;
322
- const useEditorconfig = editorconfig !== false;
323
- const onlyCwd = useEditorconfig && isObject(editorconfig) ? editorconfig.onlyCwd ?? false : false;
324
- const editorconfigCwd = useEditorconfig && isObject(editorconfig) ? editorconfig.cwd : void 0;
325
- const resolveKey = getResolveCacheKey(cwd, options.configPath);
326
- const editorconfigSearchDir = editorconfigCwd || getEditorconfigSearchDir(cwd, options.configPath);
327
- const editorconfigResolveKey = editorconfigCwd ? getEditorconfigResolveCacheKey(`${editorconfigCwd}::${options.configPath || ""}`) : getEditorconfigResolveCacheKey(resolveKey);
328
- const resolvedPath = useCache ? await cachePromise(resolveCache, resolveKey, () => resolveOxfmtrcPath(cwd, options.configPath)) : await resolveOxfmtrcPath(cwd, options.configPath);
329
- const editorconfigPath = useEditorconfig ? await (useCache ? cachePromise(resolveCache, editorconfigResolveKey, () => resolveEditorconfigPath(editorconfigSearchDir, onlyCwd)) : resolveEditorconfigPath(editorconfigSearchDir, onlyCwd)) : void 0;
330
- const anchorDir = dirname(resolvedPath || editorconfigPath || cwd);
331
- const loadTask = async () => {
332
- const oxfmtConfig = resolvedPath ? await readConfigFromFile(resolvedPath).catch((error) => {
333
- throw new Error(`Failed to parse oxfmt configuration file at ${resolvedPath}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
334
- }) : {};
335
- if (!editorconfigPath) return oxfmtConfig;
336
- const editorconfigData = await readEditorconfigFromFile(editorconfigPath, anchorDir);
337
- const mergedConfig = mergeRootOptions(oxfmtConfig, editorconfigData.rootOptions);
338
- const mergedOverrides = mergeOverrides(oxfmtConfig.overrides, editorconfigData.overrides);
339
- if (!mergedOverrides) return mergedConfig;
340
- return {
341
- ...mergedConfig,
342
- overrides: mergedOverrides
557
+ const cwd = resolve(options.cwd ?? process.cwd());
558
+ const filepath = isAbsolute(options.filepath) ? resolve(options.filepath) : resolve(cwd, options.filepath);
559
+ if (isDefaultIgnoredDir(filepath, options.withNodeModules ? { withNodeModules: true } : {})) return {
560
+ ignored: true,
561
+ reason: "default-dir"
562
+ };
563
+ if (isLockfile(filepath)) return {
564
+ ignored: true,
565
+ reason: "lockfile"
566
+ };
567
+ const explicitIgnorePaths = (typeof options.ignorePath === "string" ? [options.ignorePath] : options.ignorePath)?.map((path) => resolveIgnoreFilePath(path, cwd));
568
+ if (explicitIgnorePaths && explicitIgnorePaths.length > 0) {
569
+ for (const ignoreFilePath of explicitIgnorePaths) if (await matchIgnoreFile(filepath, ignoreFilePath, useCache)) return {
570
+ ignored: true,
571
+ reason: "ignore-path"
343
572
  };
573
+ } else for (const ignoreFile of DEFAULT_IGNORE_FILES) if (await matchIgnoreFile(filepath, resolve(cwd, ignoreFile), useCache)) return {
574
+ ignored: true,
575
+ reason: ignoreFile === ".gitignore" ? "gitignore" : "prettierignore"
344
576
  };
345
- if (!resolvedPath && !editorconfigPath) {
346
- if (!useCache) return {};
347
- return cachePromise(configCache, getConfigCacheKey(resolvedPath, editorconfigPath, resolveKey), () => Promise.resolve({}));
348
- }
349
- if (!useCache) return loadTask();
350
- return cachePromise(configCache, getConfigCacheKey(resolvedPath, editorconfigPath, resolveKey), loadTask);
577
+ const configResult = await loadOxfmtConfigResult({
578
+ cwd: options.configPath || options.disableNestedConfig ? cwd : dirname(filepath),
579
+ ...options.configPath ? { configPath: options.configPath } : {},
580
+ editorconfig: false,
581
+ useCache
582
+ });
583
+ if (configResult.dirname && configResult.config.ignorePatterns && configResult.config.ignorePatterns.length > 0 && matchConfigIgnorePatterns(filepath, configResult.dirname, configResult.config.ignorePatterns)) return {
584
+ ignored: true,
585
+ reason: "config-ignore-patterns"
586
+ };
587
+ return { ignored: false };
588
+ }
589
+ //#endregion
590
+ //#region src/legacy.ts
591
+ /**
592
+ * Legacy API that returns only the merged config object.
593
+ *
594
+ * Prefer using `loadOxfmtConfigResult` when you also need resolved config metadata.
595
+ *
596
+ * @deprecated Prefer `loadOxfmtConfigResult`.
597
+ *
598
+ * @param options - Loader options.
599
+ * @returns Parsed and merged oxfmt options.
600
+ *
601
+ * @example
602
+ * ```ts
603
+ * import { loadOxfmtConfig } from 'load-oxfmt-config'
604
+ *
605
+ * // Deprecated: prefer loadOxfmtConfigResult.
606
+ * const config = await loadOxfmtConfig({ cwd: process.cwd() })
607
+ * ```
608
+ */
609
+ async function loadOxfmtConfig(options = {}) {
610
+ return (await loadOxfmtConfigResult(options)).config;
351
611
  }
352
612
  //#endregion
353
- export { loadOxfmtConfig, resolveOxfmtrcPath };
613
+ export { isOxfmtIgnored, loadOxfmtConfig, loadOxfmtConfigResult, resolveOxfmtrcPath };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "load-oxfmt-config",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Load oxfmt config files and merge supported .editorconfig options.",
5
5
  "keywords": [
6
6
  "editorconfig",
@@ -39,23 +39,26 @@
39
39
  "access": "public"
40
40
  },
41
41
  "dependencies": {
42
- "@ntnyq/utils": "^0.12.0",
42
+ "@ntnyq/utils": "^0.13.2",
43
43
  "editorconfig": "^3.0.2",
44
- "jiti": "^2.6.1",
45
- "jsonc-parser": "^3.3.1"
44
+ "ignore": "^7.0.5",
45
+ "jiti": "^2.7.0",
46
+ "jsonc-parser": "^3.3.1",
47
+ "picomatch": "^4.0.4"
46
48
  },
47
49
  "devDependencies": {
48
50
  "@ntnyq/tsconfig": "^3.1.0",
49
51
  "@types/node": "^25.6.0",
50
- "@typescript/native-preview": "^7.0.0-dev.20260413.1",
52
+ "@types/picomatch": "^4.0.3",
53
+ "@typescript/native-preview": "^7.0.0-dev.20260506.1",
51
54
  "bumpp": "^11.0.1",
52
55
  "husky": "^9.1.7",
53
56
  "nano-staged": "^1.0.2",
54
57
  "npm-run-all2": "^8.0.4",
55
- "oxfmt": "^0.45.0",
56
- "oxlint": "^1.60.0",
57
- "tsdown": "^0.21.8",
58
- "vitest": "^4.1.4"
58
+ "oxfmt": "^0.48.0",
59
+ "oxlint": "^1.63.0",
60
+ "tsdown": "^0.21.10",
61
+ "vitest": "^4.1.5"
59
62
  },
60
63
  "peerDependencies": {
61
64
  "oxfmt": ">=0.41.0"