load-oxfmt-config 0.27.0 → 0.29.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
@@ -33,9 +33,24 @@ yarn add oxfmt load-oxfmt-config
33
33
  pnpm add oxfmt load-oxfmt-config
34
34
  ```
35
35
 
36
- > `oxfmt >=0.72.0` is a peer dependency and is installed alongside this package.
36
+ > `oxfmt >=0.74.0` is a peer dependency and is installed alongside this package.
37
37
  > Node.js `^22.13.0 || >=24` is supported.
38
38
 
39
+ Oxfmt 0.74.0 accepts BOM-prefixed JSON/JSONC configs and uses EditorConfig
40
+ `indent_size` as a fallback when `tab_width` is absent, including for tab or
41
+ unspecified indentation styles. The loader follows these changes and preserves
42
+ out-of-range widths so oxfmt reports configuration errors instead of silently
43
+ wrapping them. Config merge precedence is unchanged, and no options were added,
44
+ renamed, or removed. See the upstream
45
+ [0.74.0 release notes](https://github.com/oxc-project/oxc/releases/tag/oxfmt_v0.74.0).
46
+
47
+ Oxfmt 0.73.0 adds `associations` and `astro` options. The loader preserves
48
+ association entries and Astro options in `OxfmtOptions`; it does not select a
49
+ formatter. Association globs are relative to the returned config `dirname`, and
50
+ later matching entries win. Consumers must resolve these before calling oxfmt's
51
+ `format()` API, which does not evaluate `associations`. Symlinked configs are
52
+ already discovered, with relative patterns based on the link's directory.
53
+
39
54
  ## Usage
40
55
 
41
56
  ### Basic Usage
@@ -462,7 +477,7 @@ The loader reads the nearest `.editorconfig` file and maps the subset of fields
462
477
 
463
478
  - `end_of_line` → `endOfLine`
464
479
  - `indent_style` → `useTabs`
465
- - `indent_size` → `tabWidth` when `indent_style = space`
480
+ - `indent_size` → `tabWidth` when `indent_style = space`, or as a fallback when `tab_width` is absent
466
481
  - `tab_width` → `tabWidth`
467
482
  - `max_line_length` → `printWidth`
468
483
  - `insert_final_newline` → `insertFinalNewline`
@@ -473,7 +488,10 @@ sections also generates an override to preserve source order. Other sections
473
488
  such as `[**]` and `[*.ts]` are converted into returned `overrides` entries;
474
489
  basename patterns such as `*.ts` are normalized to `**/*.ts` to match descendants.
475
490
  The per-file API evaluates matching sections directly, without rebasing them to
476
- the oxfmt config directory.
491
+ the oxfmt config directory. It combines their raw properties before selecting
492
+ `tabWidth`, so a scoped `indent_size` preserves an inherited `tab_width` unless
493
+ the effective `indent_style` is `space`. Static overrides inherit global
494
+ indentation properties when selecting their widths.
477
495
 
478
496
  ## Ignore Strategy
479
497
 
@@ -525,7 +543,8 @@ Section-specific `.editorconfig` entries are represented as generated `overrides
525
543
 
526
544
  `loadOxfmtConfig()` returns a static merged `OxfmtOptions` shape. Its generated
527
545
  EditorConfig globs cannot represent every relationship between config directories,
528
- particularly an ancestor EditorConfig with a nested oxfmt config. Use
546
+ particularly an ancestor EditorConfig with a nested oxfmt config, or width
547
+ interactions between overlapping scoped sections. Use
529
548
  `loadOxfmtConfigForFile()` to resolve EditorConfig against its original directory
530
549
  for a specific target file. Both APIs retain the nearest-file discovery strategy;
531
550
  they do not merge multiple EditorConfig files.
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { FormatConfig } from "oxfmt";
1
+ import { FormatConfig, OxfmtAssociationConfig } from "oxfmt";
2
2
  //#region src/types.d.ts
3
3
  /**
4
4
  * Object-form `.editorconfig` option for fine-grained lookup control.
@@ -177,9 +177,14 @@ export interface OxfmtConfigOverride {
177
177
  options?: FormatConfig;
178
178
  }
179
179
  /**
180
- * Final oxfmt options, including config `ignorePatterns` and `overrides`.
180
+ * Final oxfmt options, including config associations, ignore patterns, and overrides.
181
181
  */
