@monke-together-strong/oxc-config 0.2.0 → 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
@@ -68,22 +68,50 @@ export { default } from "@monke-together-strong/oxc-config/oxfmt";
68
68
 
69
69
  ```ts
70
70
  // oxlint.config.ts
71
- export { default } from "@monke-together-strong/oxc-config/oxlint";
71
+ import { createOxlintConfig } from "@monke-together-strong/oxc-config/oxlint";
72
+
73
+ export default createOxlintConfig();
72
74
  ```
73
75
 
74
- The Oxfmt preset formats JSDoc, uses a print width of 100, ignores team-managed `skills/**` and `AGENTS.md`, and appends consumer `ignorePatterns` after its shared ignores. Other consumer fields override the shared formatter defaults.
76
+ The Oxfmt preset formats JSDoc and uses a print width of 100. It formats first-party skill code and metadata while excluding imported skills, imported references, skill Markdown, and `AGENTS.md`. Consumer `ignorePatterns` append after these shared ignores; other consumer fields override the shared formatter defaults.
75
77
 
76
- The default Oxlint preset composes Ultracite's core and Vitest presets, enables type-aware linting and TypeScript type checking, rejects console calls and direct environment access, and requires explicit module boundary types. Environment variables should be read and validated through a centralized configuration boundary. It uses `eslint-plugin-perfectionist` to sort object literals, destructuring, object types, interfaces, JSX props, enums, and heritage clauses while treating comments as intentional object and enum partitions, and disables Ultracite's `sort-keys` rule. It also loads `eslint-plugin-jsdoc` through Oxlint's JavaScript-plugin compatibility layer to reject undefined JSDoc types that Oxlint does not yet implement natively. Files named `*.test.*`, `*.spec.*`, or stored under `__tests__` are assumed to use Vitest. Its framework-neutral test policy also covers files under `test/` and `tests/`, where `typescript/no-unsafe-type-assertion` is disabled because typed mock and fixture setup is not yet ergonomic enough to justify the churn.
78
+ The default Oxlint preset composes Ultracite's core, anti-slop, and Vitest presets, enables type-aware linting and TypeScript type checking, and rejects console calls and direct environment access. Environment variables should be read and validated through a centralized configuration boundary. It uses `eslint-plugin-perfectionist` to sort object literals, destructuring, object types, interfaces, JSX props, enums, and heritage clauses while treating comments as intentional object and enum partitions, and disables Ultracite's `sort-keys` rule. It disables anti-slop's `no-array-filter-map` because its suggested `flatMap` and iterator-helper rewrites run slower than `filter().map()` on Bun. It also loads `eslint-plugin-jsdoc` through Oxlint's JavaScript-plugin compatibility layer to reject undefined JSDoc types that Oxlint does not yet implement natively. Files named `*.test.*`, `*.spec.*`, or stored under `__tests__` are assumed to use Vitest. Its framework-neutral test policy also covers files under `test/` and `tests/`, where `typescript/no-unsafe-type-assertion` is disabled because typed mock and fixture setup is not yet ergonomic enough to justify the churn.
77
79
 
78
- Mixed-framework repositories can exclude framework-owned paths from the Vitest preset:
80
+ ### Framework presets
81
+
82
+ `frameworks` enables Ultracite's React, TanStack, and shadcn presets and React Doctor's rules in the listed directories, relative to the config. `"."` covers the whole repository.
79
83
 
80
84
  ```ts
81
- import { createOxlintConfig } from "@monke-together-strong/oxc-config/oxlint";
82
- import react from "ultracite/oxlint/react";
83
- import tanstack from "ultracite/oxlint/tanstack";
85
+ export default createOxlintConfig({
86
+ frameworks: {
87
+ react: ["apps/web"],
88
+ "react-doctor": ["apps/web"],
89
+ shadcn: ["apps/web"],
90
+ tanstack: ["apps/web"]
91
+ },
92
+ rules: {
93
+ "shadcn/no-raw-colors": ["error", { allow: ["text-body"] }]
94
+ }
95
+ });
96
+ ```
97
+
98
+ Install `@shadcn/lint` for `shadcn` and `oxlint-plugin-react-doctor` for `react-doctor`. React Doctor's TanStack rules apply where the `react-doctor` and `tanstack` scopes overlap.
99
+
100
+ `createOxlintConfig` throws when the root or a workspace `package.json` depends on one of these packages without a matching `frameworks` entry:
101
+
102
+ - `react`, `@tanstack/react-router`, and `@tanstack/react-start` require the framework to cover the package that depends on them.
103
+ - `@shadcn/lint` and `oxlint-plugin-react-doctor` require the framework to list at least one directory.
104
+
105
+ Workspaces come from `workspaces` in `package.json` and `packages` in `pnpm-workspace.yaml`. Set a framework to `false` to decline it. The check reads manifests from `root`, which defaults to the nearest directory at or above the working directory that contains `.git`. If the config is not at the repository root, pass its directory as `root`.
84
106
 
