Skip to content

Commit d8c2ae7

Browse files
committed
feat: fork commander v3.0.2 to preserve critical --no- flag behavior
Fork commander v3.0.2 into src/commander-fork/ to freeze CLI behavior and prevent breaking changes from future commander upgrades. This ensures the --no- prefix options (--no-dotfiles, --no-nojekyll, --no-notfound) continue to default to true as expected. Key changes: - Created minimal commander fork (743 lines, down from 1332) - Removed unused features (subcommands, actions, executables) - Preserved critical functionality for angular-cli-ghpages - Added 17 original commander v3 tests adapted to Jest - Removed commander npm dependency - Updated build to copy only index.js to dist Fork rationale documented in src/commander-fork/FORK_PLAN.md All 175 tests passing.
1 parent 56b7e25 commit d8c2ae7

14 files changed

+1615
-188
lines changed

.claude/settings.local.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,18 @@
99
"Bash(npm test)",
1010
"Bash(npm run build)",
1111
"Bash(node src/dist/angular-cli-ghpages --dry-run --dir=custom/dist)",
12-
"Bash(npm test -- builder-passthrough.spec.ts)"
12+
"Bash(npm test -- builder-passthrough.spec.ts)",
13+
"Bash(rm -rf commander-v3)",
14+
"Bash(git clone --depth 1 --branch v3.0.0 https://github.com/tj/commander.js.git commander-v3)",
15+
"Read(//tmp/commander-v3/**)",
16+
"Bash(mkdir -p /Users/johanneshoppe/Work/angular-schule/angular-cli-ghpages/commander-fork)",
17+
"Bash(node -e \"console.log(require.resolve(''../commander-fork''))\")",
18+
"Bash(node angular-cli-ghpages --version)",
19+
"Bash(node angular-cli-ghpages --help)",
20+
"Bash(./angular-cli-ghpages --version)",
21+
"Bash(npm test -- commander-fork)",
22+
"Bash(npm test -- commander-fork/test)",
23+
"Bash(timeout 30 npm test)"
1324
],
1425
"deny": [],
1526
"ask": []

TESTING_STATUS.md

Lines changed: 0 additions & 184 deletions
This file was deleted.

src/angular-cli-ghpages

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ var path = require('path'),
66
engine = require('./engine/engine'),
77
defaults = require('./engine/defaults').defaults,
88
pjson = require('./package.json'),
9-
commander = require('commander');
9+
commander = require('./commander-fork');
1010

1111
// defaults-file was ignored for --no-dotfiles, so it is not used here
1212
// TODO: review https://github.com/tj/commander.js/issues/928 and check again if it now works