182
182
  export interface OxfmtOptions extends FormatConfig {
183
+ /**
184
+ * File language associations, relative to the config directory.
185
+ * Later matching entries take precedence. The loader preserves these for consumers.
186
+ */
187
+ associations?: OxfmtAssociationConfig[];
183
188
  /**
184
189
  * Ignore files matching these glob patterns.
185
190
  *
package/dist/index.mjs CHANGED
@@ -234,7 +234,7 @@ async function readConfigFromFile(resolvedPath, options = {}) {
234
234
  if (!useCache && extension === ".js") return readConfigDefaultExport(await importFreshJavaScriptConfigModule(resolvedPath));
235
235
  return readConfigDefaultExport(await importJitiConfigModule(resolvedPath, useCache));
236
236
  }
237
- const content = await readFile(resolvedPath, "utf8");
237
+ const content = (await readFile(resolvedPath, "utf8")).replace(/^\u{FEFF}/u, "");
238
238
  if (extension === ".jsonc") {
239
239
  const parseErrors = [];
240
240
  const parsed = parse(content, parseErrors, {
@@ -550,7 +550,8 @@ async function readEditorconfigFromFile(editorconfigPath, anchorDir, options = {
550
550
  const readSections = async () => parseBuffer(await readFile(editorconfigPath));
551
551
  const parsedSections = useCache ? await cachePromise(sectionCache, editorconfigPath, readSections) : await readSections();
552
552
  const editorconfigDir = dirname(editorconfigPath);
553
- const rootOptions = {};
553
+ let rootOptions = {};
554
+ const resolvedSection = {};
554
555
  const overrides = [];
555
556
  const relativeFile = options.filepath ? toPosixPath(relative(editorconfigDir, options.filepath)) : void 0;
556
557
  if (relativeFile !== void 0 && isPathOutsideBase(relativeFile)) return {
@@ -559,16 +560,17 @@ async function readEditorconfigFromFile(editorconfigPath, anchorDir, options = {
559
560
  };
560
561
  for (const [sectionName, sectionBody] of parsedSections) {
561
562
  if (!sectionName) continue;
562
- const mappedOptions = mapEditorconfigSectionToOptions(sectionBody);
563
- if (Object.keys(mappedOptions).length === 0) continue;
564
563
  if (relativeFile !== void 0) {
565
- if (matchesEditorconfigSection(sectionName, relativeFile, useCache)) Object.assign(rootOptions, mappedOptions);
564
+ if (matchesEditorconfigSection(sectionName, relativeFile, useCache)) Object.assign(resolvedSection, sectionBody);
566
565
  continue;
567
566
  }
567
+ const mappedOptions = mapEditorconfigSectionToOptions(sectionBody, resolvedSection);
568
568
  if (isEditorconfigGlobalSection(sectionName)) {
569
- Object.assign(rootOptions, mappedOptions);
569
+ Object.assign(resolvedSection, sectionBody);
570
+ rootOptions = mapEditorconfigSectionToOptions(resolvedSection);
570
571
  if (overrides.length === 0) continue;
571
572
  }
573
+ if (Object.keys(mappedOptions).length === 0) continue;
572
574
  overrides.push({
573
575
  options: mappedOptions,
574
576
  files: [rebaseEditorconfigPattern(sectionName, editorconfigDir, anchorDir)]
@@ -576,7 +578,7 @@ async function readEditorconfigFromFile(editorconfigPath, anchorDir, options = {
576
578
  }
577
579
  return {
578
580
  overrides,
579
- rootOptions
581
+ rootOptions: relativeFile === void 0 ? rootOptions : mapEditorconfigSectionToOptions(resolvedSection)
580
582
  };
581
583
  }
582
584
  /**
@@ -615,9 +617,10 @@ function isEditorconfigGlobalSection(sectionName) {
615
617
  * Maps a parsed EditorConfig section to the subset of oxfmt formatter options it supports.
616
618
  *
617
619
  * @param section - The parsed EditorConfig section body.
620
+ * @param inheritedSection - Global properties inherited by a static override.
618
621
  * @returns Formatter options derived from the section.
619
622
  */
620
- function mapEditorconfigSectionToOptions(section) {
623
+ function mapEditorconfigSectionToOptions(section, inheritedSection = {}) {
621
624
  const options = {};
622
625
  const endOfLine = parseEditorconfigEndOfLine(section.end_of_line);
623
626
  if (endOfLine) options.endOfLine = endOfLine;
@@ -625,8 +628,13 @@ function mapEditorconfigSectionToOptions(section) {
625
628
  else if (section.indent_style === "space") options.useTabs = false;
626
629
  const singleQuote = parseEditorconfigQuoteType(section.quote_type);
627
630
  if (isBoolean(singleQuote)) options.singleQuote = singleQuote;
628
- const tabWidth = parseEditorconfigTabWidth(section, options.useTabs);
629
- if (isNumber(tabWidth)) options.tabWidth = tabWidth;
631
+ if (section.indent_style !== void 0 || section.indent_size !== void 0 || section.tab_width !== void 0) {
632
+ const tabWidth = parseEditorconfigTabWidth({
633
+ ...inheritedSection,
634
+ ...section
635
+ });
636
+ if (isNumber(tabWidth)) options.tabWidth = tabWidth;
637
+ }
630
638
  if (section.max_line_length && section.max_line_length !== "unset") {
631
639
  const parsedPrintWidth = Number(section.max_line_length);
632
640
  if (Number.isFinite(parsedPrintWidth)) options.printWidth = parsedPrintWidth;
@@ -703,19 +711,14 @@ function parseEditorconfigQuoteType(value) {
703
711
  * Resolves the effective tab width from an EditorConfig section.
704
712
  *
705
713
  * @param section - The parsed EditorConfig section body.
706
- * @param useTabs - The resolved indent style from the same section, when available.
707
714
  * @returns The resolved tab width, or undefined when no numeric value is available.
708
715
  */
709
- function parseEditorconfigTabWidth(section, useTabs) {
710
- const indentSize = section.indent_size;
711
- const tabWidth = section.tab_width;
712
- if (useTabs === false && indentSize && indentSize !== "unset" && indentSize !== "tab") {
713
- const parsedIndentSize = Number(indentSize);
714
- if (Number.isFinite(parsedIndentSize)) return parsedIndentSize;
715
- }
716
- if (tabWidth && tabWidth !== "unset") {
717
- const parsedTabWidth = Number(tabWidth);
718
- if (Number.isFinite(parsedTabWidth)) return parsedTabWidth;
716
+ function parseEditorconfigTabWidth(section) {
717
+ const values = section.indent_style === "space" ? [section.indent_size, section.tab_width] : [section.tab_width, section.indent_size];
718
+ for (const value of values) {
719
+ if (!value || value === "unset" || value === "tab") continue;
720
+ const parsedWidth = Number(value);
721
+ if (Number.isFinite(parsedWidth)) return parsedWidth;
719
722
  }
720
723
  }
721
724
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "load-oxfmt-config",
3
3
  "type": "module",
4
- "version": "0.27.0",
4
+ "version": "0.29.0",
5
5
  "description": "Load and resolve oxfmt configuration files, including explicit JS/TS config paths, and merge supported `.editorconfig` settings for Oxfmt.",
6
6
  "keywords": [
7
7
  "editorconfig",
@@ -43,12 +43,12 @@
43
43
  },
44
44
  "sideEffects": false,
45
45
  "peerDependencies": {
46
- "oxfmt": ">=0.72.0"
46
+ "oxfmt": ">=0.74.0"
47
47
  },
48
48
  "dependencies": {
49
49
  "@ntnyq/utils": "^0.23.0",
50
50
  "editorconfig": "^3.0.2",
51
- "ignore": "^7.0.12",
51
+ "ignore": "^7.0.13",
52
52
  "jiti": "^2.7.0",
53
53
  "jsonc-parser": "^3.3.1",
54
54
  "minimatch": "~10.2.6"