itty-packager 1.3.0 โ†’ 1.6.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.
@@ -3,7 +3,8 @@
3
3
  "allow": [
4
4
  "Bash(bun bin/itty.js lint:*)",
5
5
  "Bash(bun bin/itty.js prepare:*)",
6
- "Bash(bun bin/itty.js publish:*)"
6
+ "Bash(bun bin/itty.js publish:*)",
7
+ "Bash(cat:*)"
7
8
  ],
8
9
  "deny": []
9
10
  }
@@ -2,7 +2,6 @@ import { parseArgs } from 'node:util'
2
2
  import { spawn } from 'node:child_process'
3
3
  import fs from 'fs-extra'
4
4
  import path from 'node:path'
5
- import readline from 'node:readline'
6
5
  import { prepareCommand } from './prepare.js'
7
6
 
8
7
  const SEMVER_TYPES = ['major', 'minor', 'patch']
@@ -79,41 +78,123 @@ async function getCommitMessage(newVersion, silent = false) {
79
78
  return `released v${newVersion}`
80
79
  }
81
80
 
82
- return new Promise((resolve) => {
83
- const rl = readline.createInterface({
84
- input: process.stdin,
85
- output: process.stdout
86
- })
87
-
88
- console.log('\nEnter optional commit message (empty submission skips):')
89
- console.log('\x1b[90mPress Enter to finish, Ctrl+C to skip\x1b[0m')
90
- process.stdout.write('\n')
91
-
81
+ return new Promise((resolve, reject) => {
82
+ // Show placeholder with hidden cursor
83
+ const placeholderText = '\x1b[90mpress enter to skip\x1b[0m'
84
+ process.stdout.write(`๐Ÿ’ฌ Commit message: ${placeholderText}\x1b[?25l`) // Hide cursor
85
+
92
86
  let inputLines = []
93
87
  let firstInput = true
88
+ let placeholderCleared = false
89
+ let inputBuffer = ''
90
+
91
+ // Set up raw mode for immediate key detection
92
+ process.stdin.setRawMode(true)
93
+ process.stdin.resume()
94
+
95
+ const clearPlaceholder = () => {
96
+ if (!placeholderCleared) {
97
+ // Clear line and show prompt with cursor
98
+ process.stdout.write('\r\x1b[K๐Ÿ’ฌ Commit message: \x1b[?25h') // Show cursor
99
+ placeholderCleared = true
100
+ }
101
+ }
94
102
 
95
- rl.on('line', (input) => {
96
- if (firstInput && input.trim() === '') {
97
- // First line is empty, skip custom message
98
- rl.close()
99
- resolve(`released v${newVersion}`)
103
+ const handleInput = (chunk) => {
104
+ const key = chunk.toString()
105
+
106
+ // Check for escape sequences
107
+ if (key === '\x1b') {
108
+ // Wait for potential escape sequence completion
109
+ setTimeout(() => {
110
+ process.stdout.write('\r\x1b[K๐Ÿ’ฌ Commit message: cancelled\x1b[?25h\n')
111
+ cleanup()
112
+ reject(new Error('User cancelled with Escape key'))
113
+ }, 10)
100
114
  return
101
115
  }
102
116
 
103
- firstInput = false
117
+ // Handle Ctrl+C
118
+ if (key === '\x03') {
119
+ process.stdout.write('\r\x1b[K๐Ÿ’ฌ Commit message: cancelled\x1b[?25h\n')
120
+ cleanup()
121
+ reject(new Error('User cancelled with Ctrl+C'))
122
+ return
123
+ }
124
+
125
+ // Handle Enter
126
+ if (key === '\r' || key === '\n') {
127
+ if (!placeholderCleared && inputBuffer === '') {
128
+ // Empty input, skip
129
+ process.stdout.write('\r\x1b[K๐Ÿ’ฌ Commit message: \x1b[90mskipped\x1b[0m\x1b[?25h\n')
130
+ cleanup()
131
+ resolve(`released v${newVersion}`)
132
+ return
133
+ }
134
+
135
+ // Single line input - finish immediately
136
+ if (firstInput) {
137
+ const customMessage = inputBuffer.trim()
138
+ process.stdout.write('\x1b[?25h\n')
139
+ cleanup()
140
+
141
+ if (!customMessage) {
142
+ process.stdout.write('\r๐Ÿ’ฌ Commit message: \x1b[90mskipped\x1b[0m\n')
143
+ resolve(`released v${newVersion}`)
144
+ } else {
145
+ const escapedMessage = customMessage.replace(/"/g, '\\"')
146
+ resolve(`released v${newVersion} - ${escapedMessage}`)
147
+ }
148
+ return
149
+ }
150
+
151
+ // Multi-line: empty line finishes input
152
+ if (inputBuffer.trim() === '') {
153
+ finishInput()
154
+ return
155
+ }
156
+
157
+ // Add line and continue
158
+ inputLines.push(inputBuffer)
159
+ inputBuffer = ''
160
+ firstInput = false
161
+ process.stdout.write('\n')
162
+ return
163
+ }
104
164
 
105
- if (input.trim() === '' && inputLines.length > 0) {
106
- // Empty line after content - finish input
107
- finishInput()
165
+ // Handle backspace
166
+ if (key === '\x7f' || key === '\x08') {
167
+ if (!placeholderCleared) return // Can't backspace in placeholder
168
+
169
+ if (inputBuffer.length > 0) {
170
+ inputBuffer = inputBuffer.slice(0, -1)
171
+ process.stdout.write('\b \b')
172
+ }
108
173
  return
109
174
  }
110
175
 
111
- inputLines.push(input)
112
- })
176
+ // Handle printable characters
177
+ if (key.length === 1 && key >= ' ' && key <= '~') {
178
+ if (!placeholderCleared) {
179
+ clearPlaceholder()
180
+ }
181
+
182
+ inputBuffer += key
183
+ process.stdout.write(key)
184
+ return
185
+ }
186
+ }
187
+
188
+ const cleanup = () => {
189
+ process.stdin.setRawMode(false)
190
+ process.stdin.pause()
191
+ process.stdin.removeListener('data', handleInput)
192
+ }
113
193
 
114
194
  const finishInput = () => {
115
- rl.close()
116
195
  const customMessage = inputLines.join('\n').trim()
196
+ process.stdout.write('\x1b[?25h\n') // Show cursor and newline
197
+ cleanup()
117
198
 
118
199
  if (!customMessage) {
119
200
  resolve(`released v${newVersion}`)
@@ -124,18 +205,7 @@ async function getCommitMessage(newVersion, silent = false) {
124
205
  }
125
206
  }
126
207
 
127
- rl.on('SIGINT', () => {
128
- console.log('\nSkipped. Using default commit message.')
129
- rl.close()
130
- resolve(`released v${newVersion}`)
131
- })
132
-
133
- // Handle Ctrl+D (EOF) as completion
134
- rl.on('close', () => {
135
- if (!firstInput && inputLines.length > 0) {
136
- finishInput()
137
- }
138
- })
208
+ process.stdin.on('data', handleInput)
139
209
  })
140
210
  }
141
211
 
@@ -283,10 +353,14 @@ Publish Options:
283
353
 
284
354
  Git Options:
285
355
  --tag Create git tag for release
286
- --push Push changes and tags to git remote
356
+ --push Push changes and tags to git remote (prompts for commit message)
287
357
  --no-git Skip all git operations
288
358
  -h, --help Show help
289
359
 
360
+ Interactive Options:
361
+ When using --push, you'll be prompted for an optional commit message.
362
+ Press Enter to skip, Escape or Ctrl+C to cancel and revert version.
363
+
290
364
  Examples:
291
365
  itty publish # Patch version bump and publish from dist/ (default)
292
366
  itty publish --minor --tag # Minor bump, publish, and create git tag
@@ -327,6 +401,12 @@ This creates a clean, flat package structure in node_modules.
327
401
  const silent = publishArgs.silent
328
402
  const verbose = publishArgs.verbose
329
403
 
404
+ // Read package.json and store original version for potential revert
405
+ const pkgPath = path.join(rootPath, 'package.json')
406
+ const originalPkg = await fs.readJSON(pkgPath)
407
+ const originalVersion = originalPkg.version
408
+ const newVersion = versionBump(originalVersion, releaseType)
409
+
330
410
  try {
331
411
  // Run prepare if requested
332
412
  if (shouldPrepare) {
@@ -334,12 +414,8 @@ This creates a clean, flat package structure in node_modules.
334
414
  await prepareCommand(verbose ? ['--verbose'] : [])
335
415
  console.log('โœ… Prepare completed successfully\n')
336
416
  }
337
- // Read package.json
338
- const pkgPath = path.join(rootPath, 'package.json')
339
- const pkg = await fs.readJSON(pkgPath)
340
- const newVersion = versionBump(pkg.version, releaseType)
341
417
 
342
- console.log(`๐Ÿ“ฆ Publishing ${pkg.name} v${pkg.version} โ†’ v${newVersion}`)
418
+ console.log(`๐Ÿ“ฆ Publishing ${originalPkg.name} v${originalVersion} โ†’ v${newVersion}`)
343
419
  if (verbose) console.log(`๐Ÿ“ Source: ${publishArgs.src}/`)
344
420
 
345
421
  // Check if source directory exists
@@ -353,6 +429,7 @@ This creates a clean, flat package structure in node_modules.
353
429
  await fs.ensureDir(tempDir)
354
430
 
355
431
  // Copy source files to temp directory
432
+ console.log(`๐Ÿ“‹ Copying files...`)
356
433
  if (verbose) console.log(`๐Ÿ“‹ Copying ${publishArgs.src}/ to ${path.relative(rootPath, tempDir)}/`)
357
434
 
358
435
  const filter = (src) => {
@@ -372,6 +449,7 @@ This creates a clean, flat package structure in node_modules.
372
449
  }
373
450
 
374
451
  await fs.copy(srcDir, tempDir, { filter })
452
+ if (!verbose) console.log(`โœ… Files copied`)
375
453
 
376
454
  // Copy root files that should be included in the package (only for non-root publishing)
377
455
  if (!isRootPublish) {
@@ -396,72 +474,106 @@ This creates a clean, flat package structure in node_modules.
396
474
 
397
475
  // Update package.json in temp directory with transformed paths
398
476
  const updatedPkg = isRootPublish
399
- ? { ...pkg, version: newVersion } // No path transformation for root publishing
400
- : transformPackageExports({ ...pkg, version: newVersion }, publishArgs.src)
477
+ ? { ...originalPkg, version: newVersion } // No path transformation for root publishing
478
+ : transformPackageExports({ ...originalPkg, version: newVersion }, publishArgs.src)
401
479
  const tempPkgPath = path.join(tempDir, 'package.json')
402
480
 
403
481
  const transformMessage = isRootPublish ? '' : ' (transforming paths)'
404
482
  if (verbose) console.log(`๐Ÿ“ Updating package.json to v${newVersion}${transformMessage}`)
405
483
  await fs.writeJSON(tempPkgPath, updatedPkg, { spaces: 2 })
406
484
 
407
- if (dryRun) {
408
- console.log('๐Ÿงช Dry run - skipping publish')
409
- } else {
410
- // Publish from temp directory
411
- console.log(`๐Ÿš€ Publishing to npm...`)
412
-
413
- const publishCmd = [
414
- 'npm publish',
415
- publicAccess ? '--access=public' : '',
416
- SEMVER_TYPES.includes(releaseType) ? '' : `--tag=${releaseType}`
417
- ].filter(Boolean).join(' ')
418
-
419
- if (verbose) console.log(`Running: ${publishCmd}`)
420
- await runCommand(publishCmd, tempDir, verbose)
421
-
422
- // Update root package.json
485
+ // Update root package.json first (before git operations)
486
+ if (!dryRun) {
423
487
  if (verbose) console.log(`๐Ÿ“ Updating root package.json`)
424
488
  await fs.writeJSON(pkgPath, updatedPkg, { spaces: 2 })
425
489
  }
426
490
 
427
- // Git operations
491
+ // Git operations (before publishing)
428
492
  if (!noGit && !dryRun) {
429
493
  if (shouldPush || shouldTag) {
430
- // Get commit message (interactive or default)
431
- const commitMessage = await getCommitMessage(newVersion, silent)
432
-
433
- if (verbose) console.log(`๐Ÿ“‹ Committing changes...`)
434
- await runCommand('git add .', rootPath, verbose)
435
- await runCommand(`git commit -m "${commitMessage}"`, rootPath, verbose)
494
+ try {
495
+ // Get commit message (interactive or default)
496
+ const commitMessage = await getCommitMessage(newVersion, silent)
497
+
498
+ console.log(`๐Ÿ“‹ Committing changes...`)
499
+ if (verbose) console.log(`Running: git add . && git commit`)
500
+ await runCommand('git add .', rootPath, verbose)
501
+ await runCommand(`git commit -m "${commitMessage}"`, rootPath, verbose)
502
+ if (!verbose) console.log(`โœ… Changes committed`)
503
+ } catch (error) {
504
+ if (error.message.includes('cancelled')) {
505
+ console.log('๐Ÿ“‹ Commit cancelled - reverting version and exiting')
506
+ // Revert the version we just updated
507
+ await fs.writeJSON(pkgPath, originalPkg, { spaces: 2 })
508
+ // Don't rethrow - exit cleanly since this is user-initiated
509
+ process.exit(0)
510
+ }
511
+ throw error
512
+ }
436
513
  }
437
514
 
438
515
  if (shouldTag) {
439
- if (verbose) console.log(`๐Ÿท๏ธ Creating git tag v${newVersion}`)
516
+ console.log(`๐Ÿท๏ธ Creating git tag v${newVersion}`)
440
517
  await runCommand(`git tag -a v${newVersion} -m "Release v${newVersion}"`, rootPath, verbose)
518
+ if (!verbose) console.log(`โœ… Tag created`)
441
519
  }
442
520
 
443
521
  if (shouldPush) {
444
- if (verbose) console.log(`๐Ÿ“ค Pushing to remote...`)
522
+ console.log(`๐Ÿ“ค Pushing to remote...`)
445
523
  await runCommand('git push', rootPath, verbose)
524
+ if (!verbose) console.log(`โœ… Pushed to remote`)
446
525
 
447
526
  if (shouldTag) {
448
- if (verbose) console.log(`๐Ÿ“ค Pushing tags...`)
527
+ console.log(`๐Ÿ“ค Pushing tags...`)
449
528
  await runCommand('git push --tags', rootPath, verbose)
529
+ if (!verbose) console.log(`โœ… Tags pushed`)
450
530
  }
451
531
  }
452
532
  }
453
533
 
534
+ // NPM publish as final step
535
+ if (dryRun) {
536
+ console.log('๐Ÿงช Dry run - skipping publish')
537
+ } else {
538
+ // Publish from temp directory
539
+ console.log(`๐Ÿš€ Publishing to npm...`)
540
+
541
+ const publishCmd = [
542
+ 'npm publish',
543
+ publicAccess ? '--access=public' : '',
544
+ SEMVER_TYPES.includes(releaseType) ? '' : `--tag=${releaseType}`
545
+ ].filter(Boolean).join(' ')
546
+
547
+ if (verbose) console.log(`Running: ${publishCmd}`)
548
+ await runCommand(publishCmd, tempDir, verbose)
549
+ if (!verbose) console.log(`โœ… Published to npm`)
550
+ }
551
+
454
552
  // Cleanup
455
553
  if (!noCleanup) {
456
554
  if (verbose) console.log(`๐Ÿงน Cleaning up ${publishArgs.dest}/`)
457
555
  await fs.remove(tempDir)
458
556
  }
459
557
 
460
- console.log(`โœ… Successfully published ${pkg.name}@${newVersion}`)
558
+ console.log(`โœ… Successfully published ${originalPkg.name}@${newVersion}`)
461
559
 
462
560
  } catch (error) {
463
561
  console.error(`โŒ Publish failed: ${error.message}`)
464
562
 
563
+ // Revert version in root package.json if it was changed
564
+ if (!dryRun) {
565
+ try {
566
+ const currentPkg = await fs.readJSON(pkgPath)
567
+ if (currentPkg.version !== originalVersion) {
568
+ if (verbose) console.log(`๐Ÿ”„ Reverting version from v${currentPkg.version} to v${originalVersion}`)
569
+ await fs.writeJSON(pkgPath, { ...currentPkg, version: originalVersion }, { spaces: 2 })
570
+ console.log(`โœ… Version reverted to v${originalVersion}`)
571
+ }
572
+ } catch (revertError) {
573
+ console.error(`โŒ Failed to revert version: ${revertError.message}`)
574
+ }
575
+ }
576
+
465
577
  // Cleanup on error
466
578
  if (await fs.pathExists(tempDir) && !noCleanup) {
467
579
  await fs.remove(tempDir)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "itty-packager",
3
- "version": "1.3.0",
3
+ "version": "1.6.2",
4
4
  "description": "Universal build tool for itty libraries",
5
5
  "type": "module",
6
6
  "bin": {