@take-out/scripts 0.0.93 → 0.0.95

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/src/up.ts CHANGED
@@ -1,66 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
- /**
4
- * @description Upgrade packages by name (takeout, tamagui, one, zero, better-auth)
5
- */
6
-
7
- import { existsSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
8
- import { join } from 'node:path'
9
-
10
- import { $ } from 'bun'
11
-
12
- let globalTag: string | undefined
13
- const packagePatterns: string[] = []
14
-
15
- const args = process.argv.slice(2)
16
-
17
- // check if first arg is a named upgrade set
18
- const rootDir = process.cwd()
19
- const rootPackageJson = JSON.parse(readFileSync(join(rootDir, 'package.json'), 'utf-8'))
20
- const upgradeSets: Record<string, string[]> = rootPackageJson.upgradeSets || {}
21
-
22
- for (let i = 0; i < args.length; i++) {
23
- const arg = args[i]!
24
- if (arg.startsWith('--tag=')) {
25
- const tagValue = arg.split('=')[1]
26
- if (tagValue) {
27
- globalTag = tagValue
28
- } else {
29
- console.error('Error: --tag option requires a value. Example: --tag=canary')
30
- process.exit(1)
31
- }
32
- } else if (arg === '--tag') {
33
- console.error('Error: --tag option requires a value and must use = syntax.')
34
- console.error('Correct usage: --tag=canary')
35
- console.error('Example: bun tko up --tag=canary react react-dom')
36
- process.exit(1)
37
- } else if (arg === '--canary') {
38
- globalTag = 'canary'
39
- } else if (arg === '--rc') {
40
- globalTag = 'rc'
41
- } else if (arg in upgradeSets) {
42
- // expand named upgrade set to its patterns
43
- packagePatterns.push(...upgradeSets[arg]!)
44
- } else {
45
- packagePatterns.push(arg)
46
- }
47
- }
48
-
49
- if (packagePatterns.length === 0) {
50
- const setNames = Object.keys(upgradeSets)
51
- if (setNames.length > 0) {
52
- console.info('Usage: bun tko up <target|pattern> [options]')
53
- console.info(`\nAvailable upgrade sets: ${setNames.join(', ')}`)
54
- console.info('\nOr provide package patterns directly:')
55
- console.info(' bun tko up @vxrn/* vxrn')
56
- console.info(' bun tko up --tag=canary react react-dom')
57
- } else {
58
- console.error('Please provide at least one package pattern to update.')
59
- console.error('Example: bun tko up @vxrn/* vxrn')
60
- console.error('Or with a tag: bun tko up --tag=canary @vxrn/* vxrn')
61
- }
62
- process.exit(1)
63
- }
3
+ import { cmd } from './cmd'
64
4
 
65
5
  interface PackageJson {
66
6
  dependencies?: Record<string, string>
@@ -69,383 +9,430 @@ interface PackageJson {
69
9
  optionalDependencies?: Record<string, string>
70
10
  }
71
11
 
72
- function findPackageJsonFiles(dir: string): string[] {
73
- const results: string[] = []
12
+ await cmd`upgrade packages by name or pattern`
13
+ .args('--tag string --canary boolean --rc boolean')
14
+ .run(async ({ args, $, path, fs }) => {
15
+ const { existsSync, readdirSync, readFileSync, writeFileSync, rmSync } =
16
+ await import('node:fs')
17
+
18
+ let globalTag: string | undefined = args.tag
19
+ if (args.canary) globalTag = 'canary'
20
+ if (args.rc) globalTag = 'rc'
21
+
22
+ const packagePatterns: string[] = []
23
+ const rootDir = process.cwd()
24
+ const rootPackageJson = JSON.parse(
25
+ readFileSync(path.join(rootDir, 'package.json'), 'utf-8')
26
+ )
27
+ const upgradeSets: Record<string, string[]> = rootPackageJson.upgradeSets || {}
28
+
29
+ for (const arg of args.rest) {
30
+ if (arg in upgradeSets) {
31
+ // expand named upgrade set to its patterns
32
+ packagePatterns.push(...upgradeSets[arg]!)
33
+ } else {
34
+ packagePatterns.push(arg)
35
+ }
36
+ }
74
37
 
75
- if (existsSync(join(dir, 'package.json'))) {
76
- results.push(join(dir, 'package.json'))
77
- }
38
+ if (packagePatterns.length === 0) {
39
+ const setNames = Object.keys(upgradeSets)
40
+ if (setNames.length > 0) {
41
+ console.info('Usage: bun tko up <target|pattern> [options]')
42
+ console.info(`\nAvailable upgrade sets: ${setNames.join(', ')}`)
43
+ console.info('\nOr provide package patterns directly:')
44
+ console.info(' bun tko up @vxrn/* vxrn')
45
+ console.info(' bun tko up --tag canary react react-dom')
46
+ } else {
47
+ console.error('Please provide at least one package pattern to update.')
48
+ console.error('Example: bun tko up @vxrn/* vxrn')
49
+ console.error('Or with a tag: bun tko up --tag canary @vxrn/* vxrn')
50
+ }
51
+ process.exit(1)
52
+ }
78
53
 
79
- // check if it's a monorepo with workspaces
80
- try {
81
- const packageJson = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf-8'))
82
- if (packageJson.workspaces) {
83
- let workspacePaths: string[] = []
54
+ function findPackageJsonFiles(dir: string): string[] {
55
+ const results: string[] = []
84
56
 
85
- if (Array.isArray(packageJson.workspaces)) {
86
- workspacePaths = packageJson.workspaces
87
- } else if (packageJson.workspaces.packages) {
88
- workspacePaths = packageJson.workspaces.packages
57
+ if (existsSync(path.join(dir, 'package.json'))) {
58
+ results.push(path.join(dir, 'package.json'))
89
59
  }
90
60
 
91
- for (const workspace of workspacePaths) {
92
- // handle glob patterns like "packages/*", "code/**/*", "./code/ui/**/*"
93
- const normalizedWorkspace = workspace.replace(/^\.\//, '')
94
-
95
- if (normalizedWorkspace.includes('**')) {
96
- // nested glob pattern - use glob to find all package.json files
97
- const baseDir = normalizedWorkspace.split('**')[0]!.replace(/\/$/, '')
98
- const basePath = join(dir, baseDir)
99
-
100
- if (existsSync(basePath)) {
101
- const findPackages = (searchDir: string) => {
102
- try {
103
- const entries = readdirSync(searchDir, { withFileTypes: true })
104
- for (const entry of entries) {
105
- if (entry.isDirectory() && entry.name !== 'node_modules') {
106
- const subPath = join(searchDir, entry.name)
107
- const pkgPath = join(subPath, 'package.json')
108
- if (existsSync(pkgPath)) {
109
- results.push(pkgPath)
61
+ // check if it's a monorepo with workspaces
62
+ try {
63
+ const packageJson = JSON.parse(
64
+ readFileSync(path.join(dir, 'package.json'), 'utf-8')
65
+ )
66
+ if (packageJson.workspaces) {
67
+ let workspacePaths: string[] = []
68
+
69
+ if (Array.isArray(packageJson.workspaces)) {
70
+ workspacePaths = packageJson.workspaces
71
+ } else if (packageJson.workspaces.packages) {
72
+ workspacePaths = packageJson.workspaces.packages
73
+ }
74
+
75
+ for (const workspace of workspacePaths) {
76
+ // handle glob patterns like "packages/*", "code/**/*", "./code/ui/**/*"
77
+ const normalizedWorkspace = workspace.replace(/^\.\//, '')
78
+
79
+ if (normalizedWorkspace.includes('**')) {
80
+ // nested glob pattern - use glob to find all package.json files
81
+ const baseDir = normalizedWorkspace.split('**')[0]!.replace(/\/$/, '')
82
+ const basePath = path.join(dir, baseDir)
83
+
84
+ if (existsSync(basePath)) {
85
+ const findPackages = (searchDir: string) => {
86
+ try {
87
+ const entries = readdirSync(searchDir, { withFileTypes: true })
88
+ for (const entry of entries) {
89
+ if (entry.isDirectory() && entry.name !== 'node_modules') {
90
+ const subPath = path.join(searchDir, entry.name)
91
+ const pkgPath = path.join(subPath, 'package.json')
92
+ if (existsSync(pkgPath)) {
93
+ results.push(pkgPath)
94
+ }
95
+ // recurse into subdirectories
96
+ findPackages(subPath)
97
+ }
110
98
  }
111
- // recurse into subdirectories
112
- findPackages(subPath)
99
+ } catch (_e) {
100
+ // ignore permission errors
113
101
  }
114
102
  }
115
- } catch (_e) {
116
- // ignore permission errors
103
+ findPackages(basePath)
117
104
  }
118
- }
119
- findPackages(basePath)
120
- }
121
- } else if (normalizedWorkspace.includes('*')) {
122
- // simple glob pattern like "packages/*"
123
- const workspaceDir = normalizedWorkspace.replace(/\/\*$/, '')
124
- if (existsSync(join(dir, workspaceDir))) {
125
- const subdirs = readdirSync(join(dir, workspaceDir), { withFileTypes: true })
126
- .filter((dirent) => dirent.isDirectory())
127
- .map((dirent) => join(dir, workspaceDir, dirent.name))
128
-
129
- for (const subdir of subdirs) {
130
- if (existsSync(join(subdir, 'package.json'))) {
131
- results.push(join(subdir, 'package.json'))
105
+ } else if (normalizedWorkspace.includes('*')) {
106
+ // simple glob pattern like "packages/*"
107
+ const workspaceDir = normalizedWorkspace.replace(/\/\*$/, '')
108
+ if (existsSync(path.join(dir, workspaceDir))) {
109
+ const subdirs = readdirSync(path.join(dir, workspaceDir), {
110
+ withFileTypes: true,
111
+ })
112
+ .filter((dirent) => dirent.isDirectory())
113
+ .map((dirent) => path.join(dir, workspaceDir, dirent.name))
114
+
115
+ for (const subdir of subdirs) {
116
+ if (existsSync(path.join(subdir, 'package.json'))) {
117
+ results.push(path.join(subdir, 'package.json'))
118
+ }
119
+ }
120
+ }
121
+ } else {
122
+ // exact path like "code/tamagui.dev" or "./code/sandbox"
123
+ const pkgPath = path.join(dir, normalizedWorkspace, 'package.json')
124
+ if (existsSync(pkgPath)) {
125
+ results.push(pkgPath)
132
126
  }
133
127
  }
134
128
  }
135
- } else {
136
- // exact path like "code/tamagui.dev" or "./code/sandbox"
137
- const pkgPath = join(dir, normalizedWorkspace, 'package.json')
138
- if (existsSync(pkgPath)) {
139
- results.push(pkgPath)
140
- }
141
129
  }
130
+ } catch (_error) {
131
+ // ignore errors parsing package.json
142
132
  }
143
- }
144
- } catch (_error) {
145
- // ignore errors parsing package.json
146
- }
147
133
 
148
- return results
149
- }
134
+ return results
135
+ }
150
136
 
151
- function extractDependencies(packageJsonPath: string): string[] {
152
- try {
153
- const content = readFileSync(packageJsonPath, 'utf-8')
154
- const packageJson = JSON.parse(content) as PackageJson
137
+ function extractDependencies(packageJsonPath: string): string[] {
138
+ try {
139
+ const content = readFileSync(packageJsonPath, 'utf-8')
140
+ const packageJson = JSON.parse(content) as PackageJson
155
141
 
156
- const deps: string[] = []
142
+ const deps: string[] = []
157
143
 
158
- const addNonWorkspaceDeps = (depsObject: Record<string, string> | undefined) => {
159
- if (!depsObject) return
160
- for (const [name, version] of Object.entries(depsObject)) {
161
- // skip workspace dependencies
162
- if (!version.startsWith('workspace:')) {
163
- deps.push(name)
144
+ const addNonWorkspaceDeps = (depsObject: Record<string, string> | undefined) => {
145
+ if (!depsObject) return
146
+ for (const [name, version] of Object.entries(depsObject)) {
147
+ // skip workspace dependencies
148
+ if (!version.startsWith('workspace:')) {
149
+ deps.push(name)
150
+ }
151
+ }
164
152
  }
165
- }
166
- }
167
153
 
168
- addNonWorkspaceDeps(packageJson.dependencies)
169
- addNonWorkspaceDeps(packageJson.devDependencies)
170
- addNonWorkspaceDeps(packageJson.peerDependencies)
171
- addNonWorkspaceDeps(packageJson.optionalDependencies)
154
+ addNonWorkspaceDeps(packageJson.dependencies)
155
+ addNonWorkspaceDeps(packageJson.devDependencies)
156
+ addNonWorkspaceDeps(packageJson.peerDependencies)
157
+ addNonWorkspaceDeps(packageJson.optionalDependencies)
172
158
 
173
- return deps
174
- } catch (error) {
175
- console.error(`Error parsing ${packageJsonPath}:`, error)
176
- return []
177
- }
178
- }
159
+ return deps
160
+ } catch (error) {
161
+ console.error(`Error parsing ${packageJsonPath}:`, error)
162
+ return []
163
+ }
164
+ }
179
165
 
180
- function doesPackageMatchPattern(packageName: string, pattern: string): boolean {
181
- if (pattern.includes('*')) {
182
- const regex = new RegExp(`^${pattern.replace(/\*/g, '.*')}$`)
183
- return regex.test(packageName)
184
- }
185
- return packageName === pattern
186
- }
166
+ function doesPackageMatchPattern(packageName: string, pattern: string): boolean {
167
+ if (pattern.includes('*')) {
168
+ const regex = new RegExp(`^${pattern.replace(/\*/g, '.*')}$`)
169
+ return regex.test(packageName)
170
+ }
171
+ return packageName === pattern
172
+ }
187
173
 
188
- function updatePackageJsonVersions(
189
- packageJsonPath: string,
190
- packagesToUpdate: string[],
191
- versionMap: Map<string, string>
192
- ): number {
193
- const content = readFileSync(packageJsonPath, 'utf-8')
194
- const packageJson = JSON.parse(content) as PackageJson
195
- let updatedCount = 0
196
-
197
- const updateDeps = (depsObject: Record<string, string> | undefined) => {
198
- if (!depsObject) return
199
- for (const pkg of packagesToUpdate) {
200
- const current = depsObject[pkg]
201
- if (current && !current.startsWith('workspace:')) {
202
- const newVersion = versionMap.get(pkg)
203
- if (newVersion) {
204
- // for tagged versions (canary, rc, etc), use exact version (no prefix)
205
- // otherwise preserve version prefix (^, ~, >=, etc)
206
- if (globalTag) {
207
- depsObject[pkg] = newVersion
208
- } else {
209
- // wildcard "*" means newly added placeholder, use ^ prefix
210
- if (current === '*') {
211
- depsObject[pkg] = `^${newVersion}`
174
+ function updatePackageJsonVersions(
175
+ packageJsonPath: string,
176
+ packagesToUpdate: string[],
177
+ versionMap: Map<string, string>
178
+ ): number {
179
+ const content = readFileSync(packageJsonPath, 'utf-8')
180
+ const packageJson = JSON.parse(content) as PackageJson
181
+ let updatedCount = 0
182
+
183
+ const updateDeps = (depsObject: Record<string, string> | undefined) => {
184
+ if (!depsObject) return
185
+ for (const pkg of packagesToUpdate) {
186
+ const current = depsObject[pkg]
187
+ if (current && !current.startsWith('workspace:')) {
188
+ const newVersion = versionMap.get(pkg)
189
+ if (newVersion) {
190
+ // for tagged versions (canary, rc, etc), use exact version (no prefix)
191
+ // otherwise preserve version prefix (^, ~, >=, etc)
192
+ if (globalTag) {
193
+ depsObject[pkg] = newVersion
194
+ } else {
195
+ // wildcard "*" means newly added placeholder, use ^ prefix
196
+ if (current === '*') {
197
+ depsObject[pkg] = `^${newVersion}`
198
+ updatedCount++
199
+ continue
200
+ }
201
+ const prefixMatch = current.match(/^([^\d]*)/)
202
+ const prefix = prefixMatch?.[1] || ''
203
+ depsObject[pkg] = `${prefix}${newVersion}`
204
+ }
212
205
  updatedCount++
213
- continue
214
206
  }
215
- const prefixMatch = current.match(/^([^\d]*)/)
216
- const prefix = prefixMatch?.[1] || ''
217
- depsObject[pkg] = `${prefix}${newVersion}`
218
207
  }
219
- updatedCount++
220
208
  }
221
209
  }
222
- }
223
- }
224
210
 
225
- updateDeps(packageJson.dependencies)
226
- updateDeps(packageJson.devDependencies)
227
- updateDeps(packageJson.peerDependencies)
228
- updateDeps(packageJson.optionalDependencies)
211
+ updateDeps(packageJson.dependencies)
212
+ updateDeps(packageJson.devDependencies)
213
+ updateDeps(packageJson.peerDependencies)
214
+ updateDeps(packageJson.optionalDependencies)
229
215
 
230
- if (updatedCount > 0) {
231
- writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n')
232
- }
216
+ if (updatedCount > 0) {
217
+ writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n')
218
+ }
233
219
 
234
- return updatedCount
235
- }
220
+ return updatedCount
221
+ }
236
222
 
237
- async function updatePackages(
238
- packagesByWorkspace: Map<string, { dir: string; packages: string[] }>,
239
- rootDir: string,
240
- packageJsonFiles: string[]
241
- ) {
242
- try {
243
- rmSync(`node_modules/vite`, {
244
- recursive: true,
245
- force: true,
246
- })
247
- } catch (_e) {
248
- // ignore if vite is not there
249
- }
250
-
251
- // collect all unique packages to update
252
- const allPackages = new Set<string>()
253
- for (const { packages } of packagesByWorkspace.values()) {
254
- packages.forEach((pkg) => allPackages.add(pkg))
255
- }
256
-
257
- // fetch versions for all packages (with tag if specified)
258
- const versionMap = new Map<string, string>()
259
- console.info(`\nšŸ” Fetching versions for ${allPackages.size} package(s)...`)
260
-
261
- await Promise.all(
262
- [...allPackages].map(async (pkg) => {
223
+ async function updatePackages(
224
+ packagesByWorkspace: Map<string, { dir: string; packages: string[] }>,
225
+ rootDir: string,
226
+ packageJsonFiles: string[]
227
+ ) {
263
228
  try {
264
- const tag = globalTag || 'latest'
265
- const result = await $`npm view ${pkg}@${tag} version`.quiet()
266
- const version = result.text().trim()
267
- if (version) {
268
- versionMap.set(pkg, version)
269
- console.info(` āœ“ ${pkg}@${tag} → ${version}`)
270
- }
271
- } catch {
272
- if (globalTag) {
273
- console.info(` ⊘ ${pkg}@${globalTag} not found, skipping`)
274
- } else {
275
- console.info(` ⊘ ${pkg} not found, skipping`)
276
- }
229
+ rmSync(`node_modules/vite`, {
230
+ recursive: true,
231
+ force: true,
232
+ })
233
+ } catch (_e) {
234
+ // ignore if vite is not there
235
+ }
236
+
237
+ // collect all unique packages to update
238
+ const allPackages = new Set<string>()
239
+ for (const { packages } of packagesByWorkspace.values()) {
240
+ packages.forEach((pkg) => allPackages.add(pkg))
277
241
  }
278
- })
279
- )
280
-
281
- if (versionMap.size === 0) {
282
- console.info(`\nāš ļø No packages found to update`)
283
- return
284
- }
285
-
286
- // update all package.json files directly
287
- console.info(`\nšŸ“¦ Updating ${packageJsonFiles.length} package.json file(s)...`)
288
- let totalUpdates = 0
289
-
290
- for (const packageJsonPath of packageJsonFiles) {
291
- const packagesInWorkspace =
292
- packagesByWorkspace.get(getWorkspaceName(packageJsonPath, rootDir))?.packages || []
293
-
294
- if (packagesInWorkspace.length > 0) {
295
- const updates = updatePackageJsonVersions(
296
- packageJsonPath,
297
- packagesInWorkspace,
298
- versionMap
242
+
243
+ // fetch versions for all packages (with tag if specified)
244
+ const versionMap = new Map<string, string>()
245
+ console.info(`\nšŸ” Fetching versions for ${allPackages.size} package(s)...`)
246
+
247
+ await Promise.all(
248
+ [...allPackages].map(async (pkg) => {
249
+ try {
250
+ const tag = globalTag || 'latest'
251
+ const result = await $`npm view ${pkg}@${tag} version`.quiet()
252
+ const version = result.text().trim()
253
+ if (version) {
254
+ versionMap.set(pkg, version)
255
+ console.info(` āœ“ ${pkg}@${tag} → ${version}`)
256
+ }
257
+ } catch {
258
+ if (globalTag) {
259
+ console.info(` ⊘ ${pkg}@${globalTag} not found, skipping`)
260
+ } else {
261
+ console.info(` ⊘ ${pkg} not found, skipping`)
262
+ }
263
+ }
264
+ })
299
265
  )
300
- if (updates > 0) {
301
- const name = getWorkspaceName(packageJsonPath, rootDir)
302
- console.info(` āœ“ ${name}: ${updates} package(s)`)
303
- totalUpdates += updates
266
+
267
+ if (versionMap.size === 0) {
268
+ console.info(`\nāš ļø No packages found to update`)
269
+ return
304
270
  }
305
- }
306
- }
307
-
308
- console.info(`\nšŸ“ Updated ${totalUpdates} dependency version(s)`)
309
-
310
- console.info(`\nāš™ļø Running 'bun install'...`)
311
- $.cwd(rootDir)
312
- try {
313
- await $`bun install`
314
- console.info('āœ… Done!')
315
- } catch (error: any) {
316
- const stderr = error.stderr?.toString() || error.message || ''
317
- // check if it's a version resolution error (common after new publish)
318
- if (stderr.includes('No version matching') || stderr.includes('failed to resolve')) {
319
- console.info(`āš ļø Version not in cache, clearing cache and retrying...`)
271
+
272
+ // update all package.json files directly
273
+ console.info(`\nšŸ“¦ Updating ${packageJsonFiles.length} package.json file(s)...`)
274
+ let totalUpdates = 0
275
+
276
+ for (const packageJsonPath of packageJsonFiles) {
277
+ const packagesInWorkspace =
278
+ packagesByWorkspace.get(getWorkspaceName(packageJsonPath, rootDir))?.packages ||
279
+ []
280
+
281
+ if (packagesInWorkspace.length > 0) {
282
+ const updates = updatePackageJsonVersions(
283
+ packageJsonPath,
284
+ packagesInWorkspace,
285
+ versionMap
286
+ )
287
+ if (updates > 0) {
288
+ const name = getWorkspaceName(packageJsonPath, rootDir)
289
+ console.info(` āœ“ ${name}: ${updates} package(s)`)
290
+ totalUpdates += updates
291
+ }
292
+ }
293
+ }
294
+
295
+ console.info(`\nšŸ“ Updated ${totalUpdates} dependency version(s)`)
296
+
297
+ console.info(`\nāš™ļø Running 'bun install'...`)
298
+ $.cwd(rootDir)
320
299
  try {
321
- await $`bun pm cache rm`.quiet()
322
300
  await $`bun install`
323
301
  console.info('āœ… Done!')
324
- } catch (retryError: any) {
325
- const retryStderr = retryError.stderr?.toString() || retryError.message || ''
326
- console.error(
327
- `🚨 'bun install' failed after cache clear: ${retryStderr.split('\n')[0]}`
328
- )
302
+ } catch (error: any) {
303
+ const stderr = error.stderr?.toString() || error.message || ''
304
+ // check if it's a version resolution error (common after new publish)
305
+ if (
306
+ stderr.includes('No version matching') ||
307
+ stderr.includes('failed to resolve')
308
+ ) {
309
+ console.info(`āš ļø Version not in cache, clearing cache and retrying...`)
310
+ try {
311
+ await $`bun pm cache rm`.quiet()
312
+ await $`bun install`
313
+ console.info('āœ… Done!')
314
+ } catch (retryError: any) {
315
+ const retryStderr = retryError.stderr?.toString() || retryError.message || ''
316
+ console.error(
317
+ `🚨 'bun install' failed after cache clear: ${retryStderr.split('\n')[0]}`
318
+ )
319
+ }
320
+ } else {
321
+ console.error(`🚨 'bun install' failed: ${stderr.split('\n')[0]}`)
322
+ }
329
323
  }
330
- } else {
331
- console.error(`🚨 'bun install' failed: ${stderr.split('\n')[0]}`)
332
324
  }
333
- }
334
- }
335
325
 
336
- function getWorkspaceName(packageJsonPath: string, rootDir: string): string {
337
- const dir = packageJsonPath.replace('/package.json', '')
338
- if (dir === rootDir) return 'root'
339
- return dir.replace(rootDir + '/', '')
340
- }
326
+ function getWorkspaceName(packageJsonPath: string, rootDir: string): string {
327
+ const dir = packageJsonPath.replace('/package.json', '')
328
+ if (dir === rootDir) return 'root'
329
+ return dir.replace(rootDir + '/', '')
330
+ }
341
331
 
342
- async function main() {
343
- const packageJsonFiles = findPackageJsonFiles(rootDir)
344
- console.info(`Found ${packageJsonFiles.length} package.json files`)
332
+ const packageJsonFiles = findPackageJsonFiles(rootDir)
333
+ console.info(`Found ${packageJsonFiles.length} package.json files`)
345
334
 
346
- // get workspace package names to exclude from updates
347
- const workspacePackageNames = new Set<string>()
348
- for (const packageJsonPath of packageJsonFiles) {
349
- if (packageJsonPath === join(rootDir, 'package.json')) continue
335
+ // get workspace package names to exclude from updates
336
+ const workspacePackageNames = new Set<string>()
337
+ for (const packageJsonPath of packageJsonFiles) {
338
+ if (packageJsonPath === path.join(rootDir, 'package.json')) continue
350
339
 
351
- try {
352
- const content = readFileSync(packageJsonPath, 'utf-8')
353
- const packageJson = JSON.parse(content)
354
- if (packageJson.name) {
355
- workspacePackageNames.add(packageJson.name)
340
+ try {
341
+ const content = readFileSync(packageJsonPath, 'utf-8')
342
+ const packageJson = JSON.parse(content)
343
+ if (packageJson.name) {
344
+ workspacePackageNames.add(packageJson.name)
345
+ }
346
+ } catch (_error) {
347
+ // ignore errors
356
348
  }
357
- } catch (_error) {
358
- // ignore errors
359
349
  }
360
- }
361
350
 
362
- console.info(
363
- `Found ${workspacePackageNames.size} workspace packages to exclude from updates`
364
- )
351
+ console.info(
352
+ `Found ${workspacePackageNames.size} workspace packages to exclude from updates`
353
+ )
365
354
 
366
- // build map of packages to update per workspace
367
- const packagesByWorkspace = new Map<string, { dir: string; packages: string[] }>()
368
- const allMatchingDeps = new Set<string>()
355
+ // build map of packages to update per workspace
356
+ const packagesByWorkspace = new Map<string, { dir: string; packages: string[] }>()
357
+ const allMatchingDeps = new Set<string>()
369
358
 
370
- for (const packageJsonPath of packageJsonFiles) {
371
- const deps = extractDependencies(packageJsonPath)
372
- const matchingDeps: string[] = []
359
+ for (const packageJsonPath of packageJsonFiles) {
360
+ const deps = extractDependencies(packageJsonPath)
361
+ const matchingDeps: string[] = []
373
362
 
374
- for (const dep of deps) {
375
- // skip workspace packages
376
- if (workspacePackageNames.has(dep)) continue
363
+ for (const dep of deps) {
364
+ // skip workspace packages
365
+ if (workspacePackageNames.has(dep)) continue
377
366
 
378
- for (const pattern of packagePatterns) {
379
- if (doesPackageMatchPattern(dep, pattern)) {
380
- matchingDeps.push(dep)
381
- allMatchingDeps.add(dep)
382
- break
367
+ for (const pattern of packagePatterns) {
368
+ if (doesPackageMatchPattern(dep, pattern)) {
369
+ matchingDeps.push(dep)
370
+ allMatchingDeps.add(dep)
371
+ break
372
+ }
383
373
  }
384
374
  }
385
- }
386
375
 
387
- if (matchingDeps.length > 0) {
388
- const dir = packageJsonPath.replace('/package.json', '')
389
- const name = getWorkspaceName(packageJsonPath, rootDir)
390
- packagesByWorkspace.set(name, { dir, packages: matchingDeps })
376
+ if (matchingDeps.length > 0) {
377
+ const dir = packageJsonPath.replace('/package.json', '')
378
+ const name = getWorkspaceName(packageJsonPath, rootDir)
379
+ packagesByWorkspace.set(name, { dir, packages: matchingDeps })
380
+ }
391
381
  }
392
- }
393
382
 
394
- if (allMatchingDeps.size === 0) {
395
- // no existing deps matched, but exact patterns (no wildcards) can be added fresh
396
- const exactPatterns = packagePatterns.filter((p) => !p.includes('*'))
397
- if (exactPatterns.length === 0) {
398
- console.info(
399
- `Found 0 dependencies matching patterns: ${packagePatterns.join(', ')}`
400
- )
401
- console.info('No matching packages found to update.')
402
- return
403
- }
383
+ if (allMatchingDeps.size === 0) {
384
+ // no existing deps matched, but exact patterns (no wildcards) can be added fresh
385
+ const exactPatterns = packagePatterns.filter((p) => !p.includes('*'))
386
+ if (exactPatterns.length === 0) {
387
+ console.info(
388
+ `Found 0 dependencies matching patterns: ${packagePatterns.join(', ')}`
389
+ )
390
+ console.info('No matching packages found to update.')
391
+ return
392
+ }
404
393
 
405
- // add as new dependencies to the root package.json
406
- console.info(`No existing deps found, adding to root: ${exactPatterns.join(', ')}`)
407
- const rootPkgPath = join(rootDir, 'package.json')
394
+ // add as new dependencies to the root package.json
395
+ console.info(`No existing deps found, adding to root: ${exactPatterns.join(', ')}`)
396
+ const rootPkgPath = path.join(rootDir, 'package.json')
408
397
 
409
- for (const pkg of exactPatterns) {
410
- allMatchingDeps.add(pkg)
411
- }
398
+ for (const pkg of exactPatterns) {
399
+ allMatchingDeps.add(pkg)
400
+ }
412
401
 
413
- packagesByWorkspace.set('root', {
414
- dir: rootDir,
415
- packages: exactPatterns,
416
- })
402
+ packagesByWorkspace.set('root', {
403
+ dir: rootDir,
404
+ packages: exactPatterns,
405
+ })
417
406
 
418
- // insert placeholder so updatePackageJsonVersions can set the real version
419
- const rootPkg = JSON.parse(readFileSync(rootPkgPath, 'utf-8'))
420
- if (!rootPkg.dependencies) {
421
- rootPkg.dependencies = {}
422
- }
423
- for (const pkg of exactPatterns) {
424
- if (!rootPkg.dependencies[pkg] && !rootPkg.devDependencies?.[pkg]) {
425
- rootPkg.dependencies[pkg] = '*'
407
+ // insert placeholder so updatePackageJsonVersions can set the real version
408
+ const rootPkg = JSON.parse(readFileSync(rootPkgPath, 'utf-8'))
409
+ if (!rootPkg.dependencies) {
410
+ rootPkg.dependencies = {}
411
+ }
412
+ for (const pkg of exactPatterns) {
413
+ if (!rootPkg.dependencies[pkg] && !rootPkg.devDependencies?.[pkg]) {
414
+ rootPkg.dependencies[pkg] = '*'
415
+ }
426
416
  }
417
+ writeFileSync(rootPkgPath, JSON.stringify(rootPkg, null, 2) + '\n')
418
+ } else {
419
+ console.info(
420
+ `Found ${allMatchingDeps.size} dependencies matching patterns: ${packagePatterns.join(', ')}`
421
+ )
422
+ console.info(`Found matches in ${packagesByWorkspace.size} workspace(s)`)
427
423
  }
428
- writeFileSync(rootPkgPath, JSON.stringify(rootPkg, null, 2) + '\n')
429
- } else {
430
- console.info(
431
- `Found ${allMatchingDeps.size} dependencies matching patterns: ${packagePatterns.join(', ')}`
432
- )
433
- console.info(`Found matches in ${packagesByWorkspace.size} workspace(s)`)
434
- }
435
-
436
- if (globalTag) {
437
- console.info(`šŸ·ļø Using tag '${globalTag}'`)
438
- }
439
424
 
440
- await updatePackages(packagesByWorkspace, rootDir, packageJsonFiles)
425
+ if (globalTag) {
426
+ console.info(`šŸ·ļø Using tag '${globalTag}'`)
427
+ }
441
428
 
442
- // special handling for zero - update ZERO_VERSION in .env
443
- if (packagePatterns.includes('@rocicorp/zero')) {
444
- console.info('\nšŸ”„ Updating local env for Zero...')
445
- await $`bun tko run update-local-env`
446
- }
429
+ await updatePackages(packagesByWorkspace, rootDir, packageJsonFiles)
447
430
 
448
- console.info('\nšŸŽ‰ Dependency update complete!')
449
- }
431
+ // special handling for zero - update ZERO_VERSION in .env
432
+ if (packagePatterns.includes('@rocicorp/zero')) {
433
+ console.info('\nšŸ”„ Updating local env for Zero...')
434
+ await $`bun tko run update-local-env`
435
+ }
450
436
 
451
- await main()
437
+ console.info('\nšŸŽ‰ Dependency update complete!')
438
+ })