load-oxfmt-config 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -284,11 +284,13 @@ The loader reads the nearest `.editorconfig` file and maps the subset of fields
284
284
 
285
285
  - `end_of_line` → `endOfLine`
286
286
  - `indent_style` → `useTabs`
287
- - `indent_size` / `tab_width` `tabWidth`
287
+ - `indent_size` `tabWidth` when `indent_style = space`
288
+ - `tab_width` → `tabWidth`
288
289
  - `max_line_length` → `printWidth`
289
290
  - `insert_final_newline` → `insertFinalNewline`
291
+ - `quote_type` → `singleQuote`
290
292
 
291
- Glob sections such as `[*.ts]` are converted into returned `overrides` entries.
293
+ Only `[*]` is treated as a global section to match oxfmt. Other sections such as `[**]` and `[*.ts]` are converted into returned `overrides` entries.
292
294
 
293
295
  ## Precedence
294
296
 
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { readFile, stat } from "node:fs/promises";
2
2
  import { dirname, isAbsolute, join, relative } from "node:path";
3
3
  import process from "node:process";
4
- import { interopDefault } from "@ntnyq/utils";
4
+ import { interopDefault, isBoolean, isNumber, isObject } from "@ntnyq/utils";
5
5
  import { parse } from "jsonc-parser";
6
6
  import { parseBuffer } from "editorconfig";
7
7
  //#region src/constants.ts
@@ -21,26 +21,60 @@ const EDITORCONFIG_FILE = ".editorconfig";
21
21
  /**
22
22
  * Sections that apply globally and can be merged into root-level oxfmt options.
23
23
  */
24
- const EDITORCONFIG_GLOBAL_SECTION_NAMES = ["*", "**"];
24
+ const EDITORCONFIG_GLOBAL_SECTION_NAMES = ["*"];
25
25
  //#endregion
26
26
  //#region src/editorconfig.ts
27
+ /**
28
+ * Builds the cache key used for resolved EditorConfig lookups.
29
+ *
30
+ * @param resolveKey - The base resolution key for the current lookup.
31
+ * @returns The namespaced cache key for EditorConfig resolution.
32
+ */
27
33
  function getEditorconfigResolveCacheKey(resolveKey) {
28
34
  return `editorconfig::${resolveKey}`;
29
35
  }
36
+ /**
37
+ * Determines the directory that should be used to search for a nearby .editorconfig file.
38
+ *
39
+ * @param cwd - The current working directory for the lookup.
40
+ * @param configPath - An optional config path used to anchor the search.
41
+ * @returns The directory where EditorConfig discovery should begin.
42
+ */
30
43
  function getEditorconfigSearchDir(cwd, configPath) {
31
44
  if (!configPath) return cwd;
32
45
  return dirname(isAbsolute(configPath) ? configPath : join(cwd, configPath));
33
46
  }
47
+ /**
48
+ * Merges root-level EditorConfig options with oxfmt config options.
49
+ *
50
+ * @param oxfmtConfig - The explicit oxfmt options.
51
+ * @param editorconfigRootOptions - Root options derived from .editorconfig.
52
+ * @returns A single root options object where oxfmt takes precedence.
53
+ */
34
54
  function mergeRootOptions(oxfmtConfig, editorconfigRootOptions) {
35
55
  return {
36
56
  ...editorconfigRootOptions,
37
57
  ...oxfmtConfig
38
58
  };
39
59
  }
60
+ /**
61
+ * Merges EditorConfig-derived overrides with explicit oxfmt overrides.
62
+ *
63
+ * @param oxfmtOverrides - Overrides declared in the oxfmt config.
64
+ * @param editorconfigOverrides - Overrides derived from .editorconfig sections.
65
+ * @returns The merged overrides array, or undefined when no overrides exist.
66
+ */
40
67
  function mergeOverrides(oxfmtOverrides, editorconfigOverrides) {
41
68
  const mergedOverrides = [...editorconfigOverrides, ...oxfmtOverrides || []];
42
69
  return mergedOverrides.length > 0 ? mergedOverrides : void 0;
43
70
  }
