Skip to content

Commit bf1b018

Browse files
committed
add setup
1 parent ab67f1a commit bf1b018

File tree

9 files changed

+1788
-0
lines changed

9 files changed

+1788
-0
lines changed

EDGE_CASES.md

Lines changed: 464 additions & 0 deletions
Large diffs are not rendered by default.

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@
4343
"types": "./dist/constants.d.ts",
4444
"require": "./dist/constants.js",
4545
"import": "./dist/constants.js"
46+
},
47+
"./package-json": {
48+
"types": "./dist/package-json/index.d.ts",
49+
"require": "./dist/package-json/index.js",
50+
"import": "./dist/package-json/index.js"
4651
}
4752
},
4853
"scripts": {

src/cli.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ import { downloadCommand } from './commands/download.js'
77
import { listCommand } from './commands/list.js'
88
import { removeCommand } from './commands/remove.js'
99
import { gcCommand } from './commands/gc.js'
10+
import { setupCommand } from './commands/setup.js'
1011

1112
async function main(): Promise<void> {
1213
await yargs(hideBin(process.argv))
1314
.scriptName('socket-patch')
1415
.usage('$0 <command> [options]')
1516
.command(applyCommand)
17+
.command(setupCommand)
1618
.command(downloadCommand)
1719
.command(listCommand)
1820
.command(removeCommand)

src/commands/setup.ts

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
import * as path from 'path'
2+
import * as readline from 'readline/promises'
3+
import type { CommandModule } from 'yargs'
4+
import {
5+
findPackageJsonFiles,
6+
updateMultiplePackageJsons,
7+
type UpdateResult,
8+
} from '../package-json/index.js'
9+
10+
interface SetupArgs {
11+
cwd: string
12+
'dry-run': boolean
13+
yes: boolean
14+
}
15+
16+
/**
17+
* Display a preview table of changes
18+
*/
19+
function displayPreview(results: UpdateResult[], cwd: string): void {
20+
console.log('\nPackage.json files to be updated:\n')
21+
22+
const toUpdate = results.filter(r => r.status === 'updated')
23+
const alreadyConfigured = results.filter(
24+
r => r.status === 'already-configured',
25+
)
26+
const errors = results.filter(r => r.status === 'error')
27+
28+
if (toUpdate.length > 0) {
29+
console.log('Will update:')
30+
for (const result of toUpdate) {
31+
const relativePath = path.relative(cwd, result.path)
32+
console.log(` ✓ ${relativePath}`)
33+
if (result.oldScript) {
34+
console.log(` Current: "${result.oldScript}"`)
35+
} else {
36+
console.log(` Current: (no postinstall script)`)
37+
}
38+
console.log(` New: "${result.newScript}"`)
39+
}
40+
console.log()
41+
}
42+
43+
if (alreadyConfigured.length > 0) {
44+
console.log('Already configured (will skip):')
45+
for (const result of alreadyConfigured) {
46+
const relativePath = path.relative(cwd, result.path)
47+
console.log(` ⊘ ${relativePath}`)
48+
}
49+
console.log()
50+
}
51+
52+
if (errors.length > 0) {
53+
console.log('Errors:')
54+
for (const result of errors) {
55+
const relativePath = path.relative(cwd, result.path)
56+
console.log(` ✗ ${relativePath}: ${result.error}`)
57+
}
58+
console.log()
59+
}
60+
}
61+
62+
/**
63+
* Display summary of changes made
64+
*/
65+
function displaySummary(results: UpdateResult[], dryRun: boolean): void {
66+
const updated = results.filter(r => r.status === 'updated')
67+
const alreadyConfigured = results.filter(
68+
r => r.status === 'already-configured',
69+
)
70+
const errors = results.filter(r => r.status === 'error')
71+
72+
console.log('\nSummary:')
73+
console.log(
74+
` ${updated.length} file(s) ${dryRun ? 'would be updated' : 'updated'}`,
75+
)
76+
console.log(` ${alreadyConfigured.length} file(s) already configured`)
77+
if (errors.length > 0) {
78+
console.log(` ${errors.length} error(s)`)
79+
}
80+
}
81+
82+
/**
83+
* Prompt user for confirmation
84+
*/
85+
async function promptConfirmation(): Promise<boolean> {
86+
const rl = readline.createInterface({
87+
input: process.stdin,
88+
output: process.stdout,
89+
})
90+
91+
try {
92+
const answer = await rl.question('Proceed with these changes? (y/N): ')
93+
return answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes'
94+
} finally {
95+
rl.close()
96+
}
97+
}
98+
99+
export const setupCommand: CommandModule<{}, SetupArgs> = {
100+
command: 'setup',
101+
describe: 'Configure package.json postinstall scripts to apply patches',
102+
builder: yargs => {
103+
return yargs
104+
.option('cwd', {
105+
describe: 'Working directory',
106+
type: 'string',
107+
default: process.cwd(),
108+
})
109+
.option('dry-run', {
110+
alias: 'd',
111+
describe: 'Preview changes without modifying files',
112+
type: 'boolean',
113+
default: false,
114+
})
115+
.option('yes', {
116+
alias: 'y',
117+
describe: 'Skip confirmation prompt',
118+
type: 'boolean',
119+
default: false,
120+
})
121+
},
122+
handler: async argv => {
123+
try {
124+
// Find all package.json files
125+
console.log('Searching for package.json files...')
126+
const packageJsonFiles = await findPackageJsonFiles(argv.cwd)
127+
128+
if (packageJsonFiles.length === 0) {
129+
console.log('No package.json files found')
130+
process.exit(0)
131+
}
132+
133+
console.log(`Found ${packageJsonFiles.length} package.json file(s)`)
134+
135+
// Preview changes (dry run to see what would change)
136+
const previewResults = await updateMultiplePackageJsons(
137+
packageJsonFiles.map(p => p.path),
138+
true, // Always preview first
139+
)
140+
141+
// Display preview
142+
displayPreview(previewResults, argv.cwd)
143+
144+
const toUpdate = previewResults.filter(r => r.status === 'updated')
145+
146+
if (toUpdate.length === 0) {
147+
console.log(
148+
'All package.json files are already configured with socket-patch!',
149+
)
150+
process.exit(0)
151+
}
152+
153+
// If not dry-run, ask for confirmation (unless --yes)
154+
if (!argv['dry-run']) {
155+
if (!argv.yes) {
156+
const confirmed = await promptConfirmation()
157+
if (!confirmed) {
158+
console.log('Aborted')
159+
process.exit(0)
160+
}
161+
}
162+
163+
// Apply changes
164+
console.log('\nApplying changes...')
165+
const results = await updateMultiplePackageJsons(
166+
packageJsonFiles.map(p => p.path),
167+
false,
168+
)
169+
170+
displaySummary(results, false)
171+
172+
const errors = results.filter(r => r.status === 'error')
173+
process.exit(errors.length > 0 ? 1 : 0)
174+
} else {
175+
// Dry run mode
176+
displaySummary(previewResults, true)
177+
process.exit(0)
178+
}
179+
} catch (err) {
180+
const errorMessage = err instanceof Error ? err.message : String(err)
181+
console.error(`Error: ${errorMessage}`)
182+
process.exit(1)
183+
}
184+
},
185+
}

0 commit comments

Comments
 (0)