107
+ Root `rules` for a directory-scoped framework's plugins, such as `react/*` or `shadcn/*`, apply inside its directories. When `react` is directory-scoped, an `overrides` entry that changes `react/*`, `react-perf/*`, or `jsx-a11y/*` rules must list those plugins in `plugins`. Otherwise Oxlint ignores the change.
108
+
109
+ ### Vitest scope
110
+
111
+ Mixed-framework repositories can exclude framework-owned paths from the Vitest preset:
112
+
113
+ ```ts
85
114
  export default createOxlintConfig({
86
- extends: [react, tanstack],
87
115
  ignorePatterns: [".repo-specific-output"],
88
116
  overrides: [
89
117
  {
@@ -101,6 +129,6 @@ export default createOxlintConfig({
101
129
  });
102
130
  ```
103
131
 
104
- `extends`, `ignorePatterns`, and `overrides` append after their shared lint values. Consumer `rules` and `options` override shared defaults. Other Oxlint fields pass through at the root.
132
+ `extends`, `ignorePatterns`, `jsPlugins`, and `overrides` append after the preset's values. Consumer `rules`, `options`, and `settings` override the preset's defaults. Other Oxlint fields pass through at the root.
105
133
 
106
134
  The presets are published on public npm under the MIT license.
package/dist/oxfmt.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { OxfmtConfig } from "oxfmt";
2
2
  //#region src/oxfmt.d.ts
3
- declare function createOxfmtConfig(options?: OxfmtConfig): OxfmtConfig;
4
- declare const oxfmtConfig: OxfmtConfig;
3
+ export declare function createOxfmtConfig(options?: OxfmtConfig): OxfmtConfig;
4
+ export declare const oxfmtConfig: OxfmtConfig;
5
5
  //#endregion
6
- export { createOxfmtConfig, oxfmtConfig as default, oxfmtConfig };
6
+ export { oxfmtConfig as default };
package/dist/oxfmt.mjs CHANGED
@@ -1,7 +1,9 @@
1
1
  import ultracite from "ultracite/oxfmt";
2
2
  //#region src/oxfmt.ts
3
3
  const sharedIgnorePatterns = [
4
- "skills/**",
4
+ "skills/imported/**",
5
+ "skills/references/imported/**",
6
+ "skills/**/*.md",
5
7
  "AGENTS.md",
6
8
  "**/routeTree.gen.ts"
7
9
  ];
package/dist/oxlint.d.mts CHANGED
@@ -1,12 +1,27 @@
1
1
  import { OxlintConfig } from "oxlint";
2
+ //#region src/frameworks.d.ts
3
+ declare const frameworkNames: readonly ["react", "react-doctor", "shadcn", "tanstack"];
4
+ type Framework = (typeof frameworkNames)[number];
5
+ /**
6
+ * Directories each framework preset lints, relative to the config's directory. `"."` covers the
7
+ * whole repository, and `false` declines a framework that a dependency would otherwise require.
8
+ */
9
+ type FrameworkScopes = Partial<Record<Framework, false | readonly string[]>>;
10
+ //#endregion
2
11
  //#region src/oxlint.d.ts
