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.
- package/.claude/settings.local.json +2 -1
- package/lib/commands/publish.js +182 -70
- package/package.json +1 -1
package/lib/commands/publish.js
CHANGED
|
@@ -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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 ${
|
|
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
|
-
? { ...
|
|
400
|
-
: transformPackageExports({ ...
|
|
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
|
-
|
|
408
|
-
|
|
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
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 ${
|
|
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)
|