@letstri/oxc-config 0.1.0 → 0.2.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
@@ -71,59 +71,65 @@ export default oxfmtConfig()
71
71
 
72
72
  ### Plugins
73
73
 
74
- Framework plugins are enabled automatically by detecting dependencies in the
75
- nearest `package.json` (`typescript`, `react`, `vue`, `next`, `vitest`, `jest`,
76
- `tailwind`). Toggle any of them manually — useful when the dependency lives in a
77
- nested workspace such as `apps/web/package.json`:
74
+ Plugins are enabled automatically by detecting dependencies in the nearest
75
+ `package.json`:
76
+
77
+ | Dependency | Plugins enabled |
78
+ | ------------ | ------------------- |
79
+ | `typescript` | `typescript` |
80
+ | `react` | `react`, `jsx-a11y` |
81
+ | `vue` | `vue` |
82
+ | `next` | `nextjs` |
83
+ | `vitest` | `vitest` |
84
+ | `jest` | `jest` |
85
+
86
+ Detection only reads the **nearest** `package.json`. If a dependency isn't found
87
+ there — e.g. it lives in a nested workspace like `apps/web/package.json`, or is
88
+ hoisted somewhere the scan doesn't see — its plugin won't be enabled. Add it
89
+ manually via `plugins`:
78
90
 
79
91
  ```ts
80
92
  export default oxlintConfig({
81
- vue: true, // force on
82
- jest: false, // force off
93
+ plugins: ['vue'],
83
94
  })
84
95
  ```
85
96
 
