load-oxfmt-config 0.3.1 → 0.4.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
@@ -287,6 +287,7 @@ The loader reads the nearest `.editorconfig` file and maps the subset of fields
287
287
  - `indent_size` / `tab_width` → `tabWidth`
288
288
  - `max_line_length` → `printWidth`
289
289
  - `insert_final_newline` → `insertFinalNewline`
290
+ - `quote_type` → `singleQuote`
290
291
 
291
292
  Glob sections such as `[*.ts]` are converted into returned `overrides` entries.
292
293
 
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
@@ -24,23 +24,57 @@ const EDITORCONFIG_FILE = ".editorconfig";
24
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,19 +88,59 @@ 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
  }
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
+ * @returns The resolved tab width, or undefined when no numeric value is available.
143
+ */
70
144
  function parseEditorconfigTabWidth(section) {
71
145
  const indentSize = section["indent_size"];
72
146
  const tabWidth = section["tab_width"];
@@ -79,27 +153,50 @@ function parseEditorconfigTabWidth(section) {
79
153
  if (Number.isFinite(parsedTabWidth)) return parsedTabWidth;
80
154
  }
81
155
  }
156
+ /**
157
+ * Maps a parsed EditorConfig section to the subset of oxfmt formatter options it supports.
158
+ *
159
+ * @param section - The parsed EditorConfig section body.
160
+ * @returns Formatter options derived from the section.
161
+ */
82
162
  function mapEditorconfigSectionToOptions(section) {
83
163
  const options = {};
84
164
  const endOfLine = parseEditorconfigEndOfLine(section["end_of_line"]);
85
165
  if (endOfLine) options.endOfLine = endOfLine;
86
166
  if (section["indent_style"] === "tab") options.useTabs = true;
87
167
  else if (section["indent_style"] === "space") options.useTabs = false;
168
+ const singleQuote = parseEditorconfigQuoteType(section["quote_type"]);
169
+ if (isBoolean(singleQuote)) options.singleQuote = singleQuote;
88
170
  const tabWidth = parseEditorconfigTabWidth(section);
89
- if (typeof tabWidth === "number") options.tabWidth = tabWidth;
171
+ if (isNumber(tabWidth)) options.tabWidth = tabWidth;
90
172
  if (section["max_line_length"] && section["max_line_length"] !== "unset") {
91
173
  const parsedPrintWidth = Number(section["max_line_length"]);
92
174
  if (Number.isFinite(parsedPrintWidth)) options.printWidth = parsedPrintWidth;
93
175
  }
94
176
  const insertFinalNewline = parseEditorconfigBoolean(section["insert_final_newline"]);
95
- if (typeof insertFinalNewline === "boolean") options.insertFinalNewline = insertFinalNewline;
177
+ if (isBoolean(insertFinalNewline)) options.insertFinalNewline = insertFinalNewline;
96
178
  return options;
97
179
  }
180
+ /**
181
+ * Rebases an EditorConfig section pattern from the config directory to the target anchor directory.
182
+ *
183
+ * @param pattern - The original EditorConfig section pattern.
184
+ * @param editorconfigDir - The directory containing the .editorconfig file.
185
+ * @param anchorDir - The directory used as the override pattern base.
186
+ * @returns The rebased glob pattern.
187
+ */
98
188
  function rebaseEditorconfigPattern(pattern, editorconfigDir, anchorDir) {
99
189
  const relativePrefix = toPosixPath(relative(anchorDir, editorconfigDir));
100
190
  if (!relativePrefix || relativePrefix === ".") return pattern;
101
191
  return `${relativePrefix}/${pattern.replace(/^\//, "")}`;
102
192
  }
193
+ /**
194
+ * Reads a .editorconfig file and converts it into root options and override entries.
195
+ *
196
+ * @param editorconfigPath - The absolute path to the .editorconfig file.
197
+ * @param anchorDir - The directory used to rebase section patterns.
198
+ * @returns Parsed EditorConfig data ready to merge into oxfmt config.
199
+ */
103
200
  async function readEditorconfigFromFile(editorconfigPath, anchorDir) {
104
201
  const parsedSections = parseBuffer(await readFile(editorconfigPath));
105
202
  const editorconfigDir = dirname(editorconfigPath);
@@ -223,8 +320,8 @@ async function loadOxfmtConfig(options = {}) {
223
320
  const cwd = options.cwd || process.cwd();
224
321
  const editorconfig = options.editorconfig ?? true;
225
322
  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;
323
+ const onlyCwd = useEditorconfig && isObject(editorconfig) ? editorconfig.onlyCwd ?? false : false;
324
+ const editorconfigCwd = useEditorconfig && isObject(editorconfig) ? editorconfig.cwd : void 0;
228
325
  const resolveKey = getResolveCacheKey(cwd, options.configPath);
229
326
  const editorconfigSearchDir = editorconfigCwd || getEditorconfigSearchDir(cwd, options.configPath);
230
327
  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.0",
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.12.0",
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.20260413.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.45.0",
56
+ "oxlint": "^1.60.0",
57
+ "tsdown": "^0.21.8",
58
+ "vitest": "^4.1.4"
59
59
  },
60
60
  "peerDependencies": {
61
61
  "oxfmt": ">=0.41.0"