@vinesheg/dev-forge 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/LICENSE +21 -0
- package/README.md +323 -0
- package/bin/cli.js +63 -0
- package/configs/biome.json +110 -0
- package/configs/lefthook.yml +22 -0
- package/configs/pkg-lint.json +54 -0
- package/configs/stylelint.json +52 -0
- package/index.js +29 -0
- package/lib/init.js +170 -0
- package/lib/runner.js +187 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Your Name
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
[](https://www.npmjs.com/package/dev-forge)
|
|
2
|
+
[](https://github.com/vinesheg1/dev-forge/actions)
|
|
3
|
+
[](https://github.com/vinesheg1/dev-forge/blob/main/LICENSE)
|
|
4
|
+
# dev-forge
|
|
5
|
+
|
|
6
|
+
> Zero-config developer toolkit with unified CLI for modern projects
|
|
7
|
+
|
|
8
|
+
A single-dependency meta-package that provides comprehensive tooling for linting, formatting, Git hooks, and code quality checks.
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
✨ **Zero Configuration** - Works out of the box with sensible defaults
|
|
13
|
+
🔧 **Single Dependency** - Install once, get everything you need
|
|
14
|
+
⚡ **High Performance** - Powered by Biome and Lefthook
|
|
15
|
+
🎯 **Comprehensive Checks** - Linting, formatting, dead code detection, and more
|
|
16
|
+
🪝 **Git Hooks** - Automatic pre-commit and commit-msg validation
|
|
17
|
+
📦 **Package Standards** - Validates package.json structure and content
|
|
18
|
+
|
|
19
|
+
## Tools Included
|
|
20
|
+
|
|
21
|
+
- **[Biome](https://biomejs.dev/)** - Fast linting and formatting for JS/TS/JSON
|
|
22
|
+
- **[Knip](https://knip.dev/)** - Find unused files, dependencies, and exports
|
|
23
|
+
- **[Lefthook](https://github.com/evilmartians/lefthook)** - Fast and powerful Git hooks manager
|
|
24
|
+
- **[Commitlint](https://commitlint.js.org/)** - Enforce conventional commit messages
|
|
25
|
+
- **[Stylelint](https://stylelint.io/)** - Modern CSS/SCSS linter
|
|
26
|
+
- **[npm-package-json-lint](https://npmpackagejsonlint.org/)** - Package.json validator
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install -D @vinesheg/dev-forge```
|
|
32
|
+
|
|
33
|
+
Or with yarn:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
yarn add --dev @vinesheg/dev-forge
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
1. **Initialize the toolkit** in your project:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx forge init
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
This will:
|
|
48
|
+
- Generate configuration files (biome.json, .stylelintrc.json, etc.)
|
|
49
|
+
- Set up Git hooks via Lefthook
|
|
50
|
+
- Add a `prepare` script to your package.json
|
|
51
|
+
|
|
52
|
+
2. **Run checks** on your codebase:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npx forge check
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
3. **Auto-fix** issues where possible:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npx forge fix
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## CLI Commands
|
|
65
|
+
|
|
66
|
+
### `forge init`
|
|
67
|
+
|
|
68
|
+
Initialize dev-forge in your project. Creates configuration files and sets up Git hooks.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npx forge init [options]
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Options:**
|
|
75
|
+
- `--skip-hooks` - Skip Lefthook installation
|
|
76
|
+
|
|
77
|
+
**What it does:**
|
|
78
|
+
- Creates `biome.json` that extends toolkit defaults
|
|
79
|
+
- Creates `.stylelintrc.json` for CSS/SCSS linting
|
|
80
|
+
- Creates `.npmpackagejsonlintrc.json` for package.json validation
|
|
81
|
+
- Creates `.commitlintrc.json` for commit message validation
|
|
82
|
+
- Copies `lefthook.yml` for Git hook configuration
|
|
83
|
+
- Installs Git hooks via Lefthook
|
|
84
|
+
- Adds `prepare` script to package.json (runs on `npm install`)
|
|
85
|
+
|
|
86
|
+
### `forge check`
|
|
87
|
+
|
|
88
|
+
Run all quality checks on your codebase in parallel.
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npx forge check [options]
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Options:**
|
|
95
|
+
- `--no-parallel` - Run checks sequentially instead of in parallel
|
|
96
|
+
|
|
97
|
+
**What it checks:**
|
|
98
|
+
- **Biome**: Lints and checks formatting of JS/TS/JSON files
|
|
99
|
+
- **Stylelint**: Validates CSS/SCSS/Sass files
|
|
100
|
+
- **Knip**: Finds unused files, dependencies, and exports
|
|
101
|
+
- **npm-package-json-lint**: Validates package.json structure
|
|
102
|
+
|
|
103
|
+
### `forge fix`
|
|
104
|
+
|
|
105
|
+
Auto-fix issues where possible.
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
npx forge fix
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**What it fixes:**
|
|
112
|
+
- **Biome**: Applies automatic fixes and formatting
|
|
113
|
+
- **Stylelint**: Fixes auto-fixable CSS/SCSS issues
|
|
114
|
+
|
|
115
|
+
## Git Hooks
|
|
116
|
+
|
|
117
|
+
After running `forge init`, the following Git hooks are automatically installed:
|
|
118
|
+
|
|
119
|
+
### Pre-commit Hook
|
|
120
|
+
|
|
121
|
+
Runs before each commit and checks:
|
|
122
|
+
- **Biome** on staged JS/TS/JSON files
|
|
123
|
+
- **Stylelint** on staged CSS/SCSS files
|
|
124
|
+
- **npm-package-json-lint** if package.json is staged
|
|
125
|
+
|
|
126
|
+
### Commit-msg Hook
|
|
127
|
+
|
|
128
|
+
Runs after entering a commit message and validates:
|
|
129
|
+
- **Commitlint** enforces [Conventional Commits](https://www.conventionalcommits.org/)
|
|
130
|
+
|
|
131
|
+
Example valid commit messages:
|
|
132
|
+
```
|
|
133
|
+
feat: add user authentication
|
|
134
|
+
fix: resolve memory leak in worker
|
|
135
|
+
docs: update installation instructions
|
|
136
|
+
chore: upgrade dependencies
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Configuration
|
|
140
|
+
|
|
141
|
+
All tools come pre-configured with sensible defaults, but you can customize them by editing the generated config files:
|
|
142
|
+
|
|
143
|
+
### Biome (`biome.json`)
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
|
148
|
+
"extends": ["./node_modules/dev-forge/configs/biome.json"]
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Add your own rules or override defaults:
|
|
153
|
+
|
|
154
|
+
```json
|
|
155
|
+
{
|
|
156
|
+
"extends": ["./node_modules/dev-forge/configs/biome.json"],
|
|
157
|
+
"linter": {
|
|
158
|
+
"rules": {
|
|
159
|
+
"suspicious": {
|
|
160
|
+
"noExplicitAny": "off"
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Stylelint (`.stylelintrc.json`)
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"extends": ["./node_modules/dev-forge/configs/stylelint.json"]
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### npm-package-json-lint (`.npmpackagejsonlintrc.json`)
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
{
|
|
179
|
+
"extends": "./node_modules/dev-forge/configs/pkg-lint.json"
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Lefthook (`lefthook.yml`)
|
|
184
|
+
|
|
185
|
+
The default configuration runs checks on staged files. You can add custom commands:
|
|
186
|
+
|
|
187
|
+
```yaml
|
|
188
|
+
pre-commit:
|
|
189
|
+
parallel: true
|
|
190
|
+
commands:
|
|
191
|
+
biome:
|
|
192
|
+
glob: "*.{js,jsx,ts,tsx,json,jsonc}"
|
|
193
|
+
run: npx biome check --staged {staged_files}
|
|
194
|
+
|
|
195
|
+
custom-script:
|
|
196
|
+
run: npm run my-custom-check
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Package.json Scripts
|
|
200
|
+
|
|
201
|
+
Add these to your `package.json` for convenience:
|
|
202
|
+
|
|
203
|
+
```json
|
|
204
|
+
{
|
|
205
|
+
"scripts": {
|
|
206
|
+
"prepare": "forge init",
|
|
207
|
+
"lint": "forge check",
|
|
208
|
+
"lint:fix": "forge fix"
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Then use:
|
|
214
|
+
```bash
|
|
215
|
+
npm run lint # Run all checks
|
|
216
|
+
npm run lint:fix # Auto-fix issues
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## CI/CD Integration
|
|
220
|
+
|
|
221
|
+
Add dev-forge checks to your CI pipeline:
|
|
222
|
+
|
|
223
|
+
### GitHub Actions
|
|
224
|
+
|
|
225
|
+
```yaml
|
|
226
|
+
name: Quality Checks
|
|
227
|
+
|
|
228
|
+
on: [push, pull_request]
|
|
229
|
+
|
|
230
|
+
jobs:
|
|
231
|
+
lint:
|
|
232
|
+
runs-on: ubuntu-latest
|
|
233
|
+
steps:
|
|
234
|
+
- uses: actions/checkout@v4
|
|
235
|
+
- uses: actions/setup-node@v4
|
|
236
|
+
with:
|
|
237
|
+
node-version: '18'
|
|
238
|
+
- run: npm ci
|
|
239
|
+
- run: npx forge check
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### GitLab CI
|
|
243
|
+
|
|
244
|
+
```yaml
|
|
245
|
+
lint:
|
|
246
|
+
image: node:18
|
|
247
|
+
script:
|
|
248
|
+
- npm ci
|
|
249
|
+
- npx forge check
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Knip Configuration
|
|
253
|
+
|
|
254
|
+
Knip automatically detects unused files and dependencies. To customize, create a `knip.json`:
|
|
255
|
+
|
|
256
|
+
```json
|
|
257
|
+
{
|
|
258
|
+
"entry": ["src/index.ts"],
|
|
259
|
+
"project": ["src/**/*.ts"],
|
|
260
|
+
"ignore": ["**/*.test.ts", "scripts/**"]
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Troubleshooting
|
|
265
|
+
|
|
266
|
+
### Git hooks not running
|
|
267
|
+
|
|
268
|
+
If hooks aren't running, try:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
npx forge init
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
This will reinstall Lefthook hooks.
|
|
275
|
+
|
|
276
|
+
### Lefthook installation fails
|
|
277
|
+
|
|
278
|
+
Make sure you have a `.git` directory:
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
git init
|
|
282
|
+
npx forge init
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Configuration not found
|
|
286
|
+
|
|
287
|
+
Run the init command to generate config files:
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
npx forge init
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Requirements
|
|
294
|
+
|
|
295
|
+
- Node.js >= 18.0.0
|
|
296
|
+
- Git (for Git hooks)
|
|
297
|
+
|
|
298
|
+
## Philosophy
|
|
299
|
+
|
|
300
|
+
`dev-forge` follows the principle of **convention over configuration**. It provides:
|
|
301
|
+
|
|
302
|
+
1. **Sensible defaults** that work for most projects
|
|
303
|
+
2. **Easy customization** when you need it
|
|
304
|
+
3. **Minimal setup** - one command to get started
|
|
305
|
+
4. **Fast feedback** - catch issues before they reach CI/CD
|
|
306
|
+
|
|
307
|
+
## License
|
|
308
|
+
|
|
309
|
+
MIT
|
|
310
|
+
|
|
311
|
+
## Contributing
|
|
312
|
+
|
|
313
|
+
Issues and pull requests are welcome! Please follow [Conventional Commits](https://www.conventionalcommits.org/) for commit messages.
|
|
314
|
+
|
|
315
|
+
## Acknowledgments
|
|
316
|
+
|
|
317
|
+
Built with these excellent tools:
|
|
318
|
+
- [Biome](https://biomejs.dev/)
|
|
319
|
+
- [Knip](https://knip.dev/)
|
|
320
|
+
- [Lefthook](https://github.com/evilmartians/lefthook)
|
|
321
|
+
- [Commitlint](https://commitlint.js.org/)
|
|
322
|
+
- [Stylelint](https://stylelint.io/)
|
|
323
|
+
- [npm-package-json-lint](https://npmpackagejsonlint.org/)
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Command } = require("commander");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const { initToolkit } = require("../lib/init");
|
|
6
|
+
const { runCheck, runFix } = require("../lib/runner");
|
|
7
|
+
|
|
8
|
+
const program = new Command();
|
|
9
|
+
|
|
10
|
+
program
|
|
11
|
+
.name("forge")
|
|
12
|
+
.description("Zero-config developer toolkit for modern projects")
|
|
13
|
+
.version(require("../package.json").version);
|
|
14
|
+
|
|
15
|
+
program
|
|
16
|
+
.command("init")
|
|
17
|
+
.description(
|
|
18
|
+
"Initialize dev-forge in your project (generates configs and sets up Git hooks)",
|
|
19
|
+
)
|
|
20
|
+
.option("--skip-hooks", "Skip Lefthook installation")
|
|
21
|
+
.action(async (options) => {
|
|
22
|
+
try {
|
|
23
|
+
await initToolkit(options);
|
|
24
|
+
console.log("✅ dev-forge initialized successfully!");
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error("❌ Initialization failed:", error.message);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
program
|
|
32
|
+
.command("check")
|
|
33
|
+
.description("Run all checks (Biome, Stylelint, Knip, package-json-lint)")
|
|
34
|
+
.option("--no-parallel", "Run checks sequentially instead of in parallel")
|
|
35
|
+
.action(async (options) => {
|
|
36
|
+
try {
|
|
37
|
+
await runCheck(options);
|
|
38
|
+
console.log("✅ All checks passed!");
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error("❌ Checks failed:", error.message);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
program
|
|
46
|
+
.command("fix")
|
|
47
|
+
.description("Auto-fix issues (Biome and Stylelint)")
|
|
48
|
+
.action(async () => {
|
|
49
|
+
try {
|
|
50
|
+
await runFix();
|
|
51
|
+
console.log("✅ Auto-fix completed!");
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error("❌ Auto-fix failed:", error.message);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
program.parse(process.argv);
|
|
59
|
+
|
|
60
|
+
// Show help if no command provided
|
|
61
|
+
if (!process.argv.slice(2).length) {
|
|
62
|
+
program.outputHelp();
|
|
63
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev",
|
|
3
|
+
"organizeImports": {
|
|
4
|
+
"enabled": true
|
|
5
|
+
},
|
|
6
|
+
"linter": {
|
|
7
|
+
"enabled": true,
|
|
8
|
+
"rules": {
|
|
9
|
+
"recommended": true,
|
|
10
|
+
"complexity": {
|
|
11
|
+
"noExtraBooleanCast": "error",
|
|
12
|
+
"noUselessCatch": "error",
|
|
13
|
+
"noUselessConstructor": "error",
|
|
14
|
+
"noUselessLabel": "error",
|
|
15
|
+
"noUselessRename": "error",
|
|
16
|
+
"noUselessSwitchCase": "error",
|
|
17
|
+
"useFlatMap": "warn",
|
|
18
|
+
"useOptionalChain": "warn",
|
|
19
|
+
"useSimplifiedLogicExpression": "warn"
|
|
20
|
+
},
|
|
21
|
+
"correctness": {
|
|
22
|
+
"noConstAssign": "error",
|
|
23
|
+
"noConstantCondition": "error",
|
|
24
|
+
"noEmptyPattern": "error",
|
|
25
|
+
"noGlobalObjectCalls": "error",
|
|
26
|
+
"noInvalidConstructorSuper": "error",
|
|
27
|
+
"noNonoctalDecimalEscape": "error",
|
|
28
|
+
"noPrecisionLoss": "error",
|
|
29
|
+
"noSelfAssign": "error",
|
|
30
|
+
"noSetterReturn": "error",
|
|
31
|
+
"noSwitchDeclarations": "error",
|
|
32
|
+
"noUndeclaredVariables": "error",
|
|
33
|
+
"noUnreachable": "error",
|
|
34
|
+
"noUnreachableSuper": "error",
|
|
35
|
+
"noUnsafeFinally": "error",
|
|
36
|
+
"noUnsafeOptionalChaining": "error",
|
|
37
|
+
"noUnusedLabels": "error",
|
|
38
|
+
"noUnusedVariables": "warn",
|
|
39
|
+
"useIsNan": "error",
|
|
40
|
+
"useValidForDirection": "error"
|
|
41
|
+
},
|
|
42
|
+
"security": {
|
|
43
|
+
"noDangerouslySetInnerHtml": "warn",
|
|
44
|
+
"noDangerouslySetInnerHtmlWithChildren": "error"
|
|
45
|
+
},
|
|
46
|
+
"style": {
|
|
47
|
+
"useConst": "warn",
|
|
48
|
+
"useTemplate": "warn"
|
|
49
|
+
},
|
|
50
|
+
"suspicious": {
|
|
51
|
+
"noArrayIndexKey": "warn",
|
|
52
|
+
"noAssignInExpressions": "warn",
|
|
53
|
+
"noAsyncPromiseExecutor": "error",
|
|
54
|
+
"noCatchAssign": "error",
|
|
55
|
+
"noClassAssign": "error",
|
|
56
|
+
"noCommentText": "warn",
|
|
57
|
+
"noCompareNegZero": "error",
|
|
58
|
+
"noControlCharactersInRegex": "error",
|
|
59
|
+
"noDebugger": "warn",
|
|
60
|
+
"noDoubleEquals": "warn",
|
|
61
|
+
"noDuplicateCase": "error",
|
|
62
|
+
"noDuplicateClassMembers": "error",
|
|
63
|
+
"noDuplicateObjectKeys": "error",
|
|
64
|
+
"noDuplicateParameters": "error",
|
|
65
|
+
"noEmptyBlockStatements": "warn",
|
|
66
|
+
"noExplicitAny": "warn",
|
|
67
|
+
"noExtraNonNullAssertion": "error",
|
|
68
|
+
"noFallthroughSwitchClause": "error",
|
|
69
|
+
"noFunctionAssign": "error",
|
|
70
|
+
"noGlobalAssign": "error",
|
|
71
|
+
"noImportAssign": "error",
|
|
72
|
+
"noMisleadingCharacterClass": "error",
|
|
73
|
+
"noPrototypeBuiltins": "warn",
|
|
74
|
+
"noRedeclare": "error",
|
|
75
|
+
"noShadowRestrictedNames": "error",
|
|
76
|
+
"noUnsafeNegation": "error",
|
|
77
|
+
"useGetterReturn": "error"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"formatter": {
|
|
82
|
+
"enabled": true,
|
|
83
|
+
"formatWithErrors": false,
|
|
84
|
+
"indentStyle": "space",
|
|
85
|
+
"indentWidth": 2,
|
|
86
|
+
"lineWidth": 100,
|
|
87
|
+
"lineEnding": "lf"
|
|
88
|
+
},
|
|
89
|
+
"javascript": {
|
|
90
|
+
"formatter": {
|
|
91
|
+
"quoteStyle": "single",
|
|
92
|
+
"jsxQuoteStyle": "double",
|
|
93
|
+
"trailingCommas": "es5",
|
|
94
|
+
"semicolons": "always",
|
|
95
|
+
"arrowParentheses": "always",
|
|
96
|
+
"bracketSpacing": true,
|
|
97
|
+
"bracketSameLine": false
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
"json": {
|
|
101
|
+
"formatter": {
|
|
102
|
+
"enabled": true,
|
|
103
|
+
"indentWidth": 2
|
|
104
|
+
},
|
|
105
|
+
"parser": {
|
|
106
|
+
"allowComments": true,
|
|
107
|
+
"allowTrailingCommas": false
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
pre-commit:
|
|
2
|
+
parallel: true
|
|
3
|
+
commands:
|
|
4
|
+
biome:
|
|
5
|
+
glob: "*.{js,jsx,ts,tsx,json,jsonc}"
|
|
6
|
+
run: npx biome check --staged {staged_files}
|
|
7
|
+
stage_fixed: true
|
|
8
|
+
|
|
9
|
+
stylelint:
|
|
10
|
+
glob: "*.{css,scss,sass}"
|
|
11
|
+
run: npx stylelint {staged_files}
|
|
12
|
+
stage_fixed: true
|
|
13
|
+
|
|
14
|
+
package-json-lint:
|
|
15
|
+
files: git diff --name-only --cached
|
|
16
|
+
glob: "package.json"
|
|
17
|
+
run: npx npmPkgJsonLint .
|
|
18
|
+
|
|
19
|
+
commit-msg:
|
|
20
|
+
commands:
|
|
21
|
+
commitlint:
|
|
22
|
+
run: npx commitlint --edit {1}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"rules": {
|
|
3
|
+
"require-author": "error",
|
|
4
|
+
"require-description": "error",
|
|
5
|
+
"require-keywords": "warning",
|
|
6
|
+
"require-license": "error",
|
|
7
|
+
"require-name": "error",
|
|
8
|
+
"require-repository": "warning",
|
|
9
|
+
"require-version": "error",
|
|
10
|
+
"valid-values-author": ["error", ["string", "object"]],
|
|
11
|
+
"valid-values-license": [
|
|
12
|
+
"error",
|
|
13
|
+
["MIT", "Apache-2.0", "BSD-3-Clause", "ISC", "GPL-3.0", "UNLICENSED"]
|
|
14
|
+
],
|
|
15
|
+
"name-format": "error",
|
|
16
|
+
"version-format": "error",
|
|
17
|
+
"description-format": "error",
|
|
18
|
+
"prefer-alphabetical-dependencies": "warning",
|
|
19
|
+
"prefer-alphabetical-devDependencies": "warning",
|
|
20
|
+
"prefer-alphabetical-peerDependencies": "warning",
|
|
21
|
+
"prefer-alphabetical-optionalDependencies": "warning",
|
|
22
|
+
"prefer-no-engineStrict": "error",
|
|
23
|
+
"no-duplicate-properties": "error",
|
|
24
|
+
"prefer-property-order": [
|
|
25
|
+
"warning",
|
|
26
|
+
[
|
|
27
|
+
"name",
|
|
28
|
+
"version",
|
|
29
|
+
"description",
|
|
30
|
+
"keywords",
|
|
31
|
+
"author",
|
|
32
|
+
"license",
|
|
33
|
+
"homepage",
|
|
34
|
+
"repository",
|
|
35
|
+
"bugs",
|
|
36
|
+
"main",
|
|
37
|
+
"module",
|
|
38
|
+
"types",
|
|
39
|
+
"exports",
|
|
40
|
+
"bin",
|
|
41
|
+
"files",
|
|
42
|
+
"scripts",
|
|
43
|
+
"dependencies",
|
|
44
|
+
"devDependencies",
|
|
45
|
+
"peerDependencies",
|
|
46
|
+
"optionalDependencies",
|
|
47
|
+
"engines"
|
|
48
|
+
]
|
|
49
|
+
],
|
|
50
|
+
"require-scripts": "warning",
|
|
51
|
+
"no-absolute-version-dependencies": "warning",
|
|
52
|
+
"no-absolute-version-devDependencies": "warning"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "stylelint-config-standard",
|
|
3
|
+
"rules": {
|
|
4
|
+
"at-rule-no-unknown": [
|
|
5
|
+
true,
|
|
6
|
+
{
|
|
7
|
+
"ignoreAtRules": [
|
|
8
|
+
"tailwind",
|
|
9
|
+
"apply",
|
|
10
|
+
"variants",
|
|
11
|
+
"responsive",
|
|
12
|
+
"screen",
|
|
13
|
+
"layer",
|
|
14
|
+
"mixin",
|
|
15
|
+
"include",
|
|
16
|
+
"extend",
|
|
17
|
+
"function",
|
|
18
|
+
"return",
|
|
19
|
+
"if",
|
|
20
|
+
"else",
|
|
21
|
+
"each",
|
|
22
|
+
"for",
|
|
23
|
+
"while"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"color-hex-length": "short",
|
|
28
|
+
"color-named": "never",
|
|
29
|
+
"declaration-block-no-redundant-longhand-properties": true,
|
|
30
|
+
"declaration-no-important": true,
|
|
31
|
+
"font-family-name-quotes": "always-where-recommended",
|
|
32
|
+
"font-weight-notation": "numeric",
|
|
33
|
+
"function-url-quotes": "always",
|
|
34
|
+
"max-nesting-depth": 4,
|
|
35
|
+
"no-descending-specificity": null,
|
|
36
|
+
"number-max-precision": 4,
|
|
37
|
+
"selector-class-pattern": null,
|
|
38
|
+
"selector-id-pattern": null,
|
|
39
|
+
"selector-max-compound-selectors": 4,
|
|
40
|
+
"selector-max-id": 0,
|
|
41
|
+
"selector-max-specificity": "0,4,0",
|
|
42
|
+
"selector-max-universal": 1,
|
|
43
|
+
"selector-no-qualifying-type": [
|
|
44
|
+
true,
|
|
45
|
+
{
|
|
46
|
+
"ignore": ["attribute", "class"]
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"shorthand-property-no-redundant-values": true,
|
|
50
|
+
"value-keyword-case": "lower"
|
|
51
|
+
}
|
|
52
|
+
}
|
package/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dev-forge
|
|
3
|
+
* Zero-config developer toolkit with unified CLI
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { initToolkit } = require("./lib/init");
|
|
7
|
+
const {
|
|
8
|
+
runCheck,
|
|
9
|
+
runFix,
|
|
10
|
+
runBiomeStaged,
|
|
11
|
+
runStylelintStaged,
|
|
12
|
+
getToolBin,
|
|
13
|
+
runCommand,
|
|
14
|
+
} = require("./lib/runner");
|
|
15
|
+
|
|
16
|
+
module.exports = {
|
|
17
|
+
// Initialization
|
|
18
|
+
initToolkit,
|
|
19
|
+
|
|
20
|
+
// Runners
|
|
21
|
+
runCheck,
|
|
22
|
+
runFix,
|
|
23
|
+
runBiomeStaged,
|
|
24
|
+
runStylelintStaged,
|
|
25
|
+
|
|
26
|
+
// Utilities
|
|
27
|
+
getToolBin,
|
|
28
|
+
runCommand,
|
|
29
|
+
};
|
package/lib/init.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
const fs = require("fs-extra");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const execa = require("execa");
|
|
4
|
+
|
|
5
|
+
const CWD = process.cwd();
|
|
6
|
+
const TOOLKIT_ROOT = path.join(__dirname, "..");
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Initialize the toolkit in the user's project
|
|
10
|
+
* This function is idempotent - safe to run multiple times
|
|
11
|
+
*/
|
|
12
|
+
async function initToolkit(options = {}) {
|
|
13
|
+
console.log("🔧 Initializing dev-forge...\n");
|
|
14
|
+
|
|
15
|
+
// Step 1: Generate local config files that extend the toolkit's configs
|
|
16
|
+
await generateConfigFiles();
|
|
17
|
+
|
|
18
|
+
// Step 2: Initialize Lefthook (Git hooks)
|
|
19
|
+
if (!options.skipHooks) {
|
|
20
|
+
await initializeLefthook();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Step 3: Update package.json with prepare script
|
|
24
|
+
await updatePackageJson();
|
|
25
|
+
|
|
26
|
+
console.log("\n📦 Configuration files created:");
|
|
27
|
+
console.log(" - biome.json");
|
|
28
|
+
console.log(" - .stylelintrc.json");
|
|
29
|
+
console.log(" - .npmpackagejsonlintrc.json");
|
|
30
|
+
console.log(" - .commitlintrc.json");
|
|
31
|
+
console.log(" - lefthook.yml");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Generate config files that extend the toolkit's internal configs
|
|
36
|
+
*/
|
|
37
|
+
async function generateConfigFiles() {
|
|
38
|
+
const configs = [
|
|
39
|
+
{
|
|
40
|
+
name: "biome.json",
|
|
41
|
+
content: {
|
|
42
|
+
$schema: "https://biomejs.dev/schemas/1.9.4/schema.json",
|
|
43
|
+
extends: [
|
|
44
|
+
path
|
|
45
|
+
.relative(CWD, path.join(TOOLKIT_ROOT, "configs/biome.json"))
|
|
46
|
+
.replace(/\\/g, "/"),
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: ".stylelintrc.json",
|
|
52
|
+
content: {
|
|
53
|
+
extends: [
|
|
54
|
+
path
|
|
55
|
+
.relative(CWD, path.join(TOOLKIT_ROOT, "configs/stylelint.json"))
|
|
56
|
+
.replace(/\\/g, "/"),
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: ".npmpackagejsonlintrc.json",
|
|
62
|
+
content: {
|
|
63
|
+
extends: path
|
|
64
|
+
.relative(CWD, path.join(TOOLKIT_ROOT, "configs/pkg-lint.json"))
|
|
65
|
+
.replace(/\\/g, "/"),
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: ".commitlintrc.json",
|
|
70
|
+
content: {
|
|
71
|
+
extends: ["@commitlint/config-conventional"],
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
for (const config of configs) {
|
|
77
|
+
const configPath = path.join(CWD, config.name);
|
|
78
|
+
|
|
79
|
+
// Only create if doesn't exist (idempotent)
|
|
80
|
+
if (!(await fs.pathExists(configPath))) {
|
|
81
|
+
await fs.writeJson(configPath, config.content, { spaces: 2 });
|
|
82
|
+
console.log(` ✓ Created ${config.name}`);
|
|
83
|
+
} else {
|
|
84
|
+
console.log(` ⊙ ${config.name} already exists (skipped)`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Copy lefthook.yml directly (it doesn't support extends)
|
|
89
|
+
const lefthookSrc = path.join(TOOLKIT_ROOT, "configs/lefthook.yml");
|
|
90
|
+
const lefthookDest = path.join(CWD, "lefthook.yml");
|
|
91
|
+
|
|
92
|
+
if (!(await fs.pathExists(lefthookDest))) {
|
|
93
|
+
await fs.copy(lefthookSrc, lefthookDest);
|
|
94
|
+
console.log(" ✓ Created lefthook.yml");
|
|
95
|
+
} else {
|
|
96
|
+
console.log(" ⊙ lefthook.yml already exists (skipped)");
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Initialize Lefthook Git hooks
|
|
102
|
+
*/
|
|
103
|
+
async function initializeLefthook() {
|
|
104
|
+
console.log("\n🪝 Installing Git hooks...");
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const lefthookBin = path.join(TOOLKIT_ROOT, "node_modules/.bin/lefthook");
|
|
108
|
+
|
|
109
|
+
// Check if .git directory exists
|
|
110
|
+
const gitDir = path.join(CWD, ".git");
|
|
111
|
+
if (!(await fs.pathExists(gitDir))) {
|
|
112
|
+
console.log(
|
|
113
|
+
" ⚠ No .git directory found. Skipping Git hooks installation.",
|
|
114
|
+
);
|
|
115
|
+
console.log(' Run "git init" first, then run "forge init" again.');
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
await execa(lefthookBin, ["install"], {
|
|
120
|
+
cwd: CWD,
|
|
121
|
+
stdio: "inherit",
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
console.log(" ✓ Lefthook installed successfully");
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.error(" ⚠ Lefthook installation failed:", error.message);
|
|
127
|
+
console.error(' You may need to run "lefthook install" manually.');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Update user's package.json to include the prepare script
|
|
133
|
+
*/
|
|
134
|
+
async function updatePackageJson() {
|
|
135
|
+
console.log("\n📝 Updating package.json...");
|
|
136
|
+
|
|
137
|
+
const pkgPath = path.join(CWD, "package.json");
|
|
138
|
+
|
|
139
|
+
if (!(await fs.pathExists(pkgPath))) {
|
|
140
|
+
console.log(" ⚠ No package.json found in current directory");
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const pkg = await fs.readJson(pkgPath);
|
|
145
|
+
|
|
146
|
+
// Initialize scripts if it doesn't exist
|
|
147
|
+
if (!pkg.scripts) {
|
|
148
|
+
pkg.scripts = {};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Add prepare script if it doesn't exist or doesn't include forge init
|
|
152
|
+
const prepareScript = "forge init";
|
|
153
|
+
|
|
154
|
+
if (!pkg.scripts.prepare) {
|
|
155
|
+
pkg.scripts.prepare = prepareScript;
|
|
156
|
+
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
157
|
+
console.log(' ✓ Added "prepare" script to package.json');
|
|
158
|
+
} else if (!pkg.scripts.prepare.includes("forge init")) {
|
|
159
|
+
// Append to existing prepare script
|
|
160
|
+
pkg.scripts.prepare = `${pkg.scripts.prepare} && ${prepareScript}`;
|
|
161
|
+
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
162
|
+
console.log(' ✓ Updated "prepare" script in package.json');
|
|
163
|
+
} else {
|
|
164
|
+
console.log(' ⊙ "prepare" script already includes forge init (skipped)');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
module.exports = {
|
|
169
|
+
initToolkit,
|
|
170
|
+
};
|
package/lib/runner.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
const execa = require("execa");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const fs = require("fs-extra");
|
|
4
|
+
|
|
5
|
+
const CWD = process.cwd();
|
|
6
|
+
const TOOLKIT_ROOT = path.join(__dirname, "..");
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Get the path to a tool's binary in the toolkit's node_modules
|
|
10
|
+
*/
|
|
11
|
+
function getToolBin(toolName) {
|
|
12
|
+
return path.join(TOOLKIT_ROOT, "node_modules/.bin", toolName);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Run a command and handle output
|
|
17
|
+
*/
|
|
18
|
+
async function runCommand(command, args, options = {}) {
|
|
19
|
+
const toolName = path.basename(command);
|
|
20
|
+
console.log(`\n🔍 Running ${toolName}...`);
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const result = await execa(command, args, {
|
|
24
|
+
cwd: CWD,
|
|
25
|
+
stdio: "inherit",
|
|
26
|
+
...options,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
console.log(`✅ ${toolName} completed successfully`);
|
|
30
|
+
return result;
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.error(`❌ ${toolName} failed`);
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Run all checks in parallel or sequentially
|
|
39
|
+
*/
|
|
40
|
+
async function runCheck(options = {}) {
|
|
41
|
+
console.log("🔎 Running comprehensive checks...\n");
|
|
42
|
+
|
|
43
|
+
const checks = [
|
|
44
|
+
async () => {
|
|
45
|
+
// Biome check
|
|
46
|
+
const biomeBin = getToolBin("biome");
|
|
47
|
+
return runCommand(biomeBin, ["check", "."]);
|
|
48
|
+
},
|
|
49
|
+
async () => {
|
|
50
|
+
// Stylelint check
|
|
51
|
+
const stylelintBin = getToolBin("stylelint");
|
|
52
|
+
const patterns = ["**/*.css", "**/*.scss", "**/*.sass"];
|
|
53
|
+
|
|
54
|
+
// Check if any style files exist
|
|
55
|
+
let hasStyleFiles = false;
|
|
56
|
+
for (const pattern of patterns) {
|
|
57
|
+
const files = await fs.pathExists(
|
|
58
|
+
path.join(CWD, pattern.split("/")[0]),
|
|
59
|
+
);
|
|
60
|
+
if (files) {
|
|
61
|
+
hasStyleFiles = true;
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!hasStyleFiles) {
|
|
67
|
+
console.log("\n⊙ Stylelint skipped (no style files found)");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return runCommand(stylelintBin, patterns);
|
|
72
|
+
},
|
|
73
|
+
async () => {
|
|
74
|
+
// Knip check
|
|
75
|
+
const knipBin = getToolBin("knip");
|
|
76
|
+
return runCommand(knipBin, []);
|
|
77
|
+
},
|
|
78
|
+
async () => {
|
|
79
|
+
// npm-package-json-lint
|
|
80
|
+
const pkgLintBin = getToolBin("npmPkgJsonLint");
|
|
81
|
+
const pkgPath = path.join(CWD, "package.json");
|
|
82
|
+
|
|
83
|
+
if (!(await fs.pathExists(pkgPath))) {
|
|
84
|
+
console.log("\n⊙ package-json-lint skipped (no package.json found)");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return runCommand(pkgLintBin, ["."]);
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
if (options.parallel !== false) {
|
|
93
|
+
// Run in parallel
|
|
94
|
+
const results = await Promise.allSettled(checks.map((check) => check()));
|
|
95
|
+
|
|
96
|
+
const failures = results.filter((r) => r.status === "rejected");
|
|
97
|
+
if (failures.length > 0) {
|
|
98
|
+
throw new Error(`${failures.length} check(s) failed`);
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
// Run sequentially
|
|
102
|
+
for (const check of checks) {
|
|
103
|
+
await check();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Run auto-fix operations
|
|
110
|
+
*/
|
|
111
|
+
async function runFix() {
|
|
112
|
+
console.log("🔧 Running auto-fix operations...\n");
|
|
113
|
+
|
|
114
|
+
// Biome fix
|
|
115
|
+
const biomeBin = getToolBin("biome");
|
|
116
|
+
await runCommand(biomeBin, ["check", "--write", "."]);
|
|
117
|
+
|
|
118
|
+
// Stylelint fix
|
|
119
|
+
const stylelintBin = getToolBin("stylelint");
|
|
120
|
+
const patterns = ["**/*.css", "**/*.scss", "**/*.sass"];
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
await runCommand(stylelintBin, [...patterns, "--fix"], { reject: false });
|
|
124
|
+
} catch (error) {
|
|
125
|
+
// Stylelint might fail if no files found, which is okay
|
|
126
|
+
console.log(
|
|
127
|
+
" ⊙ Stylelint fix completed (some files may not have been fixable)",
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Run Biome on staged files (for Git hooks)
|
|
134
|
+
*/
|
|
135
|
+
async function runBiomeStaged(files) {
|
|
136
|
+
if (!files || files.length === 0) {
|
|
137
|
+
console.log("⊙ No staged files to check");
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const biomeBin = getToolBin("biome");
|
|
142
|
+
const jsFiles = files.filter((f) => /\.(js|jsx|ts|tsx|json|jsonc)$/.test(f));
|
|
143
|
+
|
|
144
|
+
if (jsFiles.length === 0) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
console.log(`🔍 Checking ${jsFiles.length} file(s) with Biome...`);
|
|
149
|
+
await execa(biomeBin, ["check", ...jsFiles], {
|
|
150
|
+
cwd: CWD,
|
|
151
|
+
stdio: "inherit",
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Run Stylelint on staged files (for Git hooks)
|
|
157
|
+
*/
|
|
158
|
+
async function runStylelintStaged(files) {
|
|
159
|
+
if (!files || files.length === 0) {
|
|
160
|
+
console.log("⊙ No staged style files to check");
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const stylelintBin = getToolBin("stylelint");
|
|
165
|
+
const styleFiles = files.filter((f) => /\.(css|scss|sass)$/.test(f));
|
|
166
|
+
|
|
167
|
+
if (styleFiles.length === 0) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
console.log(
|
|
172
|
+
`🎨 Checking ${styleFiles.length} style file(s) with Stylelint...`,
|
|
173
|
+
);
|
|
174
|
+
await execa(stylelintBin, styleFiles, {
|
|
175
|
+
cwd: CWD,
|
|
176
|
+
stdio: "inherit",
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
module.exports = {
|
|
181
|
+
runCheck,
|
|
182
|
+
runFix,
|
|
183
|
+
runBiomeStaged,
|
|
184
|
+
runStylelintStaged,
|
|
185
|
+
getToolBin,
|
|
186
|
+
runCommand,
|
|
187
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vinesheg/dev-forge",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"description": "Zero-config developer toolkit with unified CLI for linting, formatting, and Git hooks",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"developer-tools",
|
|
10
|
+
"linting",
|
|
11
|
+
"formatting",
|
|
12
|
+
"git-hooks",
|
|
13
|
+
"biome",
|
|
14
|
+
"lefthook",
|
|
15
|
+
"knip",
|
|
16
|
+
"commitlint",
|
|
17
|
+
"stylelint"
|
|
18
|
+
],
|
|
19
|
+
"author": "Vinesh EG <vinesheg@gmail.com>",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/vinesheg1/dev-forge.git"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/vinesheg1/dev-forge/issues"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/vinesheg1/dev-forge#readme",
|
|
29
|
+
"main": "index.js",
|
|
30
|
+
"types": "index.d.ts",
|
|
31
|
+
"bin": {
|
|
32
|
+
"forge": "bin/cli.js"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"bin",
|
|
36
|
+
"lib",
|
|
37
|
+
"configs",
|
|
38
|
+
"README.md"
|
|
39
|
+
],
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18.0.0"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@biomejs/biome": "^2.3.14",
|
|
45
|
+
"@commitlint/cli": "^19.5.0",
|
|
46
|
+
"@commitlint/config-conventional": "^19.5.0",
|
|
47
|
+
"commander": "^12.1.0",
|
|
48
|
+
"execa": "^5.1.1",
|
|
49
|
+
"fs-extra": "^11.2.0",
|
|
50
|
+
"knip": "^5.38.2",
|
|
51
|
+
"lefthook": "^1.9.3",
|
|
52
|
+
"npm-package-json-lint": "^8.0.0",
|
|
53
|
+
"stylelint": "^16.10.0",
|
|
54
|
+
"stylelint-config-standard": "^36.0.1"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/node": "^20.17.6"
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"forge": "node ./bin/cli.js",
|
|
61
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
62
|
+
}
|
|
63
|
+
}
|