@dimensional-innovations/tool-config 2.0.0 → 3.0.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 +58 -7
- package/bin/lib/formatting.js +103 -0
- package/bin/lib/package-manager.js +87 -0
- package/bin/lib/ui.js +149 -21
- package/bin/lib/uninstall.js +199 -0
- package/bin/setup-tool-config.js +199 -21
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -39,7 +39,9 @@ npm install --save-dev @dimensional-innovations/tool-config
|
|
|
39
39
|
- 📦 **All-In-One** - All plugins and parsers included as dependencies
|
|
40
40
|
- 🔧 **Customizable** - Override any setting while keeping smart defaults
|
|
41
41
|
- 🚀 **Modern** - ESLint 9+ flat config, TypeScript with tsgo support (10x faster)
|
|
42
|
-
-
|
|
42
|
+
- 🔄 **Uninstall Support** - Clean removal with safety checks for modified files
|
|
43
|
+
- 🎬 **Smart Scripts** - Auto-generated `check-all` script runs tools in optimal order
|
|
44
|
+
- ✅ **Battle-Tested** - 556 tests with 100% coverage
|
|
43
45
|
|
|
44
46
|
## Quick Start
|
|
45
47
|
|
|
@@ -121,11 +123,14 @@ That's it! The configs will automatically detect your framework and TypeScript s
|
|
|
121
123
|
"style": "stylelint '**/*.css'",
|
|
122
124
|
"style:fix": "stylelint '**/*.css' --fix",
|
|
123
125
|
"typecheck": "tsc --noEmit",
|
|
124
|
-
"typecheck:watch": "tsc --noEmit --watch"
|
|
126
|
+
"typecheck:watch": "tsc --noEmit --watch",
|
|
127
|
+
"check-all": "npm run prettier && npm run style && npm run lint && npm run typecheck"
|
|
125
128
|
}
|
|
126
129
|
}
|
|
127
130
|
```
|
|
128
131
|
|
|
132
|
+
**Tip:** The CLI automatically adds a `check-all` script when you setup multiple tools. This runs all validation tools in the optimal order: formatting → styles → linting → type checking.
|
|
133
|
+
|
|
129
134
|
## Supported Frameworks
|
|
130
135
|
|
|
131
136
|
| Framework | ESLint | Prettier | Stylelint | TypeScript | Auto-Detect |
|
|
@@ -419,22 +424,68 @@ npx @dimensional-innovations/tool-config
|
|
|
419
424
|
- Choose which tools to configure
|
|
420
425
|
- Auto-detects framework and TypeScript
|
|
421
426
|
- Creates config files automatically
|
|
422
|
-
- Adds npm scripts to package.json
|
|
423
|
-
-
|
|
427
|
+
- Adds npm scripts to package.json (including `check-all` for multiple tools)
|
|
428
|
+
- Automated CI/CD pipeline setup
|
|
429
|
+
- **Uninstall support** - Clean removal of configs and scripts
|
|
424
430
|
- Supports dry-run mode
|
|
425
431
|
|
|
426
|
-
**Options:**
|
|
432
|
+
**Setup Options:**
|
|
427
433
|
|
|
428
434
|
```bash
|
|
435
|
+
# Interactive mode
|
|
436
|
+
npx @dimensional-innovations/tool-config # Choose tools interactively
|
|
437
|
+
|
|
438
|
+
# Single tool setup
|
|
429
439
|
npx @dimensional-innovations/tool-config eslint # Setup ESLint only
|
|
430
|
-
npx @dimensional-innovations/tool-config
|
|
431
|
-
npx @dimensional-innovations/tool-config
|
|
440
|
+
npx @dimensional-innovations/tool-config prettier # Setup Prettier only
|
|
441
|
+
npx @dimensional-innovations/tool-config stylelint # Setup Stylelint only
|
|
442
|
+
|
|
443
|
+
# Setup all tools
|
|
444
|
+
npx @dimensional-innovations/tool-config --all # Setup all tools at once
|
|
445
|
+
|
|
446
|
+
# CI/CD setup
|
|
432
447
|
npx @dimensional-innovations/tool-config --ci gitlab # Setup GitLab CI/CD
|
|
433
448
|
npx @dimensional-innovations/tool-config --ci github # Setup GitHub Actions
|
|
434
449
|
npx @dimensional-innovations/tool-config --setup-ci # Interactive CI setup
|
|
450
|
+
|
|
451
|
+
# Preview mode
|
|
452
|
+
npx @dimensional-innovations/tool-config --dry-run # Preview without creating files
|
|
453
|
+
|
|
454
|
+
# Help
|
|
435
455
|
npx @dimensional-innovations/tool-config --help # Show all options
|
|
436
456
|
```
|
|
437
457
|
|
|
458
|
+
**Uninstall Options:**
|
|
459
|
+
|
|
460
|
+
```bash
|
|
461
|
+
# Interactive uninstall
|
|
462
|
+
npx @dimensional-innovations/tool-config --uninstall # Choose which tools to remove
|
|
463
|
+
|
|
464
|
+
# Uninstall specific tool
|
|
465
|
+
npx @dimensional-innovations/tool-config --uninstall eslint
|
|
466
|
+
|
|
467
|
+
# Uninstall all detected tools
|
|
468
|
+
npx @dimensional-innovations/tool-config --uninstall --all
|
|
469
|
+
|
|
470
|
+
# Remove CI/CD configuration
|
|
471
|
+
npx @dimensional-innovations/tool-config --uninstall --ci
|
|
472
|
+
|
|
473
|
+
# Preview uninstall
|
|
474
|
+
npx @dimensional-innovations/tool-config --uninstall --dry-run
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
**Safety Features:**
|
|
478
|
+
|
|
479
|
+
The uninstall command includes safety features to protect your customizations:
|
|
480
|
+
|
|
481
|
+
- Only removes files that match the original generated templates
|
|
482
|
+
- Skips modified configuration files (warns you instead)
|
|
483
|
+
- Only removes auto-generated package.json scripts
|
|
484
|
+
- Preserves custom scripts with the same names
|
|
485
|
+
- Supports dry-run to preview what will be removed
|
|
486
|
+
- Cleans up empty directories (e.g., `.github/workflows`)
|
|
487
|
+
- Idempotent - safe to run multiple times
|
|
488
|
+
|
|
438
489
|
**CI/CD Integration:**
|
|
439
490
|
|
|
440
491
|
When you select `semantic-release` in interactive mode, the CLI will automatically prompt you to setup CI/CD for automated releases. Or use the `--ci` flag to setup CI/CD directly:
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formatting utilities for CLI output
|
|
3
|
+
* Standardized box drawing with consistent width
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import pc from 'picocolors'
|
|
7
|
+
|
|
8
|
+
const BOX_WIDTH = 70 // Standard width for all boxes
|
|
9
|
+
|
|
10
|
+
export const BOX = {
|
|
11
|
+
topLeft: '┌',
|
|
12
|
+
topRight: '┐',
|
|
13
|
+
bottomLeft: '└',
|
|
14
|
+
bottomRight: '┘',
|
|
15
|
+
vertical: '│',
|
|
16
|
+
horizontal: '─',
|
|
17
|
+
separator: '━',
|
|
18
|
+
width: BOX_WIDTH
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Create a box with standard width (70 chars)
|
|
23
|
+
* @param {string[]} lines - Array of content lines
|
|
24
|
+
* @param {string} title - Optional title for top border
|
|
25
|
+
* @returns {string} Formatted box
|
|
26
|
+
*/
|
|
27
|
+
export function createBox(lines, title = '') {
|
|
28
|
+
const width = BOX.width
|
|
29
|
+
const contentWidth = width - 4 // Remove: │ + space + space + │
|
|
30
|
+
|
|
31
|
+
// Create top border
|
|
32
|
+
let top
|
|
33
|
+
if (title) {
|
|
34
|
+
const titlePart = `─ ${title} `
|
|
35
|
+
const remaining = width - 2 - titlePart.length // -2 for corners
|
|
36
|
+
top = BOX.topLeft + titlePart + BOX.horizontal.repeat(remaining) + BOX.topRight
|
|
37
|
+
} else {
|
|
38
|
+
top = BOX.topLeft + BOX.horizontal.repeat(width - 2) + BOX.topRight
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Create content lines - pad each to exact width
|
|
42
|
+
const content = lines.map(line => {
|
|
43
|
+
const padded = line.slice(0, contentWidth).padEnd(contentWidth)
|
|
44
|
+
return `${BOX.vertical} ${padded} ${BOX.vertical}`
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
// Create bottom border
|
|
48
|
+
const bottom = BOX.bottomLeft + BOX.horizontal.repeat(width - 2) + BOX.bottomRight
|
|
49
|
+
|
|
50
|
+
return [top, ...content, bottom].join('\n')
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Create a separator line
|
|
55
|
+
* @returns {string} Separator line
|
|
56
|
+
*/
|
|
57
|
+
export function createSeparator() {
|
|
58
|
+
return BOX.separator.repeat(BOX.width)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Create a simple indented list
|
|
63
|
+
* @param {string[]} items - Array of items
|
|
64
|
+
* @param {string} bullet - Bullet character (default: '•')
|
|
65
|
+
* @returns {string} Formatted list
|
|
66
|
+
*/
|
|
67
|
+
export function createList(items, bullet = '•') {
|
|
68
|
+
return items.map(item => ` ${bullet} ${item}`).join('\n')
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Create a progress indicator
|
|
73
|
+
* @param {number} current - Current step
|
|
74
|
+
* @param {number} total - Total steps
|
|
75
|
+
* @param {string} label - Label for this step
|
|
76
|
+
* @returns {string} Progress line
|
|
77
|
+
*/
|
|
78
|
+
export function createProgress(current, total, label) {
|
|
79
|
+
return ` [${current}/${total}] ${label}`
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Color symbols for CLI output
|
|
84
|
+
*/
|
|
85
|
+
export const SYMBOLS = {
|
|
86
|
+
success: pc.green('✓'),
|
|
87
|
+
warning: pc.yellow('⚠'),
|
|
88
|
+
error: pc.red('✗'),
|
|
89
|
+
info: pc.blue('ℹ')
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Color helper functions
|
|
94
|
+
*/
|
|
95
|
+
export const colors = {
|
|
96
|
+
success: pc.green,
|
|
97
|
+
warning: pc.yellow,
|
|
98
|
+
error: pc.red,
|
|
99
|
+
info: pc.blue,
|
|
100
|
+
dim: pc.dim,
|
|
101
|
+
bold: pc.bold,
|
|
102
|
+
cyan: pc.cyan
|
|
103
|
+
}
|
|
@@ -112,3 +112,90 @@ export function updatePackageJsonScripts(tools, getScriptsFn, context) {
|
|
|
112
112
|
console.error(' ❌ Failed to update package.json:', error.message)
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Remove scripts from package.json for tools
|
|
118
|
+
* @param {string[]} tools - Array of tool names to remove scripts for
|
|
119
|
+
* @param {Function} getScriptsFn - Function that returns scripts for a tool: (tool, detected) => object
|
|
120
|
+
* @param {Object} context - Context object with {detected, cwd, dryRun}
|
|
121
|
+
*/
|
|
122
|
+
export function removePackageJsonScripts(tools, getScriptsFn, context) {
|
|
123
|
+
const { detected, cwd, dryRun = false } = context
|
|
124
|
+
const packageJsonPath = join(cwd, 'package.json')
|
|
125
|
+
|
|
126
|
+
if (!existsSync(packageJsonPath)) {
|
|
127
|
+
console.log(' ℹ️ No package.json found - skipping script removal')
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'))
|
|
133
|
+
|
|
134
|
+
if (!packageJson.scripts) {
|
|
135
|
+
console.log(' ℹ️ No scripts section in package.json')
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const scriptsToRemove = []
|
|
140
|
+
|
|
141
|
+
// Collect script names to remove based on tools
|
|
142
|
+
for (const tool of tools) {
|
|
143
|
+
const toolScripts = getScriptsFn(tool, detected)
|
|
144
|
+
for (const scriptName of Object.keys(toolScripts)) {
|
|
145
|
+
const expectedCommand = toolScripts[scriptName]
|
|
146
|
+
const actualCommand = packageJson.scripts[scriptName]
|
|
147
|
+
|
|
148
|
+
// Only remove if the script exists and matches our expected command
|
|
149
|
+
if (actualCommand === expectedCommand) {
|
|
150
|
+
scriptsToRemove.push(scriptName)
|
|
151
|
+
} else if (actualCommand) {
|
|
152
|
+
console.log(` ⚠️ Script "${scriptName}" has been modified - skipping for safety`)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Handle check-all script separately
|
|
158
|
+
const checkAllScript = packageJson.scripts['check-all']
|
|
159
|
+
if (checkAllScript) {
|
|
160
|
+
// Only remove if it's auto-generated (follows npm run X && npm run Y pattern)
|
|
161
|
+
const isAutoGenerated =
|
|
162
|
+
checkAllScript.startsWith('npm run') && checkAllScript.includes(' && npm run')
|
|
163
|
+
|
|
164
|
+
if (isAutoGenerated) {
|
|
165
|
+
scriptsToRemove.push('check-all')
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (scriptsToRemove.length === 0) {
|
|
170
|
+
console.log(' ℹ️ No scripts to remove from package.json')
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (dryRun) {
|
|
175
|
+
console.log(' 📝 Would remove scripts from package.json:')
|
|
176
|
+
for (const name of scriptsToRemove) {
|
|
177
|
+
console.log(` - ${name}`)
|
|
178
|
+
}
|
|
179
|
+
return
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Remove scripts
|
|
183
|
+
for (const scriptName of scriptsToRemove) {
|
|
184
|
+
delete packageJson.scripts[scriptName]
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Remove scripts section if empty
|
|
188
|
+
if (Object.keys(packageJson.scripts).length === 0) {
|
|
189
|
+
delete packageJson.scripts
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, 'utf8')
|
|
193
|
+
|
|
194
|
+
console.log(' ✅ Removed scripts from package.json:')
|
|
195
|
+
for (const name of scriptsToRemove) {
|
|
196
|
+
console.log(` - ${name}`)
|
|
197
|
+
}
|
|
198
|
+
} catch (error) {
|
|
199
|
+
console.error(' ❌ Failed to update package.json:', error.message)
|
|
200
|
+
}
|
|
201
|
+
}
|
package/bin/lib/ui.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* Handles help text, banners, and output formatting
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { createBox, createSeparator, SYMBOLS, colors } from './formatting.js'
|
|
7
|
+
|
|
6
8
|
const VERSION = '0.0.0-development'
|
|
7
9
|
|
|
8
10
|
/**
|
|
@@ -26,11 +28,12 @@ Options:
|
|
|
26
28
|
--all, -a Setup all tools (non-interactive)
|
|
27
29
|
--ci [provider] Setup CI/CD pipeline (gitlab, github, bitbucket)
|
|
28
30
|
--setup-ci Interactive CI/CD setup
|
|
31
|
+
--uninstall Remove tool configuration and scripts
|
|
29
32
|
--dry-run, -d Preview changes without writing files
|
|
30
33
|
--help, -h Show this help message
|
|
31
34
|
--version, -v Show version number
|
|
32
35
|
|
|
33
|
-
Examples:
|
|
36
|
+
Examples (Install):
|
|
34
37
|
setup-tool-config # Interactive mode
|
|
35
38
|
setup-tool-config eslint # Setup ESLint only
|
|
36
39
|
setup-tool-config --all # Setup all tools
|
|
@@ -40,6 +43,13 @@ Examples:
|
|
|
40
43
|
setup-tool-config --setup-ci # Interactive CI setup
|
|
41
44
|
setup-tool-config semantic-release --ci # Setup release + CI
|
|
42
45
|
|
|
46
|
+
Examples (Uninstall):
|
|
47
|
+
setup-tool-config --uninstall # Interactive uninstall
|
|
48
|
+
setup-tool-config --uninstall eslint # Remove ESLint only
|
|
49
|
+
setup-tool-config --uninstall --all # Remove all detected tools
|
|
50
|
+
setup-tool-config --uninstall --ci # Remove CI/CD configuration
|
|
51
|
+
setup-tool-config --uninstall --dry-run # Preview uninstall
|
|
52
|
+
|
|
43
53
|
For more information, visit:
|
|
44
54
|
https://gitlab.com/dimensional-innovations/tool-config
|
|
45
55
|
`)
|
|
@@ -53,19 +63,40 @@ export function showVersion() {
|
|
|
53
63
|
}
|
|
54
64
|
|
|
55
65
|
/**
|
|
56
|
-
* Show setup banner with detected configuration
|
|
66
|
+
* Show welcome and setup banner with detected configuration
|
|
57
67
|
*/
|
|
58
68
|
export function showBanner(detected) {
|
|
59
69
|
console.log('')
|
|
60
|
-
|
|
61
|
-
|
|
70
|
+
|
|
71
|
+
// Welcome box
|
|
72
|
+
const welcome = [
|
|
73
|
+
'🚀 Welcome to @dimensional-innovations/tool-config',
|
|
74
|
+
'',
|
|
75
|
+
'Zero-config setup for ESLint, Prettier, Stylelint,',
|
|
76
|
+
'TypeScript, and semantic-release.',
|
|
77
|
+
'',
|
|
78
|
+
'💡 Navigation: ↑↓ to move, Space to select, Enter to continue'
|
|
79
|
+
]
|
|
80
|
+
console.log(createBox(welcome))
|
|
81
|
+
|
|
82
|
+
console.log('')
|
|
83
|
+
|
|
84
|
+
// Condensed detection info
|
|
85
|
+
const detectedParts = []
|
|
86
|
+
if (detected.framework !== 'vanilla') {
|
|
87
|
+
detectedParts.push(detected.framework.charAt(0).toUpperCase() + detected.framework.slice(1))
|
|
88
|
+
}
|
|
89
|
+
if (detected.typescript) {
|
|
90
|
+
detectedParts.push('TypeScript')
|
|
91
|
+
}
|
|
92
|
+
if (detected.environment) {
|
|
93
|
+
detectedParts.push(detected.environment.charAt(0).toUpperCase() + detected.environment.slice(1))
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const detectedStr = detectedParts.length > 0 ? detectedParts.join(' • ') : 'JavaScript project'
|
|
97
|
+
console.log(`🔍 Auto-detected: ${detectedStr}`)
|
|
62
98
|
console.log('')
|
|
63
|
-
console.log('
|
|
64
|
-
console.log(` Framework: ${detected.framework}`)
|
|
65
|
-
console.log(` Environment: ${detected.environment}`)
|
|
66
|
-
console.log(` TypeScript: ${detected.typescript ? 'Yes' : 'No'}`)
|
|
67
|
-
console.log(` Electron: ${detected.electron ? 'Yes' : 'No'}`)
|
|
68
|
-
console.log(` Git Provider: ${detected.gitProvider || 'Unknown'}`)
|
|
99
|
+
console.log('💡 Recommended tools are pre-selected based on your project')
|
|
69
100
|
console.log('')
|
|
70
101
|
}
|
|
71
102
|
|
|
@@ -79,21 +110,70 @@ export function showSection(title) {
|
|
|
79
110
|
}
|
|
80
111
|
|
|
81
112
|
/**
|
|
82
|
-
* Show completion message
|
|
113
|
+
* Show completion message with detailed summary
|
|
114
|
+
* @param {boolean} dryRun - Whether this was a dry run
|
|
115
|
+
* @param {string[]} createdFiles - Files that were created
|
|
116
|
+
* @param {string[]} scripts - Scripts that were added
|
|
83
117
|
*/
|
|
84
|
-
export function showCompletion(dryRun) {
|
|
118
|
+
export function showCompletion(dryRun, createdFiles = [], scripts = []) {
|
|
85
119
|
console.log('')
|
|
120
|
+
|
|
86
121
|
if (dryRun) {
|
|
87
|
-
console.log(
|
|
88
|
-
console.log('
|
|
122
|
+
console.log(createSeparator())
|
|
123
|
+
console.log('')
|
|
124
|
+
const dryRunMsg = [
|
|
125
|
+
`${SYMBOLS.info} DRY RUN MODE - Preview only, no files created`,
|
|
126
|
+
'',
|
|
127
|
+
'Run without --dry-run to create these files'
|
|
128
|
+
]
|
|
129
|
+
console.log(createBox(dryRunMsg))
|
|
89
130
|
} else {
|
|
90
|
-
console.log(
|
|
131
|
+
console.log(createSeparator())
|
|
91
132
|
console.log('')
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
133
|
+
|
|
134
|
+
const completion = [colors.bold(colors.cyan('🎉 All done! Your project is ready.'))]
|
|
135
|
+
|
|
136
|
+
if (createdFiles.length > 0) {
|
|
137
|
+
completion.push('')
|
|
138
|
+
completion.push(
|
|
139
|
+
`📁 Created ${createdFiles.length} config file${createdFiles.length > 1 ? 's' : ''}:`
|
|
140
|
+
)
|
|
141
|
+
createdFiles.forEach(file => {
|
|
142
|
+
completion.push(` • ${file}`)
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (scripts.length > 0) {
|
|
147
|
+
completion.push('')
|
|
148
|
+
completion.push(`📦 Added ${scripts.length} npm script${scripts.length > 1 ? 's' : ''}:`)
|
|
149
|
+
|
|
150
|
+
// Highlight check-all if present
|
|
151
|
+
const checkAllIndex = scripts.indexOf('check-all')
|
|
152
|
+
if (checkAllIndex !== -1) {
|
|
153
|
+
completion.push(` • check-all ← Run this to validate everything`)
|
|
154
|
+
scripts
|
|
155
|
+
.filter(s => s !== 'check-all')
|
|
156
|
+
.forEach(script => {
|
|
157
|
+
completion.push(` • ${script}`)
|
|
158
|
+
})
|
|
159
|
+
} else {
|
|
160
|
+
scripts.forEach(script => {
|
|
161
|
+
completion.push(` • ${script}`)
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
completion.push('')
|
|
167
|
+
completion.push('🚀 Quick start:')
|
|
168
|
+
if (scripts.includes('check-all')) {
|
|
169
|
+
completion.push(' npm run check-all')
|
|
170
|
+
} else if (scripts.length > 0) {
|
|
171
|
+
completion.push(` npm run ${scripts[0]}`)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
console.log(createBox(completion))
|
|
96
175
|
}
|
|
176
|
+
|
|
97
177
|
console.log('')
|
|
98
178
|
}
|
|
99
179
|
|
|
@@ -103,9 +183,57 @@ export function showCompletion(dryRun) {
|
|
|
103
183
|
export function showCICompletion(dryRun) {
|
|
104
184
|
console.log('')
|
|
105
185
|
if (dryRun) {
|
|
106
|
-
console.log(
|
|
186
|
+
console.log(`${SYMBOLS.info} Dry run mode - no files were modified`)
|
|
187
|
+
} else {
|
|
188
|
+
console.log(`${SYMBOLS.success} CI/CD setup complete!`)
|
|
189
|
+
}
|
|
190
|
+
console.log('')
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Show uninstall banner
|
|
195
|
+
*/
|
|
196
|
+
export function showUninstallBanner(installedTools, installedCI) {
|
|
197
|
+
console.log('')
|
|
198
|
+
console.log('🗑️ @dimensional-innovations/tool-config Uninstall')
|
|
199
|
+
console.log('━'.repeat(50))
|
|
200
|
+
console.log('')
|
|
201
|
+
console.log('⚠️ WARNING: This will remove configuration files and scripts')
|
|
202
|
+
console.log(' Make sure you have committed or backed up your changes first!')
|
|
203
|
+
console.log('')
|
|
204
|
+
|
|
205
|
+
if (installedTools.length > 0) {
|
|
206
|
+
console.log('📦 Detected tools:')
|
|
207
|
+
for (const tool of installedTools) {
|
|
208
|
+
console.log(` - ${tool}`)
|
|
209
|
+
}
|
|
210
|
+
console.log('')
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (installedCI) {
|
|
214
|
+
console.log(`🚀 Detected CI/CD: ${installedCI}`)
|
|
215
|
+
console.log('')
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (installedTools.length === 0 && !installedCI) {
|
|
219
|
+
console.log('ℹ️ No tools or CI configuration detected')
|
|
220
|
+
console.log('')
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Show uninstall completion message
|
|
226
|
+
*/
|
|
227
|
+
export function showUninstallCompletion(dryRun, removedCount) {
|
|
228
|
+
console.log('')
|
|
229
|
+
if (dryRun) {
|
|
230
|
+
console.log(`${SYMBOLS.info} Dry run mode - no files were modified`)
|
|
231
|
+
console.log(' Run without --dry-run to apply changes')
|
|
232
|
+
} else if (removedCount > 0) {
|
|
233
|
+
console.log(`${SYMBOLS.success} Uninstall complete!`)
|
|
234
|
+
console.log(` Removed ${removedCount} file(s) and their associated scripts`)
|
|
107
235
|
} else {
|
|
108
|
-
console.log(
|
|
236
|
+
console.log(`${SYMBOLS.info} Nothing was removed`)
|
|
109
237
|
}
|
|
110
238
|
console.log('')
|
|
111
239
|
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Uninstall utilities for setup-tool-config CLI
|
|
3
|
+
* Handles safe removal of config files, CI templates, and scripts
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync, readdirSync, readFileSync, rmSync } from 'fs'
|
|
7
|
+
import { join } from 'path'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Detect which tools are currently installed
|
|
11
|
+
* @param {string} cwd - Current working directory
|
|
12
|
+
* @returns {string[]} Array of installed tool names
|
|
13
|
+
*/
|
|
14
|
+
export function detectInstalledTools(cwd) {
|
|
15
|
+
const toolFiles = {
|
|
16
|
+
eslint: 'eslint.config.js',
|
|
17
|
+
prettier: 'prettier.config.js',
|
|
18
|
+
stylelint: 'stylelint.config.js',
|
|
19
|
+
typescript: 'tsconfig.json',
|
|
20
|
+
'semantic-release': 'release.config.js'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const installed = []
|
|
24
|
+
for (const [tool, filename] of Object.entries(toolFiles)) {
|
|
25
|
+
if (existsSync(join(cwd, filename))) {
|
|
26
|
+
installed.push(tool)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return installed
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Detect which CI provider is installed
|
|
35
|
+
* @param {string} cwd - Current working directory
|
|
36
|
+
* @returns {string|null} Provider name or null
|
|
37
|
+
*/
|
|
38
|
+
export function detectInstalledCI(cwd) {
|
|
39
|
+
const ciFiles = {
|
|
40
|
+
gitlab: '.gitlab-ci.yml',
|
|
41
|
+
github: '.github/workflows/ci.yml',
|
|
42
|
+
bitbucket: 'bitbucket-pipelines.yml'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
for (const [provider, filepath] of Object.entries(ciFiles)) {
|
|
46
|
+
if (existsSync(join(cwd, filepath))) {
|
|
47
|
+
return provider
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Check if a file's content matches the expected template
|
|
56
|
+
* @param {string} filepath - Path to file
|
|
57
|
+
* @param {string} expectedContent - Expected content
|
|
58
|
+
* @returns {boolean} True if content matches
|
|
59
|
+
*/
|
|
60
|
+
function contentMatches(filepath, expectedContent) {
|
|
61
|
+
if (!existsSync(filepath)) {
|
|
62
|
+
return false
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const actualContent = readFileSync(filepath, 'utf8')
|
|
67
|
+
return actualContent.trim() === expectedContent.trim()
|
|
68
|
+
} catch {
|
|
69
|
+
return false
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Uninstall a tool by removing its config file
|
|
75
|
+
* @param {string} _tool - Tool name
|
|
76
|
+
* @param {Object} handler - Tool handler with getConfigFilename and generateConfigContent
|
|
77
|
+
* @param {string} cwd - Current working directory
|
|
78
|
+
* @param {boolean} dryRun - Preview mode
|
|
79
|
+
* @returns {boolean} True if uninstalled successfully
|
|
80
|
+
*/
|
|
81
|
+
export function uninstallTool(_tool, handler, cwd, dryRun = false) {
|
|
82
|
+
const filename = handler.getConfigFilename()
|
|
83
|
+
const filepath = join(cwd, filename)
|
|
84
|
+
|
|
85
|
+
if (!existsSync(filepath)) {
|
|
86
|
+
console.log(` ℹ️ ${filename} not found - already uninstalled`)
|
|
87
|
+
return false
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Check if file was modified by user
|
|
91
|
+
const expectedContent = handler.generateConfigContent()
|
|
92
|
+
const isUnmodified = contentMatches(filepath, expectedContent)
|
|
93
|
+
|
|
94
|
+
if (!isUnmodified) {
|
|
95
|
+
console.log(` ⚠️ ${filename} has been modified - skipping for safety`)
|
|
96
|
+
console.log(' Remove manually if you want to delete it')
|
|
97
|
+
return false
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (dryRun) {
|
|
101
|
+
console.log(` 🗑️ Would remove: ${filename}`)
|
|
102
|
+
return true
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
rmSync(filepath)
|
|
107
|
+
console.log(` ✅ Removed: ${filename}`)
|
|
108
|
+
return true
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.error(` ❌ Failed to remove ${filename}:`, error.message)
|
|
111
|
+
return false
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Uninstall CI/CD configuration
|
|
117
|
+
* @param {string} provider - CI provider (gitlab, github, bitbucket)
|
|
118
|
+
* @param {string} cwd - Current working directory
|
|
119
|
+
* @param {boolean} dryRun - Preview mode
|
|
120
|
+
* @returns {boolean} True if uninstalled successfully
|
|
121
|
+
*/
|
|
122
|
+
export function uninstallCIConfig(provider, cwd, dryRun = false) {
|
|
123
|
+
const ciPaths = {
|
|
124
|
+
gitlab: '.gitlab-ci.yml',
|
|
125
|
+
github: '.github/workflows/ci.yml',
|
|
126
|
+
bitbucket: 'bitbucket-pipelines.yml'
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const ciPath = ciPaths[provider]
|
|
130
|
+
if (!ciPath) {
|
|
131
|
+
console.log(` ⚠️ Unknown provider: ${provider}`)
|
|
132
|
+
return false
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const filepath = join(cwd, ciPath)
|
|
136
|
+
|
|
137
|
+
if (!existsSync(filepath)) {
|
|
138
|
+
console.log(` ℹ️ ${ciPath} not found - already uninstalled`)
|
|
139
|
+
return false
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (dryRun) {
|
|
143
|
+
console.log(` 🗑️ Would remove: ${ciPath}`)
|
|
144
|
+
if (provider === 'github') {
|
|
145
|
+
console.log(` 🗑️ Would clean up empty directories if needed`)
|
|
146
|
+
}
|
|
147
|
+
return true
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
rmSync(filepath)
|
|
152
|
+
console.log(` ✅ Removed: ${ciPath}`)
|
|
153
|
+
|
|
154
|
+
// Clean up empty directories for GitHub
|
|
155
|
+
if (provider === 'github') {
|
|
156
|
+
cleanupGitHubDirectories(cwd)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return true
|
|
160
|
+
} catch (error) {
|
|
161
|
+
console.error(` ❌ Failed to remove ${ciPath}:`, error.message)
|
|
162
|
+
return false
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Clean up empty GitHub directories
|
|
168
|
+
* @param {string} cwd - Current working directory
|
|
169
|
+
*/
|
|
170
|
+
function cleanupGitHubDirectories(cwd) {
|
|
171
|
+
const workflowDir = join(cwd, '.github/workflows')
|
|
172
|
+
const githubDir = join(cwd, '.github')
|
|
173
|
+
|
|
174
|
+
// Remove .github/workflows if empty
|
|
175
|
+
if (existsSync(workflowDir)) {
|
|
176
|
+
try {
|
|
177
|
+
const files = readdirSync(workflowDir)
|
|
178
|
+
if (files.length === 0) {
|
|
179
|
+
rmSync(workflowDir, { recursive: true })
|
|
180
|
+
console.log(` ✅ Removed empty directory: .github/workflows`)
|
|
181
|
+
}
|
|
182
|
+
} catch {
|
|
183
|
+
// Ignore cleanup errors
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Remove .github if empty
|
|
188
|
+
if (existsSync(githubDir)) {
|
|
189
|
+
try {
|
|
190
|
+
const files = readdirSync(githubDir)
|
|
191
|
+
if (files.length === 0) {
|
|
192
|
+
rmSync(githubDir, { recursive: true })
|
|
193
|
+
console.log(` ✅ Removed empty directory: .github`)
|
|
194
|
+
}
|
|
195
|
+
} catch {
|
|
196
|
+
// Ignore cleanup errors
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
package/bin/setup-tool-config.js
CHANGED
|
@@ -16,15 +16,23 @@ import * as prettierHandler from './lib/handlers/prettier.js'
|
|
|
16
16
|
import * as semanticReleaseHandler from './lib/handlers/semantic-release.js'
|
|
17
17
|
import * as stylelintHandler from './lib/handlers/stylelint.js'
|
|
18
18
|
import * as typescriptHandler from './lib/handlers/typescript.js'
|
|
19
|
-
import { updatePackageJsonScripts } from './lib/package-manager.js'
|
|
19
|
+
import { removePackageJsonScripts, updatePackageJsonScripts } from './lib/package-manager.js'
|
|
20
20
|
import {
|
|
21
21
|
showBanner,
|
|
22
22
|
showCICompletion,
|
|
23
23
|
showCompletion,
|
|
24
24
|
showHelp,
|
|
25
25
|
showSection,
|
|
26
|
+
showUninstallBanner,
|
|
27
|
+
showUninstallCompletion,
|
|
26
28
|
showVersion
|
|
27
29
|
} from './lib/ui.js'
|
|
30
|
+
import {
|
|
31
|
+
detectInstalledCI,
|
|
32
|
+
detectInstalledTools,
|
|
33
|
+
uninstallCIConfig,
|
|
34
|
+
uninstallTool
|
|
35
|
+
} from './lib/uninstall.js'
|
|
28
36
|
import { VALID_TOOLS, validateProvider, validateTool } from './lib/validators.js'
|
|
29
37
|
|
|
30
38
|
// Tool handler registry
|
|
@@ -46,27 +54,59 @@ function getToolScripts(tool, detected) {
|
|
|
46
54
|
|
|
47
55
|
/**
|
|
48
56
|
* Setup tools
|
|
57
|
+
* @returns {Object} Summary of created files and scripts
|
|
49
58
|
*/
|
|
50
59
|
async function setupTools(tools, detected, cwd, dryRun = false) {
|
|
51
|
-
|
|
60
|
+
const { createProgress, SYMBOLS, colors } = await import('./lib/formatting.js')
|
|
61
|
+
|
|
62
|
+
console.log('')
|
|
63
|
+
console.log('Creating configuration files...')
|
|
64
|
+
console.log('')
|
|
52
65
|
|
|
53
66
|
// Track which tools successfully created configs
|
|
54
67
|
const successfulTools = []
|
|
68
|
+
const createdFiles = []
|
|
55
69
|
|
|
70
|
+
let current = 0
|
|
56
71
|
for (const tool of tools) {
|
|
57
|
-
|
|
72
|
+
current++
|
|
58
73
|
const handler = TOOL_HANDLERS[tool]
|
|
74
|
+
const filename = handler.getConfigFilename()
|
|
75
|
+
|
|
76
|
+
const progressLine = colors.dim(createProgress(current, tools.length, filename))
|
|
77
|
+
process.stdout.write(progressLine)
|
|
78
|
+
|
|
59
79
|
const success = await handler.writeConfig(cwd, detected, dryRun)
|
|
80
|
+
|
|
60
81
|
if (success) {
|
|
61
82
|
successfulTools.push(tool)
|
|
83
|
+
createdFiles.push(filename)
|
|
84
|
+
console.log(` ${SYMBOLS.success}`)
|
|
85
|
+
} else {
|
|
86
|
+
console.log(` ${SYMBOLS.warning}`)
|
|
62
87
|
}
|
|
63
88
|
}
|
|
64
89
|
|
|
65
90
|
// Only add scripts for tools that created configs
|
|
91
|
+
const addedScripts = []
|
|
66
92
|
if (successfulTools.length > 0) {
|
|
67
|
-
|
|
93
|
+
console.log('')
|
|
94
|
+
console.log('Adding npm scripts...')
|
|
95
|
+
console.log('')
|
|
96
|
+
|
|
97
|
+
// Collect all scripts that will be added
|
|
98
|
+
for (const tool of successfulTools) {
|
|
99
|
+
const scripts = getToolScripts(tool, detected)
|
|
100
|
+
addedScripts.push(...Object.keys(scripts))
|
|
101
|
+
}
|
|
102
|
+
|
|
68
103
|
updatePackageJsonScripts(successfulTools, getToolScripts, { detected, cwd, dryRun })
|
|
69
104
|
}
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
files: createdFiles,
|
|
108
|
+
scripts: addedScripts
|
|
109
|
+
}
|
|
70
110
|
}
|
|
71
111
|
|
|
72
112
|
/**
|
|
@@ -97,9 +137,10 @@ async function setupCI(detected, cwd, dryRun, ciProvider = null) {
|
|
|
97
137
|
|
|
98
138
|
// If provider not detected or unknown, prompt for it
|
|
99
139
|
if (!provider || provider === 'unknown') {
|
|
140
|
+
const { SYMBOLS } = await import('./lib/formatting.js')
|
|
100
141
|
provider = await promptForProvider()
|
|
101
142
|
if (!provider) {
|
|
102
|
-
console.log(
|
|
143
|
+
console.log(`${SYMBOLS.warning} Skipping CI/CD setup.`)
|
|
103
144
|
console.log('')
|
|
104
145
|
return
|
|
105
146
|
}
|
|
@@ -111,6 +152,32 @@ async function setupCI(detected, cwd, dryRun, ciProvider = null) {
|
|
|
111
152
|
showCICompletion(dryRun)
|
|
112
153
|
}
|
|
113
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Uninstall tools
|
|
157
|
+
*/
|
|
158
|
+
function uninstallTools(tools, detected, cwd, dryRun = false) {
|
|
159
|
+
showSection('🗑️ Removing configuration files...')
|
|
160
|
+
|
|
161
|
+
let removedCount = 0
|
|
162
|
+
|
|
163
|
+
for (const tool of tools) {
|
|
164
|
+
console.log(`${tool}:`)
|
|
165
|
+
const handler = TOOL_HANDLERS[tool]
|
|
166
|
+
const success = uninstallTool(tool, handler, cwd, dryRun)
|
|
167
|
+
if (success) {
|
|
168
|
+
removedCount++
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Remove scripts for successfully uninstalled tools
|
|
173
|
+
if (tools.length > 0) {
|
|
174
|
+
showSection('📦 Removing package.json scripts...')
|
|
175
|
+
removePackageJsonScripts(tools, getToolScripts, { detected, cwd, dryRun })
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return removedCount
|
|
179
|
+
}
|
|
180
|
+
|
|
114
181
|
/**
|
|
115
182
|
* Main CLI function
|
|
116
183
|
*/
|
|
@@ -122,7 +189,8 @@ async function main() {
|
|
|
122
189
|
version: args.includes('--version') || args.includes('-v'),
|
|
123
190
|
dryRun: args.includes('--dry-run') || args.includes('-d'),
|
|
124
191
|
all: args.includes('--all') || args.includes('-a'),
|
|
125
|
-
setupCI: args.includes('--setup-ci')
|
|
192
|
+
setupCI: args.includes('--setup-ci'),
|
|
193
|
+
uninstall: args.includes('--uninstall')
|
|
126
194
|
}
|
|
127
195
|
|
|
128
196
|
// Check for --ci flag with optional provider
|
|
@@ -149,6 +217,102 @@ async function main() {
|
|
|
149
217
|
|
|
150
218
|
const cwd = process.cwd()
|
|
151
219
|
|
|
220
|
+
// Handle uninstall mode
|
|
221
|
+
if (flags.uninstall) {
|
|
222
|
+
const detected = autoDetect(cwd)
|
|
223
|
+
const installedTools = detectInstalledTools(cwd)
|
|
224
|
+
const installedCI = detectInstalledCI(cwd)
|
|
225
|
+
|
|
226
|
+
showUninstallBanner(installedTools, installedCI)
|
|
227
|
+
|
|
228
|
+
// No tools or CI detected
|
|
229
|
+
if (installedTools.length === 0 && !installedCI) {
|
|
230
|
+
console.log('Nothing to uninstall.')
|
|
231
|
+
process.exit(0)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Handle --uninstall --ci flag
|
|
235
|
+
if (hasCI) {
|
|
236
|
+
if (!installedCI) {
|
|
237
|
+
console.log('⚠️ No CI/CD configuration detected')
|
|
238
|
+
process.exit(0)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
showSection('🗑️ Removing CI/CD configuration...')
|
|
242
|
+
const success = uninstallCIConfig(installedCI, cwd, flags.dryRun)
|
|
243
|
+
showUninstallCompletion(flags.dryRun, success ? 1 : 0)
|
|
244
|
+
process.exit(0)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Handle --uninstall --all flag
|
|
248
|
+
if (flags.all) {
|
|
249
|
+
if (installedTools.length === 0) {
|
|
250
|
+
console.log('⚠️ No tools detected to uninstall')
|
|
251
|
+
process.exit(0)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const removedCount = uninstallTools(installedTools, detected, cwd, flags.dryRun)
|
|
255
|
+
showUninstallCompletion(flags.dryRun, removedCount)
|
|
256
|
+
process.exit(0)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Handle --uninstall <tool> flag
|
|
260
|
+
if (singleTool) {
|
|
261
|
+
if (!installedTools.includes(singleTool)) {
|
|
262
|
+
console.log(`⚠️ ${singleTool} is not installed`)
|
|
263
|
+
process.exit(0)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const removedCount = uninstallTools([singleTool], detected, cwd, flags.dryRun)
|
|
267
|
+
showUninstallCompletion(flags.dryRun, removedCount)
|
|
268
|
+
process.exit(0)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Interactive uninstall mode
|
|
272
|
+
if (installedTools.length === 0) {
|
|
273
|
+
console.log('⚠️ No tools detected to uninstall')
|
|
274
|
+
process.exit(0)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const response = await prompts({
|
|
278
|
+
type: 'multiselect',
|
|
279
|
+
name: 'tools',
|
|
280
|
+
message: 'Which tools would you like to uninstall?',
|
|
281
|
+
choices: installedTools.map(tool => ({
|
|
282
|
+
title: tool,
|
|
283
|
+
value: tool,
|
|
284
|
+
selected: false
|
|
285
|
+
})),
|
|
286
|
+
hint: '- Space to select. Return to submit'
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
if (!response.tools || response.tools.length === 0) {
|
|
290
|
+
console.log('')
|
|
291
|
+
console.log('❌ No tools selected. Exiting.')
|
|
292
|
+
process.exit(0)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const removedCount = uninstallTools(response.tools, detected, cwd, flags.dryRun)
|
|
296
|
+
|
|
297
|
+
// Ask about CI removal if detected
|
|
298
|
+
if (installedCI) {
|
|
299
|
+
const ciResponse = await prompts({
|
|
300
|
+
type: 'confirm',
|
|
301
|
+
name: 'removeCI',
|
|
302
|
+
message: `Remove ${installedCI} CI/CD configuration?`,
|
|
303
|
+
initial: false
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
if (ciResponse.removeCI) {
|
|
307
|
+
showSection('🗑️ Removing CI/CD configuration...')
|
|
308
|
+
uninstallCIConfig(installedCI, cwd, flags.dryRun)
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
showUninstallCompletion(flags.dryRun, removedCount)
|
|
313
|
+
process.exit(0)
|
|
314
|
+
}
|
|
315
|
+
|
|
152
316
|
// Show banner and auto-detect
|
|
153
317
|
const detected = autoDetect(cwd)
|
|
154
318
|
showBanner(detected)
|
|
@@ -168,22 +332,22 @@ async function main() {
|
|
|
168
332
|
|
|
169
333
|
// Handle --all flag
|
|
170
334
|
if (flags.all) {
|
|
171
|
-
await setupTools(VALID_TOOLS, detected, cwd, flags.dryRun)
|
|
172
|
-
showCompletion(flags.dryRun)
|
|
335
|
+
const summary = await setupTools(VALID_TOOLS, detected, cwd, flags.dryRun)
|
|
336
|
+
showCompletion(flags.dryRun, summary.files, summary.scripts)
|
|
173
337
|
process.exit(0)
|
|
174
338
|
}
|
|
175
339
|
|
|
176
340
|
// Handle single tool mode
|
|
177
341
|
if (singleTool) {
|
|
178
342
|
validateTool(singleTool)
|
|
179
|
-
await setupTools([singleTool], detected, cwd, flags.dryRun)
|
|
343
|
+
const summary = await setupTools([singleTool], detected, cwd, flags.dryRun)
|
|
180
344
|
|
|
181
345
|
// If semantic-release + --ci flag, setup CI
|
|
182
346
|
if (singleTool === 'semantic-release' && hasCI) {
|
|
183
347
|
await setupCI(detected, cwd, flags.dryRun, ciProvider)
|
|
184
348
|
}
|
|
185
349
|
|
|
186
|
-
showCompletion(flags.dryRun)
|
|
350
|
+
showCompletion(flags.dryRun, summary.files, summary.scripts)
|
|
187
351
|
process.exit(0)
|
|
188
352
|
}
|
|
189
353
|
|
|
@@ -192,23 +356,35 @@ async function main() {
|
|
|
192
356
|
{
|
|
193
357
|
type: 'multiselect',
|
|
194
358
|
name: 'tools',
|
|
195
|
-
message: '
|
|
359
|
+
message: 'Select development tools to configure:',
|
|
196
360
|
choices: [
|
|
197
|
-
{ title: 'ESLint (JavaScript/TypeScript linting)', value: 'eslint', selected: true },
|
|
198
|
-
{ title: 'Prettier (Code formatting)', value: 'prettier', selected: true },
|
|
199
|
-
{ title: 'Stylelint (CSS/style linting)', value: 'stylelint', selected: false },
|
|
200
361
|
{
|
|
201
|
-
title: '
|
|
362
|
+
title: 'ESLint Catch bugs and enforce standards',
|
|
363
|
+
value: 'eslint',
|
|
364
|
+
selected: true
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
title: 'Prettier Auto-format code consistently',
|
|
368
|
+
value: 'prettier',
|
|
369
|
+
selected: true
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
title: 'Stylelint Lint CSS, SCSS, and style files',
|
|
373
|
+
value: 'stylelint',
|
|
374
|
+
selected: false
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
title: 'TypeScript Type safety and better tooling',
|
|
202
378
|
value: 'typescript',
|
|
203
379
|
selected: detected.typescript
|
|
204
380
|
},
|
|
205
381
|
{
|
|
206
|
-
title: 'semantic-release
|
|
382
|
+
title: 'semantic-release Automated versioning & releases',
|
|
207
383
|
value: 'semantic-release',
|
|
208
384
|
selected: false
|
|
209
385
|
}
|
|
210
386
|
],
|
|
211
|
-
hint: '
|
|
387
|
+
hint: ' (Recommended tools are pre-selected)'
|
|
212
388
|
}
|
|
213
389
|
])
|
|
214
390
|
|
|
@@ -218,16 +394,18 @@ async function main() {
|
|
|
218
394
|
process.exit(0)
|
|
219
395
|
}
|
|
220
396
|
|
|
221
|
-
await setupTools(response.tools, detected, cwd, flags.dryRun)
|
|
397
|
+
const summary = await setupTools(response.tools, detected, cwd, flags.dryRun)
|
|
222
398
|
|
|
223
399
|
// If semantic-release selected, prompt for CI setup
|
|
224
400
|
if (response.tools.includes('semantic-release')) {
|
|
225
|
-
|
|
401
|
+
console.log('')
|
|
402
|
+
console.log('🚀 semantic-release needs CI/CD to automate releases')
|
|
403
|
+
console.log('')
|
|
226
404
|
|
|
227
405
|
const ciResponse = await prompts({
|
|
228
406
|
type: 'confirm',
|
|
229
407
|
name: 'setupCI',
|
|
230
|
-
message: 'Setup CI/CD pipeline
|
|
408
|
+
message: 'Setup CI/CD pipeline now?',
|
|
231
409
|
initial: true
|
|
232
410
|
})
|
|
233
411
|
|
|
@@ -252,7 +430,7 @@ async function main() {
|
|
|
252
430
|
}
|
|
253
431
|
}
|
|
254
432
|
|
|
255
|
-
showCompletion(flags.dryRun)
|
|
433
|
+
showCompletion(flags.dryRun, summary.files, summary.scripts)
|
|
256
434
|
}
|
|
257
435
|
|
|
258
436
|
// Run CLI
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dimensional-innovations/tool-config",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Universal configuration package for ESLint, Prettier, Stylelint, TypeScript, and semantic-release with auto-detection for React, Vue, Svelte, Solid, Astro, Angular, and more",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -81,6 +81,7 @@
|
|
|
81
81
|
"eslint-plugin-svelte": "^3.12.4",
|
|
82
82
|
"eslint-plugin-vue": "^10.5.0",
|
|
83
83
|
"globals": "^16.4.0",
|
|
84
|
+
"picocolors": "^1.1.1",
|
|
84
85
|
"postcss": "^8.4.49",
|
|
85
86
|
"postcss-html": "^1.8.0",
|
|
86
87
|
"postcss-scss": "^4.0.9",
|