@dimensional-innovations/tool-config 2.0.0 → 3.0.1

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/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
- console.log('đŸ“Ļ @dimensional-innovations/tool-config Setup')
61
- console.log('━'.repeat(50))
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('🔍 Detected project configuration:')
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('â„šī¸ Dry run mode - no files were modified')
88
- console.log('Run without --dry-run to apply changes')
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('✅ Setup complete!')
131
+ console.log(createSeparator())
91
132
  console.log('')
92
- console.log('Next steps:')
93
- console.log(' 1. Review the generated config files')
94
- console.log(' 2. Install peer dependencies if needed')
95
- console.log(' 3. Run: npm run lint (or other added scripts)')
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('â„šī¸ Dry run mode - no files were modified')
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('✅ CI/CD setup complete!')
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
+ }