@take-out/scripts 0.2.5 → 0.2.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@take-out/scripts",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "type": "module",
5
5
  "main": "./src/cmd.ts",
6
6
  "sideEffects": false,
@@ -29,8 +29,8 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@clack/prompts": "^0.8.2",
32
- "@take-out/helpers": "0.2.5",
33
- "@take-out/run": "0.2.5",
32
+ "@take-out/helpers": "0.2.7",
33
+ "@take-out/run": "0.2.7",
34
34
  "picocolors": "^1.1.1"
35
35
  }
36
36
  }
package/src/env-update.ts CHANGED
@@ -146,6 +146,11 @@ await cmd`sync environment variables from package.json to matching files`
146
146
  files.push(...expandPattern(p))
147
147
  }
148
148
 
149
+ // ensure .env is always processed (may not exist yet for fresh projects)
150
+ if (!files.includes('.env')) {
151
+ files.push('.env')
152
+ }
153
+
149
154
  // markers
150
155
  const markerStart = 'šŸ”’ start - this is generated by "bun env:update"'
151
156
  const markerEnd = 'šŸ”’ end - this is generated by "bun env:update"'
@@ -154,7 +159,16 @@ await cmd`sync environment variables from package.json to matching files`
154
159
  const jsStart = `// ${markerStart}`
155
160
  const jsEnd = `// ${markerEnd}`
156
161
 
157
- type Strategy = 'yaml-markers' | 'js-markers' | 'dotenv-inline' | 'ts-inline'
162
+ // .env auto-generated section markers
163
+ const dotenvSectionStart = '# ---- BEGIN AUTO-GENERATED (DO NOT EDIT) ----'
164
+ const dotenvSectionEnd = '# ---- END AUTO-GENERATED ----'
165
+
166
+ type Strategy =
167
+ | 'yaml-markers'
168
+ | 'js-markers'
169
+ | 'dotenv-section'
170
+ | 'dotenv-inline'
171
+ | 'ts-inline'
158
172
 