src/commander-fork/FORK_NOTICE.md

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# Commander Fork Notice
2+
3+
## What We Did
4+
5+
We forked commander v3.0.2 and stripped it down from **1332 lines** to **744 lines** (~44% reduction).
6+
7+
### Removed Features (~588 lines)
8+
9+
1. **Subcommands** - `.command()` with action handlers
10+
2. **Git-style executable subcommands** - `executeSubCommand()`, child_process.spawn
11+
3. **Action handlers** - `.action()` method
12+
4. **Argument parsing** - `.arguments()`, `.parseExpectedArgs()`
13+
5. **Complex command hierarchy** - parent/child commands, aliases
14+
6. **Inspector port incrementing** - `incrementNodeInspectorPort()`
15+
7. **File existence checks** - `exists()` helper (only used for executables)
16+
8. **Command help** - `.commandHelp()`, `.prepareCommands()`
17+
9. **Variadic arguments** - `.variadicArgNotLast()`
18+
10. **Missing argument errors** - `.missingArgument()` (we don't use positional args)
19+
20+
### Removed Dependencies
21+
22+
-`child_process` (spawn) - only needed for executable subcommands
23+
-`fs` - only needed for checking executable file existence
24+
-`path.dirname` - only needed for finding executable paths
25+
26+
### Kept Dependencies (Built-ins)
27+
28+
-`events.EventEmitter` - needed for option parsing event system
29+
-`util.inherits` - needed to extend EventEmitter
30+
-`path.basename` - needed for guessing command name from argv[1]
31+
32+
## What We Kept
33+
34+
### Core Methods
35+
36+
- `.version(str, flags?, description?)` - Auto-register version flag
37+
- `.description(str)` - Set command description
38+
- `.option(flags, description, fn?, defaultValue?)` - **THE MOST IMPORTANT METHOD**
39+
- `.parse(argv)` - Parse command-line arguments
40+
- `.opts()` - Get parsed options as object
41+
- `.usage(str?)` - Get/set usage string
42+
- `.name(str?)` - Get/set command name
43+
- `.allowUnknownOption(arg?)` - Allow unknown flags
44+
- `.help(cb?)` - Output help and exit
45+
- `.outputHelp(cb?)` - Output help without exiting
46+
- `.helpOption(flags, description)` - Customize help flags
47+
48+
### Critical Behavior Preserved
49+
50+
**The `--no-` prefix handling** - This is why we forked!
51+
52+
```javascript
53+
.option('--no-dotfiles', 'Exclude dotfiles')
54+
55+
// Without flag: program.dotfiles === true (implicit default!)
56+
// With --no-dotfiles: program.dotfiles === false
57+
```
58+
59+
In commander v9+, this requires explicit defaults. We're freezing v3 behavior.
60+
61+
### Option Parsing Features
62+
63+
- ✅ Short flags (`-d`)
64+
- ✅ Long flags (`--dir`)
65+
- ✅ Required arguments (`--dir <path>`)
66+
- ✅ Optional arguments (`--file [path]`)
67+
- ✅ Boolean flags (`--verbose`)
68+
- ✅ Negatable flags (`--no-dotfiles`)
69+
- ✅ Default values
70+
- ✅ Custom coercion functions
71+
- ✅ Argument normalization (`-abc``-a -b -c`)
72+
- ✅ Equal sign support (`--foo=bar``--foo bar`)
73+
- ✅ Concatenated short options (`-d80``-d 80`)
74+
- ✅ Argument terminator (`--`)
75+
- ✅ Property access (`program.dir` after parse)
76+
77+
### Help System
78+
79+
- ✅ Auto-generated `--help` output
80+
- ✅ Option listing with descriptions
81+
- ✅ Default value display
82+
- ✅ Custom help callback support
83+
84+
## File Size Comparison
85+
86+
| Version | Lines | Size | Change |
87+
|---------|-------|------|--------|
88+
| Original v3.0.2 | 1332 | 32KB | - |
89+
| Commander-fork | 744 | 17KB | **-47% lines, -47% size** |
90+
91+
## Why We Forked
92+
93+
Commander v4-v14 introduced many breaking changes:
94+
95+
1. **v9.0.0** - Boolean option default values behavior changed
96+
2. **v9.0.0** - Option property names now match flag casing exactly
97+
3. **v9.0.0** - Excess arguments now error by default
98+
4. **v12.0.0** - Default export removed (must use named import)
99+
100+
Each of these would break existing user scripts. Rather than force users to adapt, we froze v3.0.2 behavior permanently.
101+
102+
## Maintenance Plan
103+
104+
This fork is **FROZEN**. We will only update for:
105+
106+
1. **Critical security vulnerabilities**
107+
2. **Bugs that affect angular-cli-ghpages**
108+
3. **Node.js compatibility issues**
109+
110+
For any fixes:
111+
- Update `commander-fork/index.js`
112+
- Add test in `commander-fork/__tests__/`
113+
- Bump version: `3.0.2-fork.1``3.0.2-fork.2`
114+
- Document the fix here
115+
116+
## Testing
117+
118+
We maintain test coverage for our usage patterns:
119+
120+
- ✅ All option types (boolean, required, optional, negatable)
121+
- ✅ Short and long flags
122+
- ✅ Default value handling
123+
-`--no-` prefix behavior (critical!)
124+
- ✅ Version flag
125+
- ✅ Help output
126+
- ✅ Property access after parsing
127+
- ✅ Unknown option errors
128+
129+
## License
130+
131+
MIT License - same as original commander.js
132+
133+
Original work Copyright (c) 2011 TJ Holowaychuk
134+
Modified work Copyright (c) 2025 angular-cli-ghpages contributors
135+
136+
See LICENSE file for full text.
137+
138+
## Credits
139+
140+
Huge thanks to TJ Holowaychuk and all commander.js contributors for creating this excellent library. This fork exists only to preserve specific v3 behavior for our narrow use case.
141+
142+
Original project: https://github.com/tj/commander.js

0 commit comments

Comments
 (0)