@letstri/oxc-config 0.2.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
@@ -4,15 +4,25 @@ Opinionated, shared [oxlint](https://oxc.rs/docs/guide/usage/linter.html) and [o
4
4
 
5
5
  > [!NOTE]
6
6
  > This is an **opinionated** config — it ships a curated set of rules and
7
- > formatting defaults meant to work out of the box. Override anything you
8
- > disagree with via the [`override`](#overrides) option.
7
+ > formatting defaults meant to work out of the box. [Override](#overrides)
8
+ > anything you disagree with.
9
9
 
10
10
  ## Install
11
11
 
12
12
  ```bash
13
- pnpm add -D @letstri/oxc-config oxlint oxfmt
13
+ npm i -D @letstri/oxc-config oxlint oxfmt
14
14
  ```
15
15
 
16
+ Then scaffold the config files and editor settings with the `oxc-config` CLI:
17
+
18
+ ```bash
19
+ npx oxc-config config # create oxlint.config.ts + oxfmt.config.ts
20
+ npx oxc-config editors # write .vscode + .zed settings
21
+ ```
22
+
23
+ Run `oxc-config --help` for all commands. Details: [Usage](#usage) and
24
+ [Editor setup](#editor-setup).
25
+
16
26
  ## AI setup prompt
17
27
 
18
28
  Paste this into Claude Code, Cursor, or any coding agent to wire everything up:
@@ -44,7 +54,7 @@ Set up @letstri/oxc-config (oxlint + oxfmt) in this project:
44
54
  - "format:check": "oxfmt --check"
45
55
  6. Framework plugins auto-enable from the nearest package.json. If a framework
46
56
  dep (react/vue/next/vitest/jest/typescript) lives in a nested workspace,
47
- enable it manually, e.g. `oxlintConfig({ vue: true })`.
57
+ enable it manually, e.g. `oxlintConfig({ plugins: ['vue'] })`.
48
58
  7. Add the VS Code and Zed editor settings from the @letstri/oxc-config README.
49
59
  8. Run `pnpm lint` and `pnpm format` and fix anything reported.
50
60
  ````
@@ -96,33 +106,45 @@ export default oxlintConfig({
96
106
 
97
107
  ### Overrides
98
108
 
99
- Anything you pass to `oxlintConfig` / `oxfmtConfig` is deep-merged over the base
100
- config via [defu](https://github.com/unjs/defu):
109
+ `oxlintConfig` / `oxfmtConfig` accept **any number of config objects**, all
110
+ deep-merged over the base config via [defu](https://github.com/unjs/defu) (arrays
111
+ are concatenated, so plugins from different pieces combine instead of
112
+ overwriting):
101
113
 
102
114
  ```ts
103
- export default oxlintConfig({
104
- rules: {
105
- 'no-console': 'off',
106
- },
107
- })
115
+ export default oxlintConfig({ rules: { 'no-console': 'off' } }, { plugins: ['vue'] })
108
116
  ```
109
117
 
110
118
  ### Tailwind
111
119
 
112
120
  `tailwind({ entryPoint })` returns a config chunk for
113
121
  [`eslint-plugin-better-tailwindcss`](https://github.com/schoero/eslint-plugin-better-tailwindcss).
114
- Spread it into `oxlintConfig`:
122
+ Pass it as an argument to `oxlintConfig`:
115
123
 
116
124
  ```ts
117
125
  import { oxlintConfig, tailwind } from '@letstri/oxc-config'
118
126
 
119
- export default oxlintConfig({
120
- ...tailwind({ entryPoint: 'app/globals.css' }),
121
- })
127
+ export default oxlintConfig(
128
+ { plugins: ['react', 'jsx-a11y'] },
129
+ tailwind({ entryPoint: 'app/globals.css' }),
130
+ )
122
131
  ```
123
132
 
124
- `entryPoint` (required) is your Tailwind entry CSS, so the plugin can resolve
125
- class names. The plugin is an **optional peer dependency** — install it yourself:
133
+ Because arguments are merged (not spread), Tailwind's plugins combine with the
134
+ ones above rather than overwriting them.
135
+
136
+ Options:
137
+
138
+ - `entryPoint` (required) — your Tailwind entry CSS, so the plugin can resolve
139
+ class names.
140
+ - `ignoreClasses` — class names to exempt from `no-unknown-classes` (e.g. classes
141
+ a component library generates that the plugin can't resolve):
142
+
143
+ ```ts
144
+ tailwind({ entryPoint: 'app/globals.css', ignoreClasses: ['toaster'] })
145
+ ```
146
+
147
+ The plugin is an **optional peer dependency** — install it yourself:
126
148
 
127
149
  ```bash
128
150
  pnpm add -D eslint-plugin-better-tailwindcss
@@ -135,119 +157,30 @@ If the plugin is missing, `tailwind()` throws with an install hint.
135
157
  Both editors use the official [oxc](https://oxc.rs) tooling — `oxlint` for
136
158
  linting and `oxfmt` for formatting — replacing ESLint and Prettier.
137
159
 
138
- ### VS Code
139
-
140
- Install the [`oxc.oxc-vscode`](https://marketplace.visualstudio.com/items?itemName=oxc.oxc-vscode)
141
- extension.
142
-
143
- <details>
144
- <summary>Show VS Code config</summary>
145
-
146
- `.vscode/extensions.json`:
160
+ The `oxc-config editors` command writes (or updates) the editor configs for you,
161
+ deep-merging into any existing files so your other settings are kept:
147
162
 
148
- ```json
149
- {
150
- "recommendations": ["oxc.oxc-vscode"]
151
- }
152
- ```
163
+ ```bash
164
+ # both editors
165
+ pnpm exec oxc-config editors
153
166
 
154
- `.vscode/settings.json`:
155
-
156
- ```jsonc
157
- {
158
- "oxc.configPath": "oxlint.config.ts",
159
- "oxc.fmt.configPath": "oxfmt.config.ts",
160
- "oxc.typeAware": true,
161
- "oxc.unusedDisableDirectives": "deny",
162
- "oxc.enable": true,
163
- "prettier.enable": false,
164
- "editor.defaultFormatter": "oxc.oxc-vscode",
165
- "editor.formatOnSave": true,
166
- "editor.formatOnSaveMode": "file", // oxfmt can only format whole files
167
- "editor.codeActionsOnSave": {
168
- "source.fixAll.oxc": "explicit",
169
- "source.organizeImports": "never", // let oxfmt handle import organization
170
- },
171
- }
167
+ # or just one
168
+ pnpm exec oxc-config editors --vscode
169
+ pnpm exec oxc-config editors --zed
172
170
  ```
173
171
 
174
- </details>
172
+ It's idempotent — safe to re-run to pull the latest recommended settings.
175
173
 
176
- ### Zed
177
-
178
- Zed ships with the oxc language servers built in.
174
+ ### VS Code
179
175
 
180
- <details>
181
- <summary>Show Zed config</summary>
182
-
183
- `.zed/settings.json`:
184
-
185
- ```jsonc
186
- {
187
- "lsp": {
188
- "oxlint": {
189
- "initialization_options": {
190
- "settings": {
191
- "configPath": null,
192
- "run": "onType",
193
- "disableNestedConfig": false,
194
- "fixKind": "safe_fix",
195
- "unusedDisableDirectives": "deny",
196
- },
197
- },
198
- },
199
- "oxfmt": {
200
- "initialization_options": {
201
- "settings": {
202
- "fmt.configPath": null,
203
- "run": "onSave",
204
- },
205
- },
206
- },
207
- },
208
- "languages": {
209
- "TypeScript": {
210
- "format_on_save": "on",
211
- "prettier": { "allowed": false },
212
- "language_servers": ["...", "oxlint", "oxfmt"],
213
- "formatter": [
214
- { "language_server": { "name": "oxfmt" } },
215
- { "code_action": "source.fixAll.oxc" },
216
- ],
217
- },
218
- "TSX": {
219
- "format_on_save": "on",
220
- "prettier": { "allowed": false },
221
- "language_servers": ["...", "oxlint", "oxfmt"],
222
- "formatter": [
223
- { "language_server": { "name": "oxfmt" } },
224
- { "code_action": "source.fixAll.oxc" },
225
- ],
226
- },
227
- "JavaScript": {
228
- "format_on_save": "on",
229
- "prettier": { "allowed": false },
230
- "language_servers": ["...", "oxlint", "oxfmt"],
231
- "formatter": [
232
- { "language_server": { "name": "oxfmt" } },
233
- { "code_action": "source.fixAll.oxc" },
234
- ],
235
- },
236
- "JSON": {
237
- "format_on_save": "on",
238
- "prettier": { "allowed": false },
239
- "formatter": [{ "language_server": { "name": "oxfmt" } }],
240
- },
241
- "Markdown": {
242
- "format_on_save": "on",
243
- "prettier": { "allowed": false },
244
- "formatter": [{ "language_server": { "name": "oxfmt" } }],
245
- },
246
- },
247
- }
248
- ```
176
+ Install the [`oxc.oxc-vscode`](https://marketplace.visualstudio.com/items?itemName=oxc.oxc-vscode)
177
+ extension (`oxc-config editors --vscode` also adds it to `.vscode/extensions.json`). The
178
+ CLI writes `.vscode/settings.json` — oxlint as linter, oxfmt as the default
179
+ formatter with format-on-save, and Prettier's import organization turned off.
249
180
 
250
- Apply the same `languages` block to any other file types you want oxfmt to
251
- format (`JSONC`, `CSS`, `HTML`, `YAML`, …).
181
+ ### Zed
252
182
 
253
- </details>
183
+ Zed ships with the oxc language servers built in, so no extension is needed.
184
+ `oxc-config editors --zed` writes `.zed/settings.json` — oxfmt as the formatter (with
185
+ `source.fixAll.oxc` on save for JS/TS) and Prettier disabled, across the file
186
+ types oxfmt supports.
package/dist/cli.d.mts ADDED
@@ -0,0 +1 @@
1
+ export { };
package/dist/cli.mjs ADDED
@@ -0,0 +1,165 @@
1
+ #!/usr/bin/env node
2
+ import process from "node:process";
3
+ import { createDefu } from "defu";
4
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
+ import { dirname, resolve } from "node:path";
6
+ import { boolean, command, run } from "@drizzle-team/brocli";
7
+ import { parse } from "jsonc-parser";
8
+ //#region src/editors.ts
9
+ /**
10
+ * Editor config templates, adapted from the official oxc examples:
11
+ * - https://github.com/oxc-project/oxc-vscode/blob/main/.vscode/settings.json
12
+ * - https://github.com/oxc-project/oxc-zed/blob/main/examples/both/.zed/settings.json
13
+ *
14
+ * `configPath` values point at this library's TypeScript config files.
15
+ */
16
+ const vscodeExtensions = { recommendations: ["oxc.oxc-vscode"] };
17
+ const vscodeSettings = {
18
+ "oxc.configPath": "oxlint.config.ts",
19
+ "oxc.fmt.configPath": "oxfmt.config.ts",
20
+ "oxc.typeAware": true,
21
+ "oxc.unusedDisableDirectives": "deny",
22
+ "editor.defaultFormatter": "oxc.oxc-vscode",
23
+ "editor.formatOnSave": true,
24
+ "editor.formatOnSaveMode": "file",
25
+ "editor.codeActionsOnSave": { "source.organizeImports": "never" }
26
+ };
27
+ const oxfmtFormatter = { language_server: { name: "oxfmt" } };
28
+ function formatOnSave(withFix = false) {
29
+ return {
30
+ format_on_save: "on",
31
+ prettier: { allowed: false },
32
+ formatter: withFix ? [oxfmtFormatter, { code_action: "source.fixAll.oxc" }] : [oxfmtFormatter]
33
+ };
34
+ }
35
+ const cssLike = [
36
+ "CSS",
37
+ "GraphQL",
38
+ "Handlebars",
39
+ "HTML",
40
+ "JSON",
41
+ "JSON5",
42
+ "JSONC",
43
+ "Less",
44
+ "Markdown",
45
+ "MDX",
46
+ "SCSS",
47
+ "YAML"
48
+ ];
49
+ const codeLike = [
50
+ "JavaScript",
51
+ "TypeScript",
52
+ "TSX",
53
+ "Vue.js"
54
+ ];
55
+ const zedSettings = {
56
+ lsp: {
57
+ oxlint: { initialization_options: { settings: {
58
+ configPath: null,
59
+ run: "onType",
60
+ disableNestedConfig: false,
61
+ fixKind: "safe_fix",
62
+ unusedDisableDirectives: "deny"
63
+ } } },
64
+ oxfmt: { initialization_options: { settings: {
65
+ "fmt.configPath": null,
66
+ "run": "onSave"
67
+ } } }
68
+ },
69
+ languages: {
70
+ ...Object.fromEntries(cssLike.map((lang) => [lang, formatOnSave()])),
71
+ ...Object.fromEntries(codeLike.map((lang) => [lang, formatOnSave(true)]))
72
+ }
73
+ };
74
+ //#endregion
75
+ //#region src/cli.ts
76
+ /**
77
+ * Like defu, but arrays are merged as a de-duplicated union instead of being
78
+ * concatenated — keeps updates idempotent and preserves user-added entries.
79
+ */
80
+ const mergeConfig = createDefu((obj, key, value) => {
81
+ const current = obj[key];
82
+ if (Array.isArray(current) && Array.isArray(value)) {
83
+ const seen = /* @__PURE__ */ new Set();
84
+ obj[key] = [...value, ...current].filter((item) => {
85
+ const id = JSON.stringify(item);
86
+ if (seen.has(id)) return false;
87
+ seen.add(id);
88
+ return true;
89
+ });
90
+ return true;
91
+ }
92
+ return false;
93
+ });
94
+ function readJsonc(path) {
95
+ if (!existsSync(path)) return {};
96
+ return parse(readFileSync(path, "utf-8"), [], { allowTrailingComma: true }) ?? {};
97
+ }
98
+ /** Deep-merge `base` into the JSON(C) file at `path`, creating it if absent. */
99
+ function mergeJsonFile(path, base) {
100
+ const existed = existsSync(path);
101
+ const merged = mergeConfig(base, readJsonc(path));
102
+ mkdirSync(dirname(path), { recursive: true });
103
+ writeFileSync(path, `${JSON.stringify(merged, null, 2)}\n`);
104
+ return existed ? "updated" : "created";
105
+ }
106
+ /** Write `content` to `path`, skipping an existing file unless `force`. */
107
+ function writeFile(path, content, force) {
108
+ const existed = existsSync(path);
109
+ if (existed && !force) return "skipped";
110
+ mkdirSync(dirname(path), { recursive: true });
111
+ writeFileSync(path, content);
112
+ return existed ? "overwritten" : "created";
113
+ }
114
+ const OXLINT_CONFIG = `import { oxlintConfig } from '@letstri/oxc-config'
115
+
116
+ export default oxlintConfig()
117
+ `;
118
+ const OXFMT_CONFIG = `import { oxfmtConfig } from '@letstri/oxc-config'
119
+
120
+ export default oxfmtConfig()
121
+ `;
122
+ function version() {
123
+ try {
124
+ return JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8")).version ?? "0.0.0";
125
+ } catch {
126
+ return "0.0.0";
127
+ }
128
+ }
129
+ run([command({
130
+ name: "config",
131
+ desc: "Create oxlint.config.ts and oxfmt.config.ts",
132
+ options: { force: boolean().alias("f").desc("Overwrite existing files").default(false) },
133
+ handler: ({ force }) => {
134
+ const cwd = process.cwd();
135
+ const lint = writeFile(resolve(cwd, "oxlint.config.ts"), OXLINT_CONFIG, force);
136
+ const fmt = writeFile(resolve(cwd, "oxfmt.config.ts"), OXFMT_CONFIG, force);
137
+ process.stdout.write(`config: ${lint} oxlint.config.ts, ${fmt} oxfmt.config.ts\n`);
138
+ }
139
+ }), command({
140
+ name: "editors",
141
+ desc: "Write/update VS Code and Zed editor configs (deep-merged into existing files)",
142
+ options: {
143
+ vscode: boolean().desc("Only VS Code").default(false),
144
+ zed: boolean().desc("Only Zed").default(false)
145
+ },
146
+ handler: ({ vscode, zed }) => {
147
+ const both = !vscode && !zed;
148
+ const cwd = process.cwd();
149
+ if (both || vscode) {
150
+ const s = mergeJsonFile(resolve(cwd, ".vscode/settings.json"), vscodeSettings);
151
+ const e = mergeJsonFile(resolve(cwd, ".vscode/extensions.json"), vscodeExtensions);
152
+ process.stdout.write(`vscode: ${s} .vscode/settings.json, ${e} .vscode/extensions.json\n`);
153
+ }
154
+ if (both || zed) {
155
+ const s = mergeJsonFile(resolve(cwd, ".zed/settings.json"), zedSettings);
156
+ process.stdout.write(`zed: ${s} .zed/settings.json\n`);
157
+ }
158
+ }
159
+ })], {
160
+ name: "oxc-config",
161
+ description: "Set up @letstri/oxc-config in your project",
162
+ version: version()
163
+ });
164
+ //#endregion
165
+ export {};
package/dist/index.d.mts CHANGED
@@ -8,7 +8,10 @@ type OxlintOptions$1 = Parameters<typeof defineConfig>[0];
8
8
  * Plugins are auto-enabled by detecting their package (`typescript`, `react`,
9
9
  * `vue`, `next`, `vitest`, `jest`) in the nearest `package.json`. To enable a
10
10
  * plugin whose dependency lives elsewhere — e.g. a nested workspace like
11
- * `apps/web/package.json` — add it through `overrides.plugins`.
11
+ * `apps/web/package.json` — add it through `plugins`.
12
+ *
13
+ * Pass any number of config objects; they are deep-merged (arrays concatenated),
14
+ * so pieces like {@link tailwind} compose without clobbering each other.
12
15
  *
13
16
  * @example
14
17
  * ```ts
@@ -22,15 +25,22 @@ type OxlintOptions$1 = Parameters<typeof defineConfig>[0];
22
25
  * ```
23
26
  * @example
24
27
  * ```ts
25
- * // add Tailwind linting
26
- * export default oxlintConfig({ ...tailwind({ entryPoint: 'app/globals.css' }) })
28
+ * // compose Tailwind — its plugins merge with the ones above, not overwrite
29
+ * export default oxlintConfig(
30
+ * { plugins: ['react', 'jsx-a11y'] },
31
+ * tailwind({ entryPoint: 'app/globals.css' }),
32
+ * )
27
33
  * ```
28
34
  */
29
- declare function oxlintConfig(overrides?: OxlintOptions$1): OxlintOptions$1;
35
+ declare function oxlintConfig(...overrides: OxlintOptions$1[]): OxlintOptions$1;
30
36
  //#endregion
31
37
  //#region src/oxfmt.d.ts
32
38
  type OxfmtOptions = Parameters<typeof defineConfig$1>[0];
33
- declare function oxfmtConfig(overrides?: OxfmtOptions): OxfmtOptions;
39
+ /**
40
+ * Build an oxfmt config. Pass any number of config objects; they are deep-merged
41
+ * over the base config via defu.
42
+ */
43
+ declare function oxfmtConfig(...overrides: OxfmtOptions[]): OxfmtOptions;
34
44
  //#endregion
35
45
  //#region src/tailwind.d.ts
36
46
  type OxlintOptions = Parameters<typeof defineConfig>[0];
@@ -40,6 +50,11 @@ interface TailwindOptions {
40
50
  * class names.
41
51
  */
42
52
  entryPoint: string;
53
+ /**
54
+ * Class names to exempt from `no-unknown-classes` — e.g. classes generated by
55
+ * component libraries that the plugin can't resolve from the entry CSS.
56
+ */
57
+ ignoreClasses?: string[];
43
58
  /**
44
59
  * Directory scanned to check the plugin is installed.
45
60
  *
@@ -49,16 +64,19 @@ interface TailwindOptions {
49
64
  }
50
65
  /**
51
66
  * Tailwind linting via [`eslint-plugin-better-tailwindcss`](https://github.com/schoero/eslint-plugin-better-tailwindcss).
52
- * Spread the result into `oxlintConfig`:
67
+ * Pass the result as an argument to `oxlintConfig`:
53
68
  *
54
69
  * ```ts
55
- * export default oxlintConfig({ ...tailwind({ entryPoint: 'app/globals.css' }) })
70
+ * export default oxlintConfig(tailwind({
71
+ * entryPoint: 'app/globals.css',
72
+ * ignoreClasses: ['toaster'],
73
+ * }))
56
74
  * ```
57
75
  *
58
76
  * The plugin is an optional peer dependency — install it yourself
59
77
  * (`pnpm add -D eslint-plugin-better-tailwindcss`). If it is missing, an error
60
78
  * is thrown.
61
79
  */
62
- declare function tailwind({ entryPoint, cwd }: TailwindOptions): OxlintOptions;
80
+ declare function tailwind({ entryPoint, ignoreClasses, cwd }: TailwindOptions): OxlintOptions;
63
81
  //#endregion
64
82
  export { oxfmtConfig, oxlintConfig, tailwind };
package/dist/index.mjs CHANGED
@@ -534,7 +534,10 @@ function resolvePlugins(cwd) {
534
534
  * Plugins are auto-enabled by detecting their package (`typescript`, `react`,
535
535
  * `vue`, `next`, `vitest`, `jest`) in the nearest `package.json`. To enable a
536
536
  * plugin whose dependency lives elsewhere — e.g. a nested workspace like
537
- * `apps/web/package.json` — add it through `overrides.plugins`.
537
+ * `apps/web/package.json` — add it through `plugins`.
538
+ *
539
+ * Pass any number of config objects; they are deep-merged (arrays concatenated),
540
+ * so pieces like {@link tailwind} compose without clobbering each other.
538
541
  *
539
542
  * @example
540
543
  * ```ts
@@ -548,41 +551,52 @@ function resolvePlugins(cwd) {
548
551
  * ```
549
552
  * @example
550
553
  * ```ts
551
- * // add Tailwind linting
552
- * export default oxlintConfig({ ...tailwind({ entryPoint: 'app/globals.css' }) })
554
+ * // compose Tailwind — its plugins merge with the ones above, not overwrite
555
+ * export default oxlintConfig(
556
+ * { plugins: ['react', 'jsx-a11y'] },
557
+ * tailwind({ entryPoint: 'app/globals.css' }),
558
+ * )
553
559
  * ```
554
560
  */
555
- function oxlintConfig(overrides = {}) {
556
- return defu(overrides, { plugins: resolvePlugins(process.cwd()) }, baseOxlintConfig);
561
+ function oxlintConfig(...overrides) {
562
+ return defu({}, ...overrides, { plugins: resolvePlugins(process.cwd()) }, baseOxlintConfig);
557
563
  }
558
564
  //#endregion
559
565
  //#region src/oxfmt.ts
560
566
  const baseOxfmtConfig = defineConfig$1({
567
+ printWidth: 100,
561
568
  singleQuote: true,
562
569
  semi: false,
563
570
  arrowParens: "avoid",
564
571
  sortImports: true,
565
572
  quoteProps: "consistent"
566
573
  });
567
- function oxfmtConfig(overrides = {}) {
568
- return defu(overrides, baseOxfmtConfig);
574
+ /**
575
+ * Build an oxfmt config. Pass any number of config objects; they are deep-merged
576
+ * over the base config via defu.
577
+ */
578
+ function oxfmtConfig(...overrides) {
579
+ return defu({}, ...overrides, baseOxfmtConfig);
569
580
  }
570
581
  //#endregion
571
582
  //#region src/tailwind.ts
572
583
  const TAILWIND_PLUGIN = "eslint-plugin-better-tailwindcss";
573
584
  /**
574
585
  * Tailwind linting via [`eslint-plugin-better-tailwindcss`](https://github.com/schoero/eslint-plugin-better-tailwindcss).
575
- * Spread the result into `oxlintConfig`:
586
+ * Pass the result as an argument to `oxlintConfig`:
576
587
  *
577
588
  * ```ts
578
- * export default oxlintConfig({ ...tailwind({ entryPoint: 'app/globals.css' }) })
589
+ * export default oxlintConfig(tailwind({
590
+ * entryPoint: 'app/globals.css',
591
+ * ignoreClasses: ['toaster'],
592
+ * }))
579
593
  * ```
580
594
  *
581
595
  * The plugin is an optional peer dependency — install it yourself
582
596
  * (`pnpm add -D eslint-plugin-better-tailwindcss`). If it is missing, an error
583
597
  * is thrown.
584
598
  */
585
- function tailwind({ entryPoint, cwd = process.cwd() }) {
599
+ function tailwind({ entryPoint, ignoreClasses = [], cwd = process.cwd() }) {
586
600
  if (!getInstalledPackages(cwd).has(TAILWIND_PLUGIN)) throw new Error(`[@letstri/oxc-config] Tailwind linting needs "${TAILWIND_PLUGIN}". Install it: pnpm add -D ${TAILWIND_PLUGIN}`);
587
601
  return defineConfig({
588
602
  jsPlugins: [TAILWIND_PLUGIN],
@@ -595,7 +609,7 @@ function tailwind({ entryPoint, cwd = process.cwd() }) {
595
609
  "better-tailwindcss/no-conflicting-classes": "error",
596
610
  "better-tailwindcss/no-deprecated-classes": "error",
597
611
  "better-tailwindcss/no-duplicate-classes": "error",
598
- "better-tailwindcss/no-unknown-classes": "error"
612
+ "better-tailwindcss/no-unknown-classes": ["error", { ignore: ignoreClasses }]
599
613
  }
600
614
  });
601
615
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letstri/oxc-config",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "description": "Opinionated Oxlint and Oxfmt configs",
5
5
  "homepage": "https://github.com/letstri/oxc-config#readme",
6
6
  "bugs": {
@@ -12,6 +12,9 @@
12
12
  "type": "git",
13
13
  "url": "git+https://github.com/letstri/oxc-config.git"
14
14
  },
15
+ "bin": {
16
+ "oxc-config": "./dist/cli.mjs"
17
+ },
15
18
  "files": [
16
19
  "dist"
17
20
  ],
@@ -28,7 +31,9 @@
28
31
  "access": "public"
29
32
  },
30
33
  "dependencies": {
31
- "defu": "^6.1.7"
34
+ "@drizzle-team/brocli": "^0.12.0",
35
+ "defu": "^6.1.7",
36
+ "jsonc-parser": "^3.3.1"
32
37
  },
33
38
  "devDependencies": {
34
39
  "@types/node": "^26.1.1",