159
173
  function detectStrategy(filePath: string, content: string): Strategy | null {
160
174
  if (content.includes(yamlStart) && content.includes(yamlEnd)) {
@@ -163,7 +177,11 @@ await cmd`sync environment variables from package.json to matching files`
163
177
  if (content.includes(jsStart) && content.includes(jsEnd)) {
164
178
  return 'js-markers'
165
179
  }
166
- if (/^\.env/.test(path.basename(filePath))) {
180
+ const basename = path.basename(filePath)
181
+ if (basename === '.env') {
182
+ return 'dotenv-section'
183
+ }
184
+ if (/^\.env/.test(basename)) {
167
185
  return 'dotenv-inline'
168
186
  }
169
187
  if (filePath.endsWith('.ts') && Object.keys(depResolved).length > 0) {
@@ -234,6 +252,39 @@ await cmd`sync environment variables from package.json to matching files`
234
252
  return replaceMarkerSection(content, jsStart, jsEnd, envExports)
235
253
  }
236
254
 
255
+ function applyDotenvSection(_filePath: string, content: string): string {
256
+ const lines: string[] = [dotenvSectionStart]
257
+
258
+ for (const [key, value] of Object.entries(envVars)) {
259
+ const dep = resolveDepVersion(value, packageJson.dependencies)
260
+ if (dep) {
261
+ lines.push(`${key}=${dep}`)
262
+ } else if (typeof value === 'string' && value.startsWith('$dep:')) {
263
+ console.warn(
264
+ `could not resolve dependency version for ${value.slice('$dep:'.length)}`
265
+ )
266
+ } else if (typeof value === 'string' && value !== '') {
267
+ lines.push(`${key}=${value}`)
268
+ }
269
+ }
270
+
271
+ lines.push(dotenvSectionEnd)
272
+ const section = lines.join('\n')
273
+
274
+ const beginIndex = content.indexOf(dotenvSectionStart)
275
+ const endIndex = content.indexOf(dotenvSectionEnd)
276
+
277
+ if (beginIndex !== -1 && endIndex !== -1 && endIndex > beginIndex) {
278
+ const before = content.substring(0, beginIndex).trimEnd()
279
+ const after = content.substring(endIndex + dotenvSectionEnd.length).trimStart()
280
+ return [before, section, after].filter(Boolean).join('\n\n')
281
+ }
282
+
283
+ // no existing section — append
284
+ const trimmed = content.trimEnd()
285
+ return trimmed ? `${trimmed}\n\n${section}` : section
286
+ }
287
+
237
288
  function applyDotenvInline(_filePath: string, content: string): string {
238
289
  let result = content
239
290
  for (const [key, value] of Object.entries(depResolved)) {
@@ -261,6 +312,7 @@ await cmd`sync environment variables from package.json to matching files`
261
312
  const strategies: Record<Strategy, (filePath: string, content: string) => string> = {
262
313
  'yaml-markers': applyYamlMarkers,
263
314
  'js-markers': applyJsMarkers,
315
+ 'dotenv-section': applyDotenvSection,
264
316
  'dotenv-inline': applyDotenvInline,
265
317
  'ts-inline': applyTsInline,
266
318
  }
@@ -272,7 +324,12 @@ await cmd`sync environment variables from package.json to matching files`
272
324
  try {
273
325
  content = fs.readFileSync(filePath, 'utf-8')
274
326
  } catch {
275
- continue
327
+ // allow .env to be created from scratch
328
+ if (path.basename(filePath) === '.env') {
329
+ content = ''
330
+ } else {
331
+ continue
332
+ }
276
333
  }
277
334
 
278
335
  const strategy = detectStrategy(filePath, content)
package/src/up.ts CHANGED
@@ -426,10 +426,14 @@ await cmd`upgrade packages by name or pattern`
426
426
 
427
427
  await updatePackages(packagesByWorkspace, rootDir, packageJsonFiles)
428
428
 
429
- // special handling for zero - update ZERO_VERSION in .env
430
- if (packagePatterns.includes('@rocicorp/zero')) {
431
- console.info('\nšŸ”„ Updating local env for Zero...')
432
- await $`bun tko run generate-env`
429
+ // sync resolved $dep: values (like ZERO_VERSION) to all env targets
430
+ if (
431
+ Object.values(packageJson.env || {}).some(
432
+ (v: any) => typeof v === 'string' && v.startsWith('$dep:')
433
+ )
434
+ ) {
435
+ console.info('\nšŸ”„ Syncing env variables...')
436
+ await $`bun tko run env-update`
433
437
  }
434
438
 
435
439
  console.info('\nšŸŽ‰ Dependency update complete!')
@@ -1,145 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- import fs from 'node:fs'
4
-
5
- import { cmd } from './cmd'
6
- import { resolveDepVersion } from './helpers/resolve-dep-version'
7
-
8
- await cmd`sync auto-generated env vars to local .env file`.run(async ({ path }) => {
9
- const ENV_PATH = path.join(process.cwd(), '.env')
10
- const ENV_BACKUP_PATH = path.join(process.cwd(), '.env.backup')
11
- const ENV_TEMP_PATH = path.join(process.cwd(), '.env.tmp')
12
-
13
- // auto-generated section markers
14
- const BEGIN_MARKER = '# ---- BEGIN AUTO-GENERATED (DO NOT EDIT) ----'
15
- const END_MARKER = '# ---- END AUTO-GENERATED ----'
16
-
17
- function getAutoGeneratedContent(): string {
18
- const packageJsonPath = path.join(process.cwd(), 'package.json')
19
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))
20
- const envVars = packageJson.env as Record<string, boolean | string>
21
-
22
- if (!envVars || typeof envVars !== 'object') {
23
- console.warn('No env section found in package.json')
24
- return ''
25
- }
26
-
27
- const lines: string[] = [BEGIN_MARKER, `# Generated at: ${new Date().toISOString()}`]
28
-
29
- for (const [key, value] of Object.entries(envVars)) {
30
- const depVersion = resolveDepVersion(value, packageJson.dependencies)
31
- if (depVersion) {
32
- lines.push(`${key}=${depVersion}`)
33
- } else if (typeof value === 'string' && value.startsWith('$dep:')) {
34
- console.warn(
35
- `Could not resolve dependency version for ${value.slice('$dep:'.length)}`
36
- )
37
- } else if (typeof value === 'string' && value !== '') {
38
- // non-empty string default
39
- lines.push(`${key}=${value}`)
40
- }
41
- // skip true (required, no default) and "" (no meaningful default)
42
- }
43
-
44
- lines.push(END_MARKER)
45
- return lines.join('\n')
46
- }
47
-
48
- function updateEnvFile(): void {
49
- // ensure .env exists
50
- if (!fs.existsSync(ENV_PATH)) {
51
- fs.writeFileSync(ENV_PATH, '')
52
- console.info('Created empty .env file')
53
- }
54
-
55
- try {
56
- // create backup
57
- if (fs.existsSync(ENV_PATH)) {
58
- fs.copyFileSync(ENV_PATH, ENV_BACKUP_PATH)
59
- }
60
-
61
- const currentContent = fs.readFileSync(ENV_PATH, 'utf-8')
62
-
63
- const beginIndex = currentContent.indexOf(BEGIN_MARKER)
64
- const endIndex = currentContent.indexOf(END_MARKER)
65
-
66
- let newContent: string
67
-
68
- if (beginIndex !== -1 && endIndex !== -1 && endIndex > beginIndex) {
69
- // replace existing auto-generated section
70
- const beforeSection = currentContent.substring(0, beginIndex).trimEnd()
71
- const afterSection = currentContent
72
- .substring(endIndex + END_MARKER.length)
73
- .trimStart()
74
-
75
- newContent = [beforeSection, getAutoGeneratedContent(), afterSection]
76
- .filter(Boolean)
77
- .join('\n\n')
78
- } else if (beginIndex !== -1 || endIndex !== -1) {
79
- // malformed markers - preserve content and append new section
80
- console.warn('Found malformed auto-generated section, appending new section')
81
- newContent = currentContent.trimEnd() + '\n\n' + getAutoGeneratedContent()
82
- } else {
83
- // no existing section - append to end
84
- const trimmedContent = currentContent.trimEnd()
85
- newContent = trimmedContent
86
- ? trimmedContent + '\n\n' + getAutoGeneratedContent()
87
- : getAutoGeneratedContent()
88
- }
89
-
90
- // write to temp file first (atomic operation)
91
- fs.writeFileSync(ENV_TEMP_PATH, newContent)
92
-
93
- // validate temp file
94
- const tempContent = fs.readFileSync(ENV_TEMP_PATH, 'utf-8')
95
- if (!tempContent.includes(BEGIN_MARKER) || !tempContent.includes(END_MARKER)) {
96
- throw new Error('Generated content validation failed')
97
- }
98
-
99
- // atomic replace
100
- fs.renameSync(ENV_TEMP_PATH, ENV_PATH)
101
-
102
- if (fs.existsSync(ENV_BACKUP_PATH)) {
103
- try {
104
- fs.unlinkSync(ENV_BACKUP_PATH)
105
- } catch {
106
- // ignore cleanup errors
107
- }
108
- }
109
-
110
- console.info('Updated .env auto-generated section')
111
- } catch (error) {
112
- console.error('Failed to update .env file:', error)
113
-
114
- // attempt to restore backup
115
- if (fs.existsSync(ENV_BACKUP_PATH)) {
116
- try {
117
- fs.copyFileSync(ENV_BACKUP_PATH, ENV_PATH)
118
- console.info('Restored .env from backup')
119
- } catch (restoreError) {
120
- console.error('Failed to restore backup:', restoreError)
121
- }
122
- }
123
-
124
- // clean up temp file
125
- if (fs.existsSync(ENV_TEMP_PATH)) {
126
- try {
127
- fs.unlinkSync(ENV_TEMP_PATH)
128
- } catch {
129
- // ignore cleanup errors
130
- }
131
- }
132
-
133
- // don't fail the install process
134
- process.exit(0)
135
- }
136
- }
137
-
138
- try {
139
- updateEnvFile()
140
- } catch (error) {
141
- // catch any unexpected errors and exit gracefully
142
- console.error('Bootstrap script error:', error)
143
- process.exit(0)
144
- }
145
- })