@luxkit/cli 1.0.0 → 1.0.2
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 +49 -208
- package/dist/index.js +228 -428
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -4,45 +4,39 @@
|
|
|
4
4
|
|
|
5
5
|
**One-click project formatting & VSCode config CLI**
|
|
6
6
|
|
|
7
|
-
[](https://www.npmjs.com/package/@luxkit/cli)
|
|
8
8
|
[](https://nodejs.org/)
|
|
9
9
|
[](https://opensource.org/licenses/ISC)
|
|
10
10
|
[](https://www.typescriptlang.org/)
|
|
11
11
|
[](https://nodejs.org/api/esm.html)
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
<a href="#-english">English</a> | <a href="#-中文">中文</a>
|
|
13
|
+
**English** | [中文](./README_Zh.md)
|
|
16
14
|
|
|
17
15
|
</div>
|
|
18
16
|
|
|
19
17
|
---
|
|
20
18
|
|
|
21
|
-
<br />
|
|
22
|
-
|
|
23
|
-
## 🇬🇧 English
|
|
24
|
-
|
|
25
|
-
<br />
|
|
26
|
-
|
|
27
19
|
### What is lux?
|
|
28
20
|
|
|
29
21
|
`lux` is a CLI tool that initializes project formatting configs and VSCode workspace settings with a single command. It generates ESLint, Prettier, Stylelint, CSpell, EditorConfig files and VSCode settings from battle-tested presets — with smart merge and conflict resolution.
|
|
30
22
|
|
|
31
|
-
<
|
|
23
|
+
<div align="center">
|
|
24
|
+
<img src="demo.gif" alt="lux demo" width="640" />
|
|
25
|
+
</div>
|
|
32
26
|
|
|
33
27
|
### ✨ Key Highlights
|
|
34
28
|
|
|
35
|
-
| Feature
|
|
36
|
-
|
|
37
|
-
| 🎯 **One Command Setup**
|
|
38
|
-
| 🔧 **5 Fmt Presets**
|
|
39
|
-
| 🖥️ **6 VSCode Presets**
|
|
40
|
-
| 🔀 **Smart Merge**
|
|
41
|
-
| 🛡️ **Conflict Resolution** | `neverOverwrite` / `forceOverwrite` lists + `--force` flag
|
|
42
|
-
| 📦 **Auto Install**
|
|
43
|
-
| 🔍 **Fuzzy Matching**
|
|
44
|
-
| 🧪 **Dry Run**
|
|
45
|
-
| 🔗 **Script Injection**
|
|
29
|
+
| Feature | Description |
|
|
30
|
+
| :------------------------- | :----------------------------------------------------------------------------- |
|
|
31
|
+
| 🎯 **One Command Setup** | `lux fmt web` generates all linting & formatting configs instantly |
|
|
32
|
+
| 🔧 **5 Fmt Presets** | `web` · `electron` · `uniapp` · `node` · `nest` — each with curated rules |
|
|
33
|
+
| 🖥️ **6 VSCode Presets** | `web` · `electron` · `uniapp` · `node` · `nest` · `go` — settings + extensions |
|
|
34
|
+
| 🔀 **Smart Merge** | Preset wins for linting keys; user wins for personal preferences |
|
|
35
|
+
| 🛡️ **Conflict Resolution** | `neverOverwrite` / `forceOverwrite` lists + `--force` flag |
|
|
36
|
+
| 📦 **Auto Install** | Detects bun / pnpm / yarn / npm and installs devDependencies |
|
|
37
|
+
| 🔍 **Fuzzy Matching** | Typo a preset name? Levenshtein distance finds the closest match |
|
|
38
|
+
| 🧪 **Dry Run** | Preview all changes with `--dry-run` before writing anything |
|
|
39
|
+
| 🔗 **Script Injection** | Auto-injects `<pm> lint` / `<pm> format` scripts into package.json |
|
|
46
40
|
|
|
47
41
|
<br />
|
|
48
42
|
|
|
@@ -50,15 +44,15 @@
|
|
|
50
44
|
|
|
51
45
|
```bash
|
|
52
46
|
# Install globally (pick your package manager)
|
|
53
|
-
npm install -g
|
|
47
|
+
npm install -g @luxkit/cli
|
|
54
48
|
# or
|
|
55
|
-
bun add -g
|
|
49
|
+
bun add -g @luxkit/cli
|
|
56
50
|
|
|
57
51
|
# Initialize formatting configs
|
|
58
|
-
lux fmt
|
|
52
|
+
lux fmt web # Generate ESLint, Prettier, Stylelint, CSpell, EditorConfig
|
|
59
53
|
|
|
60
54
|
# Initialize VSCode settings
|
|
61
|
-
lux vscode
|
|
55
|
+
lux vscode web # Generate .vscode/settings.json + extensions.json
|
|
62
56
|
|
|
63
57
|
# List available presets
|
|
64
58
|
lux fmt list
|
|
@@ -69,34 +63,34 @@ lux vscode list
|
|
|
69
63
|
|
|
70
64
|
### CLI Commands
|
|
71
65
|
|
|
72
|
-
| Command
|
|
73
|
-
|
|
74
|
-
| `lux fmt
|
|
75
|
-
| `lux fmt list`
|
|
76
|
-
| `lux vscode
|
|
77
|
-
| `lux vscode list`
|
|
78
|
-
| `lux vpn cmd`
|
|
79
|
-
| `lux vpn pw`
|
|
66
|
+
| Command | Description |
|
|
67
|
+
| :------------------------- | :------------------------------------------ |
|
|
68
|
+
| `lux fmt <preset>` | Initialize formatting config files |
|
|
69
|
+
| `lux fmt list` | List available fmt presets |
|
|
70
|
+
| `lux vscode <preset>` | Initialize VSCode workspace settings |
|
|
71
|
+
| `lux vscode list` | List available VSCode presets |
|
|
72
|
+
| `lux vpn cmd` | Copy CMD proxy env-vars to clipboard |
|
|
73
|
+
| `lux vpn pw` | Copy PowerShell proxy env-vars to clipboard |
|
|
80
74
|
|
|
81
75
|
<br />
|
|
82
76
|
|
|
83
77
|
### Available Presets
|
|
84
78
|
|
|
85
|
-
| Preset
|
|
86
|
-
|
|
87
|
-
| `web`
|
|
88
|
-
| `electron` | ✅
|
|
89
|
-
| `uniapp`
|
|
90
|
-
| `node`
|
|
91
|
-
| `nest`
|
|
92
|
-
| `go`
|
|
79
|
+
| Preset | Fmt | VSCode | Stack |
|
|
80
|
+
| :--------- | :-: | :----: | :--------------------------- |
|
|
81
|
+
| `web` | ✅ | ✅ | Vue / React / TS / CSS |
|
|
82
|
+
| `electron` | ✅ | ✅ | Electron + Web stack |
|
|
83
|
+
| `uniapp` | ✅ | ✅ | UniApp / WeChat Mini Program |
|
|
84
|
+
| `node` | ✅ | ✅ | Node.js backend |
|
|
85
|
+
| `nest` | ✅ | ✅ | NestJS backend |
|
|
86
|
+
| `go` | — | ✅ | Go backend |
|
|
93
87
|
|
|
94
88
|
<br />
|
|
95
89
|
|
|
96
90
|
### Options
|
|
97
91
|
|
|
98
92
|
```bash
|
|
99
|
-
lux fmt
|
|
93
|
+
lux fmt <preset> [options]
|
|
100
94
|
|
|
101
95
|
--force Force overwrite existing files
|
|
102
96
|
--no-install Skip dependency installation
|
|
@@ -108,7 +102,7 @@ lux fmt init <preset> [options]
|
|
|
108
102
|
### How It Works
|
|
109
103
|
|
|
110
104
|
```
|
|
111
|
-
lux fmt
|
|
105
|
+
lux fmt web
|
|
112
106
|
│
|
|
113
107
|
▼
|
|
114
108
|
Parse CLI args ──► Resolve preset (fuzzy match on typo)
|
|
@@ -132,15 +126,15 @@ lux fmt init web
|
|
|
132
126
|
|
|
133
127
|
### Tech Stack
|
|
134
128
|
|
|
135
|
-
| Category | Technology
|
|
136
|
-
|
|
137
|
-
| Language | TypeScript 6.0 (ESM-only)
|
|
138
|
-
| Runtime
|
|
139
|
-
| Build
|
|
140
|
-
| Test
|
|
141
|
-
| CLI
|
|
142
|
-
| Output
|
|
143
|
-
| Bundle
|
|
129
|
+
| Category | Technology |
|
|
130
|
+
| :------- | :----------------------------------------- |
|
|
131
|
+
| Language | TypeScript 6.0 (ESM-only) |
|
|
132
|
+
| Runtime | Node.js 18+ |
|
|
133
|
+
| Build | tsup |
|
|
134
|
+
| Test | Vitest (unit + acceptance) |
|
|
135
|
+
| CLI | Commander.js |
|
|
136
|
+
| Output | Chalk |
|
|
137
|
+
| Bundle | Zero runtime deps (chalk + commander only) |
|
|
144
138
|
|
|
145
139
|
<br />
|
|
146
140
|
|
|
@@ -152,7 +146,7 @@ cd lux
|
|
|
152
146
|
bun install
|
|
153
147
|
|
|
154
148
|
bun link # Register `lux` globally for testing
|
|
155
|
-
lux fmt
|
|
149
|
+
lux fmt web # Test it on any project
|
|
156
150
|
|
|
157
151
|
bun test # Run tests
|
|
158
152
|
bun build # Build to dist/
|
|
@@ -167,157 +161,4 @@ bun code:check:all # lint + format + spell check
|
|
|
167
161
|
|
|
168
162
|
<br />
|
|
169
163
|
|
|
170
|
-
<p align="right"><a href="
|
|
171
|
-
|
|
172
|
-
---
|
|
173
|
-
|
|
174
|
-
<br />
|
|
175
|
-
|
|
176
|
-
## 🇨🇳 中文
|
|
177
|
-
|
|
178
|
-
<br />
|
|
179
|
-
|
|
180
|
-
### lux 是什么?
|
|
181
|
-
|
|
182
|
-
`lux` 是一个 CLI 工具,只需一条命令即可初始化项目格式化配置和 VSCode 工作区设置。它从经过实战检验的预设中生成 ESLint、Prettier、Stylelint、CSpell、EditorConfig 配置文件以及 VSCode 设置 —— 并带有智能合并和冲突解决机制。
|
|
183
|
-
|
|
184
|
-
<br />
|
|
185
|
-
|
|
186
|
-
### ✨ 核心亮点
|
|
187
|
-
|
|
188
|
-
| 特性 | 说明 |
|
|
189
|
-
|:-----|:-----|
|
|
190
|
-
| 🎯 **一键配置** | `lux fmt init web` 即可生成所有 lint 与格式化配置 |
|
|
191
|
-
| 🔧 **5 种格式化预设** | `web` · `electron` · `uniapp` · `node` · `nest` — 各配备精选规则 |
|
|
192
|
-
| 🖥️ **6 种 VSCode 预设** | `web` · `electron` · `uniapp` · `node` · `nest` · `go` — 设置 + 扩展推荐 |
|
|
193
|
-
| 🔀 **智能合并** | 预设优先覆盖 linting 相关键;用户配置优先保留个人偏好 |
|
|
194
|
-
| 🛡️ **冲突解决** | `neverOverwrite` / `forceOverwrite` 列表 + `--force` 标志 |
|
|
195
|
-
| 📦 **自动安装** | 自动检测 bun / pnpm / yarn / npm 并安装 devDependencies |
|
|
196
|
-
| 🔍 **模糊匹配** | 预设名拼错了?Levenshtein 距离帮你找到最接近的匹配 |
|
|
197
|
-
| 🧪 **Dry Run** | 使用 `--dry-run` 预览所有变更,不写入任何文件 |
|
|
198
|
-
| 🔗 **脚本注入** | 自动将 `<pm> lint` / `<pm> format` 脚本注入 package.json |
|
|
199
|
-
|
|
200
|
-
<br />
|
|
201
|
-
|
|
202
|
-
### 快速开始
|
|
203
|
-
|
|
204
|
-
```bash
|
|
205
|
-
# 全局安装(选择你的包管理器)
|
|
206
|
-
npm install -g lux
|
|
207
|
-
# 或
|
|
208
|
-
bun add -g lux
|
|
209
|
-
|
|
210
|
-
# 初始化格式化配置
|
|
211
|
-
lux fmt init web # 生成 ESLint、Prettier、Stylelint、CSpell、EditorConfig
|
|
212
|
-
|
|
213
|
-
# 初始化 VSCode 设置
|
|
214
|
-
lux vscode init web # 生成 .vscode/settings.json + extensions.json
|
|
215
|
-
|
|
216
|
-
# 查看可用预设
|
|
217
|
-
lux fmt list
|
|
218
|
-
lux vscode list
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
<br />
|
|
222
|
-
|
|
223
|
-
### CLI 命令
|
|
224
|
-
|
|
225
|
-
| 命令 | 说明 |
|
|
226
|
-
|:-----|:-----|
|
|
227
|
-
| `lux fmt init <preset>` | 初始化格式化配置文件 |
|
|
228
|
-
| `lux fmt list` | 列出可用的格式化预设 |
|
|
229
|
-
| `lux vscode init <preset>` | 初始化 VSCode 工作区设置 |
|
|
230
|
-
| `lux vscode list` | 列出可用的 VSCode 预设 |
|
|
231
|
-
| `lux vpn cmd` | 复制 CMD 代理环境变量到剪贴板 |
|
|
232
|
-
| `lux vpn pw` | 复制 PowerShell 代理环境变量到剪贴板 |
|
|
233
|
-
|
|
234
|
-
<br />
|
|
235
|
-
|
|
236
|
-
### 可用预设
|
|
237
|
-
|
|
238
|
-
| 预设 | 格式化 | VSCode | 技术栈 |
|
|
239
|
-
|:-----|:------:|:------:|:-------|
|
|
240
|
-
| `web` | ✅ | ✅ | Vue / React / TS / CSS |
|
|
241
|
-
| `electron` | ✅ | ✅ | Electron + Web 技术栈 |
|
|
242
|
-
| `uniapp` | ✅ | ✅ | UniApp / 微信小程序 |
|
|
243
|
-
| `node` | ✅ | ✅ | Node.js 后端 |
|
|
244
|
-
| `nest` | ✅ | ✅ | NestJS 后端 |
|
|
245
|
-
| `go` | — | ✅ | Go 后端 |
|
|
246
|
-
|
|
247
|
-
<br />
|
|
248
|
-
|
|
249
|
-
### 命令选项
|
|
250
|
-
|
|
251
|
-
```bash
|
|
252
|
-
lux fmt init <preset> [options]
|
|
253
|
-
|
|
254
|
-
--force 强制覆盖已有文件
|
|
255
|
-
--no-install 跳过依赖安装
|
|
256
|
-
--dry-run 预览模式,不写入文件
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
<br />
|
|
260
|
-
|
|
261
|
-
### 工作原理
|
|
262
|
-
|
|
263
|
-
```
|
|
264
|
-
lux fmt init web
|
|
265
|
-
│
|
|
266
|
-
▼
|
|
267
|
-
解析 CLI 参数 ──► 解析预设(拼写错误时自动模糊匹配)
|
|
268
|
-
│
|
|
269
|
-
▼
|
|
270
|
-
遍历每个配置文件:
|
|
271
|
-
│
|
|
272
|
-
├── 文件不存在? ──► 创建
|
|
273
|
-
├── 在 neverOverwrite 中? ──► 跳过
|
|
274
|
-
├── 在 forceOverwrite 中? ──► 覆盖
|
|
275
|
-
└── 已存在 + --force? ──► 覆盖 / 跳过
|
|
276
|
-
│
|
|
277
|
-
▼
|
|
278
|
-
注入脚本到 package.json(<pm> → bun run / pnpm run / ...)
|
|
279
|
-
│
|
|
280
|
-
▼
|
|
281
|
-
自动安装 devDependencies(检测 lockfile 判断包管理器)
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
<br />
|
|
285
|
-
|
|
286
|
-
### 技术栈
|
|
287
|
-
|
|
288
|
-
| 分类 | 技术 |
|
|
289
|
-
|:-----|:-----|
|
|
290
|
-
| 语言 | TypeScript 6.0(纯 ESM) |
|
|
291
|
-
| 运行时 | Node.js 18+ |
|
|
292
|
-
| 构建 | tsup |
|
|
293
|
-
| 测试 | Vitest(单元测试 + 验收测试) |
|
|
294
|
-
| CLI | Commander.js |
|
|
295
|
-
| 输出 | Chalk |
|
|
296
|
-
| 依赖 | 零运行时依赖(仅 chalk + commander) |
|
|
297
|
-
|
|
298
|
-
<br />
|
|
299
|
-
|
|
300
|
-
### 开发
|
|
301
|
-
|
|
302
|
-
```bash
|
|
303
|
-
git clone git@github.com:TTT1231/lux.git
|
|
304
|
-
cd lux
|
|
305
|
-
bun install
|
|
306
|
-
|
|
307
|
-
bun link # 全局注册 `lux` 用于测试
|
|
308
|
-
lux fmt init web # 在任意项目上测试
|
|
309
|
-
|
|
310
|
-
bun test # 运行测试
|
|
311
|
-
bun build # 构建到 dist/
|
|
312
|
-
bun code:check:all # lint + 格式化 + 拼写检查
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
<br />
|
|
316
|
-
|
|
317
|
-
### 📄 许可证
|
|
318
|
-
|
|
319
|
-
[ISC](https://opensource.org/licenses/ISC) — 可自由使用、修改和分发。
|
|
320
|
-
|
|
321
|
-
<br />
|
|
322
|
-
|
|
323
|
-
<p align="right"><a href="#-english">← Switch to English</a></p>
|
|
164
|
+
<p align="right"><a href="./README_Zh.md">切换到中文 →</a></p>
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { program } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/fmt.ts
|
|
7
|
-
import
|
|
7
|
+
import path4 from "path";
|
|
8
8
|
|
|
9
9
|
// src/presets/fmt/electron.ts
|
|
10
10
|
var electronFmt = {
|
|
@@ -39,46 +39,12 @@ export default [
|
|
|
39
39
|
null,
|
|
40
40
|
2
|
|
41
41
|
) + "\n",
|
|
42
|
-
prettierIgnore: () =>
|
|
43
|
-
|
|
44
|
-
pnpm-lock.yaml
|
|
45
|
-
package-lock.json
|
|
46
|
-
|
|
47
|
-
# Build outputs
|
|
42
|
+
prettierIgnore: () => `node_modules/
|
|
43
|
+
<lockfile>
|
|
48
44
|
dist/
|
|
49
45
|
release/
|
|
50
46
|
out/
|
|
51
|
-
*.tsbuildinfo
|
|
52
|
-
|
|
53
|
-
# Logs
|
|
54
|
-
*.log
|
|
55
|
-
logs/
|
|
56
|
-
|
|
57
|
-
# Environment files
|
|
58
|
-
.env
|
|
59
|
-
.env.local
|
|
60
|
-
.env.*.local
|
|
61
|
-
|
|
62
|
-
# IDE files
|
|
63
|
-
.vscode/
|
|
64
|
-
.idea/
|
|
65
|
-
*.swp
|
|
66
|
-
*.swo
|
|
67
|
-
|
|
68
|
-
# Coverage
|
|
69
47
|
coverage/
|
|
70
|
-
|
|
71
|
-
# Static assets
|
|
72
|
-
*.svg
|
|
73
|
-
*.png
|
|
74
|
-
*.jpg
|
|
75
|
-
*.jpeg
|
|
76
|
-
*.gif
|
|
77
|
-
*.ico
|
|
78
|
-
*.woff
|
|
79
|
-
*.woff2
|
|
80
|
-
*.ttf
|
|
81
|
-
*.eot
|
|
82
48
|
`,
|
|
83
49
|
stylelint: () => `export default {
|
|
84
50
|
plugins: ['stylelint-order', '@stylistic/stylelint-plugin'],
|
|
@@ -101,33 +67,10 @@ coverage/
|
|
|
101
67
|
},
|
|
102
68
|
}
|
|
103
69
|
`,
|
|
104
|
-
stylelintIgnore: () =>
|
|
105
|
-
node_modules/
|
|
106
|
-
pnpm-lock.yaml
|
|
107
|
-
|
|
108
|
-
# Build outputs
|
|
70
|
+
stylelintIgnore: () => `node_modules/
|
|
109
71
|
dist/
|
|
110
72
|
release/
|
|
111
73
|
out/
|
|
112
|
-
*.tsbuildinfo
|
|
113
|
-
|
|
114
|
-
# Logs
|
|
115
|
-
*.log
|
|
116
|
-
|
|
117
|
-
# Environment files
|
|
118
|
-
.env
|
|
119
|
-
.env.local
|
|
120
|
-
|
|
121
|
-
# Coverage
|
|
122
|
-
coverage/
|
|
123
|
-
|
|
124
|
-
# Static assets
|
|
125
|
-
*.svg
|
|
126
|
-
*.png
|
|
127
|
-
*.jpg
|
|
128
|
-
*.jpeg
|
|
129
|
-
*.gif
|
|
130
|
-
*.ico
|
|
131
74
|
`,
|
|
132
75
|
cspell: () => JSON.stringify(
|
|
133
76
|
{
|
|
@@ -135,8 +78,7 @@ coverage/
|
|
|
135
78
|
version: "0.2",
|
|
136
79
|
language: "en,en-US",
|
|
137
80
|
allowCompoundWords: true,
|
|
138
|
-
words: ["vite", "pinia", "vueuse", "unplugin", "
|
|
139
|
-
ignorePaths: ["pnpm-lock.yaml", "/coverage/", "*.svg", "*.png"]
|
|
81
|
+
words: ["vite", "pinia", "vueuse", "unplugin", "electron", "electron-builder"]
|
|
140
82
|
},
|
|
141
83
|
null,
|
|
142
84
|
2
|
|
@@ -208,42 +150,11 @@ var nestFmt = {
|
|
|
208
150
|
null,
|
|
209
151
|
2
|
|
210
152
|
) + "\n",
|
|
211
|
-
prettierIgnore: () =>
|
|
212
|
-
|
|
213
|
-
pnpm-lock.yaml
|
|
214
|
-
package-lock.json
|
|
215
|
-
|
|
216
|
-
# Build outputs
|
|
153
|
+
prettierIgnore: () => `node_modules/
|
|
154
|
+
<lockfile>
|
|
217
155
|
dist/
|
|
218
156
|
build/
|
|
219
|
-
*.tsbuildinfo
|
|
220
|
-
|
|
221
|
-
# Logs
|
|
222
|
-
*.log
|
|
223
|
-
logs/
|
|
224
|
-
|
|
225
|
-
# Environment files
|
|
226
|
-
.env
|
|
227
|
-
.env.local
|
|
228
|
-
.env.*.local
|
|
229
|
-
|
|
230
|
-
# IDE files
|
|
231
|
-
.vscode/
|
|
232
|
-
|
|
233
|
-
# Coverage
|
|
234
157
|
coverage/
|
|
235
|
-
|
|
236
|
-
# Static assets
|
|
237
|
-
*.svg
|
|
238
|
-
*.png
|
|
239
|
-
*.jpg
|
|
240
|
-
*.jpeg
|
|
241
|
-
*.gif
|
|
242
|
-
*.ico
|
|
243
|
-
*.woff
|
|
244
|
-
*.woff2
|
|
245
|
-
*.ttf
|
|
246
|
-
*.eot
|
|
247
158
|
`,
|
|
248
159
|
// No stylelint for NestJS
|
|
249
160
|
cspell: () => JSON.stringify(
|
|
@@ -252,8 +163,7 @@ coverage/
|
|
|
252
163
|
version: "0.2",
|
|
253
164
|
language: "en,en-US",
|
|
254
165
|
allowCompoundWords: true,
|
|
255
|
-
words: ["nestjs", "typeorm", "dtos"]
|
|
256
|
-
ignorePaths: ["pnpm-lock.yaml", "*.svg", "*.png"]
|
|
166
|
+
words: ["nestjs", "typeorm", "dtos"]
|
|
257
167
|
},
|
|
258
168
|
null,
|
|
259
169
|
2
|
|
@@ -295,7 +205,7 @@ import tseslint from 'typescript-eslint'
|
|
|
295
205
|
|
|
296
206
|
export default defineConfig(
|
|
297
207
|
{
|
|
298
|
-
ignores: ['eslint.config.mjs', 'dist/'
|
|
208
|
+
ignores: ['eslint.config.mjs', 'dist/'],
|
|
299
209
|
},
|
|
300
210
|
eslint.configs.recommended,
|
|
301
211
|
...tseslint.configs.recommended,
|
|
@@ -345,42 +255,11 @@ export default defineConfig(
|
|
|
345
255
|
null,
|
|
346
256
|
2
|
|
347
257
|
) + "\n",
|
|
348
|
-
prettierIgnore: () =>
|
|
349
|
-
|
|
350
|
-
bun.lock
|
|
351
|
-
package-lock.json
|
|
352
|
-
|
|
353
|
-
# Build outputs
|
|
258
|
+
prettierIgnore: () => `node_modules/
|
|
259
|
+
<lockfile>
|
|
354
260
|
dist/
|
|
355
261
|
build/
|
|
356
|
-
*.tsbuildinfo
|
|
357
|
-
|
|
358
|
-
# Logs
|
|
359
|
-
*.log
|
|
360
|
-
logs/
|
|
361
|
-
|
|
362
|
-
# Environment files
|
|
363
|
-
.env
|
|
364
|
-
.env.local
|
|
365
|
-
.env.*.local
|
|
366
|
-
|
|
367
|
-
# IDE files
|
|
368
|
-
.vscode/
|
|
369
|
-
|
|
370
|
-
# Coverage
|
|
371
262
|
coverage/
|
|
372
|
-
|
|
373
|
-
# Static assets
|
|
374
|
-
*.svg
|
|
375
|
-
*.png
|
|
376
|
-
*.jpg
|
|
377
|
-
*.jpeg
|
|
378
|
-
*.gif
|
|
379
|
-
*.ico
|
|
380
|
-
*.woff
|
|
381
|
-
*.woff2
|
|
382
|
-
*.ttf
|
|
383
|
-
*.eot
|
|
384
263
|
`,
|
|
385
264
|
// No stylelint for node projects
|
|
386
265
|
cspell: () => JSON.stringify(
|
|
@@ -389,8 +268,7 @@ coverage/
|
|
|
389
268
|
version: "0.2",
|
|
390
269
|
language: "en,en-US",
|
|
391
270
|
allowCompoundWords: true,
|
|
392
|
-
words: []
|
|
393
|
-
ignorePaths: ["bun.lock", "*.svg", "*.png"]
|
|
271
|
+
words: []
|
|
394
272
|
},
|
|
395
273
|
null,
|
|
396
274
|
2
|
|
@@ -466,44 +344,11 @@ export default [
|
|
|
466
344
|
null,
|
|
467
345
|
2
|
|
468
346
|
) + "\n",
|
|
469
|
-
prettierIgnore: () =>
|
|
470
|
-
|
|
471
|
-
pnpm-lock.yaml
|
|
472
|
-
package-lock.json
|
|
473
|
-
|
|
474
|
-
# Build outputs (UniApp \u7F16\u8BD1\u8F93\u51FA)
|
|
347
|
+
prettierIgnore: () => `node_modules/
|
|
348
|
+
<lockfile>
|
|
475
349
|
dist/
|
|
476
350
|
unpackage/
|
|
477
|
-
|
|
478
|
-
# Logs
|
|
479
|
-
*.log
|
|
480
|
-
logs/
|
|
481
|
-
|
|
482
|
-
# Environment files
|
|
483
|
-
.env
|
|
484
|
-
.env.local
|
|
485
|
-
.env.*.local
|
|
486
|
-
|
|
487
|
-
# IDE files
|
|
488
|
-
.vscode/
|
|
489
|
-
.idea/
|
|
490
|
-
*.swp
|
|
491
|
-
*.swo
|
|
492
|
-
|
|
493
|
-
# Coverage
|
|
494
351
|
coverage/
|
|
495
|
-
|
|
496
|
-
# Static assets
|
|
497
|
-
*.svg
|
|
498
|
-
*.png
|
|
499
|
-
*.jpg
|
|
500
|
-
*.jpeg
|
|
501
|
-
*.gif
|
|
502
|
-
*.ico
|
|
503
|
-
*.woff
|
|
504
|
-
*.woff2
|
|
505
|
-
*.ttf
|
|
506
|
-
*.eot
|
|
507
352
|
`,
|
|
508
353
|
stylelint: () => `export default {
|
|
509
354
|
plugins: ['stylelint-order', '@stylistic/stylelint-plugin'],
|
|
@@ -526,31 +371,9 @@ coverage/
|
|
|
526
371
|
},
|
|
527
372
|
}
|
|
528
373
|
`,
|
|
529
|
-
stylelintIgnore: () =>
|
|
530
|
-
node_modules/
|
|
531
|
-
pnpm-lock.yaml
|
|
532
|
-
|
|
533
|
-
# Build outputs (UniApp \u7F16\u8BD1\u8F93\u51FA)
|
|
374
|
+
stylelintIgnore: () => `node_modules/
|
|
534
375
|
dist/
|
|
535
376
|
unpackage/
|
|
536
|
-
|
|
537
|
-
# Logs
|
|
538
|
-
*.log
|
|
539
|
-
|
|
540
|
-
# Environment files
|
|
541
|
-
.env
|
|
542
|
-
.env.local
|
|
543
|
-
|
|
544
|
-
# Coverage
|
|
545
|
-
coverage/
|
|
546
|
-
|
|
547
|
-
# Static assets
|
|
548
|
-
*.svg
|
|
549
|
-
*.png
|
|
550
|
-
*.jpg
|
|
551
|
-
*.jpeg
|
|
552
|
-
*.gif
|
|
553
|
-
*.ico
|
|
554
377
|
`,
|
|
555
378
|
cspell: () => JSON.stringify(
|
|
556
379
|
{
|
|
@@ -558,8 +381,7 @@ coverage/
|
|
|
558
381
|
version: "0.2",
|
|
559
382
|
language: "en,en-US",
|
|
560
383
|
allowCompoundWords: true,
|
|
561
|
-
words: ["vite", "pinia", "vueuse", "unplugin", "
|
|
562
|
-
ignorePaths: ["pnpm-lock.yaml", "/coverage/", "*.svg", "*.png"]
|
|
384
|
+
words: ["vite", "pinia", "vueuse", "unplugin", "uniapp"]
|
|
563
385
|
},
|
|
564
386
|
null,
|
|
565
387
|
2
|
|
@@ -643,44 +465,10 @@ export default [
|
|
|
643
465
|
null,
|
|
644
466
|
2
|
|
645
467
|
) + "\n",
|
|
646
|
-
prettierIgnore: () =>
|
|
647
|
-
|
|
648
|
-
pnpm-lock.yaml
|
|
649
|
-
package-lock.json
|
|
650
|
-
|
|
651
|
-
# Build outputs
|
|
468
|
+
prettierIgnore: () => `node_modules/
|
|
469
|
+
<lockfile>
|
|
652
470
|
dist/
|
|
653
|
-
*.tsbuildinfo
|
|
654
|
-
|
|
655
|
-
# Logs
|
|
656
|
-
*.log
|
|
657
|
-
logs/
|
|
658
|
-
|
|
659
|
-
# Environment files
|
|
660
|
-
.env
|
|
661
|
-
.env.local
|
|
662
|
-
.env.*.local
|
|
663
|
-
|
|
664
|
-
# IDE files
|
|
665
|
-
.vscode/
|
|
666
|
-
.idea/
|
|
667
|
-
*.swp
|
|
668
|
-
*.swo
|
|
669
|
-
|
|
670
|
-
# Coverage
|
|
671
471
|
coverage/
|
|
672
|
-
|
|
673
|
-
# Static assets
|
|
674
|
-
*.svg
|
|
675
|
-
*.png
|
|
676
|
-
*.jpg
|
|
677
|
-
*.jpeg
|
|
678
|
-
*.gif
|
|
679
|
-
*.ico
|
|
680
|
-
*.woff
|
|
681
|
-
*.woff2
|
|
682
|
-
*.ttf
|
|
683
|
-
*.eot
|
|
684
472
|
`,
|
|
685
473
|
stylelint: () => `export default {
|
|
686
474
|
plugins: ['stylelint-order', '@stylistic/stylelint-plugin'],
|
|
@@ -703,31 +491,8 @@ coverage/
|
|
|
703
491
|
},
|
|
704
492
|
}
|
|
705
493
|
`,
|
|
706
|
-
stylelintIgnore: () =>
|
|
707
|
-
node_modules/
|
|
708
|
-
pnpm-lock.yaml
|
|
709
|
-
|
|
710
|
-
# Build outputs
|
|
494
|
+
stylelintIgnore: () => `node_modules/
|
|
711
495
|
dist/
|
|
712
|
-
*.tsbuildinfo
|
|
713
|
-
|
|
714
|
-
# Logs
|
|
715
|
-
*.log
|
|
716
|
-
|
|
717
|
-
# Environment files
|
|
718
|
-
.env
|
|
719
|
-
.env.local
|
|
720
|
-
|
|
721
|
-
# Coverage
|
|
722
|
-
coverage/
|
|
723
|
-
|
|
724
|
-
# Static assets
|
|
725
|
-
*.svg
|
|
726
|
-
*.png
|
|
727
|
-
*.jpg
|
|
728
|
-
*.jpeg
|
|
729
|
-
*.gif
|
|
730
|
-
*.ico
|
|
731
496
|
`,
|
|
732
497
|
cspell: () => JSON.stringify(
|
|
733
498
|
{
|
|
@@ -735,8 +500,7 @@ coverage/
|
|
|
735
500
|
version: "0.2",
|
|
736
501
|
language: "en,en-US",
|
|
737
502
|
allowCompoundWords: true,
|
|
738
|
-
words: ["vite", "pinia", "vueuse", "unplugin"
|
|
739
|
-
ignorePaths: ["pnpm-lock.yaml", "/coverage/", "*.svg", "*.png"]
|
|
503
|
+
words: ["vite", "pinia", "vueuse", "unplugin"]
|
|
740
504
|
},
|
|
741
505
|
null,
|
|
742
506
|
2
|
|
@@ -793,26 +557,17 @@ var FMT_PRESETS = [webFmt, electronFmt, uniappFmt, nodeFmt, nestFmt];
|
|
|
793
557
|
// src/utils/logger.ts
|
|
794
558
|
import chalk from "chalk";
|
|
795
559
|
var logger = {
|
|
796
|
-
|
|
797
|
-
console.log(
|
|
560
|
+
log(msg) {
|
|
561
|
+
console.log(msg);
|
|
798
562
|
},
|
|
799
563
|
success(msg) {
|
|
800
|
-
console.log(chalk.green(
|
|
564
|
+
console.log(chalk.green(msg));
|
|
801
565
|
},
|
|
802
566
|
warn(msg) {
|
|
803
|
-
console.
|
|
567
|
+
console.warn(chalk.yellow(msg));
|
|
804
568
|
},
|
|
805
569
|
error(msg) {
|
|
806
|
-
console.error(chalk.red(
|
|
807
|
-
},
|
|
808
|
-
skip(file, reason) {
|
|
809
|
-
console.log(chalk.yellow("\u2298"), chalk.gray(file), chalk.gray(`(${reason})`));
|
|
810
|
-
},
|
|
811
|
-
create(file) {
|
|
812
|
-
console.log(chalk.green("\u271A"), file);
|
|
813
|
-
},
|
|
814
|
-
overwrite(file) {
|
|
815
|
-
console.log(chalk.cyan("\u21BB"), file);
|
|
570
|
+
console.error(chalk.red(msg));
|
|
816
571
|
}
|
|
817
572
|
};
|
|
818
573
|
|
|
@@ -868,7 +623,7 @@ function resolvePreset(presets, name) {
|
|
|
868
623
|
name,
|
|
869
624
|
presets.map((p) => p.name)
|
|
870
625
|
);
|
|
871
|
-
console.error(chalk2.red(
|
|
626
|
+
console.error(chalk2.red(err.message));
|
|
872
627
|
process.exit(1);
|
|
873
628
|
}
|
|
874
629
|
return found;
|
|
@@ -936,89 +691,46 @@ function generateConfigFile(preset, filename, content, opts) {
|
|
|
936
691
|
const filepath = path2.join(opts.cwd, filename);
|
|
937
692
|
const exists = fileExists(filepath);
|
|
938
693
|
const action = resolveConflict(filename, exists, preset, opts.force);
|
|
939
|
-
if (action === "skip")
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
logger.info(`[dry-run] Would ${exists ? "overwrite" : "create"} ${filename}`);
|
|
945
|
-
return true;
|
|
946
|
-
}
|
|
947
|
-
writeFile(filepath, content);
|
|
948
|
-
if (exists) {
|
|
949
|
-
logger.overwrite(filename);
|
|
950
|
-
} else {
|
|
951
|
-
logger.create(filename);
|
|
952
|
-
}
|
|
953
|
-
return true;
|
|
694
|
+
if (action === "skip") return "skipped";
|
|
695
|
+
if (opts.dryRun) return exists ? "overwritten" : "created";
|
|
696
|
+
const resolved = opts.lockfile ? content.replace(/<lockfile>/g, opts.lockfile) : content.replace(/<lockfile>\n?/g, "");
|
|
697
|
+
writeFile(filepath, resolved);
|
|
698
|
+
return exists ? "overwritten" : "created";
|
|
954
699
|
}
|
|
955
700
|
function generateAllFmt(preset, opts) {
|
|
956
|
-
const
|
|
701
|
+
const result = { created: [], overwritten: [], skipped: [] };
|
|
957
702
|
for (const { filename, getContent } of CONFIG_FILES) {
|
|
958
703
|
const content = getContent(preset);
|
|
959
704
|
if (content === void 0) continue;
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
return generated;
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
// src/presets/versions.ts
|
|
968
|
-
var DEPS_VERSIONS = {
|
|
969
|
-
eslint: "^10.2.0",
|
|
970
|
-
"@eslint/js": "^10.0.1",
|
|
971
|
-
"typescript-eslint": "^8.58.0",
|
|
972
|
-
"eslint-plugin-prettier": "^5.5.5",
|
|
973
|
-
"eslint-config-prettier": "^10.1.8",
|
|
974
|
-
"@vue/eslint-config-typescript": "^14.7.0",
|
|
975
|
-
"@vue/eslint-config-prettier": "^10.2.0",
|
|
976
|
-
"eslint-plugin-vue": "^10.8.0",
|
|
977
|
-
prettier: "^3.8.1",
|
|
978
|
-
stylelint: "^17.6.0",
|
|
979
|
-
"stylelint-config-standard-scss": "^17.0.0",
|
|
980
|
-
"stylelint-order": "^8.1.1",
|
|
981
|
-
"stylelint-scss": "^7.0.0",
|
|
982
|
-
"@stylistic/stylelint-plugin": "^5.1.0",
|
|
983
|
-
"postcss-html": "^1.8.1",
|
|
984
|
-
"postcss-scss": "^4.0.9",
|
|
985
|
-
cspell: "^10.0.0"
|
|
986
|
-
};
|
|
987
|
-
function resolveVersions(packages) {
|
|
988
|
-
return packages.map((pkg) => {
|
|
989
|
-
const version = DEPS_VERSIONS[pkg];
|
|
990
|
-
return version ? `${pkg}@${version}` : pkg;
|
|
991
|
-
});
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
// src/utils/execFileNoThrow.ts
|
|
995
|
-
import { exec } from "child_process";
|
|
996
|
-
import { promisify } from "util";
|
|
997
|
-
var execAsync = promisify(exec);
|
|
998
|
-
async function execFileNoThrow(command, args, options) {
|
|
999
|
-
const cmdStr = args.length > 0 ? `${command} ${args.join(" ")}` : command;
|
|
1000
|
-
try {
|
|
1001
|
-
const { stdout, stderr } = await execAsync(cmdStr, {
|
|
1002
|
-
cwd: options?.cwd
|
|
1003
|
-
});
|
|
1004
|
-
return { stdout: stdout.trim(), stderr: stderr.trim(), exitCode: 0 };
|
|
1005
|
-
} catch (err) {
|
|
1006
|
-
const error = err;
|
|
1007
|
-
return {
|
|
1008
|
-
stdout: (error.stdout ?? "").trim(),
|
|
1009
|
-
stderr: (error.stderr ?? "").trim(),
|
|
1010
|
-
exitCode: error.code === "ENOENT" ? null : 1
|
|
1011
|
-
};
|
|
705
|
+
const action = generateConfigFile(preset, filename, content, opts);
|
|
706
|
+
if (action === "created") result.created.push(filename);
|
|
707
|
+
else if (action === "overwritten") result.overwritten.push(filename);
|
|
708
|
+
else if (action === "skipped") result.skipped.push(filename);
|
|
1012
709
|
}
|
|
710
|
+
return result;
|
|
1013
711
|
}
|
|
1014
712
|
|
|
1015
713
|
// src/utils/deps.ts
|
|
714
|
+
import path3 from "path";
|
|
715
|
+
import { spawn } from "child_process";
|
|
1016
716
|
function detectPackageManager(cwd) {
|
|
1017
717
|
if (fileExists(`${cwd}/bun.lockb`) || fileExists(`${cwd}/bun.lock`)) return "bun";
|
|
1018
718
|
if (fileExists(`${cwd}/pnpm-lock.yaml`)) return "pnpm";
|
|
1019
719
|
if (fileExists(`${cwd}/yarn.lock`)) return "yarn";
|
|
1020
720
|
return "npm";
|
|
1021
721
|
}
|
|
722
|
+
function getLockfileName(pm) {
|
|
723
|
+
switch (pm) {
|
|
724
|
+
case "bun":
|
|
725
|
+
return "bun.lock";
|
|
726
|
+
case "pnpm":
|
|
727
|
+
return "pnpm-lock.yaml";
|
|
728
|
+
case "yarn":
|
|
729
|
+
return "yarn.lock";
|
|
730
|
+
case "npm":
|
|
731
|
+
return "package-lock.json";
|
|
732
|
+
}
|
|
733
|
+
}
|
|
1022
734
|
function getRunPrefix(pm) {
|
|
1023
735
|
switch (pm) {
|
|
1024
736
|
case "bun":
|
|
@@ -1031,85 +743,150 @@ function getRunPrefix(pm) {
|
|
|
1031
743
|
return "npm run";
|
|
1032
744
|
}
|
|
1033
745
|
}
|
|
1034
|
-
var INSTALL_CMDS = {
|
|
1035
|
-
bun: ["bun", ["add"]],
|
|
1036
|
-
pnpm: ["pnpm", ["add"]],
|
|
1037
|
-
yarn: ["yarn", ["add"]],
|
|
1038
|
-
npm: ["npm", ["install"]]
|
|
1039
|
-
};
|
|
1040
746
|
async function installDevDeps(packages, cwd, pm) {
|
|
1041
747
|
const manager = pm ?? detectPackageManager(cwd);
|
|
1042
|
-
const
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
const
|
|
748
|
+
const pkg = readJson(path3.join(cwd, "package.json"));
|
|
749
|
+
if (!pkg) {
|
|
750
|
+
throw new Error("package.json not found");
|
|
751
|
+
}
|
|
752
|
+
const devDeps = pkg.devDependencies ?? {};
|
|
753
|
+
const missing = packages.filter((pkg2) => !devDeps[pkg2]);
|
|
754
|
+
if (missing.length === 0) return;
|
|
755
|
+
const addCmd = manager === "npm" ? "npm install -D" : `${manager} add -D`;
|
|
756
|
+
const exitCode = await new Promise((resolve) => {
|
|
757
|
+
const child = spawn(`${addCmd} ${missing.join(" ")}`, {
|
|
758
|
+
cwd,
|
|
759
|
+
shell: true,
|
|
760
|
+
stdio: "inherit"
|
|
761
|
+
});
|
|
762
|
+
const timer = setTimeout(() => {
|
|
763
|
+
child.kill();
|
|
764
|
+
resolve(null);
|
|
765
|
+
}, 12e4);
|
|
766
|
+
child.on("close", (code) => {
|
|
767
|
+
clearTimeout(timer);
|
|
768
|
+
resolve(code);
|
|
769
|
+
});
|
|
770
|
+
child.on("error", () => {
|
|
771
|
+
clearTimeout(timer);
|
|
772
|
+
resolve(1);
|
|
773
|
+
});
|
|
774
|
+
});
|
|
775
|
+
if (exitCode === null) {
|
|
776
|
+
throw new Error("Dependency installation timed out (120s)");
|
|
777
|
+
}
|
|
1047
778
|
if (exitCode !== 0) {
|
|
1048
|
-
logger.error(`Failed to install dependencies: ${stderr}`);
|
|
1049
779
|
throw new Error(`Dependency installation failed (exit code ${exitCode})`);
|
|
1050
780
|
}
|
|
1051
|
-
logger.success(`Installed ${resolvedPackages.length} packages`);
|
|
1052
781
|
}
|
|
1053
782
|
|
|
1054
783
|
// src/commands/fmt.ts
|
|
1055
784
|
function registerFmtCommand(program2) {
|
|
1056
|
-
const fmt = program2.command("fmt");
|
|
1057
|
-
fmt.
|
|
785
|
+
const fmt = program2.command("fmt").description("Initialize formatting config with preset");
|
|
786
|
+
fmt.argument("<preset>").option("-F, --force", "Force overwrite existing files").option("--no-install", "Skip dependency installation").option("--dry-run", "Preview without writing files").action(
|
|
1058
787
|
async (presetName, options) => {
|
|
1059
788
|
const preset = resolvePreset(FMT_PRESETS, presetName);
|
|
1060
789
|
if (!preset) return;
|
|
1061
790
|
const cwd = process.cwd();
|
|
791
|
+
const pm = fileExists(path4.join(cwd, "package.json")) ? detectPackageManager(cwd) : void 0;
|
|
1062
792
|
const opts = {
|
|
1063
793
|
cwd,
|
|
1064
794
|
force: options.force ?? false,
|
|
1065
|
-
dryRun: options.dryRun ?? false
|
|
795
|
+
dryRun: options.dryRun ?? false,
|
|
796
|
+
lockfile: pm ? getLockfileName(pm) : void 0
|
|
1066
797
|
};
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
798
|
+
const result = generateAllFmt(preset, opts);
|
|
799
|
+
const allFiles = [...result.created, ...result.overwritten];
|
|
800
|
+
if (allFiles.length === 0 && result.skipped.length === 0) {
|
|
801
|
+
logger.warn("No files to generate for this preset");
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
logGenerationResult(result, opts.dryRun);
|
|
805
|
+
if (!pm) {
|
|
806
|
+
warnMissingPackageJson(preset, options.install !== false);
|
|
1072
807
|
return;
|
|
1073
808
|
}
|
|
1074
|
-
logger.success(`Generated ${generated.length} config file(s)`);
|
|
1075
809
|
if (preset.scripts) {
|
|
1076
|
-
await injectScripts(preset.scripts, opts);
|
|
810
|
+
await injectScripts(preset.scripts, opts, pm);
|
|
1077
811
|
}
|
|
1078
|
-
if (preset.dependencies?.dev
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
812
|
+
if (!preset.dependencies?.dev) return;
|
|
813
|
+
if (options.install === false) {
|
|
814
|
+
logger.log(`Dependencies: ${preset.dependencies.dev.join(", ")}`);
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
if (opts.dryRun) {
|
|
818
|
+
logger.log(`[dry-run] Would install: ${preset.dependencies.dev.join(", ")}`);
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
try {
|
|
822
|
+
logger.log(`Installing dependencies with ${pm}...`);
|
|
823
|
+
await installDevDeps(preset.dependencies.dev, cwd, pm);
|
|
824
|
+
logger.success("Dependencies installed successfully");
|
|
825
|
+
} catch {
|
|
826
|
+
logger.warn("Dependency installation failed. You can install manually.");
|
|
1093
827
|
}
|
|
1094
|
-
logger.success("fmt init complete!");
|
|
1095
828
|
}
|
|
1096
829
|
);
|
|
1097
830
|
fmt.command("list").description("List available fmt presets").action(() => {
|
|
1098
|
-
logger.info("Available fmt presets:");
|
|
1099
831
|
for (const p of FMT_PRESETS) {
|
|
1100
|
-
console.log(
|
|
832
|
+
console.log(`${p.name.padEnd(12)} ${p.description}`);
|
|
1101
833
|
}
|
|
1102
834
|
});
|
|
1103
835
|
}
|
|
1104
|
-
|
|
1105
|
-
const
|
|
836
|
+
function logGenerationResult(result, dryRun) {
|
|
837
|
+
const files = [...result.created, ...result.overwritten];
|
|
838
|
+
if (dryRun) {
|
|
839
|
+
if (files.length > 0) {
|
|
840
|
+
logger.log(`[dry-run] Would create ${files.join(", ")}`);
|
|
841
|
+
}
|
|
842
|
+
if (result.skipped.length > 0) {
|
|
843
|
+
logger.log(`[dry-run] Skipped ${result.skipped.join(", ")} (already exists)`);
|
|
844
|
+
}
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
if (result.created.length > 0) {
|
|
848
|
+
logger.log(
|
|
849
|
+
`Created ${summarizeFiles(result.created)} config ${result.created.length} file${result.created.length > 1 ? "s" : ""}`
|
|
850
|
+
);
|
|
851
|
+
}
|
|
852
|
+
if (result.overwritten.length > 0) {
|
|
853
|
+
logger.log(
|
|
854
|
+
`Overwritten ${summarizeFiles(result.overwritten)} config ${result.overwritten.length} file${result.overwritten.length > 1 ? "s" : ""}`
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
if (result.skipped.length > 0) {
|
|
858
|
+
logger.log(
|
|
859
|
+
`Skipped ${result.skipped.length} file${result.skipped.length > 1 ? "s" : ""} (already exists)`
|
|
860
|
+
);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
function warnMissingPackageJson(preset, installEnabled) {
|
|
864
|
+
const tasks = [];
|
|
865
|
+
if (preset.scripts) tasks.push("script injection");
|
|
866
|
+
if (preset.dependencies?.dev && installEnabled) tasks.push("dependency installation");
|
|
867
|
+
if (tasks.length > 0) {
|
|
868
|
+
logger.warn(`package.json not found, skipping ${tasks.join(" and ")}`);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
function summarizeFiles(filenames) {
|
|
872
|
+
const categories = /* @__PURE__ */ new Set();
|
|
873
|
+
for (const name of filenames) {
|
|
874
|
+
if (name.includes("eslint")) categories.add("eslint");
|
|
875
|
+
else if (name.includes("prettier")) categories.add("prettier");
|
|
876
|
+
else if (name.includes("stylelint")) categories.add("stylelint");
|
|
877
|
+
else if (name.includes("cspell")) categories.add("cspell");
|
|
878
|
+
else if (name.includes("editorconfig")) categories.add("editorconfig");
|
|
879
|
+
}
|
|
880
|
+
return [...categories].join(", ");
|
|
881
|
+
}
|
|
882
|
+
async function injectScripts(scripts, opts, pm) {
|
|
883
|
+
const pkgPath = path4.join(opts.cwd, "package.json");
|
|
1106
884
|
const pkg = readJson(pkgPath);
|
|
1107
885
|
if (!pkg) {
|
|
1108
|
-
logger.warn("package.json not found, skipping
|
|
886
|
+
logger.warn("package.json not found, skipping script injection");
|
|
1109
887
|
return;
|
|
1110
888
|
}
|
|
1111
889
|
const existingScripts = pkg.scripts ?? {};
|
|
1112
|
-
const pm = detectPackageManager(opts.cwd);
|
|
1113
890
|
const prefix = getRunPrefix(pm);
|
|
1114
891
|
const resolvedScripts = {};
|
|
1115
892
|
for (const [key, value] of Object.entries(scripts)) {
|
|
@@ -1119,12 +896,10 @@ async function injectScripts(scripts, opts) {
|
|
|
1119
896
|
let skipped = 0;
|
|
1120
897
|
for (const [key, value] of Object.entries(resolvedScripts)) {
|
|
1121
898
|
if (existingScripts[key] !== void 0 && !opts.force) {
|
|
1122
|
-
logger.skip(`script:${key}`, "already exists");
|
|
1123
899
|
skipped++;
|
|
1124
900
|
continue;
|
|
1125
901
|
}
|
|
1126
902
|
if (opts.dryRun) {
|
|
1127
|
-
logger.info(`[dry-run] Would add script: ${key}`);
|
|
1128
903
|
added++;
|
|
1129
904
|
continue;
|
|
1130
905
|
}
|
|
@@ -1134,7 +909,30 @@ async function injectScripts(scripts, opts) {
|
|
|
1134
909
|
if (added > 0 && !opts.dryRun) {
|
|
1135
910
|
pkg.scripts = existingScripts;
|
|
1136
911
|
writeJson(pkgPath, pkg);
|
|
1137
|
-
logger.
|
|
912
|
+
logger.log(
|
|
913
|
+
`Added ${added} script${added > 1 ? "s" : ""} to package.json${skipped > 0 ? ` (${skipped} skipped)` : ""}`
|
|
914
|
+
);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// src/utils/execFileNoThrow.ts
|
|
919
|
+
import { exec } from "child_process";
|
|
920
|
+
import { promisify } from "util";
|
|
921
|
+
var execAsync = promisify(exec);
|
|
922
|
+
async function execFileNoThrow(command, args, options) {
|
|
923
|
+
const cmdStr = args.length > 0 ? `${command} ${args.join(" ")}` : command;
|
|
924
|
+
try {
|
|
925
|
+
const { stdout, stderr } = await execAsync(cmdStr, {
|
|
926
|
+
cwd: options?.cwd
|
|
927
|
+
});
|
|
928
|
+
return { stdout: stdout.trim(), stderr: stderr.trim(), exitCode: 0 };
|
|
929
|
+
} catch (err) {
|
|
930
|
+
const error = err;
|
|
931
|
+
return {
|
|
932
|
+
stdout: (error.stdout ?? "").trim(),
|
|
933
|
+
stderr: (error.stderr ?? "").trim(),
|
|
934
|
+
exitCode: error.code === "ENOENT" ? null : 1
|
|
935
|
+
};
|
|
1138
936
|
}
|
|
1139
937
|
}
|
|
1140
938
|
|
|
@@ -1187,22 +985,19 @@ async function performUpdate(pm) {
|
|
|
1187
985
|
function registerUpdateCommand(program2) {
|
|
1188
986
|
program2.command("update").description("Update @luxkit/cli to the latest version").option("--check", "Only check for updates without installing").action(async (options) => {
|
|
1189
987
|
try {
|
|
1190
|
-
logger.info("Checking for updates...");
|
|
1191
988
|
const current = getCurrentVersion();
|
|
1192
989
|
const latest = await fetchLatestVersion();
|
|
1193
990
|
if (current === latest) {
|
|
1194
|
-
logger.
|
|
991
|
+
logger.log(`Already up to date (v${current})`);
|
|
1195
992
|
return;
|
|
1196
993
|
}
|
|
1197
994
|
if (options.check) {
|
|
1198
|
-
logger.
|
|
1199
|
-
logger.info(`Run \`lux update\` to update.`);
|
|
995
|
+
logger.log(`Update available: v${current} \u2192 v${latest}`);
|
|
1200
996
|
return;
|
|
1201
997
|
}
|
|
1202
998
|
const pm = detectGlobalPackageManager();
|
|
1203
|
-
logger.info(`Updating via ${pm}...`);
|
|
1204
999
|
await performUpdate(pm);
|
|
1205
|
-
logger.
|
|
1000
|
+
logger.log(`Updated to v${latest}`);
|
|
1206
1001
|
} catch (err) {
|
|
1207
1002
|
const message = err instanceof Error ? err.message : String(err);
|
|
1208
1003
|
logger.error(message);
|
|
@@ -1885,49 +1680,44 @@ function isPlainObject(val) {
|
|
|
1885
1680
|
// src/generators/vscode.ts
|
|
1886
1681
|
function generateVscodeSettings(preset, opts) {
|
|
1887
1682
|
const settingsPath = `${opts.cwd}/.vscode/settings.json`;
|
|
1888
|
-
const presetSettings = preset.settings();
|
|
1889
1683
|
if (opts.dryRun) {
|
|
1890
|
-
|
|
1891
|
-
return
|
|
1684
|
+
const existingSettings2 = readJson(settingsPath);
|
|
1685
|
+
return existingSettings2 ? "overwritten" : "created";
|
|
1892
1686
|
}
|
|
1687
|
+
const presetSettings = preset.settings();
|
|
1893
1688
|
const existingSettings = readJson(settingsPath);
|
|
1894
1689
|
if (existingSettings) {
|
|
1895
1690
|
const backupPath = `${settingsPath}.bak`;
|
|
1896
1691
|
if (!fileExists(backupPath)) {
|
|
1897
1692
|
writeFile(backupPath, JSON.stringify(existingSettings, null, 2) + "\n");
|
|
1898
|
-
logger.
|
|
1693
|
+
logger.log(`Backed up .vscode/settings.json \u2192 settings.json.bak`);
|
|
1899
1694
|
}
|
|
1900
1695
|
const merged = mergeVscodeSettings(presetSettings, existingSettings);
|
|
1901
1696
|
writeJson(settingsPath, merged);
|
|
1902
|
-
|
|
1903
|
-
} else {
|
|
1904
|
-
writeJson(settingsPath, presetSettings);
|
|
1905
|
-
logger.create(".vscode/settings.json");
|
|
1697
|
+
return "overwritten";
|
|
1906
1698
|
}
|
|
1907
|
-
|
|
1699
|
+
writeJson(settingsPath, presetSettings);
|
|
1700
|
+
return "created";
|
|
1908
1701
|
}
|
|
1909
1702
|
function generateVscodeExtensions(preset, opts) {
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
logger.info(`[dry-run] Would create .vscode/extensions.json`);
|
|
1914
|
-
return true;
|
|
1915
|
-
}
|
|
1916
|
-
writeJson(extensionsPath, { recommendations: extensions });
|
|
1917
|
-
logger.create(".vscode/extensions.json");
|
|
1918
|
-
return true;
|
|
1703
|
+
if (opts.dryRun) return "created";
|
|
1704
|
+
writeJson(`${opts.cwd}/.vscode/extensions.json`, { recommendations: preset.extensions() });
|
|
1705
|
+
return "created";
|
|
1919
1706
|
}
|
|
1920
1707
|
function generateAllVscode(preset, opts) {
|
|
1921
|
-
const
|
|
1922
|
-
|
|
1923
|
-
if (
|
|
1924
|
-
|
|
1708
|
+
const result = { created: [], overwritten: [], skipped: [] };
|
|
1709
|
+
const settingsAction = generateVscodeSettings(preset, opts);
|
|
1710
|
+
if (settingsAction === "created") result.created.push(".vscode/settings.json");
|
|
1711
|
+
else if (settingsAction === "overwritten") result.overwritten.push(".vscode/settings.json");
|
|
1712
|
+
const extAction = generateVscodeExtensions(preset, opts);
|
|
1713
|
+
if (extAction === "created") result.created.push(".vscode/extensions.json");
|
|
1714
|
+
return result;
|
|
1925
1715
|
}
|
|
1926
1716
|
|
|
1927
1717
|
// src/commands/vscode.ts
|
|
1928
1718
|
function registerVscodeCommand(program2) {
|
|
1929
|
-
const vscode = program2.command("vscode");
|
|
1930
|
-
vscode.
|
|
1719
|
+
const vscode = program2.command("vscode").description("Initialize VSCode config with preset");
|
|
1720
|
+
vscode.argument("<preset>").option("-F, --force", "Force overwrite existing files").option("--dry-run", "Preview without writing files").action(async (presetName, options) => {
|
|
1931
1721
|
const preset = resolvePreset(VSCODE_PRESETS, presetName);
|
|
1932
1722
|
if (!preset) return;
|
|
1933
1723
|
const cwd = process.cwd();
|
|
@@ -1936,20 +1726,21 @@ function registerVscodeCommand(program2) {
|
|
|
1936
1726
|
force: options.force ?? false,
|
|
1937
1727
|
dryRun: options.dryRun ?? false
|
|
1938
1728
|
};
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
if (generated.length === 0) {
|
|
1729
|
+
const result = generateAllVscode(preset, opts);
|
|
1730
|
+
const files = [...result.created, ...result.overwritten];
|
|
1731
|
+
if (files.length === 0) {
|
|
1943
1732
|
logger.warn("No files generated");
|
|
1944
1733
|
return;
|
|
1945
1734
|
}
|
|
1946
|
-
|
|
1947
|
-
|
|
1735
|
+
if (opts.dryRun) {
|
|
1736
|
+
logger.log(`[dry-run] Would create ${files.join(", ")}`);
|
|
1737
|
+
return;
|
|
1738
|
+
}
|
|
1739
|
+
logger.log(`Created ${files.join(", ")}`);
|
|
1948
1740
|
});
|
|
1949
1741
|
vscode.command("list").description("List available vscode presets").action(() => {
|
|
1950
|
-
logger.info("Available vscode presets:");
|
|
1951
1742
|
for (const p of VSCODE_PRESETS) {
|
|
1952
|
-
console.log(
|
|
1743
|
+
console.log(`${p.name.padEnd(12)} ${p.description}`);
|
|
1953
1744
|
}
|
|
1954
1745
|
});
|
|
1955
1746
|
}
|
|
@@ -1965,6 +1756,13 @@ function buildCommands(shell, httpProxy, socksProxy) {
|
|
|
1965
1756
|
`set all_proxy=${socksProxy}`
|
|
1966
1757
|
].join("\r\n");
|
|
1967
1758
|
}
|
|
1759
|
+
if (shell === "bash") {
|
|
1760
|
+
return [
|
|
1761
|
+
`export https_proxy=${httpProxy}`,
|
|
1762
|
+
`export http_proxy=${httpProxy}`,
|
|
1763
|
+
`export all_proxy=${socksProxy}`
|
|
1764
|
+
].join("\n");
|
|
1765
|
+
}
|
|
1968
1766
|
return [
|
|
1969
1767
|
`$env:https_proxy="${httpProxy}"`,
|
|
1970
1768
|
`$env:http_proxy="${httpProxy}"`,
|
|
@@ -1984,15 +1782,16 @@ function parseProxy(proxy) {
|
|
|
1984
1782
|
}
|
|
1985
1783
|
var SHELL_LABELS = {
|
|
1986
1784
|
cmd: "CMD",
|
|
1987
|
-
pw: "PowerShell"
|
|
1785
|
+
pw: "PowerShell",
|
|
1786
|
+
bash: "Bash"
|
|
1988
1787
|
};
|
|
1989
1788
|
function handleCopy(shell, proxy) {
|
|
1990
1789
|
const { httpProxy, socksProxy } = parseProxy(proxy);
|
|
1991
1790
|
const commands = buildCommands(shell, httpProxy, socksProxy);
|
|
1992
1791
|
if (copyToClipboard(commands)) {
|
|
1993
|
-
logger.
|
|
1792
|
+
logger.log(`Copied to clipboard \u2014 paste in ${SHELL_LABELS[shell]}`);
|
|
1994
1793
|
} else {
|
|
1995
|
-
logger.error("
|
|
1794
|
+
logger.error("Failed to copy to clipboard");
|
|
1996
1795
|
console.log(commands);
|
|
1997
1796
|
}
|
|
1998
1797
|
}
|
|
@@ -2000,6 +1799,7 @@ function registerVpnCommand(program2) {
|
|
|
2000
1799
|
const vpn = program2.command("vpn");
|
|
2001
1800
|
vpn.command("cmd").description("Copy CMD proxy commands to clipboard").option("--proxy <addr>", "Proxy address", DEFAULT_PROXY).action((options) => handleCopy("cmd", options.proxy));
|
|
2002
1801
|
vpn.command("pw").description("Copy PowerShell proxy commands to clipboard").option("--proxy <addr>", "Proxy address", DEFAULT_PROXY).action((options) => handleCopy("pw", options.proxy));
|
|
1802
|
+
vpn.command("bash").description("Copy Bash proxy commands to clipboard").option("--proxy <addr>", "Proxy address", DEFAULT_PROXY).action((options) => handleCopy("bash", options.proxy));
|
|
2003
1803
|
}
|
|
2004
1804
|
|
|
2005
1805
|
// src/index.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@luxkit/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "One-click project formatting & VSCode config CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -28,8 +28,7 @@
|
|
|
28
28
|
"prepublishOnly": "cross-env NODE_ENV=production bun run build",
|
|
29
29
|
"test": "vitest run",
|
|
30
30
|
"test:watch": "vitest",
|
|
31
|
-
"test:coverage": "vitest run --coverage"
|
|
32
|
-
"bump:deps": "bunx tsx scripts/bump-deps.ts"
|
|
31
|
+
"test:coverage": "vitest run --coverage"
|
|
33
32
|
},
|
|
34
33
|
"keywords": [],
|
|
35
34
|
"author": "",
|