@letstri/oxc-config 0.4.0 → 0.5.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 +20 -15
- package/dist/cli.mjs +72 -72
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -13,14 +13,23 @@ Opinionated, shared [oxlint](https://oxc.rs/docs/guide/usage/linter.html) and [o
|
|
|
13
13
|
npm i -D @letstri/oxc-config oxlint oxfmt
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
Then scaffold
|
|
16
|
+
Then scaffold everything with the `oxc-config` CLI:
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
npx oxc-config
|
|
20
|
-
npx oxc-config editors # write .vscode + .zed settings
|
|
19
|
+
npx oxc-config init
|
|
21
20
|
```
|
|
22
21
|
|
|
23
|
-
|
|
22
|
+
`init` prompts you to pick what to set up — oxlint config, oxfmt config, VS Code,
|
|
23
|
+
Zed. Pass flags to skip the prompt (useful in CI/scripts):
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx oxc-config init --oxlint --oxfmt # just the config files
|
|
27
|
+
npx oxc-config init --vscode --zed # just the editor settings
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
With flags, only the chosen targets run; with none in a non-interactive shell,
|
|
31
|
+
all four run. Config files are skipped if they already exist (`--force` to
|
|
32
|
+
overwrite); editor settings are deep-merged into any existing files. See
|
|
24
33
|
[Editor setup](#editor-setup).
|
|
25
34
|
|
|
26
35
|
## AI setup prompt
|
|
@@ -157,16 +166,12 @@ If the plugin is missing, `tailwind()` throws with an install hint.
|
|
|
157
166
|
Both editors use the official [oxc](https://oxc.rs) tooling — `oxlint` for
|
|
158
167
|
linting and `oxfmt` for formatting — replacing ESLint and Prettier.
|
|
159
168
|
|
|
160
|
-
|
|
161
|
-
|
|
169
|
+
`oxc-config init` writes (or updates) the editor configs for you, deep-merging
|
|
170
|
+
into any existing files so your other settings are kept:
|
|
162
171
|
|
|
163
172
|
```bash
|
|
164
173
|
# both editors
|
|
165
|
-
pnpm exec oxc-config
|
|
166
|
-
|
|
167
|
-
# or just one
|
|
168
|
-
pnpm exec oxc-config editors --vscode
|
|
169
|
-
pnpm exec oxc-config editors --zed
|
|
174
|
+
pnpm exec oxc-config init --vscode --zed
|
|
170
175
|
```
|
|
171
176
|
|
|
172
177
|
It's idempotent — safe to re-run to pull the latest recommended settings.
|
|
@@ -174,13 +179,13 @@ It's idempotent — safe to re-run to pull the latest recommended settings.
|
|
|
174
179
|
### VS Code
|
|
175
180
|
|
|
176
181
|
Install the [`oxc.oxc-vscode`](https://marketplace.visualstudio.com/items?itemName=oxc.oxc-vscode)
|
|
177
|
-
extension (`
|
|
178
|
-
|
|
179
|
-
|
|
182
|
+
extension (`init --vscode` also adds it to `.vscode/extensions.json`). It writes
|
|
183
|
+
`.vscode/settings.json` — oxlint as linter, oxfmt as the default formatter with
|
|
184
|
+
format-on-save, and Prettier's import organization turned off.
|
|
180
185
|
|
|
181
186
|
### Zed
|
|
182
187
|
|
|
183
188
|
Zed ships with the oxc language servers built in, so no extension is needed.
|
|
184
|
-
`
|
|
189
|
+
`init --zed` writes `.zed/settings.json` — oxfmt as the formatter (with
|
|
185
190
|
`source.fixAll.oxc` on save for JS/TS) and Prettier disabled, across the file
|
|
186
191
|
types oxfmt supports.
|
package/dist/cli.mjs
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import process from "node:process";
|
|
3
3
|
import { createDefu } from "defu";
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
-
import { dirname
|
|
6
|
-
import {
|
|
5
|
+
import { dirname } from "node:path";
|
|
6
|
+
import { cancel, isCI, isCancel, multiselect } from "@clack/prompts";
|
|
7
7
|
import { parse } from "jsonc-parser";
|
|
8
8
|
//#region src/editors.ts
|
|
9
9
|
/**
|
|
@@ -73,93 +73,93 @@ const zedSettings = {
|
|
|
73
73
|
};
|
|
74
74
|
//#endregion
|
|
75
75
|
//#region src/cli.ts
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
* concatenated — keeps updates idempotent and preserves user-added entries.
|
|
79
|
-
*/
|
|
80
|
-
const mergeConfig = createDefu((obj, key, value) => {
|
|
76
|
+
const log = (message) => process.stdout.write(`${message}\n`);
|
|
77
|
+
const merge = createDefu((obj, key, value) => {
|
|
81
78
|
const current = obj[key];
|
|
82
79
|
if (Array.isArray(current) && Array.isArray(value)) {
|
|
83
|
-
const
|
|
84
|
-
obj[key] = [...
|
|
85
|
-
const id = JSON.stringify(item);
|
|
86
|
-
if (seen.has(id)) return false;
|
|
87
|
-
seen.add(id);
|
|
88
|
-
return true;
|
|
89
|
-
});
|
|
80
|
+
const union = [...value, ...current].map((item) => JSON.stringify(item));
|
|
81
|
+
obj[key] = [...new Set(union)].map((item) => JSON.parse(item));
|
|
90
82
|
return true;
|
|
91
83
|
}
|
|
92
84
|
return false;
|
|
93
85
|
});
|
|
94
|
-
function
|
|
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) {
|
|
86
|
+
function mergeJson(path, base) {
|
|
100
87
|
const existed = existsSync(path);
|
|
101
|
-
const
|
|
88
|
+
const current = existed ? parse(readFileSync(path, "utf-8"), [], { allowTrailingComma: true }) : {};
|
|
102
89
|
mkdirSync(dirname(path), { recursive: true });
|
|
103
|
-
writeFileSync(path, `${JSON.stringify(
|
|
90
|
+
writeFileSync(path, `${JSON.stringify(merge(base, current ?? {}), null, 2)}\n`);
|
|
104
91
|
return existed ? "updated" : "created";
|
|
105
92
|
}
|
|
106
|
-
|
|
107
|
-
function writeFile(path, content, force) {
|
|
93
|
+
function writeConfig(path, content, force) {
|
|
108
94
|
const existed = existsSync(path);
|
|
109
95
|
if (existed && !force) return "skipped";
|
|
110
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
111
96
|
writeFileSync(path, content);
|
|
112
97
|
return existed ? "overwritten" : "created";
|
|
113
98
|
}
|
|
114
|
-
const
|
|
99
|
+
const template = (fn) => `import { ${fn} } from '@letstri/oxc-config'\n\nexport default ${fn}()\n`;
|
|
100
|
+
const TARGETS = {
|
|
101
|
+
oxlint: {
|
|
102
|
+
label: "oxlint.config.ts",
|
|
103
|
+
run: (force) => `oxlint: ${writeConfig("oxlint.config.ts", template("oxlintConfig"), force)}`
|
|
104
|
+
},
|
|
105
|
+
oxfmt: {
|
|
106
|
+
label: "oxfmt.config.ts",
|
|
107
|
+
run: (force) => `oxfmt: ${writeConfig("oxfmt.config.ts", template("oxfmtConfig"), force)}`
|
|
108
|
+
},
|
|
109
|
+
vscode: {
|
|
110
|
+
label: "VS Code settings (.vscode)",
|
|
111
|
+
run: () => `vscode: ${mergeJson(".vscode/settings.json", vscodeSettings)} settings, ${mergeJson(".vscode/extensions.json", vscodeExtensions)} extensions`
|
|
112
|
+
},
|
|
113
|
+
zed: {
|
|
114
|
+
label: "Zed settings (.zed)",
|
|
115
|
+
run: () => `zed: ${mergeJson(".zed/settings.json", zedSettings)}`
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
const ALL = Object.keys(TARGETS);
|
|
119
|
+
async function pickTargets(flags) {
|
|
120
|
+
const flagged = ALL.filter((target) => flags.has(`--${target}`));
|
|
121
|
+
if (flagged.length > 0) return flagged;
|
|
122
|
+
if (isCI() || !process.stdout.isTTY) return ALL;
|
|
123
|
+
const selected = await multiselect({
|
|
124
|
+
message: "What do you want to set up?",
|
|
125
|
+
options: ALL.map((value) => ({
|
|
126
|
+
value,
|
|
127
|
+
label: TARGETS[value].label
|
|
128
|
+
})),
|
|
129
|
+
initialValues: ALL,
|
|
130
|
+
required: true
|
|
131
|
+
});
|
|
132
|
+
if (isCancel(selected)) {
|
|
133
|
+
cancel("Cancelled.");
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
return selected;
|
|
137
|
+
}
|
|
138
|
+
function version() {
|
|
139
|
+
const url = new URL("../package.json", import.meta.url);
|
|
140
|
+
return JSON.parse(readFileSync(url, "utf-8")).version;
|
|
141
|
+
}
|
|
142
|
+
const HELP = `oxc-config — set up @letstri/oxc-config in your project
|
|
115
143
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const OXFMT_CONFIG = `import { oxfmtConfig } from '@letstri/oxc-config'
|
|
144
|
+
Usage:
|
|
145
|
+
oxc-config [flags]
|
|
119
146
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
147
|
+
Flags (default: prompt for what to set up):
|
|
148
|
+
--oxlint create oxlint.config.ts
|
|
149
|
+
--oxfmt create oxfmt.config.ts
|
|
150
|
+
--vscode write .vscode settings
|
|
151
|
+
--zed write .zed settings
|
|
152
|
+
-f, --force overwrite existing config files
|
|
153
|
+
-h, --help show this help
|
|
154
|
+
-v, --version show version`;
|
|
155
|
+
const argv = process.argv.slice(2);
|
|
156
|
+
if (argv.includes("--help") || argv.includes("-h")) log(HELP);
|
|
157
|
+
else if (argv.includes("--version") || argv.includes("-v")) log(version());
|
|
158
|
+
else {
|
|
159
|
+
const flags = new Set(argv);
|
|
160
|
+
const force = flags.has("--force") || flags.has("-f");
|
|
161
|
+
const targets = await pickTargets(flags);
|
|
162
|
+
for (const target of targets ?? []) log(TARGETS[target].run(force));
|
|
128
163
|
}
|
|
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
164
|
//#endregion
|
|
165
165
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@letstri/oxc-config",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Opinionated Oxlint and Oxfmt configs",
|
|
5
5
|
"homepage": "https://github.com/letstri/oxc-config#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"access": "public"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@
|
|
34
|
+
"@clack/prompts": "^1.7.0",
|
|
35
35
|
"defu": "^6.1.7",
|
|
36
36
|
"jsonc-parser": "^3.3.1"
|
|
37
37
|
},
|