3
- interface CreateOxlintConfigOptions extends OxlintConfig {
12
+ export interface CreateOxlintConfigOptions extends OxlintConfig {
13
+ /** Ultracite framework presets and the directories they lint. */
14
+ frameworks?: FrameworkScopes;
15
+ /**
16
+ * Directory holding the config, whose package manifests are checked for framework dependencies.
17
+ * Defaults to the nearest directory at or above the working directory that contains `.git`.
18
+ */
19
+ root?: string;
4
20
  /** Files receiving framework-neutral test policy, including test helpers. */
5
21
  testFiles?: readonly string[];
6
22
  /** Non-Vitest test files to exclude from the default Vitest preset. */
7
23
  vitestExcludeFiles?: readonly string[];
8
24
  }
9
- declare function createOxlintConfig(options?: CreateOxlintConfigOptions): OxlintConfig;
10
- declare const oxlintConfig: OxlintConfig;
25
+ export declare function createOxlintConfig(options?: CreateOxlintConfigOptions): OxlintConfig;
11
26
  //#endregion
12
- export { CreateOxlintConfigOptions, createOxlintConfig, oxlintConfig as default, oxlintConfig };
27
+ export type { Framework, FrameworkScopes };
package/dist/oxlint.mjs CHANGED
@@ -1,6 +1,202 @@
1
1
  import { createRequire } from "node:module";
2
+ import antiSlop from "ultracite/oxlint/anti-slop";
2
3
  import core from "ultracite/oxlint/core";
3
4
  import vitest from "ultracite/oxlint/vitest";
5
+ import { existsSync, globSync, readFileSync } from "node:fs";
6
+ import path from "node:path";
7
+ import { selectJsPlugins } from "ultracite/oxlint/js-plugins";
8
+ import react from "ultracite/oxlint/react";
9
+ import shadcn from "ultracite/oxlint/shadcn";
10
+ import tanstack from "ultracite/oxlint/tanstack";
11
+ import tanstackReactDoctor from "ultracite/oxlint/tanstack/js-plugins";
12
+ import { parse } from "yaml";
13
+ import * as z from "zod";
14
+ //#region src/frameworks.ts
15
+ const frameworkNames = [
16
+ "react",
17
+ "react-doctor",
18
+ "shadcn",
19
+ "tanstack"
20
+ ];
21
+ const frameworkPresets = {
22
+ react: {
23
+ jsPluginNamespaces: [],
24
+ presets: (directories) => [{
25
+ directories,
26
+ preset: react
27
+ }]
28
+ },
29
+ "react-doctor": {
30
+ jsPluginNamespaces: ["react-doctor"],
31
+ presets: (directories, scopes) => [{
32
+ directories,
33
+ preset: selectJsPlugins(["react-doctor"])
34
+ }, {
35
+ directories: overlap(directories, normalizeDirectories(scopes.tanstack)),
36
+ preset: tanstackReactDoctor
37
+ }]
38
+ },
39
+ shadcn: {
40
+ jsPluginNamespaces: ["shadcn"],
41
+ presets: (directories) => [{
42
+ directories,
43
+ preset: shadcn
44
+ }]
45
+ },
46
+ tanstack: {
47
+ jsPluginNamespaces: [],
48
+ presets: (directories) => [{
49
+ directories,
50
+ preset: tanstack
51
+ }]
52
+ }
53
+ };
54
+ const frameworkDependencies = /* @__PURE__ */ new Map([
55
+ ["@shadcn/lint", {
56
+ coversPackage: false,
57
+ framework: "shadcn"
58
+ }],
59
+ ["@tanstack/react-router", {
60
+ coversPackage: true,
61
+ framework: "tanstack"
62
+ }],
63
+ ["@tanstack/react-start", {
64
+ coversPackage: true,
65
+ framework: "tanstack"
66
+ }],
67
+ ["oxlint-plugin-react-doctor", {
68
+ coversPackage: false,
69
+ framework: "react-doctor"
70
+ }],
71
+ ["react", {
72
+ coversPackage: true,
73
+ framework: "react"
74
+ }]
75
+ ]);
76
+ const manifestSchema = z.object({
77
+ dependencies: z.record(z.string(), z.unknown()).optional(),
78
+ devDependencies: z.record(z.string(), z.unknown()).optional(),
79
+ peerDependencies: z.record(z.string(), z.unknown()).optional(),
80
+ workspaces: z.union([z.array(z.string()), z.object({ packages: z.array(z.string()).optional() })]).optional()
81
+ });
82
+ const pnpmWorkspaceSchema = z.object({ packages: z.array(z.string()).optional() }).nullable();
83
+ /**
84
+ * Returns the nearest directory at or above `start` that contains `.git`, or `start` when none
85
+ * does. Vite+ evaluates the config with the invoking directory, which can be a subdirectory of the
86
+ * repository, as the working directory.
87
+ */
88
+ function findRepositoryRoot(start = process.cwd()) {
89
+ for (let directory = start;; directory = path.dirname(directory)) {
90
+ if (existsSync(path.join(directory, ".git"))) return directory;
91
+ if (path.dirname(directory) === directory) return start;
92
+ }
93
+ }
94
+ /** Throws when a package depends on a framework that `scopes` neither covers nor declines. */
95
+ function checkFrameworkCoverage(root, scopes) {
96
+ const rootManifest = readManifest(root);
97
+ if (!rootManifest) return;
98
+ const problems = [".", ...workspaceDirectories(root, rootManifest)].flatMap((directory) => {
99
+ const manifestPath = directory === "." ? "package.json" : `${directory}/package.json`;
100
+ const location = directory === "." ? "the repository root" : directory;
101
+ return dependencyNames(readManifest(path.join(root, directory))).flatMap((dependency) => {
102
+ const signal = frameworkDependencies.get(dependency);
103
+ const scope = signal && scopes[signal.framework];
104
+ if (!signal || scope === false) return [];
105
+ if (!signal.coversPackage) return normalizeDirectories(scope).length === 0 ? [`${manifestPath} depends on ${dependency}, but frameworks["${signal.framework}"] lists no directories.`] : [];
106
+ return covers(scope ?? [], directory) ? [] : [`${manifestPath} depends on ${dependency}, but frameworks["${signal.framework}"] does not cover ${location}.`];
107
+ });
108
+ });
109
+ if (problems.length > 0) throw new Error([
110
+ "Framework presets do not cover these dependencies:",
111
+ ...problems.map((problem) => `- ${problem}`),
112
+ "Set each framework to the directories it lints (\".\" for the whole repository), or to false to decline it."
113
+ ].join("\n"));
114
+ }
115
+ /**
116
+ * Applies framework presets. Repository-wide presets extend the config. Directory-scoped presets
117
+ * become overrides, followed by a copy of the root `rules` for their plugins, because Oxlint
118
+ * applies overrides after root rules.
119
+ */
120
+ function resolveFrameworks(scopes, rules) {
121
+ const resolved = {
122
+ extends: [],
123
+ overrides: [],
124
+ settings: {}
125
+ };
126
+ for (const framework of frameworkNames) {
127
+ const directories = normalizeDirectories(scopes[framework]);
128
+ if (directories.length === 0) continue;
129
+ const { jsPluginNamespaces, presets } = frameworkPresets[framework];
130
+ const placed = presets(directories, scopes).filter((entry) => entry.directories.length > 0);
131
+ for (const { directories: presetDirectories, preset } of placed) {
132
+ resolved.settings = {
133
+ ...resolved.settings,
134
+ ...preset.settings
135
+ };
136
+ if (presetDirectories.includes(".")) resolved.extends.push(preset);
137
+ else resolved.overrides.push(...scopePreset(preset, presetDirectories));
138
+ }
139
+ const scoped = [...new Set(placed.flatMap((entry) => entry.directories.includes(".") ? [] : entry.directories))];
140
+ const plugins = placed.flatMap(({ preset }) => preset.plugins ?? []);
141
+ const namespaces = /* @__PURE__ */ new Set([...plugins, ...jsPluginNamespaces]);
142
+ const tuning = Object.entries(rules).filter(([rule]) => namespaces.has(rule.split("/")[0] ?? ""));
143
+ if (scoped.length > 0 && tuning.length > 0) resolved.overrides.push({
144
+ files: within(scoped, ["**"]),
145
+ plugins,
146
+ rules: Object.fromEntries(tuning)
147
+ });
148
+ }
149
+ return resolved;
150
+ }
151
+ function scopePreset(preset, directories) {
152
+ const scoped = { files: within(directories, ["**"]) };
153
+ if (preset.plugins) scoped.plugins = preset.plugins;
154
+ if (preset.jsPlugins) scoped.jsPlugins = preset.jsPlugins;
155
+ if (preset.rules) scoped.rules = preset.rules;
156
+ return [scoped, ...(preset.overrides ?? []).map((override) => {
157
+ const nested = {
158
+ ...override,
159
+ files: within(directories, override.files)
160
+ };
161
+ if (override.excludeFiles) nested.excludeFiles = within(directories, override.excludeFiles);
162
+ return nested;
163
+ })];
164
+ }
165
+ function within(directories, globs) {
166
+ return directories.flatMap((directory) => globs.map((glob) => `${directory}/${glob}`));
167
+ }
168
+ function normalizeDirectories(scope) {
169
+ return (scope === false ? [] : scope ?? []).map((directory) => path.posix.normalize(directory).replace(/\/$/u, ""));
170
+ }
171
+ function covers(scope, directory) {
172
+ return normalizeDirectories(scope).some((scoped) => scoped === "." || directory === scoped || directory.startsWith(`${scoped}/`));
173
+ }
174
+ /** Returns the directories covered by both scopes, keeping the narrower directory of each pair. */
175
+ function overlap(left, right) {
176
+ return [...new Set(left.flatMap((outer) => right.flatMap((inner) => {
177
+ if (covers([outer], inner)) return [inner];
178
+ return covers([inner], outer) ? [outer] : [];
179
+ })))];
180
+ }
181
+ function readManifest(directory) {
182
+ const file = path.join(directory, "package.json");
183
+ return existsSync(file) ? manifestSchema.parse(JSON.parse(readFileSync(file, "utf-8"))) : void 0;
184
+ }
185
+ function dependencyNames(manifest) {
186
+ return Object.keys({
187
+ ...manifest?.dependencies,
188
+ ...manifest?.devDependencies,
189
+ ...manifest?.peerDependencies
190
+ });
191
+ }
192
+ function workspaceDirectories(root, manifest) {
193
+ const pnpmWorkspace = path.join(root, "pnpm-workspace.yaml");
194
+ const patterns = [...Array.isArray(manifest.workspaces) ? manifest.workspaces : manifest.workspaces?.packages ?? [], ...existsSync(pnpmWorkspace) ? pnpmWorkspaceSchema.parse(parse(readFileSync(pnpmWorkspace, "utf-8")))?.packages ?? [] : []];
195
+ const expand = (globs) => new Set(globs.flatMap((glob) => globSync(glob, { cwd: root })).map((match) => path.posix.normalize(match.split(path.sep).join("/"))));
196
+ const excluded = expand(patterns.filter((pattern) => pattern.startsWith("!")).map((pattern) => pattern.slice(1)));
197
+ return [...expand(patterns.filter((pattern) => !pattern.startsWith("!")))].filter((directory) => directory !== "." && !excluded.has(directory) && existsSync(path.join(root, directory, "package.json")));
198
+ }
199
+ //#endregion
4
200
  //#region src/oxlint.ts
5
201
  const localRequire = createRequire(import.meta.url);
6
202
  const jsdocPlugin = {
@@ -12,25 +208,28 @@ const perfectionistPlugin = {
12
208
  specifier: localRequire.resolve("eslint-plugin-perfectionist")
13
209
  };
14
210
  const defaultTestFiles = [
15
- "**/*.{test,spec}.{ts,tsx,js,jsx}",
16
- "**/__tests__/**/*.{ts,tsx,js,jsx}",
17
- "**/__mocks__/**/*.{ts,tsx,js,jsx}",
18
- "**/{test,tests}/**/*.{ts,tsx,js,jsx}"
211
+ "**/*.{test,spec}.{ts,tsx,js,jsx,mts,cts,mjs,cjs}",
212
+ "**/__tests__/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}",
213
+ "**/__mocks__/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}",
214
+ "**/{test,tests}/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}"
19
215
  ];
20
- const executableTestFiles = ["**/*.{test,spec}.{ts,tsx,js,jsx}"];
216
+ const executableTestFiles = ["**/*.{test,spec}.{ts,tsx,js,jsx,mts,cts,mjs,cjs}"];
21
217
  const generatedFileIgnorePatterns = ["**/routeTree.gen.ts"];
22
218
  function createOxlintConfig(options = {}) {
23
- const { extends: extensions = [], ignorePatterns = [], jsPlugins = [], options: lintOptions = {}, overrides = [], rules = {}, testFiles = defaultTestFiles, vitestExcludeFiles = [], ...config } = options;
219
+ const { extends: extensions = [], frameworks = {}, ignorePatterns = [], jsPlugins = [], options: lintOptions = {}, overrides = [], root = findRepositoryRoot(), rules = {}, settings = {}, testFiles = defaultTestFiles, vitestExcludeFiles = [], ...config } = options;
220
+ checkFrameworkCoverage(root, frameworks);
221
+ const framework = resolveFrameworks(frameworks, rules);
24
222
  const vitestOverrides = (vitest.overrides ?? []).map((override) => {
25
223
  const excludeFiles = [...override.excludeFiles ?? [], ...vitestExcludeFiles];
26
- return {
224
+ const resolvedOverride = {
27
225
  ...override,
28
- ...excludeFiles.length > 0 ? { excludeFiles } : {},
29
226
  rules: {
30
227
  ...override.rules,
31
228
  "vitest/max-expects": "off"
32
229
  }
33
230
  };
231
+ if (excludeFiles.length > 0) resolvedOverride.excludeFiles = excludeFiles;
232
+ return resolvedOverride;
34
233
  });
35
234
  const vitestConfig = {
36
235
  ...vitest,
@@ -40,7 +239,9 @@ function createOxlintConfig(options = {}) {
40
239
  ...config,
41
240
  extends: [
42
241
  core,
242
+ antiSlop,
43
243
  vitestConfig,
244
+ ...framework.extends,
44
245
  ...extensions
45
246
  ],
46
247
  ignorePatterns: [
@@ -88,9 +289,12 @@ function createOxlintConfig(options = {}) {
88
289
  files: ["**/*.{ts,tsx}"],
89
290
  rules: { "unicorn/no-useless-undefined": ["error", { checkArguments: false }] }
90
291
  },
292
+ ...framework.overrides,
91
293
  ...overrides
92
294
  ],
93
295
  rules: {
296
+ "anti-slop/no-array-filter-map": "off",
297
+ "anti-slop/no-conditional-empty-object-spread": "off",
94
298
  "class-methods-use-this": "off",
95
299
  eqeqeq: ["error", "smart"],
96
300
  "func-style": "off",
@@ -136,9 +340,12 @@ function createOxlintConfig(options = {}) {
136
340
  "unicorn/no-negated-condition": "off",
137
341
  "unicorn/no-nested-ternary": "off",
138
342
  ...rules
343
+ },
344
+ settings: {
345
+ ...framework.settings,
346
+ ...settings
139
347
  }
140
348
  };
141
349
  }
142
- const oxlintConfig = createOxlintConfig();
143
350
  //#endregion
144
- export { createOxlintConfig, oxlintConfig as default, oxlintConfig };
351
+ export { createOxlintConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monke-together-strong/oxc-config",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Shared TypeScript, Oxlint, and Oxfmt presets.",
5
5
  "homepage": "https://github.com/monke-together-strong/monke-tools/tree/main/packages/oxc-config#readme",
6
6
  "bugs": {
@@ -39,15 +39,17 @@
39
39
  "dependencies": {
40
40
  "eslint-plugin-jsdoc": "63.3.3",
41
41
  "eslint-plugin-perfectionist": "5.10.1",
42
- "ultracite": "7.9.4"
42
+ "ultracite": "8.0.1",
43
+ "yaml": "^2.8.3",
44
+ "zod": "^4.5.4"
43
45
  },
44
46
  "devDependencies": {
45
- "oxfmt": "0.62.0",
46
- "oxlint": "1.77.0"
47
+ "oxfmt": "0.73.0",
48
+ "oxlint": "1.88.0"
47
49
  },
48
50
  "peerDependencies": {
49
- "oxfmt": "^0.62.0",
50
- "oxlint": "^1.77.0",
51
- "oxlint-tsgolint": ">=7.0.2001"
51
+ "oxfmt": ">=0.71.0 <1",
52
+ "oxlint": "^1.86.0",
53
+ "oxlint-tsgolint": ">=7.0.2004"
52
54
  }
53
55
  }