71
+ /**
72
+ * Resolves the nearest .editorconfig file starting from a directory and optionally walking upward.
73
+ *
74
+ * @param startDir - The directory where the search starts.
75
+ * @param onlyCwd - When true, only checks the starting directory.
76
+ * @returns The resolved .editorconfig path, or undefined when none is found.
77
+ */
44
78
  async function resolveEditorconfigPath(startDir, onlyCwd = false) {
45
79
  let currentDir = startDir;
46
80
  while (true) {
@@ -54,23 +88,64 @@ async function resolveEditorconfigPath(startDir, onlyCwd = false) {
54
88
  currentDir = parentDir;
55
89
  }
56
90
  }
91
+ /**
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
+ */
57
97
  function toPosixPath(path) {
58
98
  return path.replaceAll("\\", "/");
59
99
  }
100
+ /**
101
+ * Checks whether an EditorConfig section should be treated as a global section.
102
+ *
103
+ * @param sectionName - The section name parsed from .editorconfig.
104
+ * @returns True when the section applies globally.
105
+ */
60
106
  function isEditorconfigGlobalSection(sectionName) {
61
107
  return Boolean(sectionName && EDITORCONFIG_GLOBAL_SECTION_NAMES.includes(sectionName));
62
108
  }
109
+ /**
110
+ * Parses an EditorConfig boolean string.
111
+ *
112
+ * @param value - The raw EditorConfig value.
113
+ * @returns The parsed boolean, or undefined when the value is unsupported.
114
+ */
63
115
  function parseEditorconfigBoolean(value) {
64
116
  if (value === "true") return true;
65
117
  if (value === "false") return false;
66
118
  }
119
+ /**
120
+ * Parses the end-of-line style supported by oxfmt.
121
+ *
122
+ * @param value - The raw EditorConfig end_of_line value.
123
+ * @returns The normalized line ending value, or undefined when unsupported.
124
+ */
67
125
  function parseEditorconfigEndOfLine(value) {
68
126
  if (value === "lf" || value === "crlf" || value === "cr") return value;
69
127
  }
70
- function parseEditorconfigTabWidth(section) {
128
+ /**
129
+ * Parses the single quote preference from an EditorConfig section.
130
+ *
131
+ * @param value - The raw EditorConfig value for single quote preference.
132
+ * @returns True if single quotes are preferred, false if double quotes are preferred, or undefined when auto.
133
+ */
134
+ function parseEditorconfigQuoteType(value) {
135
+ if (value === "single") return true;
136
+ if (value === "double") return false;
137
+ }
138
+ /**
139
+ * Resolves the effective tab width from an EditorConfig section.
140
+ *
141
+ * @param section - The parsed EditorConfig section body.
142
+ * @param useTabs - The resolved indent style from the same section, when available.
143
+ * @returns The resolved tab width, or undefined when no numeric value is available.
144
+ */
145
+ function parseEditorconfigTabWidth(section, useTabs) {
71
146
  const indentSize = section["indent_size"];
72
147
  const tabWidth = section["tab_width"];
73
- if (indentSize && indentSize !== "unset" && indentSize !== "tab") {
148
+ if (useTabs === false && indentSize && indentSize !== "unset" && indentSize !== "tab") {
74
149
  const parsedIndentSize = Number(indentSize);
75
150
  if (Number.isFinite(parsedIndentSize)) return parsedIndentSize;
76
151
  }
@@ -79,27 +154,50 @@ function parseEditorconfigTabWidth(section) {
79
154
  if (Number.isFinite(parsedTabWidth)) return parsedTabWidth;
80
155
  }
81
156
  }
157
+ /**
158
+ * Maps a parsed EditorConfig section to the subset of oxfmt formatter options it supports.
159
+ *
160
+ * @param section - The parsed EditorConfig section body.
161
+ * @returns Formatter options derived from the section.
162
+ */
82
163
  function mapEditorconfigSectionToOptions(section) {
83
164
  const options = {};
84
165
  const endOfLine = parseEditorconfigEndOfLine(section["end_of_line"]);
85
166
  if (endOfLine) options.endOfLine = endOfLine;
86
167
  if (section["indent_style"] === "tab") options.useTabs = true;
87
168
  else if (section["indent_style"] === "space") options.useTabs = false;
88
- const tabWidth = parseEditorconfigTabWidth(section);
89
- if (typeof tabWidth === "number") options.tabWidth = tabWidth;
169
+ const singleQuote = parseEditorconfigQuoteType(section["quote_type"]);
170
+ if (isBoolean(singleQuote)) options.singleQuote = singleQuote;
171
+ const tabWidth = parseEditorconfigTabWidth(section, options.useTabs);
172
+ if (isNumber(tabWidth)) options.tabWidth = tabWidth;
90
173
  if (section["max_line_length"] && section["max_line_length"] !== "unset") {
91
174
  const parsedPrintWidth = Number(section["max_line_length"]);
92
175
  if (Number.isFinite(parsedPrintWidth)) options.printWidth = parsedPrintWidth;
93
176
  }
94
177
  const insertFinalNewline = parseEditorconfigBoolean(section["insert_final_newline"]);
95
- if (typeof insertFinalNewline === "boolean") options.insertFinalNewline = insertFinalNewline;
178
+ if (isBoolean(insertFinalNewline)) options.insertFinalNewline = insertFinalNewline;
96
179
  return options;
97
180
  }
181
+ /**
182
+ * Rebases an EditorConfig section pattern from the config directory to the target anchor directory.
183
+ *
184
+ * @param pattern - The original EditorConfig section pattern.
185
+ * @param editorconfigDir - The directory containing the .editorconfig file.
186
+ * @param anchorDir - The directory used as the override pattern base.
187
+ * @returns The rebased glob pattern.
188
+ */
98
189
  function rebaseEditorconfigPattern(pattern, editorconfigDir, anchorDir) {
99
190
  const relativePrefix = toPosixPath(relative(anchorDir, editorconfigDir));
100
191
  if (!relativePrefix || relativePrefix === ".") return pattern;
101
192
  return `${relativePrefix}/${pattern.replace(/^\//, "")}`;
102
193
  }
194
+ /**
195
+ * Reads a .editorconfig file and converts it into root options and override entries.
196
+ *
197
+ * @param editorconfigPath - The absolute path to the .editorconfig file.
198
+ * @param anchorDir - The directory used to rebase section patterns.
199
+ * @returns Parsed EditorConfig data ready to merge into oxfmt config.
200
+ */
103
201
  async function readEditorconfigFromFile(editorconfigPath, anchorDir) {
104
202
  const parsedSections = parseBuffer(await readFile(editorconfigPath));
105
203
  const editorconfigDir = dirname(editorconfigPath);
@@ -223,8 +321,8 @@ async function loadOxfmtConfig(options = {}) {
223
321
  const cwd = options.cwd || process.cwd();
224
322
  const editorconfig = options.editorconfig ?? true;
225
323
  const useEditorconfig = editorconfig !== false;
226
- const onlyCwd = useEditorconfig && typeof editorconfig === "object" ? editorconfig.onlyCwd ?? false : false;
227
- const editorconfigCwd = useEditorconfig && typeof editorconfig === "object" ? editorconfig.cwd : void 0;
324
+ const onlyCwd = useEditorconfig && isObject(editorconfig) ? editorconfig.onlyCwd ?? false : false;
325
+ const editorconfigCwd = useEditorconfig && isObject(editorconfig) ? editorconfig.cwd : void 0;
228
326
  const resolveKey = getResolveCacheKey(cwd, options.configPath);
229
327
  const editorconfigSearchDir = editorconfigCwd || getEditorconfigSearchDir(cwd, options.configPath);
230
328
  const editorconfigResolveKey = editorconfigCwd ? getEditorconfigResolveCacheKey(`${editorconfigCwd}::${options.configPath || ""}`) : getEditorconfigResolveCacheKey(resolveKey);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "load-oxfmt-config",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
4
4
  "description": "Load oxfmt config files and merge supported .editorconfig options.",
5
5
  "keywords": [
6
6
  "editorconfig",
@@ -39,23 +39,23 @@
39
39
  "access": "public"
40
40
  },
41
41
  "dependencies": {
42
- "@ntnyq/utils": "^0.11.2",
42
+ "@ntnyq/utils": "^0.13.2",
43
43
  "editorconfig": "^3.0.2",
44
44
  "jiti": "^2.6.1",
45
45
  "jsonc-parser": "^3.3.1"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@ntnyq/tsconfig": "^3.1.0",
49
- "@types/node": "^25.5.0",
50
- "@typescript/native-preview": "^7.0.0-dev.20260328.1",
49
+ "@types/node": "^25.6.0",
50
+ "@typescript/native-preview": "^7.0.0-dev.20260426.1",
51
51
  "bumpp": "^11.0.1",
52
52
  "husky": "^9.1.7",
53
- "nano-staged": "^0.9.0",
53
+ "nano-staged": "^1.0.2",
54
54
  "npm-run-all2": "^8.0.4",
55
- "oxfmt": "^0.42.0",
56
- "oxlint": "^1.57.0",
57
- "tsdown": "^0.21.7",
58
- "vitest": "^4.1.2"
55
+ "oxfmt": "^0.46.0",
56
+ "oxlint": "^1.61.0",
57
+ "tsdown": "^0.21.10",
58
+ "vitest": "^4.1.5"
59
59
  },
60
60
  "peerDependencies": {
61
61
  "oxfmt": ">=0.41.0"