create-bingstack-app 0.1.0 → 0.1.1
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/index.mjs +4 -33
- package/package.json +3 -3
package/index.mjs
CHANGED
|
@@ -6,10 +6,7 @@ import fs from 'fs/promises'
|
|
|
6
6
|
import path from 'path'
|
|
7
7
|
import { existsSync } from 'fs'
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
// Your GitHub template repo in the format "username/repo"
|
|
11
|
-
const TEMPLATE_REPO = 'GITHUB_USERNAME/TEMPLATE_REPO'
|
|
12
|
-
// ───────────────────────────────────────────────────────────────────────────────
|
|
9
|
+
const TEMPLATE_REPO = 'binghatch/bingstack-template'
|
|
13
10
|
|
|
14
11
|
const args = process.argv.slice(2)
|
|
15
12
|
const argProjectName = args.find((a) => !a.startsWith('--'))
|
|
@@ -19,7 +16,6 @@ async function main() {
|
|
|
19
16
|
console.log()
|
|
20
17
|
p.intro('create-bingstack-app')
|
|
21
18
|
|
|
22
|
-
// ── Step 1: Project name ────────────────────────────────────────────────────
|
|
23
19
|
let projectName = argProjectName
|
|
24
20
|
|
|
25
21
|
if (!projectName) {
|
|
@@ -31,7 +27,6 @@ async function main() {
|
|
|
31
27
|
if (p.isCancel(projectName)) return cancel()
|
|
32
28
|
}
|
|
33
29
|
|
|
34
|
-
// ── Step 2: Protected route name ────────────────────────────────────────────
|
|
35
30
|
const protectedRoute = await p.text({
|
|
36
31
|
message: 'Main protected page name (shown after sign-in)',
|
|
37
32
|
placeholder: 'dashboard',
|
|
@@ -40,7 +35,6 @@ async function main() {
|
|
|
40
35
|
})
|
|
41
36
|
if (p.isCancel(protectedRoute)) return cancel()
|
|
42
37
|
|
|
43
|
-
// ── Step 3: Package manager ─────────────────────────────────────────────────
|
|
44
38
|
const pkgManager = await p.select({
|
|
45
39
|
message: 'Package manager',
|
|
46
40
|
options: [
|
|
@@ -52,7 +46,6 @@ async function main() {
|
|
|
52
46
|
})
|
|
53
47
|
if (p.isCancel(pkgManager)) return cancel()
|
|
54
48
|
|
|
55
|
-
// ── Confirm ─────────────────────────────────────────────────────────────────
|
|
56
49
|
const targetDir = path.resolve(process.cwd(), projectName)
|
|
57
50
|
|
|
58
51
|
if (existsSync(targetDir)) {
|
|
@@ -67,30 +60,21 @@ async function main() {
|
|
|
67
60
|
s.stop('Removed')
|
|
68
61
|
}
|
|
69
62
|
|
|
70
|
-
// ── Clone template ───────────────────────────────────────────────────────────
|
|
71
63
|
{
|
|
72
64
|
const s = p.spinner()
|
|
73
65
|
s.start('Cloning template...')
|
|
74
66
|
try {
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
await emitter.clone(targetDir)
|
|
67
|
+
const { downloadTemplate } = await import('giget')
|
|
68
|
+
const templateSource = argTemplate ?? `gh:${TEMPLATE_REPO}`
|
|
69
|
+
await downloadTemplate(templateSource, { dir: targetDir, force: true })
|
|
79
70
|
s.stop('Template cloned')
|
|
80
71
|
} catch (err) {
|
|
81
72
|
s.stop('Failed to clone template')
|
|
82
73
|
p.log.error(String(err.message ?? err))
|
|
83
|
-
if (!argTemplate) {
|
|
84
|
-
p.log.info(
|
|
85
|
-
`Make sure you've set TEMPLATE_REPO in index.mjs to your GitHub "username/repo".\n` +
|
|
86
|
-
`Or pass a local path: npx create-bingstack-app --template=../my-template`,
|
|
87
|
-
)
|
|
88
|
-
}
|
|
89
74
|
process.exit(1)
|
|
90
75
|
}
|
|
91
76
|
}
|
|
92
77
|
|
|
93
|
-
// ── Apply replacements ───────────────────────────────────────────────────────
|
|
94
78
|
{
|
|
95
79
|
const s = p.spinner()
|
|
96
80
|
s.start('Configuring project...')
|
|
@@ -98,7 +82,6 @@ async function main() {
|
|
|
98
82
|
s.stop('Project configured')
|
|
99
83
|
}
|
|
100
84
|
|
|
101
|
-
// ── Install dependencies ─────────────────────────────────────────────────────
|
|
102
85
|
{
|
|
103
86
|
const s = p.spinner()
|
|
104
87
|
s.start(`Installing dependencies with ${pkgManager}...`)
|
|
@@ -106,7 +89,6 @@ async function main() {
|
|
|
106
89
|
s.stop('Dependencies installed')
|
|
107
90
|
}
|
|
108
91
|
|
|
109
|
-
// ── Done ─────────────────────────────────────────────────────────────────────
|
|
110
92
|
p.outro(
|
|
111
93
|
`Done! Next steps:\n\n` +
|
|
112
94
|
` cd ${projectName}\n` +
|
|
@@ -115,12 +97,9 @@ async function main() {
|
|
|
115
97
|
)
|
|
116
98
|
}
|
|
117
99
|
|
|
118
|
-
// ── Replacements ────────────────────────────────────────────────────────────────
|
|
119
|
-
|
|
120
100
|
async function applyReplacements(dir, appName, protectedRoute) {
|
|
121
101
|
const oldRoute = 'workspace'
|
|
122
102
|
|
|
123
|
-
// 1. Rename the protected route file if needed
|
|
124
103
|
if (protectedRoute !== oldRoute) {
|
|
125
104
|
const oldFile = path.join(dir, `src/routes/_protected/${oldRoute}.tsx`)
|
|
126
105
|
const newFile = path.join(dir, `src/routes/_protected/${protectedRoute}.tsx`)
|
|
@@ -129,11 +108,9 @@ async function applyReplacements(dir, appName, protectedRoute) {
|
|
|
129
108
|
}
|
|
130
109
|
}
|
|
131
110
|
|
|
132
|
-
// 2. Walk all text files and do string replacements
|
|
133
111
|
const files = await walkFiles(dir, ['node_modules', '.git', 'dist', '.cache', '.turbo'])
|
|
134
112
|
|
|
135
113
|
for (const file of files) {
|
|
136
|
-
// Only process text files
|
|
137
114
|
if (!isTextFile(file)) continue
|
|
138
115
|
|
|
139
116
|
let content = await fs.readFile(file, 'utf-8').catch(() => null)
|
|
@@ -141,15 +118,12 @@ async function applyReplacements(dir, appName, protectedRoute) {
|
|
|
141
118
|
|
|
142
119
|
const original = content
|
|
143
120
|
|
|
144
|
-
// Replace package name
|
|
145
121
|
if (file.endsWith('package.json')) {
|
|
146
122
|
content = content.replace(/"name":\s*"app"/, `"name": "${appName}"`)
|
|
147
123
|
}
|
|
148
124
|
|
|
149
|
-
// Replace page title
|
|
150
125
|
content = content.replaceAll('TanStack Start Starter', toTitleCase(appName))
|
|
151
126
|
|
|
152
|
-
// Replace protected route references (case-sensitive variants)
|
|
153
127
|
if (protectedRoute !== oldRoute) {
|
|
154
128
|
content = content
|
|
155
129
|
.replaceAll(`/_protected/${oldRoute}`, `/_protected/${protectedRoute}`)
|
|
@@ -165,8 +139,6 @@ async function applyReplacements(dir, appName, protectedRoute) {
|
|
|
165
139
|
}
|
|
166
140
|
}
|
|
167
141
|
|
|
168
|
-
// ── Helpers ─────────────────────────────────────────────────────────────────────
|
|
169
|
-
|
|
170
142
|
async function walkFiles(dir, ignore = []) {
|
|
171
143
|
const results = []
|
|
172
144
|
const entries = await fs.readdir(dir, { withFileTypes: true })
|
|
@@ -192,7 +164,6 @@ const TEXT_EXTENSIONS = new Set([
|
|
|
192
164
|
function isTextFile(filePath) {
|
|
193
165
|
const ext = path.extname(filePath)
|
|
194
166
|
const base = path.basename(filePath)
|
|
195
|
-
// Handle extension-less dotfiles like .env
|
|
196
167
|
return TEXT_EXTENSIONS.has(ext) || TEXT_EXTENSIONS.has(base)
|
|
197
168
|
}
|
|
198
169
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-bingstack-app",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Create a new Bingstack app with auth, routing, and database pre-configured",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"@clack/prompts": "^0.9.1",
|
|
11
|
-
"
|
|
12
|
-
"
|
|
11
|
+
"execa": "^9.5.2",
|
|
12
|
+
"giget": "^3.1.2"
|
|
13
13
|
},
|
|
14
14
|
"engines": {
|
|
15
15
|
"node": ">=18"
|