load-oxfmt-config 0.23.0 → 0.25.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 +57 -11
- package/dist/index.d.mts +146 -129
- package/dist/index.mjs +571 -466
- package/package.json +22 -44
package/dist/index.mjs
CHANGED
|
@@ -7,6 +7,7 @@ import { readFile, realpath, stat } from "node:fs/promises";
|
|
|
7
7
|
import { pathToFileURL } from "node:url";
|
|
8
8
|
import { parse, printParseErrorCode } from "jsonc-parser";
|
|
9
9
|
import { parseBuffer } from "editorconfig";
|
|
10
|
+
import { Minimatch } from "minimatch";
|
|
10
11
|
import ignore from "ignore";
|
|
11
12
|
//#region src/constants.ts
|
|
12
13
|
/**
|
|
@@ -79,6 +80,26 @@ const EDITORCONFIG_GLOBAL_SECTION_NAMES = ["*"];
|
|
|
79
80
|
//#endregion
|
|
80
81
|
//#region src/utils.ts
|
|
81
82
|
/**
|
|
83
|
+
* Return a cached promise by key, creating and storing it on miss.
|
|
84
|
+
*
|
|
85
|
+
* If the promise rejects, the cache entry is removed so future calls can retry.
|
|
86
|
+
*
|
|
87
|
+
* @param cache - Map used to store inflight/resolved promises.
|
|
88
|
+
* @param key - Cache key.
|
|
89
|
+
* @param factory - Factory to create the promise when missing.
|
|
90
|
+
* @returns Cached or newly created promise.
|
|
91
|
+
*/
|
|
92
|
+
function cachePromise(cache, key, factory) {
|
|
93
|
+
const cached = getCacheValue(cache, key);
|
|
94
|
+
if (cached) return cached;
|
|
95
|
+
const task = factory().catch((error) => {
|
|
96
|
+
if (cache.get(key) === task) cache.delete(key);
|
|
97
|
+
throw error;
|
|
98
|
+
});
|
|
99
|
+
setCacheValue(cache, key, task);
|
|
100
|
+
return task;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
82
103
|
* Read a cache value and move it to the most-recent position.
|
|
83
104
|
*
|
|
84
105
|
* @param cache - Cache map to read.
|
|
@@ -93,6 +114,19 @@ function getCacheValue(cache, key) {
|
|
|
93
114
|
return value;
|
|
94
115
|
}
|
|
95
116
|
/**
|
|
117
|
+
* Check whether a relative path points outside its intended base directory.
|
|
118
|
+
*
|
|
119
|
+
* This also recognizes Windows drive-qualified paths when running on another
|
|
120
|
+
* platform so the behavior can be tested consistently.
|
|
121
|
+
*
|
|
122
|
+
* @param path - Relative path candidate.
|
|
123
|
+
* @returns True when the path escapes or is absolute.
|
|
124
|
+
*/
|
|
125
|
+
function isPathOutsideBase(path) {
|
|
126
|
+
const normalizedPath = toPosixPath(path);
|
|
127
|
+
return normalizedPath === ".." || normalizedPath.startsWith("../") || normalizedPath.startsWith("/") || /^[A-Za-z]:\//u.test(normalizedPath);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
96
130
|
* Store a cache value and evict the least-recent entry when over capacity.
|
|
97
131
|
*
|
|
98
132
|
* @param cache - Cache map to update.
|
|
@@ -107,35 +141,6 @@ function setCacheValue(cache, key, value) {
|
|
|
107
141
|
if (!oldestKey.done) cache.delete(oldestKey.value);
|
|
108
142
|
}
|
|
109
143
|
/**
|
|
110
|
-
* Return a cached promise by key, creating and storing it on miss.
|
|
111
|
-
*
|
|
112
|
-
* If the promise rejects, the cache entry is removed so future calls can retry.
|
|
113
|
-
*
|
|
114
|
-
* @param cache - Map used to store inflight/resolved promises.
|
|
115
|
-
* @param key - Cache key.
|
|
116
|
-
* @param factory - Factory to create the promise when missing.
|
|
117
|
-
* @returns Cached or newly created promise.
|
|
118
|
-
*/
|
|
119
|
-
function cachePromise(cache, key, factory) {
|
|
120
|
-
const cached = getCacheValue(cache, key);
|
|
121
|
-
if (cached) return cached;
|
|
122
|
-
const task = factory().catch((error) => {
|
|
123
|
-
if (cache.get(key) === task) cache.delete(key);
|
|
124
|
-
throw error;
|
|
125
|
-
});
|
|
126
|
-
setCacheValue(cache, key, task);
|
|
127
|
-
return task;
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Normalize a filesystem path to POSIX-style separators.
|
|
131
|
-
*
|
|
132
|
-
* @param path - Original path.
|
|
133
|
-
* @returns Path using `/` as separator.
|
|
134
|
-
*/
|
|
135
|
-
function toPosixPath(path) {
|
|
136
|
-
return path.replaceAll("\\", "/");
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
144
|
* Split a path into non-empty segments.
|
|
140
145
|
*
|
|
141
146
|
* @param path - Original path.
|
|
@@ -145,17 +150,13 @@ function splitPathSegments(path) {
|
|
|
145
150
|
return path.split(/[\\/]+/u).filter(Boolean);
|
|
146
151
|
}
|
|
147
152
|
/**
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
* This also recognizes Windows drive-qualified paths when running on another
|
|
151
|
-
* platform so the behavior can be tested consistently.
|
|
153
|
+
* Normalize a filesystem path to POSIX-style separators.
|
|
152
154
|
*
|
|
153
|
-
* @param path -
|
|
154
|
-
* @returns
|
|
155
|
+
* @param path - Original path.
|
|
156
|
+
* @returns Path using `/` as separator.
|
|
155
157
|
*/
|
|
156
|
-
function
|
|
157
|
-
|
|
158
|
-
return normalizedPath === ".." || normalizedPath.startsWith("../") || normalizedPath.startsWith("/") || /^[A-Za-z]:\//u.test(normalizedPath);
|
|
158
|
+
function toPosixPath(path) {
|
|
159
|
+
return path.replaceAll("\\", "/");
|
|
159
160
|
}
|
|
160
161
|
//#endregion
|
|
161
162
|
//#region src/config.ts
|
|
@@ -185,105 +186,109 @@ const syntaxFallbackMessages = [
|
|
|
185
186
|
*/
|
|
186
187
|
const freshJavaScriptModuleCache = /* @__PURE__ */ new Map();
|
|
187
188
|
/**
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
* @param value - Runtime value to validate.
|
|
191
|
-
* @returns True when the value can be treated as an oxfmt options object.
|
|
192
|
-
*/
|
|
193
|
-
function isConfigObject(value) {
|
|
194
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
195
|
-
const prototype = Object.getPrototypeOf(value);
|
|
196
|
-
return prototype === Object.prototype || prototype === null;
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* Read a parsed JSON-like config value as an oxfmt config object.
|
|
200
|
-
*
|
|
201
|
-
* @param config - Runtime config value.
|
|
202
|
-
* @returns Oxfmt config object.
|
|
203
|
-
* @throws When the config value is not an object.
|
|
204
|
-
*/
|
|
205
|
-
function readParsedConfigObject(config) {
|
|
206
|
-
if (!isConfigObject(config)) throw new Error("Configuration file must be an object.");
|
|
207
|
-
return config;
|
|
208
|
-
}
|
|
209
|
-
/**
|
|
210
|
-
* Read the default export from a JavaScript or TypeScript config module.
|
|
189
|
+
* Build a cache key for config content; prefixes missing entries.
|
|
211
190
|
*
|
|
212
|
-
* @param
|
|
213
|
-
* @
|
|
214
|
-
* @
|
|
191
|
+
* @param resolvedPath - Resolved oxfmt config path.
|
|
192
|
+
* @param editorconfigPath - Resolved editorconfig path.
|
|
193
|
+
* @param resolveKey - Resolution cache key.
|
|
194
|
+
* @returns Cache key for config content cache.
|
|
215
195
|
*/
|
|
216
|
-
function
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
196
|
+
function getConfigCacheKey(resolvedPath, editorconfigPath, resolveKey) {
|
|
197
|
+
return JSON.stringify([
|
|
198
|
+
resolvedPath ?? null,
|
|
199
|
+
editorconfigPath ?? null,
|
|
200
|
+
resolveKey
|
|
201
|
+
]);
|
|
221
202
|
}
|
|
222
203
|
/**
|
|
223
|
-
*
|
|
204
|
+
* Build a cache key for path resolution (cwd + configPath).
|
|
224
205
|
*
|
|
225
|
-
* @param
|
|
226
|
-
* @
|
|
227
|
-
* @
|
|
206
|
+
* @param cwd - Current working directory.
|
|
207
|
+
* @param configPath - Optional config path.
|
|
208
|
+
* @returns Cache key for resolve cache.
|
|
228
209
|
*/
|
|
229
|
-
function
|
|
230
|
-
|
|
231
|
-
return config;
|
|
210
|
+
function getResolveCacheKey(cwd, configPath) {
|
|
211
|
+
return JSON.stringify([cwd, configPath ?? null]);
|
|
232
212
|
}
|
|
233
213
|
/**
|
|
234
|
-
*
|
|
214
|
+
* Read and parse an oxfmt config file.
|
|
235
215
|
*
|
|
236
|
-
* @param resolvedPath - Absolute
|
|
237
|
-
* @param
|
|
238
|
-
* @
|
|
216
|
+
* @param resolvedPath - Absolute path to config file.
|
|
217
|
+
* @param options - Config loading options.
|
|
218
|
+
* @param options.useCache - Whether to reuse cached config modules.
|
|
219
|
+
* @returns Parsed config object.
|
|
239
220
|
*/
|
|
240
|
-
async function
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
221
|
+
async function readConfigFromFile(resolvedPath, options = {}) {
|
|
222
|
+
const extension = extname(resolvedPath);
|
|
223
|
+
const useCache = options.useCache !== false;
|
|
224
|
+
if ([
|
|
225
|
+
".cjs",
|
|
226
|
+
".cts",
|
|
227
|
+
".js",
|
|
228
|
+
".mjs",
|
|
229
|
+
".mts",
|
|
230
|
+
".ts"
|
|
231
|
+
].includes(extension)) {
|
|
232
|
+
if (!useCache && extension === ".cjs") return requireFreshCommonJSConfig(resolvedPath);
|
|
233
|
+
if (!useCache && extension === ".mjs") return readConfigDefaultExport(await importNativeFreshModule(resolvedPath));
|
|
234
|
+
if (!useCache && extension === ".js") return readConfigDefaultExport(await importFreshJavaScriptConfigModule(resolvedPath));
|
|
235
|
+
return readConfigDefaultExport(await importJitiConfigModule(resolvedPath, useCache));
|
|
236
|
+
}
|
|
237
|
+
const content = await readFile(resolvedPath, "utf8");
|
|
238
|
+
if (extension === ".jsonc") {
|
|
239
|
+
const parseErrors = [];
|
|
240
|
+
const parsed = parse(content, parseErrors, {
|
|
241
|
+
allowEmptyContent: true,
|
|
242
|
+
allowTrailingComma: true
|
|
243
|
+
});
|
|
244
|
+
if (parseErrors.length > 0) {
|
|
245
|
+
const firstError = parseErrors[0];
|
|
246
|
+
const errorCode = isUndefined(firstError) ? "Unknown" : printParseErrorCode(firstError.error);
|
|
247
|
+
throw new Error(`Invalid JSONC syntax: ${errorCode}`);
|
|
248
|
+
}
|
|
249
|
+
return readParsedConfigObject(isUndefined(parsed) && content.trim() === "" ? {} : parsed);
|
|
250
|
+
}
|
|
251
|
+
if (extension === ".json") return readParsedConfigObject(JSON.parse(content));
|
|
252
|
+
if (!extension) return readParsedConfigObject(JSON.parse(content));
|
|
253
|
+
if (!OXFMT_EXPLICIT_CONFIG_EXTENSIONS.includes(extension)) throw new Error(`Unsupported oxfmt config extension "${extension}" at ${resolvedPath}`);
|
|
254
|
+
return readParsedConfigObject(JSON.parse(content));
|
|
245
255
|
}
|
|
246
256
|
/**
|
|
247
|
-
*
|
|
257
|
+
* Resolve the oxfmt config file path.
|
|
248
258
|
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
|
|
252
|
-
async function getFreshImportCacheKey(resolvedPath) {
|
|
253
|
-
const content = await readFile(resolvedPath);
|
|
254
|
-
return createHash("sha256").update(content).digest("hex");
|
|
255
|
-
}
|
|
256
|
-
/**
|
|
257
|
-
* Read a Node-style `code` field from an unknown thrown value.
|
|
259
|
+
* - If `configPath` is provided, absolute paths are returned as-is;
|
|
260
|
+
* relative paths are joined to `cwd`.
|
|
261
|
+
* - Otherwise, walk upward from `cwd` to find known filenames.
|
|
258
262
|
*
|
|
259
|
-
* @param
|
|
260
|
-
* @
|
|
261
|
-
|
|
262
|
-
function getErrorCode(error) {
|
|
263
|
-
return isObject(error) && "code" in error && isString(error.code) ? error.code : void 0;
|
|
264
|
-
}
|
|
265
|
-
/**
|
|
266
|
-
* Check whether a loader error should continue the JS config fallback chain.
|
|
263
|
+
* @param cwd - Starting directory for resolution.
|
|
264
|
+
* @param configPath - Optional explicit path.
|
|
265
|
+
* @returns Absolute path to config file, or undefined when not found.
|
|
267
266
|
*
|
|
268
|
-
* @
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*/
|
|
272
|
-
function isJavaScriptLoaderFallbackError(error, resolvedPaths) {
|
|
273
|
-
const code = getErrorCode(error);
|
|
274
|
-
if (code && requireFallbackErrorCodes.has(code)) return true;
|
|
275
|
-
return error instanceof SyntaxError && resolvedPaths.some((resolvedPath) => error.stack?.startsWith(`${resolvedPath}:`) === true) && syntaxFallbackMessages.some((pattern) => pattern.test(error.message));
|
|
276
|
-
}
|
|
277
|
-
/**
|
|
278
|
-
* Import an ESM config module with entry-file cache busting.
|
|
267
|
+
* @example
|
|
268
|
+
* ```ts
|
|
269
|
+
* import { resolveOxfmtrcPath } from 'load-oxfmt-config'
|
|
279
270
|
*
|
|
280
|
-
*
|
|
281
|
-
*
|
|
271
|
+
* const path = await resolveOxfmtrcPath(process.cwd())
|
|
272
|
+
* ```
|
|
282
273
|
*/
|
|
283
|
-
async function
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
274
|
+
async function resolveOxfmtrcPath(cwd, configPath) {
|
|
275
|
+
const resolvedCwd = resolve(cwd);
|
|
276
|
+
if (configPath) return isAbsolute(configPath) ? configPath : join(resolvedCwd, configPath);
|
|
277
|
+
let currentDir = resolvedCwd;
|
|
278
|
+
while (true) {
|
|
279
|
+
for (const filename of OXFMT_CONFIG_FILES) {
|
|
280
|
+
const configFilePath = join(currentDir, filename);
|
|
281
|
+
try {
|
|
282
|
+
if ((await stat(configFilePath)).isFile()) return configFilePath;
|
|
283
|
+
} catch (error) {
|
|
284
|
+
const code = error.code;
|
|
285
|
+
if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const parentDir = join(currentDir, "..");
|
|
289
|
+
if (parentDir === currentDir) break;
|
|
290
|
+
currentDir = parentDir;
|
|
291
|
+
}
|
|
287
292
|
}
|
|
288
293
|
/**
|
|
289
294
|
* Delete a CommonJS module and all cached children reachable from it.
|
|
@@ -301,26 +306,23 @@ function deleteRequireCacheTree(requirePath, seen = /* @__PURE__ */ new Set()) {
|
|
|
301
306
|
Reflect.deleteProperty(require.cache, requirePath);
|
|
302
307
|
}
|
|
303
308
|
/**
|
|
304
|
-
*
|
|
309
|
+
* Read a Node-style `code` field from an unknown thrown value.
|
|
305
310
|
*
|
|
306
|
-
* @param
|
|
307
|
-
* @returns
|
|
311
|
+
* @param error - Thrown value to inspect.
|
|
312
|
+
* @returns Error code when present.
|
|
308
313
|
*/
|
|
309
|
-
function
|
|
310
|
-
|
|
311
|
-
deleteRequireCacheTree(requirePath);
|
|
312
|
-
return require(requirePath);
|
|
314
|
+
function getErrorCode(error) {
|
|
315
|
+
return isObject(error) && "code" in error && isString(error.code) ? error.code : void 0;
|
|
313
316
|
}
|
|
314
317
|
/**
|
|
315
|
-
*
|
|
318
|
+
* Build a cache-busting import key from entry file contents.
|
|
316
319
|
*
|
|
317
|
-
* @param resolvedPath - Absolute
|
|
318
|
-
* @returns
|
|
320
|
+
* @param resolvedPath - Absolute config file path.
|
|
321
|
+
* @returns Stable key that changes when the entry file contents change.
|
|
319
322
|
*/
|
|
320
|
-
function
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
return readConfigModuleExports(configModule);
|
|
323
|
+
async function getFreshImportCacheKey(resolvedPath) {
|
|
324
|
+
const content = await readFile(resolvedPath);
|
|
325
|
+
return createHash("sha256").update(content).digest("hex");
|
|
324
326
|
}
|
|
325
327
|
/**
|
|
326
328
|
* Load a JavaScript config file without reusing stale module contents.
|
|
@@ -372,105 +374,117 @@ async function importFreshJavaScriptConfigModule(resolvedPath) {
|
|
|
372
374
|
}
|
|
373
375
|
}
|
|
374
376
|
/**
|
|
375
|
-
*
|
|
377
|
+
* Import a JavaScript or TypeScript config module through jiti.
|
|
376
378
|
*
|
|
377
|
-
* @param
|
|
378
|
-
* @param
|
|
379
|
-
* @returns
|
|
379
|
+
* @param resolvedPath - Absolute config file path to import.
|
|
380
|
+
* @param useCache - Whether jiti should use its filesystem and module caches.
|
|
381
|
+
* @returns Imported module namespace.
|
|
380
382
|
*/
|
|
381
|
-
function
|
|
382
|
-
return
|
|
383
|
+
async function importJitiConfigModule(resolvedPath, useCache) {
|
|
384
|
+
return (await interopDefault(import("jiti")))(import.meta.url, useCache ? void 0 : {
|
|
385
|
+
fsCache: false,
|
|
386
|
+
moduleCache: false
|
|
387
|
+
}).import(resolvedPath);
|
|
383
388
|
}
|
|
384
389
|
/**
|
|
385
|
-
*
|
|
390
|
+
* Import an ESM config module with entry-file cache busting.
|
|
386
391
|
*
|
|
387
|
-
* @param resolvedPath -
|
|
388
|
-
* @
|
|
389
|
-
* @param resolveKey - Resolution cache key.
|
|
390
|
-
* @returns Cache key for config content cache.
|
|
392
|
+
* @param resolvedPath - Absolute config file path to import.
|
|
393
|
+
* @returns Imported module namespace.
|
|
391
394
|
*/
|
|
392
|
-
function
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
resolveKey
|
|
397
|
-
]);
|
|
395
|
+
async function importNativeFreshModule(resolvedPath) {
|
|
396
|
+
const url = pathToFileURL(resolvedPath);
|
|
397
|
+
url.searchParams.set("oxfmtConfigCacheBust", await getFreshImportCacheKey(resolvedPath));
|
|
398
|
+
return import(url.href);
|
|
398
399
|
}
|
|
399
400
|
/**
|
|
400
|
-
*
|
|
401
|
-
*
|
|
402
|
-
* - If `configPath` is provided, absolute paths are returned as-is;
|
|
403
|
-
* relative paths are joined to `cwd`.
|
|
404
|
-
* - Otherwise, walk upward from `cwd` to find known filenames.
|
|
401
|
+
* Check whether an unknown config export is an object accepted by oxfmt.
|
|
405
402
|
*
|
|
406
|
-
* @param
|
|
407
|
-
* @
|
|
408
|
-
|
|
403
|
+
* @param value - Runtime value to validate.
|
|
404
|
+
* @returns True when the value can be treated as an oxfmt options object.
|
|
405
|
+
*/
|
|
406
|
+
function isConfigObject(value) {
|
|
407
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
408
|
+
const prototype = Object.getPrototypeOf(value);
|
|
409
|
+
return prototype === Object.prototype || prototype === null;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Check whether a loader error should continue the JS config fallback chain.
|
|
409
413
|
*
|
|
410
|
-
* @
|
|
411
|
-
*
|
|
412
|
-
*
|
|
414
|
+
* @param error - Thrown value from require or native import.
|
|
415
|
+
* @param resolvedPaths - Entry module paths that may appear in parser errors.
|
|
416
|
+
* @returns True when the error is from loader compatibility, not config execution.
|
|
417
|
+
*/
|
|
418
|
+
function isJavaScriptLoaderFallbackError(error, resolvedPaths) {
|
|
419
|
+
const code = getErrorCode(error);
|
|
420
|
+
if (code && requireFallbackErrorCodes.has(code)) return true;
|
|
421
|
+
return error instanceof SyntaxError && resolvedPaths.some((resolvedPath) => error.stack?.startsWith(`${resolvedPath}:`) === true) && syntaxFallbackMessages.some((pattern) => pattern.test(error.message));
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Read the default export from a JavaScript or TypeScript config module.
|
|
413
425
|
*
|
|
414
|
-
*
|
|
415
|
-
*
|
|
426
|
+
* @param mod - Imported config module namespace.
|
|
427
|
+
* @returns Oxfmt config object from the module default export.
|
|
428
|
+
* @throws When the module has no default export or the default export is not an object.
|
|
416
429
|
*/
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
for (const filename of OXFMT_CONFIG_FILES) {
|
|
423
|
-
const configFilePath = join(currentDir, filename);
|
|
424
|
-
try {
|
|
425
|
-
if ((await stat(configFilePath)).isFile()) return configFilePath;
|
|
426
|
-
} catch (error) {
|
|
427
|
-
const code = error.code;
|
|
428
|
-
if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
const parentDir = join(currentDir, "..");
|
|
432
|
-
if (parentDir === currentDir) break;
|
|
433
|
-
currentDir = parentDir;
|
|
434
|
-
}
|
|
430
|
+
function readConfigDefaultExport(mod) {
|
|
431
|
+
if (!hasOwn(mod, "default")) throw new Error("Configuration file has no default export.");
|
|
432
|
+
const config = mod.default;
|
|
433
|
+
if (!isConfigObject(config)) throw new Error("Configuration file must have a default export that is an object.");
|
|
434
|
+
return config;
|
|
435
435
|
}
|
|
436
436
|
/**
|
|
437
|
-
* Read
|
|
437
|
+
* Read a CommonJS module export as an oxfmt config object.
|
|
438
438
|
*
|
|
439
|
-
* @param
|
|
440
|
-
* @
|
|
441
|
-
* @
|
|
439
|
+
* @param config - Runtime CommonJS export value.
|
|
440
|
+
* @returns Oxfmt config object from the module export.
|
|
441
|
+
* @throws When the module export is not an object.
|
|
442
442
|
*/
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
if (
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
443
|
+
function readConfigModuleExports(config) {
|
|
444
|
+
if (!isConfigObject(config)) throw new Error("Configuration file must export an object.");
|
|
445
|
+
return config;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Read a parsed JSON-like config value as an oxfmt config object.
|
|
449
|
+
*
|
|
450
|
+
* @param config - Runtime config value.
|
|
451
|
+
* @returns Oxfmt config object.
|
|
452
|
+
* @throws When the config value is not an object.
|
|
453
|
+
*/
|
|
454
|
+
function readParsedConfigObject(config) {
|
|
455
|
+
if (!isConfigObject(config)) throw new Error("Configuration file must be an object.");
|
|
456
|
+
return config;
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Load a CommonJS config file without reusing Node's require cache.
|
|
460
|
+
*
|
|
461
|
+
* @param resolvedPath - Absolute `.cjs` config file path.
|
|
462
|
+
* @returns Oxfmt config object from default or module exports.
|
|
463
|
+
*/
|
|
464
|
+
function requireFreshCommonJSConfig(resolvedPath) {
|
|
465
|
+
const configModule = requireFreshModule(resolvedPath);
|
|
466
|
+
if (Object.prototype.toString.call(configModule) === "[object Module]") return readConfigDefaultExport(configModule);
|
|
467
|
+
return readConfigModuleExports(configModule);
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Require a CommonJS module after removing it from Node's require cache.
|
|
471
|
+
*
|
|
472
|
+
* @param resolvedPath - Absolute config file path to require.
|
|
473
|
+
* @returns Runtime module export value.
|
|
474
|
+
*/
|
|
475
|
+
function requireFreshModule(resolvedPath) {
|
|
476
|
+
const requirePath = require.resolve(resolvedPath);
|
|
477
|
+
deleteRequireCacheTree(requirePath);
|
|
478
|
+
return require(requirePath);
|
|
470
479
|
}
|
|
471
480
|
//#endregion
|
|
472
481
|
//#region src/editorconfig.ts
|
|
473
482
|
/**
|
|
483
|
+
* Shared input caches for static and per-file EditorConfig resolution.
|
|
484
|
+
*/
|
|
485
|
+
const sectionCache = /* @__PURE__ */ new Map();
|
|
486
|
+
const matcherCache = /* @__PURE__ */ new Map();
|
|
487
|
+
/**
|
|
474
488
|
* Builds the cache key used for resolved EditorConfig lookups.
|
|
475
489
|
*
|
|
476
490
|
* @param resolveKey - The base resolution key for the current lookup.
|
|
@@ -492,19 +506,6 @@ function getEditorconfigSearchDir(cwd, configPath) {
|
|
|
492
506
|
return dirname(resolvedConfigPath);
|
|
493
507
|
}
|
|
494
508
|
/**
|
|
495
|
-
* Merges root-level EditorConfig options with oxfmt config options.
|
|
496
|
-
*
|
|
497
|
-
* @param oxfmtConfig - The explicit oxfmt options.
|
|
498
|
-
* @param editorconfigRootOptions - Root options derived from .editorconfig.
|
|
499
|
-
* @returns A single root options object where oxfmt takes precedence.
|
|
500
|
-
*/
|
|
501
|
-
function mergeRootOptions(oxfmtConfig, editorconfigRootOptions) {
|
|
502
|
-
return {
|
|
503
|
-
...editorconfigRootOptions,
|
|
504
|
-
...oxfmtConfig
|
|
505
|
-
};
|
|
506
|
-
}
|
|
507
|
-
/**
|
|
508
509
|
* Merges EditorConfig-derived overrides with explicit oxfmt overrides.
|
|
509
510
|
*
|
|
510
511
|
* @param oxfmtConfig - Explicit root options and overrides from the oxfmt config.
|
|
@@ -522,6 +523,63 @@ function mergeOverrides(oxfmtConfig, editorconfigOverrides) {
|
|
|
522
523
|
return mergedOverrides.length > 0 ? mergedOverrides : void 0;
|
|
523
524
|
}
|
|
524
525
|
/**
|
|
526
|
+
* Merges root-level EditorConfig options with oxfmt config options.
|
|
527
|
+
*
|
|
528
|
+
* @param oxfmtConfig - The explicit oxfmt options.
|
|
529
|
+
* @param editorconfigRootOptions - Root options derived from .editorconfig.
|
|
530
|
+
* @returns A single root options object where oxfmt takes precedence.
|
|
531
|
+
*/
|
|
532
|
+
function mergeRootOptions(oxfmtConfig, editorconfigRootOptions) {
|
|
533
|
+
return {
|
|
534
|
+
...editorconfigRootOptions,
|
|
535
|
+
...oxfmtConfig
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Reads a .editorconfig file and converts it into root options and override entries.
|
|
540
|
+
*
|
|
541
|
+
* @param editorconfigPath - The absolute path to the .editorconfig file.
|
|
542
|
+
* @param anchorDir - The directory used to rebase section patterns.
|
|
543
|
+
* @param options - Optional per-file resolution and cache controls.
|
|
544
|
+
* @param options.filepath - Resolve matching sections for this absolute file path.
|
|
545
|
+
* @param options.useCache - Reuse parsed sections and compiled matchers.
|
|
546
|
+
* @returns Parsed EditorConfig data ready to merge into oxfmt config.
|
|
547
|
+
*/
|
|
548
|
+
async function readEditorconfigFromFile(editorconfigPath, anchorDir, options = {}) {
|
|
549
|
+
const useCache = options.useCache !== false;
|
|
550
|
+
const readSections = async () => parseBuffer(await readFile(editorconfigPath));
|
|
551
|
+
const parsedSections = useCache ? await cachePromise(sectionCache, editorconfigPath, readSections) : await readSections();
|
|
552
|
+
const editorconfigDir = dirname(editorconfigPath);
|
|
553
|
+
const rootOptions = {};
|
|
554
|
+
const overrides = [];
|
|
555
|
+
const relativeFile = options.filepath ? toPosixPath(relative(editorconfigDir, options.filepath)) : void 0;
|
|
556
|
+
if (relativeFile !== void 0 && isPathOutsideBase(relativeFile)) return {
|
|
557
|
+
overrides,
|
|
558
|
+
rootOptions
|
|
559
|
+
};
|
|
560
|
+
for (const [sectionName, sectionBody] of parsedSections) {
|
|
561
|
+
if (!sectionName) continue;
|
|
562
|
+
const mappedOptions = mapEditorconfigSectionToOptions(sectionBody);
|
|
563
|
+
if (Object.keys(mappedOptions).length === 0) continue;
|
|
564
|
+
if (relativeFile !== void 0) {
|
|
565
|
+
if (matchesEditorconfigSection(sectionName, relativeFile, useCache)) Object.assign(rootOptions, mappedOptions);
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
568
|
+
if (isEditorconfigGlobalSection(sectionName)) {
|
|
569
|
+
Object.assign(rootOptions, mappedOptions);
|
|
570
|
+
if (overrides.length === 0) continue;
|
|
571
|
+
}
|
|
572
|
+
overrides.push({
|
|
573
|
+
options: mappedOptions,
|
|
574
|
+
files: [rebaseEditorconfigPattern(sectionName, editorconfigDir, anchorDir)]
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
return {
|
|
578
|
+
overrides,
|
|
579
|
+
rootOptions
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
525
583
|
* Resolves the nearest .editorconfig file starting from a directory and optionally walking upward.
|
|
526
584
|
*
|
|
527
585
|
* @param startDir - The directory where the search starts.
|
|
@@ -554,6 +612,61 @@ function isEditorconfigGlobalSection(sectionName) {
|
|
|
554
612
|
return Boolean(sectionName && EDITORCONFIG_GLOBAL_SECTION_NAMES.includes(sectionName));
|
|
555
613
|
}
|
|
556
614
|
/**
|
|
615
|
+
* Maps a parsed EditorConfig section to the subset of oxfmt formatter options it supports.
|
|
616
|
+
*
|
|
617
|
+
* @param section - The parsed EditorConfig section body.
|
|
618
|
+
* @returns Formatter options derived from the section.
|
|
619
|
+
*/
|
|
620
|
+
function mapEditorconfigSectionToOptions(section) {
|
|
621
|
+
const options = {};
|
|
622
|
+
const endOfLine = parseEditorconfigEndOfLine(section.end_of_line);
|
|
623
|
+
if (endOfLine) options.endOfLine = endOfLine;
|
|
624
|
+
if (section.indent_style === "tab") options.useTabs = true;
|
|
625
|
+
else if (section.indent_style === "space") options.useTabs = false;
|
|
626
|
+
const singleQuote = parseEditorconfigQuoteType(section.quote_type);
|
|
627
|
+
if (isBoolean(singleQuote)) options.singleQuote = singleQuote;
|
|
628
|
+
const tabWidth = parseEditorconfigTabWidth(section, options.useTabs);
|
|
629
|
+
if (isNumber(tabWidth)) options.tabWidth = tabWidth;
|
|
630
|
+
if (section.max_line_length && section.max_line_length !== "unset") {
|
|
631
|
+
const parsedPrintWidth = Number(section.max_line_length);
|
|
632
|
+
if (Number.isFinite(parsedPrintWidth)) options.printWidth = parsedPrintWidth;
|
|
633
|
+
}
|
|
634
|
+
const insertFinalNewline = parseEditorconfigBoolean(section.insert_final_newline);
|
|
635
|
+
if (isBoolean(insertFinalNewline)) options.insertFinalNewline = insertFinalNewline;
|
|
636
|
+
return options;
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Match a section against its source-relative file path without oxfmt rebasing.
|
|
640
|
+
*
|
|
641
|
+
* @param pattern - Original section name.
|
|
642
|
+
* @param filepath - File path relative to the EditorConfig directory.
|
|
643
|
+
* @param useCache - Whether compiled matchers can be reused.
|
|
644
|
+
* @returns Whether the section applies to the file.
|
|
645
|
+
*/
|
|
646
|
+
function matchesEditorconfigSection(pattern, filepath, useCache) {
|
|
647
|
+
let matcher = useCache ? getCacheValue(matcherCache, pattern) : void 0;
|
|
648
|
+
if (!matcher) {
|
|
649
|
+
matcher = new Minimatch(normalizeEditorconfigPattern(pattern), {
|
|
650
|
+
dot: true,
|
|
651
|
+
nocomment: true,
|
|
652
|
+
noext: true,
|
|
653
|
+
nonegate: true
|
|
654
|
+
});
|
|
655
|
+
if (useCache) setCacheValue(matcherCache, pattern, matcher);
|
|
656
|
+
}
|
|
657
|
+
return matcher.match(filepath);
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Keep basename patterns recursive without changing path-scoped patterns.
|
|
661
|
+
*
|
|
662
|
+
* @param pattern - Original EditorConfig section name.
|
|
663
|
+
* @returns Pattern suitable for matching a path relative to EditorConfig.
|
|
664
|
+
*/
|
|
665
|
+
function normalizeEditorconfigPattern(pattern) {
|
|
666
|
+
if (pattern === "*" || pattern === "**") return "**";
|
|
667
|
+
return pattern.includes("/") ? pattern : `**/${pattern}`;
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
557
670
|
* Parses an EditorConfig boolean string.
|
|
558
671
|
*
|
|
559
672
|
* @param value - The raw EditorConfig value.
|
|
@@ -570,7 +683,11 @@ function parseEditorconfigBoolean(value) {
|
|
|
570
683
|
* @returns The normalized line ending value, or undefined when unsupported.
|
|
571
684
|
*/
|
|
572
685
|
function parseEditorconfigEndOfLine(value) {
|
|
573
|
-
|
|
686
|
+
switch (value) {
|
|
687
|
+
case "cr":
|
|
688
|
+
case "crlf":
|
|
689
|
+
case "lf": return value;
|
|
690
|
+
}
|
|
574
691
|
}
|
|
575
692
|
/**
|
|
576
693
|
* Parses the single quote preference from an EditorConfig section.
|
|
@@ -590,8 +707,8 @@ function parseEditorconfigQuoteType(value) {
|
|
|
590
707
|
* @returns The resolved tab width, or undefined when no numeric value is available.
|
|
591
708
|
*/
|
|
592
709
|
function parseEditorconfigTabWidth(section, useTabs) {
|
|
593
|
-
const indentSize = section
|
|
594
|
-
const tabWidth = section
|
|
710
|
+
const indentSize = section.indent_size;
|
|
711
|
+
const tabWidth = section.tab_width;
|
|
595
712
|
if (useTabs === false && indentSize && indentSize !== "unset" && indentSize !== "tab") {
|
|
596
713
|
const parsedIndentSize = Number(indentSize);
|
|
597
714
|
if (Number.isFinite(parsedIndentSize)) return parsedIndentSize;
|
|
@@ -602,30 +719,6 @@ function parseEditorconfigTabWidth(section, useTabs) {
|
|
|
602
719
|
}
|
|
603
720
|
}
|
|
604
721
|
/**
|
|
605
|
-
* Maps a parsed EditorConfig section to the subset of oxfmt formatter options it supports.
|
|
606
|
-
*
|
|
607
|
-
* @param section - The parsed EditorConfig section body.
|
|
608
|
-
* @returns Formatter options derived from the section.
|
|
609
|
-
*/
|
|
610
|
-
function mapEditorconfigSectionToOptions(section) {
|
|
611
|
-
const options = {};
|
|
612
|
-
const endOfLine = parseEditorconfigEndOfLine(section["end_of_line"]);
|
|
613
|
-
if (endOfLine) options.endOfLine = endOfLine;
|
|
614
|
-
if (section["indent_style"] === "tab") options.useTabs = true;
|
|
615
|
-
else if (section["indent_style"] === "space") options.useTabs = false;
|
|
616
|
-
const singleQuote = parseEditorconfigQuoteType(section["quote_type"]);
|
|
617
|
-
if (isBoolean(singleQuote)) options.singleQuote = singleQuote;
|
|
618
|
-
const tabWidth = parseEditorconfigTabWidth(section, options.useTabs);
|
|
619
|
-
if (isNumber(tabWidth)) options.tabWidth = tabWidth;
|
|
620
|
-
if (section["max_line_length"] && section["max_line_length"] !== "unset") {
|
|
621
|
-
const parsedPrintWidth = Number(section["max_line_length"]);
|
|
622
|
-
if (Number.isFinite(parsedPrintWidth)) options.printWidth = parsedPrintWidth;
|
|
623
|
-
}
|
|
624
|
-
const insertFinalNewline = parseEditorconfigBoolean(section["insert_final_newline"]);
|
|
625
|
-
if (isBoolean(insertFinalNewline)) options.insertFinalNewline = insertFinalNewline;
|
|
626
|
-
return options;
|
|
627
|
-
}
|
|
628
|
-
/**
|
|
629
722
|
* Rebases an EditorConfig section pattern from the config directory to the target anchor directory.
|
|
630
723
|
*
|
|
631
724
|
* @param pattern - The original EditorConfig section pattern.
|
|
@@ -634,40 +727,10 @@ function mapEditorconfigSectionToOptions(section) {
|
|
|
634
727
|
* @returns The rebased glob pattern.
|
|
635
728
|
*/
|
|
636
729
|
function rebaseEditorconfigPattern(pattern, editorconfigDir, anchorDir) {
|
|
730
|
+
const normalizedPattern = normalizeEditorconfigPattern(pattern);
|
|
637
731
|
const relativePrefix = toPosixPath(relative(anchorDir, editorconfigDir));
|
|
638
|
-
if (!relativePrefix || relativePrefix === ".") return
|
|
639
|
-
return `${relativePrefix}/${
|
|
640
|
-
}
|
|
641
|
-
/**
|
|
642
|
-
* Reads a .editorconfig file and converts it into root options and override entries.
|
|
643
|
-
*
|
|
644
|
-
* @param editorconfigPath - The absolute path to the .editorconfig file.
|
|
645
|
-
* @param anchorDir - The directory used to rebase section patterns.
|
|
646
|
-
* @returns Parsed EditorConfig data ready to merge into oxfmt config.
|
|
647
|
-
*/
|
|
648
|
-
async function readEditorconfigFromFile(editorconfigPath, anchorDir) {
|
|
649
|
-
const content = await readFile(editorconfigPath);
|
|
650
|
-
const parsedSections = parseBuffer(content);
|
|
651
|
-
const editorconfigDir = dirname(editorconfigPath);
|
|
652
|
-
const rootOptions = {};
|
|
653
|
-
const overrides = [];
|
|
654
|
-
for (const [sectionName, sectionBody] of parsedSections) {
|
|
655
|
-
if (!sectionName) continue;
|
|
656
|
-
const mappedOptions = mapEditorconfigSectionToOptions(sectionBody);
|
|
657
|
-
if (Object.keys(mappedOptions).length === 0) continue;
|
|
658
|
-
if (isEditorconfigGlobalSection(sectionName)) {
|
|
659
|
-
Object.assign(rootOptions, mappedOptions);
|
|
660
|
-
continue;
|
|
661
|
-
}
|
|
662
|
-
overrides.push({
|
|
663
|
-
files: [rebaseEditorconfigPattern(sectionName, editorconfigDir, anchorDir)],
|
|
664
|
-
options: mappedOptions
|
|
665
|
-
});
|
|
666
|
-
}
|
|
667
|
-
return {
|
|
668
|
-
overrides,
|
|
669
|
-
rootOptions
|
|
670
|
-
};
|
|
732
|
+
if (!relativePrefix || relativePrefix === ".") return normalizedPattern;
|
|
733
|
+
return `${relativePrefix}/${normalizedPattern.replace(/^\//u, "")}`;
|
|
671
734
|
}
|
|
672
735
|
//#endregion
|
|
673
736
|
//#region src/core.ts
|
|
@@ -680,6 +743,10 @@ const resolveCache = /* @__PURE__ */ new Map();
|
|
|
680
743
|
*/
|
|
681
744
|
const configCache = /* @__PURE__ */ new Map();
|
|
682
745
|
/**
|
|
746
|
+
* Share raw config reads across files without caching file-specific fallbacks.
|
|
747
|
+
*/
|
|
748
|
+
const sourceCache = /* @__PURE__ */ new Map();
|
|
749
|
+
/**
|
|
683
750
|
* Resolve config + editorconfig and return merged config with metadata.
|
|
684
751
|
*
|
|
685
752
|
* @param options - Loader settings.
|
|
@@ -694,6 +761,26 @@ const configCache = /* @__PURE__ */ new Map();
|
|
|
694
761
|
* ```
|
|
695
762
|
*/
|
|
696
763
|
async function loadOxfmtConfig(options = {}) {
|
|
764
|
+
return loadConfig(options);
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Load config with EditorConfig fallbacks resolved for one file.
|
|
768
|
+
* Explicit oxfmt overrides remain unevaluated and retain their config-relative globs.
|
|
769
|
+
*
|
|
770
|
+
* @param options - Loader settings with the target file path.
|
|
771
|
+
* @returns Config with per-file EditorConfig defaults and config metadata.
|
|
772
|
+
*/
|
|
773
|
+
async function loadOxfmtConfigForFile(options) {
|
|
774
|
+
return loadConfig(options, true);
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* Share discovery and caches between static and per-file loading.
|
|
778
|
+
*
|
|
779
|
+
* @param options - Loader settings.
|
|
780
|
+
* @param resolveEditorconfig - Resolve EditorConfig sections against the target file.
|
|
781
|
+
* @returns Loaded config and metadata.
|
|
782
|
+
*/
|
|
783
|
+
async function loadConfig(options, resolveEditorconfig = false) {
|
|
697
784
|
const useCache = options.useCache !== false;
|
|
698
785
|
const cwd = resolve(options.cwd || process.cwd());
|
|
699
786
|
const filepath = options.filepath ? resolve(cwd, options.filepath) : void 0;
|
|
@@ -709,12 +796,17 @@ async function loadOxfmtConfig(options = {}) {
|
|
|
709
796
|
const resolvedPath = useCache ? await cachePromise(resolveCache, resolveKey, () => resolveOxfmtrcPath(configLookupCwd, options.configPath)) : await resolveOxfmtrcPath(configLookupCwd, options.configPath);
|
|
710
797
|
const editorconfigPath = useEditorconfig ? await (useCache ? cachePromise(resolveCache, editorconfigResolveKey, () => resolveEditorconfigPath(editorconfigSearchDir, onlyCwd)) : resolveEditorconfigPath(editorconfigSearchDir, onlyCwd)) : void 0;
|
|
711
798
|
const anchorDir = dirname(resolvedPath || editorconfigPath || cwd);
|
|
799
|
+
const configKey = getConfigCacheKey(resolvedPath, editorconfigPath, JSON.stringify([resolveKey, resolveEditorconfig ? filepath : null]));
|
|
712
800
|
const loadTask = async () => {
|
|
713
|
-
const
|
|
801
|
+
const readConfig = async () => resolvedPath ? await readConfigFromFile(resolvedPath, { useCache }).catch((error) => {
|
|
714
802
|
throw new Error(`Failed to parse oxfmt configuration file at ${resolvedPath}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
715
803
|
}) : {};
|
|
804
|
+
const oxfmtConfig = useCache && resolvedPath ? await cachePromise(sourceCache, resolvedPath, readConfig) : await readConfig();
|
|
716
805
|
if (!editorconfigPath) return oxfmtConfig;
|
|
717
|
-
const editorconfigData = await readEditorconfigFromFile(editorconfigPath, anchorDir
|
|
806
|
+
const editorconfigData = await readEditorconfigFromFile(editorconfigPath, anchorDir, {
|
|
807
|
+
filepath: resolveEditorconfig ? filepath : void 0,
|
|
808
|
+
useCache
|
|
809
|
+
});
|
|
718
810
|
const mergedConfig = mergeRootOptions(oxfmtConfig, editorconfigData.rootOptions);
|
|
719
811
|
const mergedOverrides = mergeOverrides(oxfmtConfig, editorconfigData.overrides);
|
|
720
812
|
if (!mergedOverrides) return mergedConfig;
|
|
@@ -723,15 +815,11 @@ async function loadOxfmtConfig(options = {}) {
|
|
|
723
815
|
overrides: mergedOverrides
|
|
724
816
|
};
|
|
725
817
|
};
|
|
726
|
-
const hasNoConfigSources = !resolvedPath && !editorconfigPath;
|
|
727
818
|
return {
|
|
728
|
-
config: await (
|
|
729
|
-
if (hasNoConfigSources) return useCache ? await cachePromise(configCache, getConfigCacheKey(resolvedPath, editorconfigPath, resolveKey), () => Promise.resolve({})) : {};
|
|
730
|
-
return useCache ? await cachePromise(configCache, getConfigCacheKey(resolvedPath, editorconfigPath, resolveKey), loadTask) : await loadTask();
|
|
731
|
-
})(),
|
|
819
|
+
config: useCache ? await cachePromise(configCache, configKey, loadTask) : await loadTask(),
|
|
732
820
|
...resolvedPath ? {
|
|
733
|
-
|
|
734
|
-
|
|
821
|
+
dirname: dirname(resolvedPath),
|
|
822
|
+
filepath: resolvedPath
|
|
735
823
|
} : {}
|
|
736
824
|
};
|
|
737
825
|
}
|
|
@@ -746,10 +834,108 @@ const ignoreMatcherCache = /* @__PURE__ */ new Map();
|
|
|
746
834
|
*/
|
|
747
835
|
const configIgnoreMatcherCache = /* @__PURE__ */ new Map();
|
|
748
836
|
/**
|
|
837
|
+
* Resolve whether a file should be ignored using oxfmt-like CLI semantics.
|
|
838
|
+
*
|
|
839
|
+
* @param options - Ignore resolution options.
|
|
840
|
+
* @returns Ignore status with optional reason.
|
|
841
|
+
*
|
|
842
|
+
* @example
|
|
843
|
+
* ```ts
|
|
844
|
+
* import { isOxfmtIgnored } from 'load-oxfmt-config'
|
|
845
|
+
*
|
|
846
|
+
* const result = await isOxfmtIgnored({
|
|
847
|
+
* cwd: process.cwd(),
|
|
848
|
+
* filepath: 'src/generated/file.ts',
|
|
849
|
+
* })
|
|
850
|
+
* ```
|
|
851
|
+
*/
|
|
852
|
+
async function isOxfmtIgnored(options) {
|
|
853
|
+
const useCache = options.useCache !== false;
|
|
854
|
+
const includeConfigIgnorePatterns = options.includeConfigIgnorePatterns !== false;
|
|
855
|
+
const loadConfigForIgnorePatterns = options.loadConfigForIgnorePatterns !== false;
|
|
856
|
+
const cwd = resolve(options.cwd ?? process.cwd());
|
|
857
|
+
const filepath = isAbsolute(options.filepath) ? resolve(options.filepath) : resolve(cwd, options.filepath);
|
|
858
|
+
if (options.includeDefaultIgnores !== false) {
|
|
859
|
+
const defaultResult = await resolveDefaultIgnores(filepath, cwd, options, useCache);
|
|
860
|
+
if (defaultResult.ignored) return defaultResult;
|
|
861
|
+
}
|
|
862
|
+
if (!loadConfigForIgnorePatterns) return { ignored: false };
|
|
863
|
+
const configResult = await loadOxfmtConfig({
|
|
864
|
+
cwd,
|
|
865
|
+
filepath,
|
|
866
|
+
...options.disableNestedConfig === true ? { disableNestedConfig: true } : {},
|
|
867
|
+
...options.configPath ? { configPath: options.configPath } : {},
|
|
868
|
+
editorconfig: false,
|
|
869
|
+
useCache
|
|
870
|
+
});
|
|
871
|
+
if (includeConfigIgnorePatterns && configResult.dirname && configResult.config.ignorePatterns && configResult.config.ignorePatterns.length > 0 && matchConfigIgnorePatterns(filepath, configResult.dirname, configResult.config.ignorePatterns, useCache)) return {
|
|
872
|
+
ignored: true,
|
|
873
|
+
reason: "config-ignore-patterns"
|
|
874
|
+
};
|
|
875
|
+
return { ignored: false };
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* Collect `.gitignore` files from file directory up to git repo boundary.
|
|
879
|
+
*
|
|
880
|
+
* @param filepath - Absolute file path to test.
|
|
881
|
+
* @returns Collected ignore files and repo root when found.
|
|
882
|
+
*/
|
|
883
|
+
async function collectGitignorePaths(filepath) {
|
|
884
|
+
const fileDir = dirname(filepath);
|
|
885
|
+
const repoRoot = await findGitRepoRoot(fileDir);
|
|
886
|
+
const paths = [];
|
|
887
|
+
let current = fileDir;
|
|
888
|
+
while (true) {
|
|
889
|
+
paths.push(join(current, ".gitignore"));
|
|
890
|
+
if (repoRoot && current === repoRoot) break;
|
|
891
|
+
const parent = dirname(current);
|
|
892
|
+
if (parent === current) break;
|
|
893
|
+
current = parent;
|
|
894
|
+
}
|
|
895
|
+
return {
|
|
896
|
+
paths,
|
|
897
|
+
repoRoot
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Find the nearest git repo root by walking up from a start directory.
|
|
902
|
+
*
|
|
903
|
+
* @param fromDir - Directory to start from.
|
|
904
|
+
* @returns Repo root directory, or undefined when no git boundary is found.
|
|
905
|
+
*/
|
|
906
|
+
async function findGitRepoRoot(fromDir) {
|
|
907
|
+
let current = fromDir;
|
|
908
|
+
while (true) {
|
|
909
|
+
if (await hasGitEntry(current)) return current;
|
|
910
|
+
const parent = dirname(current);
|
|
911
|
+
if (parent === current) return;
|
|
912
|
+
current = parent;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* Check whether a directory looks like a git repo root.
|
|
917
|
+
*
|
|
918
|
+
* A `.git` entry can be either a directory (regular repo) or a file (worktree/submodule).
|
|
919
|
+
*
|
|
920
|
+
* @param dir - Directory to inspect.
|
|
921
|
+
* @returns True when `.git` exists under the directory.
|
|
922
|
+
*/
|
|
923
|
+
async function hasGitEntry(dir) {
|
|
924
|
+
try {
|
|
925
|
+
await stat(join(dir, ".git"));
|
|
926
|
+
return true;
|
|
927
|
+
} catch (error) {
|
|
928
|
+
const code = error.code;
|
|
929
|
+
if (code === "ENOENT" || code === "ENOTDIR") return false;
|
|
930
|
+
throw error;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
/**
|
|
749
934
|
* Check whether a file is under oxfmt's default ignored directories.
|
|
750
935
|
*
|
|
751
936
|
* @param filepath - Absolute file path to test.
|
|
752
937
|
* @param options - Matching options.
|
|
938
|
+
* @param options.withNodeModules - Whether node_modules should remain eligible.
|
|
753
939
|
* @returns True when the path should be ignored by default directory rules.
|
|
754
940
|
*/
|
|
755
941
|
function isDefaultIgnoredDir(filepath, options) {
|
|
@@ -787,14 +973,25 @@ function loadIgnoreMatcher(ignoreFilePath, useCache) {
|
|
|
787
973
|
return cachePromise(ignoreMatcherCache, ignoreFilePath, loadTask);
|
|
788
974
|
}
|
|
789
975
|
/**
|
|
790
|
-
*
|
|
976
|
+
* Match `ignorePatterns` from config with support for negated patterns.
|
|
791
977
|
*
|
|
792
|
-
* @param
|
|
793
|
-
* @param
|
|
794
|
-
* @
|
|
978
|
+
* @param filepath - Absolute file path.
|
|
979
|
+
* @param configDir - Resolved config directory.
|
|
980
|
+
* @param patterns - Config ignore patterns.
|
|
981
|
+
* @param useCache - Whether to reuse compiled pattern matchers.
|
|
982
|
+
* @returns True when patterns mark the file as ignored.
|
|
795
983
|
*/
|
|
796
|
-
function
|
|
797
|
-
|
|
984
|
+
function matchConfigIgnorePatterns(filepath, configDir, patterns, useCache) {
|
|
985
|
+
validateConfigIgnorePatterns(patterns);
|
|
986
|
+
const relativeFile = relativeSafe(configDir, filepath);
|
|
987
|
+
if (isPathOutsideBase(relativeFile)) return false;
|
|
988
|
+
if (!useCache) return ignore().add(patterns).ignores(relativeFile);
|
|
989
|
+
const cacheKey = `${configDir}::${JSON.stringify(patterns)}`;
|
|
990
|
+
const cachedMatcher = getCacheValue(configIgnoreMatcherCache, cacheKey);
|
|
991
|
+
if (cachedMatcher) return cachedMatcher.ignores(relativeFile);
|
|
992
|
+
const matcher = ignore().add(patterns);
|
|
993
|
+
setCacheValue(configIgnoreMatcherCache, cacheKey, matcher);
|
|
994
|
+
return matcher.ignores(relativeFile);
|
|
798
995
|
}
|
|
799
996
|
/**
|
|
800
997
|
* Match file path against one ignore file.
|
|
@@ -813,6 +1010,28 @@ async function matchIgnoreFile(filepath, ignoreFilePath, useCache, baseDir) {
|
|
|
813
1010
|
return matcher.ignores(relativeToIgnore);
|
|
814
1011
|
}
|
|
815
1012
|
/**
|
|
1013
|
+
* Match a file path against ordered ignore files while preserving negation state.
|
|
1014
|
+
*
|
|
1015
|
+
* @param filepath - Absolute file path.
|
|
1016
|
+
* @param ignoreFileEntries - Ignore files in increasing precedence order.
|
|
1017
|
+
* @param useCache - Whether to use matcher cache.
|
|
1018
|
+
* @param stopAtIgnoredDirectory - Whether ignored directories prevent reading nested ignore files.
|
|
1019
|
+
* @returns True when the ordered ignore files mark the file as ignored.
|
|
1020
|
+
*/
|
|
1021
|
+
async function matchIgnoreFileChain(filepath, ignoreFileEntries, useCache, stopAtIgnoredDirectory = false) {
|
|
1022
|
+
const matcherEntries = [];
|
|
1023
|
+
for (const entry of ignoreFileEntries) {
|
|
1024
|
+
const baseDir = entry.baseDir ?? dirname(entry.path);
|
|
1025
|
+
if (stopAtIgnoredDirectory && matchLoadedIgnoreMatchers(baseDir, matcherEntries, true).ignored) break;
|
|
1026
|
+
const matcher = await loadIgnoreMatcher(entry.path, useCache);
|
|
1027
|
+
if (matcher) matcherEntries.push({
|
|
1028
|
+
baseDir,
|
|
1029
|
+
matcher
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
return matchLoadedIgnoreMatchers(filepath, matcherEntries);
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
816
1035
|
* Match a path against loaded ignore matchers in precedence order.
|
|
817
1036
|
*
|
|
818
1037
|
* @param filepath - Absolute file or directory path to test.
|
|
@@ -842,54 +1061,57 @@ function matchLoadedIgnoreMatchers(filepath, matcherEntries, isDirectory = false
|
|
|
842
1061
|
};
|
|
843
1062
|
}
|
|
844
1063
|
/**
|
|
845
|
-
*
|
|
846
|
-
*
|
|
847
|
-
* @param filepath - Absolute file path.
|
|
848
|
-
* @param ignoreFileEntries - Ignore files in increasing precedence order.
|
|
849
|
-
* @param useCache - Whether to use matcher cache.
|
|
850
|
-
* @param stopAtIgnoredDirectory - Whether ignored directories prevent reading nested ignore files.
|
|
851
|
-
* @returns True when the ordered ignore files mark the file as ignored.
|
|
852
|
-
*/
|
|
853
|
-
async function matchIgnoreFileChain(filepath, ignoreFileEntries, useCache, stopAtIgnoredDirectory = false) {
|
|
854
|
-
const matcherEntries = [];
|
|
855
|
-
for (const entry of ignoreFileEntries) {
|
|
856
|
-
const baseDir = entry.baseDir ?? dirname(entry.path);
|
|
857
|
-
if (stopAtIgnoredDirectory && matchLoadedIgnoreMatchers(baseDir, matcherEntries, true).ignored) break;
|
|
858
|
-
const matcher = await loadIgnoreMatcher(entry.path, useCache);
|
|
859
|
-
if (matcher) matcherEntries.push({
|
|
860
|
-
baseDir,
|
|
861
|
-
matcher
|
|
862
|
-
});
|
|
863
|
-
}
|
|
864
|
-
return matchLoadedIgnoreMatchers(filepath, matcherEntries);
|
|
865
|
-
}
|
|
866
|
-
/**
|
|
867
|
-
* Resolve an ignore file path against cwd when needed.
|
|
1064
|
+
* Build a POSIX relative path.
|
|
868
1065
|
*
|
|
869
|
-
* @param
|
|
870
|
-
* @param
|
|
871
|
-
* @returns
|
|
1066
|
+
* @param from - Base directory.
|
|
1067
|
+
* @param to - Target path.
|
|
1068
|
+
* @returns POSIX relative path.
|
|
872
1069
|
*/
|
|
873
|
-
function
|
|
874
|
-
return
|
|
1070
|
+
function relativeSafe(from, to) {
|
|
1071
|
+
return toPosixPath(relative(from, to));
|
|
875
1072
|
}
|
|
876
1073
|
/**
|
|
877
|
-
*
|
|
1074
|
+
* Evaluate global ignore rules separately from explicit config patterns.
|
|
878
1075
|
*
|
|
879
|
-
*
|
|
880
|
-
*
|
|
881
|
-
* @param
|
|
882
|
-
* @
|
|
1076
|
+
* @param filepath - Absolute target file path.
|
|
1077
|
+
* @param cwd - Base directory for ignore file resolution.
|
|
1078
|
+
* @param options - Ignore file and node_modules settings.
|
|
1079
|
+
* @param useCache - Whether ignore matchers can be reused.
|
|
1080
|
+
* @returns Global ignore status.
|
|
883
1081
|
*/
|
|
884
|
-
async function
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
1082
|
+
async function resolveDefaultIgnores(filepath, cwd, options, useCache) {
|
|
1083
|
+
if (isDefaultIgnoredDir(filepath, options.withNodeModules ? { withNodeModules: true } : {})) return {
|
|
1084
|
+
ignored: true,
|
|
1085
|
+
reason: "default-dir"
|
|
1086
|
+
};
|
|
1087
|
+
if (isLockfile(filepath)) return {
|
|
1088
|
+
ignored: true,
|
|
1089
|
+
reason: "lockfile"
|
|
1090
|
+
};
|
|
1091
|
+
const explicitIgnorePaths = (typeof options.ignorePath === "string" ? [options.ignorePath] : options.ignorePath)?.map((path) => resolveIgnoreFilePath(path, cwd));
|
|
1092
|
+
const { paths: gitignorePaths, repoRoot } = await collectGitignorePaths(filepath);
|
|
1093
|
+
const gitignoreResult = await matchIgnoreFileChain(filepath, [...gitignorePaths].reverse().map((path) => ({ path })), useCache, true);
|
|
1094
|
+
if (gitignoreResult.ignored) return {
|
|
1095
|
+
ignored: true,
|
|
1096
|
+
reason: "gitignore"
|
|
1097
|
+
};
|
|
1098
|
+
if (repoRoot && !gitignoreResult.matched) {
|
|
1099
|
+
const infoExcludePath = await resolveGitInfoExcludePath(repoRoot);
|
|
1100
|
+
if (infoExcludePath && await matchIgnoreFile(filepath, infoExcludePath, useCache, repoRoot)) return {
|
|
1101
|
+
ignored: true,
|
|
1102
|
+
reason: "git-info-exclude"
|
|
1103
|
+
};
|
|
892
1104
|
}
|
|
1105
|
+
if (explicitIgnorePaths && explicitIgnorePaths.length > 0) {
|
|
1106
|
+
if ((await matchIgnoreFileChain(filepath, explicitIgnorePaths.map((path) => ({ path })), useCache)).ignored) return {
|
|
1107
|
+
ignored: true,
|
|
1108
|
+
reason: "ignore-path"
|
|
1109
|
+
};
|
|
1110
|
+
} else if (await matchIgnoreFile(filepath, resolve(cwd, ".prettierignore"), useCache)) return {
|
|
1111
|
+
ignored: true,
|
|
1112
|
+
reason: "prettierignore"
|
|
1113
|
+
};
|
|
1114
|
+
return { ignored: false };
|
|
893
1115
|
}
|
|
894
1116
|
/**
|
|
895
1117
|
* Resolve the git info exclude file from a repo root.
|
|
@@ -927,42 +1149,14 @@ async function resolveGitInfoExcludePath(repoRoot) {
|
|
|
927
1149
|
}
|
|
928
1150
|
}
|
|
929
1151
|
/**
|
|
930
|
-
*
|
|
931
|
-
*
|
|
932
|
-
* @param fromDir - Directory to start from.
|
|
933
|
-
* @returns Repo root directory, or undefined when no git boundary is found.
|
|
934
|
-
*/
|
|
935
|
-
async function findGitRepoRoot(fromDir) {
|
|
936
|
-
let current = fromDir;
|
|
937
|
-
while (true) {
|
|
938
|
-
if (await hasGitEntry(current)) return current;
|
|
939
|
-
const parent = dirname(current);
|
|
940
|
-
if (parent === current) return;
|
|
941
|
-
current = parent;
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
/**
|
|
945
|
-
* Collect `.gitignore` files from file directory up to git repo boundary.
|
|
1152
|
+
* Resolve an ignore file path against cwd when needed.
|
|
946
1153
|
*
|
|
947
|
-
* @param
|
|
948
|
-
* @
|
|
1154
|
+
* @param path - Absolute or relative path.
|
|
1155
|
+
* @param cwd - Current working directory.
|
|
1156
|
+
* @returns Absolute ignore file path.
|
|
949
1157
|
*/
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
const repoRoot = await findGitRepoRoot(fileDir);
|
|
953
|
-
const paths = [];
|
|
954
|
-
let current = fileDir;
|
|
955
|
-
while (true) {
|
|
956
|
-
paths.push(join(current, ".gitignore"));
|
|
957
|
-
if (repoRoot && current === repoRoot) break;
|
|
958
|
-
const parent = dirname(current);
|
|
959
|
-
if (parent === current) break;
|
|
960
|
-
current = parent;
|
|
961
|
-
}
|
|
962
|
-
return {
|
|
963
|
-
paths,
|
|
964
|
-
repoRoot
|
|
965
|
-
};
|
|
1158
|
+
function resolveIgnoreFilePath(path, cwd) {
|
|
1159
|
+
return isAbsolute(path) ? path : resolve(cwd, path);
|
|
966
1160
|
}
|
|
967
1161
|
/**
|
|
968
1162
|
* Validate config ignore patterns using oxfmt's path constraints.
|
|
@@ -976,94 +1170,5 @@ function validateConfigIgnorePatterns(patterns) {
|
|
|
976
1170
|
if ((pathPart.endsWith(String.raw`\ `) ? pathPart : pathPart.trimEnd()).split("/").includes("..")) throw new Error(`Invalid pattern \`${pattern}\` in \`ignorePatterns\`: \`..\` is not supported, patterns are resolved within the config file's directory`);
|
|
977
1171
|
}
|
|
978
1172
|
}
|
|
979
|
-
/**
|
|
980
|
-
* Match `ignorePatterns` from config with support for negated patterns.
|
|
981
|
-
*
|
|
982
|
-
* @param filepath - Absolute file path.
|
|
983
|
-
* @param configDir - Resolved config directory.
|
|
984
|
-
* @param patterns - Config ignore patterns.
|
|
985
|
-
* @param useCache - Whether to reuse compiled pattern matchers.
|
|
986
|
-
* @returns True when patterns mark the file as ignored.
|
|
987
|
-
*/
|
|
988
|
-
function matchConfigIgnorePatterns(filepath, configDir, patterns, useCache) {
|
|
989
|
-
validateConfigIgnorePatterns(patterns);
|
|
990
|
-
const relativeFile = relativeSafe(configDir, filepath);
|
|
991
|
-
if (isPathOutsideBase(relativeFile)) return false;
|
|
992
|
-
if (!useCache) return ignore().add(patterns).ignores(relativeFile);
|
|
993
|
-
const cacheKey = `${configDir}::${JSON.stringify(patterns)}`;
|
|
994
|
-
const cachedMatcher = getCacheValue(configIgnoreMatcherCache, cacheKey);
|
|
995
|
-
if (cachedMatcher) return cachedMatcher.ignores(relativeFile);
|
|
996
|
-
const matcher = ignore().add(patterns);
|
|
997
|
-
setCacheValue(configIgnoreMatcherCache, cacheKey, matcher);
|
|
998
|
-
return matcher.ignores(relativeFile);
|
|
999
|
-
}
|
|
1000
|
-
/**
|
|
1001
|
-
* Resolve whether a file should be ignored using oxfmt-like CLI semantics.
|
|
1002
|
-
*
|
|
1003
|
-
* @param options - Ignore resolution options.
|
|
1004
|
-
* @returns Ignore status with optional reason.
|
|
1005
|
-
*
|
|
1006
|
-
* @example
|
|
1007
|
-
* ```ts
|
|
1008
|
-
* import { isOxfmtIgnored } from 'load-oxfmt-config'
|
|
1009
|
-
*
|
|
1010
|
-
* const result = await isOxfmtIgnored({
|
|
1011
|
-
* cwd: process.cwd(),
|
|
1012
|
-
* filepath: 'src/generated/file.ts',
|
|
1013
|
-
* })
|
|
1014
|
-
* ```
|
|
1015
|
-
*/
|
|
1016
|
-
async function isOxfmtIgnored(options) {
|
|
1017
|
-
const useCache = options.useCache !== false;
|
|
1018
|
-
const includeConfigIgnorePatterns = options.includeConfigIgnorePatterns !== false;
|
|
1019
|
-
const loadConfigForIgnorePatterns = options.loadConfigForIgnorePatterns !== false;
|
|
1020
|
-
const cwd = resolve(options.cwd ?? process.cwd());
|
|
1021
|
-
const filepath = isAbsolute(options.filepath) ? resolve(options.filepath) : resolve(cwd, options.filepath);
|
|
1022
|
-
if (isDefaultIgnoredDir(filepath, options.withNodeModules ? { withNodeModules: true } : {})) return {
|
|
1023
|
-
ignored: true,
|
|
1024
|
-
reason: "default-dir"
|
|
1025
|
-
};
|
|
1026
|
-
if (isLockfile(filepath)) return {
|
|
1027
|
-
ignored: true,
|
|
1028
|
-
reason: "lockfile"
|
|
1029
|
-
};
|
|
1030
|
-
const explicitIgnorePaths = (typeof options.ignorePath === "string" ? [options.ignorePath] : options.ignorePath)?.map((path) => resolveIgnoreFilePath(path, cwd));
|
|
1031
|
-
const { paths: gitignorePaths, repoRoot } = await collectGitignorePaths(filepath);
|
|
1032
|
-
const gitignoreResult = await matchIgnoreFileChain(filepath, [...gitignorePaths].reverse().map((path) => ({ path })), useCache, true);
|
|
1033
|
-
if (gitignoreResult.ignored) return {
|
|
1034
|
-
ignored: true,
|
|
1035
|
-
reason: "gitignore"
|
|
1036
|
-
};
|
|
1037
|
-
if (repoRoot && !gitignoreResult.matched) {
|
|
1038
|
-
const infoExcludePath = await resolveGitInfoExcludePath(repoRoot);
|
|
1039
|
-
if (infoExcludePath && await matchIgnoreFile(filepath, infoExcludePath, useCache, repoRoot)) return {
|
|
1040
|
-
ignored: true,
|
|
1041
|
-
reason: "git-info-exclude"
|
|
1042
|
-
};
|
|
1043
|
-
}
|
|
1044
|
-
if (explicitIgnorePaths && explicitIgnorePaths.length > 0) {
|
|
1045
|
-
if ((await matchIgnoreFileChain(filepath, explicitIgnorePaths.map((path) => ({ path })), useCache)).ignored) return {
|
|
1046
|
-
ignored: true,
|
|
1047
|
-
reason: "ignore-path"
|
|
1048
|
-
};
|
|
1049
|
-
} else if (await matchIgnoreFile(filepath, resolve(cwd, ".prettierignore"), useCache)) return {
|
|
1050
|
-
ignored: true,
|
|
1051
|
-
reason: "prettierignore"
|
|
1052
|
-
};
|
|
1053
|
-
if (!loadConfigForIgnorePatterns) return { ignored: false };
|
|
1054
|
-
const configResult = await loadOxfmtConfig({
|
|
1055
|
-
cwd,
|
|
1056
|
-
filepath,
|
|
1057
|
-
...options.disableNestedConfig === true ? { disableNestedConfig: true } : {},
|
|
1058
|
-
...options.configPath ? { configPath: options.configPath } : {},
|
|
1059
|
-
editorconfig: false,
|
|
1060
|
-
useCache
|
|
1061
|
-
});
|
|
1062
|
-
if (includeConfigIgnorePatterns && configResult.dirname && configResult.config.ignorePatterns && configResult.config.ignorePatterns.length > 0 && matchConfigIgnorePatterns(filepath, configResult.dirname, configResult.config.ignorePatterns, useCache)) return {
|
|
1063
|
-
ignored: true,
|
|
1064
|
-
reason: "config-ignore-patterns"
|
|
1065
|
-
};
|
|
1066
|
-
return { ignored: false };
|
|
1067
|
-
}
|
|
1068
1173
|
//#endregion
|
|
1069
|
-
export { getConfigCacheKey, getResolveCacheKey, isOxfmtIgnored, loadOxfmtConfig, readConfigFromFile, resolveOxfmtrcPath };
|
|
1174
|
+
export { getConfigCacheKey, getResolveCacheKey, isOxfmtIgnored, loadOxfmtConfig, loadOxfmtConfigForFile, readConfigFromFile, resolveOxfmtrcPath };
|