86
- ### Tailwind
87
-
88
- `tailwind: true` enables [`eslint-plugin-better-tailwindcss`](https://github.com/schoero/eslint-plugin-better-tailwindcss)
89
- (auto-detected from a `tailwindcss` dependency). The plugin is an **optional peer
90
- dependency** — install it yourself:
91
-
92
- ```bash
93
- pnpm add -D eslint-plugin-better-tailwindcss
94
- ```
97
+ ### Overrides
95
98
 
96
- If Tailwind is enabled but the plugin is missing, the config logs a reminder and
97
- skips Tailwind linting instead of crashing. Point the plugin at your Tailwind
98
- entry CSS so it can resolve class names:
99
+ Anything you pass to `oxlintConfig` / `oxfmtConfig` is deep-merged over the base
100
+ config via [defu](https://github.com/unjs/defu):
99
101
 
100
102
  ```ts
101
103
  export default oxlintConfig({
102
- tailwind: true,
103
- override: {
104
- settings: {
105
- 'better-tailwindcss': {
106
- entryPoint: 'src/styles/globals.css',
107
- },
108
- },
104
+ rules: {
105
+ 'no-console': 'off',
109
106
  },
110
107
  })
111
108
  ```
112
109
 
113
- ### Overrides
110
+ ### Tailwind
114
111
 
115
- `override` is deep-merged over the base config via [defu](https://github.com/unjs/defu):
112
+ `tailwind(options?)` returns a config chunk for
113
+ [`eslint-plugin-better-tailwindcss`](https://github.com/schoero/eslint-plugin-better-tailwindcss).
114
+ Spread it into `oxlintConfig`:
116
115
 
117
116
  ```ts
117
+ import { oxlintConfig, tailwind } from '@letstri/oxc-config'
118
+
118
119
  export default oxlintConfig({
119
- override: {
120
- rules: {
121
- 'no-console': 'off',
122
- },
123
- },
120
+ ...tailwind({ entryPoint: 'app/globals.css' }),
124
121
  })
125
122
  ```
126
123
 
124
+ `entryPoint` is your Tailwind entry CSS, so the plugin can resolve class names. The plugin is an **optional peer dependency** — install it yourself:
125
+
126
+ ```bash
127
+ pnpm add -D eslint-plugin-better-tailwindcss
128
+ ```
129
+
130
+ If the plugin is missing, `tailwind()` logs a reminder and returns an empty
131
+ config instead of crashing oxlint.
132
+
127
133
  ## Editor setup
128
134
 
129
135
  Both editors use the official [oxc](https://oxc.rs) tooling — `oxlint` for
package/dist/index.d.mts CHANGED
@@ -3,59 +3,59 @@ import { defineConfig as defineConfig$1 } from "oxlint";
3
3
  //#region src/index.d.ts
4
4
  type OxlintOptions = Parameters<typeof defineConfig$1>[0];
5
5
  type OxfmtOptions = Parameters<typeof defineConfig>[0];
6
- declare const pluginToggles: {
7
- typescript: {
8
- packages: string[];
9
- plugins: "typescript"[];
10
- };
11
- react: {
12
- packages: string[];
13
- plugins: ("react" | "jsx-a11y" | "react-perf")[];
14
- };
15
- vue: {
16
- packages: string[];
17
- plugins: "vue"[];
18
- };
19
- next: {
20
- packages: string[];
21
- plugins: "nextjs"[];
22
- };
23
- vitest: {
24
- packages: string[];
25
- plugins: "vitest"[];
26
- };
27
- jest: {
28
- packages: string[];
29
- plugins: "jest"[];
30
- };
31
- };
32
- type ToggleName = keyof typeof pluginToggles;
33
- interface OxlintConfigOptions extends Partial<Record<ToggleName, boolean>> {
6
+ /**
7
+ * Build an oxlint config.
8
+ *
9
+ * Plugins are auto-enabled by detecting their package (`typescript`, `react`,
10
+ * `vue`, `next`, `vitest`, `jest`) in the nearest `package.json`. To enable a
11
+ * plugin whose dependency lives elsewhere — e.g. a nested workspace like
12
+ * `apps/web/package.json` — add it through `overrides.plugins`.
13
+ *
14
+ * `overrides` is deep-merged over the base config via defu.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * // auto-detect from the current package.json
19
+ * export default oxlintConfig()
20
+ * ```
21
+ * @example
22
+ * ```ts
23
+ * // enable a plugin by hand, tweak a rule
24
+ * export default oxlintConfig({ plugins: ['vue'], rules: { 'no-console': 'off' } })
25
+ * ```
26
+ * @example
27
+ * ```ts
28
+ * // add Tailwind linting
29
+ * export default oxlintConfig({ ...tailwind('app/globals.css') })
30
+ * ```
31
+ */
32
+ declare function oxlintConfig(overrides?: OxlintOptions): OxlintOptions;
33
+ interface TailwindOptions {
34
34
  /**
35
- * Enable `eslint-plugin-better-tailwindcss`. Auto-detected from a `tailwindcss`
36
- * dependency; set it explicitly when Tailwind lives in a nested workspace.
37
- *
38
- * The plugin is an optional peer dependency — install it yourself
39
- * (`pnpm add -D eslint-plugin-better-tailwindcss`). If it is missing, a warning
40
- * is logged and Tailwind linting is skipped. Point the plugin at your entry
41
- * CSS via `override.settings`.
42
- */
43
- tailwind?: boolean;
44
- /**
45
- * Deep-merged over the base config via defu.
35
+ * Path to the Tailwind entry CSS. Required — the plugin needs it to resolve
36
+ * class names.
46
37
  */
47
- override?: OxlintOptions;
38
+ entryPoint: string;
48
39
  /**
49
- * Directory whose `package.json` is scanned to auto-detect plugins.
40
+ * Directory scanned to check the plugin is installed.
50
41
  *
51
42
  * @default process.cwd()
52
43
  */
53
44
  cwd?: string;
54
45
  }
55
- interface OxfmtConfigOptions {
56
- override?: OxfmtOptions;
57
- }
58
- declare function oxlintConfig(options?: OxlintConfigOptions): OxlintOptions;
59
- declare function oxfmtConfig({ override }?: OxfmtConfigOptions): OxfmtOptions;
46
+ /**
47
+ * Tailwind linting via [`eslint-plugin-better-tailwindcss`](https://github.com/schoero/eslint-plugin-better-tailwindcss).
48
+ * Spread the result into {@link oxlintConfig}:
49
+ *
50
+ * ```ts
51
+ * export default oxlintConfig({ ...tailwind({ entryPoint: 'app/globals.css' }) })
52
+ * ```
53
+ *
54
+ * The plugin is an optional peer dependency — install it yourself
55
+ * (`pnpm add -D eslint-plugin-better-tailwindcss`). If it is missing, a warning
56
+ * is logged and an empty config is returned so oxlint does not crash.
57
+ */
58
+ declare function tailwind({ entryPoint, cwd }: TailwindOptions): OxlintOptions;
59
+ declare function oxfmtConfig(overrides?: OxfmtOptions): OxfmtOptions;
60
60
  //#endregion
61
- export { oxfmtConfig, oxlintConfig };
61
+ export { oxfmtConfig, oxlintConfig, tailwind };
package/dist/index.mjs CHANGED
@@ -489,18 +489,19 @@ const basePlugins = [
489
489
  "promise",
490
490
  "oxc"
491
491
  ];
492
- const pluginToggles = {
492
+ const TAILWIND_PLUGIN = "eslint-plugin-better-tailwindcss";
493
+ /**
494
+ * Map of a dependency to the oxlint plugins it enables. A plugin is registered
495
+ * when any of its packages is found in the nearest `package.json`.
496
+ */
497
+ const pluginDetectors = {
493
498
  typescript: {
494
499
  packages: ["typescript"],
495
500
  plugins: ["typescript"]
496
501
  },
497
502
  react: {
498
503
  packages: ["react"],
499
- plugins: [
500
- "react",
501
- "jsx-a11y",
502
- "react-perf"
503
- ]
504
+ plugins: ["react", "jsx-a11y"]
504
505
  },
505
506
  vue: {
506
507
  packages: ["vue"],
@@ -519,7 +520,6 @@ const pluginToggles = {
519
520
  plugins: ["jest"]
520
521
  }
521
522
  };
522
- const tailwindConfig = defineConfig$1({ jsPlugins: ["eslint-plugin-better-tailwindcss"] });
523
523
  function getInstalledPackages(cwd) {
524
524
  const names = /* @__PURE__ */ new Set();
525
525
  try {
@@ -532,22 +532,21 @@ function getInstalledPackages(cwd) {
532
532
  } catch {}
533
533
  return names;
534
534
  }
535
- function resolvePlugins(options, installed) {
535
+ function resolvePlugins(cwd) {
536
+ const installed = getInstalledPackages(cwd);
536
537
  const plugins = [...basePlugins];
537
- for (const name of Object.keys(pluginToggles)) {
538
- const toggle = pluginToggles[name];
539
- if (options[name] ?? toggle.packages.some((pkg) => installed.has(pkg))) plugins.push(...toggle.plugins);
540
- }
538
+ for (const { packages, plugins: enabled } of Object.values(pluginDetectors)) if (packages.some((pkg) => installed.has(pkg))) plugins.push(...enabled);
541
539
  return [...new Set(plugins)];
542
540
  }
543
541
  /**
544
542
  * Build an oxlint config.
545
543
  *
546
- * Framework plugins (`typescript`, `react`, `vue`, `next`, `vitest`, `jest`,
547
- * `tailwind`) are auto-enabled by detecting their package in the nearest
548
- * `package.json`. If a dependency is not found there — e.g. it lives in a nested
549
- * workspace like `apps/web/package.json` — its plugin stays off, so enable it
550
- * manually:
544
+ * Plugins are auto-enabled by detecting their package (`typescript`, `react`,
545
+ * `vue`, `next`, `vitest`, `jest`) in the nearest `package.json`. To enable a
546
+ * plugin whose dependency lives elsewhere — e.g. a nested workspace like
547
+ * `apps/web/package.json` — add it through `overrides.plugins`.
548
+ *
549
+ * `overrides` is deep-merged over the base config via defu.
551
550
  *
552
551
  * @example
553
552
  * ```ts
@@ -556,33 +555,52 @@ function resolvePlugins(options, installed) {
556
555
  * ```
557
556
  * @example
558
557
  * ```ts
559
- * // dep lives in a nested workspace turn the plugin on by hand
560
- * export default oxlintConfig({ vue: true })
558
+ * // enable a plugin by hand, tweak a rule
559
+ * export default oxlintConfig({ plugins: ['vue'], rules: { 'no-console': 'off' } })
561
560
  * ```
562
561
  * @example
563
562
  * ```ts
564
- * // Tailwind, pointed at the entry CSS so classes can be resolved
565
- * export default oxlintConfig({
566
- * tailwind: true,
567
- * override: {
568
- * settings: { 'better-tailwindcss': { entryPoint: 'src/styles/globals.css' } },
569
- * },
570
- * })
563
+ * // add Tailwind linting
564
+ * export default oxlintConfig({ ...tailwind('app/globals.css') })
571
565
  * ```
572
566
  */
573
- const TAILWIND_PLUGIN = "eslint-plugin-better-tailwindcss";
574
- function oxlintConfig(options = {}) {
575
- const { override = {} } = options;
576
- const installed = getInstalledPackages(options.cwd ?? process.cwd());
577
- let tailwind = options.tailwind ?? installed.has("tailwindcss");
578
- if (tailwind && !installed.has(TAILWIND_PLUGIN)) {
567
+ function oxlintConfig(overrides = {}) {
568
+ return defu(overrides, { plugins: resolvePlugins(process.cwd()) }, baseOxlintConfig);
569
+ }
570
+ /**
571
+ * Tailwind linting via [`eslint-plugin-better-tailwindcss`](https://github.com/schoero/eslint-plugin-better-tailwindcss).
572
+ * Spread the result into {@link oxlintConfig}:
573
+ *
574
+ * ```ts
575
+ * export default oxlintConfig({ ...tailwind({ entryPoint: 'app/globals.css' }) })
576
+ * ```
577
+ *
578
+ * The plugin is an optional peer dependency — install it yourself
579
+ * (`pnpm add -D eslint-plugin-better-tailwindcss`). If it is missing, a warning
580
+ * is logged and an empty config is returned so oxlint does not crash.
581
+ */
582
+ function tailwind({ entryPoint, cwd = process.cwd() }) {
583
+ if (!getInstalledPackages(cwd).has(TAILWIND_PLUGIN)) {
579
584
  console.warn(`[@letstri/oxc-config] Tailwind linting needs "${TAILWIND_PLUGIN}". Install it: pnpm add -D ${TAILWIND_PLUGIN}`);
580
- tailwind = false;
585
+ return {};
581
586
  }
582
- return defu(override, { plugins: resolvePlugins(options, installed) }, tailwind ? tailwindConfig : {}, baseOxlintConfig);
587
+ return defineConfig$1({
588
+ jsPlugins: [TAILWIND_PLUGIN],
589
+ settings: { "better-tailwindcss": { entryPoint } },
590
+ rules: {
591
+ "better-tailwindcss/enforce-consistent-class-order": "error",
592
+ "better-tailwindcss/enforce-consistent-line-wrapping": "off",
593
+ "better-tailwindcss/no-unnecessary-whitespace": "off",
594
+ "better-tailwindcss/enforce-canonical-classes": "error",
595
+ "better-tailwindcss/no-conflicting-classes": "error",
596
+ "better-tailwindcss/no-deprecated-classes": "error",
597
+ "better-tailwindcss/no-duplicate-classes": "error",
598
+ "better-tailwindcss/no-unknown-classes": "error"
599
+ }
600
+ });
583
601
  }
584
- function oxfmtConfig({ override = {} } = {}) {
585
- return defu(override, baseOxfmtConfig);
602
+ function oxfmtConfig(overrides = {}) {
603
+ return defu(overrides, baseOxfmtConfig);
586
604
  }
587
605
  //#endregion
588
- export { oxfmtConfig, oxlintConfig };
606
+ export { oxfmtConfig, oxlintConfig, tailwind };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letstri/oxc-config",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Opinionated Oxlint and Oxfmt configs",
5
5
  "homepage": "https://github.com/letstri/oxc-config#readme",
6
6
  "bugs": {
@@ -27,19 +27,6 @@
27
27
  "publishConfig": {
28
28
  "access": "public"
29
29
  },
30
- "scripts": {
31
- "build": "tsdown",
32
- "dev": "tsdown --watch",
33
- "check-types": "tsc --noEmit",
34
- "lint": "oxlint -c oxlint.config.ts",
35
- "lint:fix": "oxlint -c oxlint.config.ts --fix",
36
- "format": "oxfmt -c oxfmt.config.ts",
37
- "format:check": "oxfmt -c oxfmt.config.ts --check",
38
- "check": "run-p lint check-types format:check",
39
- "update": "taze -r -I major",
40
- "prepare": "husky",
41
- "prepublishOnly": "pnpm run build"
42
- },
43
30
  "dependencies": {
44
31
  "defu": "^6.1.7"
45
32
  },
@@ -66,5 +53,15 @@
66
53
  "engines": {
67
54
  "node": ">=22"
68
55
  },
69
- "packageManager": "pnpm@11.11.0"
70
- }
56
+ "scripts": {
57
+ "build": "tsdown",
58
+ "dev": "tsdown --watch",
59
+ "check-types": "tsc --noEmit",
60
+ "lint": "oxlint -c oxlint.config.ts",
61
+ "lint:fix": "oxlint -c oxlint.config.ts --fix",
62
+ "format": "oxfmt -c oxfmt.config.ts",
63
+ "format:check": "oxfmt -c oxfmt.config.ts --check",
64
+ "check": "run-p lint check-types format:check",
65
+ "update": "taze -r -I major"
66
+ }
67
+ }