@take-out/scripts 0.0.37 → 0.0.39

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.0.37",
3
+ "version": "0.0.39",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "sideEffects": false,
@@ -24,7 +24,7 @@
24
24
  "access": "public"
25
25
  },
26
26
  "dependencies": {
27
- "@take-out/helpers": "0.0.37"
27
+ "@take-out/helpers": "0.0.39"
28
28
  },
29
29
  "devDependencies": {
30
30
  "vxrn": "*"
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env bun
2
+
3
+ /**
4
+ * @description Bootstrap project workspace and build initial packages. This is
5
+ * mostly important for Takeout starter kit itself.
6
+ */
7
+
8
+ import { existsSync, mkdirSync, readFileSync, symlinkSync } from 'node:fs'
9
+ import { exists } from 'node:fs/promises'
10
+ import { join } from 'node:path'
11
+
12
+ import { $ } from 'bun'
13
+
14
+ const hasPackages = await exists(`./packages`)
15
+
16
+ // only run once:
17
+ if (!(await exists(`./node_modules/.bin/tko`))) {
18
+ if (hasPackages) {
19
+ symlinkBins()
20
+ }
21
+ }
22
+
23
+ // check if critical packages are built - both helpers and takeout are needed for tko to work
24
+ if (hasPackages) {
25
+ const needsBuild =
26
+ !(await exists(`./packages/helpers/dist`)) ||
27
+ !(await exists(`./packages/takeout/dist/esm`))
28
+
29
+ if (needsBuild) {
30
+ // build helpers first as other packages depend on it
31
+ await $`cd packages/helpers && bun run build`
32
+
33
+ // then build all other packages in parallel
34
+ await $`bun ./packages/scripts/src/run.ts build --no-root`
35
+ }
36
+ }
37
+
38
+ // show welcome message if not onboarded
39
+ checkAndShowWelcome()
40
+
41
+ function checkAndShowWelcome(cwd: string = process.cwd()): void {
42
+ try {
43
+ const packagePath = join(cwd, 'package.json')
44
+ const pkg = JSON.parse(readFileSync(packagePath, 'utf-8'))
45
+
46
+ if (pkg.takeout?.onboarded === false) {
47
+ console.info()
48
+ console.info(`
49
+ ████████╗ █████╗ ██╗ ██╗███████╗ ██████╗ ██╗ ██╗████████╗
50
+ ╚══██╔══╝██╔══██╗██║ ██╔╝██╔════╝██╔═══██╗██║ ██║╚══██╔══╝
51
+ ██║ ███████║█████╔╝ █████╗ ██║ ██║██║ ██║ ██║
52
+ ██║ ██╔══██║██╔═██╗ ██╔══╝ ██║ ██║██║ ██║ ██║
53
+ ██║ ██║ ██║██║ ██╗███████╗╚██████╔╝╚██████╔╝ ██║
54
+ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝
55
+ 麵 碼 飯
56
+ `)
57
+ console.info()
58
+ console.info(' welcome to takeout! 🥡')
59
+ console.info()
60
+ console.info(' run \x1b[32mbun onboard\x1b[0m to get things set up')
61
+ console.info()
62
+ }
63
+ } catch {
64
+ // silently fail if package.json doesn't exist or is malformed
65
+ }
66
+ }
67
+
68
+ function symlinkBins() {
69
+ // workaround for https://github.com/oven-sh/bun/issues/19782
70
+ // bun doesn't create symlinks for workspace packages properly
71
+ const packagesWithCLI = [
72
+ { name: 'takeout', cliFile: 'cli.mjs', alias: 'tko' },
73
+ { name: 'postgres', cliFile: 'cli.cjs' },
74
+ ]
75
+
76
+ const binDir = join(process.cwd(), 'node_modules', '.bin')
77
+
78
+ if (!existsSync(binDir)) {
79
+ mkdirSync(binDir, { recursive: true })
80
+ }
81
+
82
+ for (const pkg of packagesWithCLI) {
83
+ const binPath = join(binDir, pkg.name)
84
+ const sourcePath = join(process.cwd(), 'packages', pkg.name, pkg.cliFile)
85
+ symlinkTo(sourcePath, binPath)
86
+
87
+ if (pkg.alias) {
88
+ const aliasPath = join(binDir, pkg.alias)
89
+ // create alias symlink pointing to the source
90
+ symlinkTo(sourcePath, aliasPath)
91
+ }
92
+ }
93
+
94
+ // helper function to create symlink with existence check
95
+ function symlinkTo(source: string, target: string): void {
96
+ if (existsSync(target)) {
97
+ console.info(`✓ Symlink already exists: ${target}`)
98
+ return
99
+ }
100
+
101
+ symlinkSync(source, target)
102
+ console.info(`→ Created symlink: ${source} ⇢ ${target}`)
103
+ }
104
+ }
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env bun
2
+
3
+ /**
4
+ * @description Bootstrap project dependencies and workspace packages
5
+ *
6
+ * This script runs automatically during `bun install` via the prepare lifecycle.
7
+ * It manages the .env file configuration with the following responsibilities:
8
+ *
9
+ * 1. Creates .env from .env.template if it doesn't exist
10
+ * 2. Maintains an auto-generated section in .env with package metadata
11
+ * 3. Currently syncs ZERO_VERSION from package.json dependencies
12
+ * 4. Preserves all user-defined environment variables
13
+ * 5. Creates backups before modifications for safety
14
+ *
15
+ * The auto-generated section is clearly marked and should not be edited manually.
16
+ * All operations are defensive and will not fail the install process.
17
+ */
18
+
19
+ import {
20
+ existsSync,
21
+ readFileSync,
22
+ writeFileSync,
23
+ copyFileSync,
24
+ renameSync,
25
+ unlinkSync,
26
+ } from 'node:fs'
27
+ import { join } from 'node:path'
28
+
29
+ import { getZeroVersion } from '@take-out/scripts/helpers/zero-get-version'
30
+
31
+ const ENV_PATH = join(process.cwd(), '.env')
32
+ const ENV_TEMPLATE_PATH = join(process.cwd(), '.env.template')
33
+ const ENV_BACKUP_PATH = join(process.cwd(), '.env.backup')
34
+ const ENV_TEMP_PATH = join(process.cwd(), '.env.tmp')
35
+
36
+ // auto-generated section markers
37
+ const BEGIN_MARKER = '# ---- BEGIN AUTO-GENERATED (DO NOT EDIT) ----'
38
+ const END_MARKER = '# ---- END AUTO-GENERATED ----'
39
+
40
+ function createEnvFromTemplate(): boolean {
41
+ if (!existsSync(ENV_TEMPLATE_PATH)) {
42
+ console.info('No .env.template found, skipping .env creation')
43
+ return false
44
+ }
45
+
46
+ try {
47
+ copyFileSync(ENV_TEMPLATE_PATH, ENV_PATH)
48
+ console.info('Created .env from .env.template')
49
+ return true
50
+ } catch (error) {
51
+ console.error('Failed to create .env from .env.template:', error)
52
+ return false
53
+ }
54
+ }
55
+
56
+ function getAutoGeneratedContent(): string {
57
+ const zeroVersion = getZeroVersion()
58
+ if (!zeroVersion) {
59
+ console.warn('Could not determine Zero version')
60
+ return ''
61
+ }
62
+
63
+ // build the auto-generated content
64
+ const lines = [
65
+ BEGIN_MARKER,
66
+ `# Generated at: ${new Date().toISOString()}`,
67
+ `ZERO_VERSION=${zeroVersion}`,
68
+ END_MARKER,
69
+ ]
70
+
71
+ return lines.join('\n')
72
+ }
73
+
74
+ function updateEnvFile(): void {
75
+ // ensure .env exists
76
+ if (!existsSync(ENV_PATH)) {
77
+ const created = createEnvFromTemplate()
78
+ if (!created && !existsSync(ENV_PATH)) {
79
+ // create empty .env if no template exists
80
+ writeFileSync(ENV_PATH, '')
81
+ console.info('Created empty .env file')
82
+ }
83
+ }
84
+
85
+ try {
86
+ // create backup
87
+ if (existsSync(ENV_PATH)) {
88
+ copyFileSync(ENV_PATH, ENV_BACKUP_PATH)
89
+ }
90
+
91
+ // read current content
92
+ const currentContent = readFileSync(ENV_PATH, 'utf-8')
93
+
94
+ // find existing auto-generated section
95
+ const beginIndex = currentContent.indexOf(BEGIN_MARKER)
96
+ const endIndex = currentContent.indexOf(END_MARKER)
97
+
98
+ let newContent: string
99
+
100
+ if (beginIndex !== -1 && endIndex !== -1 && endIndex > beginIndex) {
101
+ // replace existing auto-generated section
102
+ const beforeSection = currentContent.substring(0, beginIndex).trimEnd()
103
+ const afterSection = currentContent
104
+ .substring(endIndex + END_MARKER.length)
105
+ .trimStart()
106
+
107
+ newContent = [beforeSection, getAutoGeneratedContent(), afterSection]
108
+ .filter(Boolean)
109
+ .join('\n\n')
110
+ } else if (beginIndex !== -1 || endIndex !== -1) {
111
+ // malformed markers - preserve content and append new section
112
+ console.warn('Found malformed auto-generated section, appending new section')
113
+ newContent = currentContent.trimEnd() + '\n\n' + getAutoGeneratedContent()
114
+ } else {
115
+ // no existing section - append to end
116
+ const trimmedContent = currentContent.trimEnd()
117
+ newContent = trimmedContent
118
+ ? trimmedContent + '\n\n' + getAutoGeneratedContent()
119
+ : getAutoGeneratedContent()
120
+ }
121
+
122
+ // write to temp file first (atomic operation)
123
+ writeFileSync(ENV_TEMP_PATH, newContent)
124
+
125
+ // validate temp file
126
+ const tempContent = readFileSync(ENV_TEMP_PATH, 'utf-8')
127
+ if (!tempContent.includes(BEGIN_MARKER) || !tempContent.includes(END_MARKER)) {
128
+ throw new Error('Generated content validation failed')
129
+ }
130
+
131
+ // atomic replace
132
+ renameSync(ENV_TEMP_PATH, ENV_PATH)
133
+
134
+ if (existsSync(ENV_BACKUP_PATH)) {
135
+ try {
136
+ unlinkSync(ENV_BACKUP_PATH)
137
+ } catch {
138
+ // ignore cleanup errors
139
+ }
140
+ }
141
+
142
+ console.info('Updated .env auto-generated section')
143
+ } catch (error) {
144
+ console.error('Failed to update .env file:', error)
145
+
146
+ // attempt to restore backup
147
+ if (existsSync(ENV_BACKUP_PATH)) {
148
+ try {
149
+ copyFileSync(ENV_BACKUP_PATH, ENV_PATH)
150
+ console.info('Restored .env from backup')
151
+ } catch (restoreError) {
152
+ console.error('Failed to restore backup:', restoreError)
153
+ }
154
+ }
155
+
156
+ // clean up temp file
157
+ if (existsSync(ENV_TEMP_PATH)) {
158
+ try {
159
+ unlinkSync(ENV_TEMP_PATH)
160
+ } catch {
161
+ // ignore cleanup errors
162
+ }
163
+ }
164
+
165
+ // don't fail the install process
166
+ process.exit(0)
167
+ }
168
+ }
169
+
170
+ // main execution
171
+ // skip bootstrap in CI environments
172
+ if (process.env.CI === 'true') {
173
+ console.info('Skipping bootstrap in CI environment')
174
+ process.exit(0)
175
+ }
176
+
177
+ try {
178
+ updateEnvFile()
179
+ } catch (error) {
180
+ // catch any unexpected errors and exit gracefully
181
+ console.error('Bootstrap script error:', error)
182
+ process.exit(0)
183
+ }