create-start-app 0.3.1 → 0.4.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/README.md +53 -9
- package/dist/add-ons.js +69 -0
- package/dist/cli.js +78 -0
- package/dist/constants.js +4 -0
- package/dist/create-app.js +371 -0
- package/dist/index.js +2 -347
- package/dist/mcp.js +169 -0
- package/dist/options.js +261 -0
- package/dist/{utils/getPackageManager.js → package-manager.js} +1 -0
- package/dist/types.js +1 -0
- package/images/mcp-configuration.png +0 -0
- package/package.json +8 -4
- package/src/add-ons.ts +156 -0
- package/src/cli.ts +114 -0
- package/src/constants.ts +7 -0
- package/src/create-app.ts +582 -0
- package/src/index.ts +2 -507
- package/src/mcp.ts +205 -0
- package/src/options.ts +309 -0
- package/src/{utils/getPackageManager.ts → package-manager.ts} +1 -0
- package/src/types.ts +30 -0
- package/templates/react/add-on/clerk/README.md +3 -0
- package/templates/react/add-on/clerk/assets/_dot_env.local.append +2 -0
- package/templates/react/add-on/clerk/assets/src/integrations/clerk/header-user.tsx +19 -0
- package/templates/react/add-on/clerk/assets/src/integrations/clerk/provider.tsx +18 -0
- package/templates/react/add-on/clerk/assets/src/routes/demo.clerk.tsx +20 -0
- package/templates/react/add-on/clerk/info.json +13 -0
- package/templates/react/add-on/clerk/package.json +5 -0
- package/templates/react/add-on/convex/README.md +4 -0
- package/templates/react/add-on/convex/assets/_dot_cursorrules.append +93 -0
- package/templates/react/add-on/convex/assets/_dot_env.local.append +3 -0
- package/templates/react/add-on/convex/assets/convex/products.ts +8 -0
- package/templates/react/add-on/convex/assets/convex/schema.ts +10 -0
- package/templates/react/add-on/convex/assets/src/integrations/convex/provider.tsx +20 -0
- package/templates/react/add-on/convex/assets/src/routes/demo.convex.tsx +33 -0
- package/templates/react/add-on/convex/info.json +13 -0
- package/templates/react/add-on/convex/package.json +6 -0
- package/templates/react/add-on/form/assets/src/routes/demo.form.tsx.ejs +62 -0
- package/templates/react/add-on/form/info.json +13 -0
- package/templates/react/add-on/form/package.json +5 -0
- package/templates/react/add-on/module-federation/assets/module-federation.config.js.ejs +31 -0
- package/templates/react/add-on/module-federation/assets/src/demo-mf-component.tsx +3 -0
- package/templates/react/add-on/module-federation/assets/src/demo-mf-self-contained.tsx +11 -0
- package/templates/react/add-on/module-federation/info.json +7 -0
- package/templates/react/add-on/module-federation/package.json +5 -0
- package/templates/react/add-on/netlify/README.md +11 -0
- package/templates/react/add-on/netlify/info.json +7 -0
- package/templates/react/add-on/sentry/assets/_dot_cursorrules.append +22 -0
- package/templates/react/add-on/sentry/assets/_dot_env.local.append +2 -0
- package/templates/react/add-on/sentry/assets/src/app/global-middleware.ts +25 -0
- package/templates/react/add-on/sentry/assets/src/routes/demo.sentry.testing.tsx +480 -0
- package/templates/react/add-on/sentry/info.json +14 -0
- package/templates/react/add-on/sentry/package.json +7 -0
- package/templates/react/add-on/shadcn/README.md +7 -0
- package/templates/react/add-on/shadcn/assets/_dot_cursorrules.append +7 -0
- package/templates/react/add-on/shadcn/info.json +11 -0
- package/templates/react/add-on/start/assets/app.config.ts +16 -0
- package/templates/react/add-on/start/assets/postcss.config.ts +5 -0
- package/templates/react/add-on/start/assets/src/api.ts +6 -0
- package/templates/react/add-on/start/assets/src/client.tsx +10 -0
- package/templates/react/add-on/start/assets/src/router.tsx.ejs +51 -0
- package/templates/react/add-on/start/assets/src/routes/api.demo-names.ts +11 -0
- package/templates/react/add-on/start/assets/src/routes/demo.start.api-request.tsx.ejs +33 -0
- package/templates/react/add-on/start/assets/src/routes/demo.start.server-funcs.tsx +49 -0
- package/templates/react/add-on/start/assets/src/ssr.tsx +12 -0
- package/templates/react/add-on/start/info.json +19 -0
- package/templates/react/add-on/start/package.json +14 -0
- package/templates/react/add-on/store/assets/src/lib/demo-store.ts +13 -0
- package/templates/react/add-on/store/assets/src/routes/demo.store.tsx.ejs +75 -0
- package/templates/react/add-on/store/info.json +13 -0
- package/templates/react/add-on/store/package.json +6 -0
- package/templates/react/add-on/tanstack-query/assets/src/integrations/tanstack-query/layout.tsx +5 -0
- package/templates/react/add-on/tanstack-query/assets/src/integrations/tanstack-query/provider.tsx +9 -0
- package/templates/react/add-on/tanstack-query/assets/src/routes/demo.tanstack-query.tsx.ejs +38 -0
- package/templates/react/add-on/tanstack-query/info.json +13 -0
- package/templates/react/add-on/tanstack-query/package.json +6 -0
- package/templates/{base → react/base}/README.md.ejs +10 -1
- package/templates/react/base/src/components/Header.tsx.ejs +25 -0
- package/templates/{base/tsconfig.json → react/base/tsconfig.json.ejs} +5 -1
- package/templates/react/base/vite.config.js.ejs +24 -0
- package/templates/{code-router → react/code-router}/src/main.tsx.ejs +18 -1
- package/templates/react/example/tanchat/README.md +37 -0
- package/templates/react/example/tanchat/assets/_dot_env.local.append +2 -0
- package/templates/react/example/tanchat/assets/src/components/demo.SettingsDialog.tsx +148 -0
- package/templates/react/example/tanchat/assets/src/demo.index.css +220 -0
- package/templates/react/example/tanchat/assets/src/routes/example.chat.tsx.ejs +375 -0
- package/templates/react/example/tanchat/assets/src/store/demo.hooks.ts +21 -0
- package/templates/react/example/tanchat/assets/src/store/demo.store.ts +133 -0
- package/templates/react/example/tanchat/assets/src/utils/demo.ai.ts +108 -0
- package/templates/react/example/tanchat/info.json +15 -0
- package/templates/react/example/tanchat/package.json +10 -0
- package/templates/{file-router → react/file-router}/src/main.tsx.ejs +1 -1
- package/templates/react/file-router/src/routes/__root.tsx.ejs +71 -0
- package/templates/solid/add-on/form/assets/src/routes/demo.form.tsx.ejs +148 -0
- package/templates/solid/add-on/form/info.json +13 -0
- package/templates/solid/add-on/form/package.json +5 -0
- package/templates/solid/add-on/module-federation/assets/module-federation.config.js.ejs +27 -0
- package/templates/solid/add-on/module-federation/assets/src/demo-mf-component.tsx +3 -0
- package/templates/solid/add-on/module-federation/assets/src/demo-mf-self-contained.tsx +9 -0
- package/templates/solid/add-on/module-federation/info.json +7 -0
- package/templates/solid/add-on/module-federation/package.json +5 -0
- package/templates/solid/add-on/sentry/assets/_dot_cursorrules.append +22 -0
- package/templates/solid/add-on/sentry/assets/_dot_env.local.append +2 -0
- package/templates/solid/add-on/sentry/assets/src/routes/demo.sentry.bad-event-handler.tsx +20 -0
- package/templates/solid/add-on/sentry/info.json +13 -0
- package/templates/solid/add-on/sentry/package.json +5 -0
- package/templates/solid/add-on/solid-ui/README.md +9 -0
- package/templates/solid/add-on/solid-ui/assets/src/lib/utils.ts +6 -0
- package/templates/solid/add-on/solid-ui/assets/src/styles.css +138 -0
- package/templates/solid/add-on/solid-ui/assets/ui.config.json +13 -0
- package/templates/solid/add-on/solid-ui/info.json +11 -0
- package/templates/solid/add-on/solid-ui/package.json +9 -0
- package/templates/solid/add-on/store/assets/src/lib/demo-store.ts +13 -0
- package/templates/solid/add-on/store/assets/src/routes/demo.store.tsx.ejs +77 -0
- package/templates/solid/add-on/store/info.json +13 -0
- package/templates/solid/add-on/store/package.json +6 -0
- package/templates/solid/add-on/tanstack-query/assets/src/integrations/tanstack-query/header-user.tsx +5 -0
- package/templates/solid/add-on/tanstack-query/assets/src/integrations/tanstack-query/provider.tsx +15 -0
- package/templates/solid/add-on/tanstack-query/assets/src/routes/demo.tanstack-query.tsx +30 -0
- package/templates/solid/add-on/tanstack-query/info.json +13 -0
- package/templates/solid/add-on/tanstack-query/package.json +6 -0
- package/templates/solid/base/README.md.ejs +200 -0
- package/templates/solid/base/_dot_cursorrules.append +35 -0
- package/templates/solid/base/_dot_gitignore +5 -0
- package/templates/solid/base/_dot_vscode/settings.json +11 -0
- package/templates/solid/base/index.html.ejs +20 -0
- package/templates/solid/base/package.json +22 -0
- package/templates/solid/base/package.ts.json +5 -0
- package/templates/solid/base/package.tw.json +6 -0
- package/templates/solid/base/public/favicon.ico +0 -0
- package/templates/solid/base/public/logo192.png +0 -0
- package/templates/solid/base/public/logo512.png +0 -0
- package/templates/solid/base/public/manifest.json +25 -0
- package/templates/solid/base/public/robots.txt +3 -0
- package/templates/solid/base/src/App.css +0 -0
- package/templates/solid/base/src/App.tsx.ejs +47 -0
- package/templates/solid/base/src/components/Header.tsx.ejs +26 -0
- package/templates/solid/base/src/logo.svg +120 -0
- package/templates/solid/base/src/styles.css.ejs +15 -0
- package/templates/solid/base/tsconfig.json.ejs +30 -0
- package/templates/solid/base/vite.config.js.ejs +22 -0
- package/templates/solid/code-router/src/main.tsx.ejs +69 -0
- package/templates/solid/file-router/package.fr.json +5 -0
- package/templates/solid/file-router/src/main.tsx.ejs +44 -0
- package/templates/solid/file-router/src/routes/__root.tsx.ejs +41 -0
- package/templates/solid/file-router/src/routes/index.tsx +43 -0
- package/templates/base/vite.config.js.ejs +0 -15
- package/templates/file-router/src/routes/__root.tsx +0 -11
- /package/templates/{base → react/base}/_dot_gitignore +0 -0
- /package/templates/{base → react/base}/_dot_vscode/settings.json +0 -0
- /package/templates/{base → react/base}/index.html.ejs +0 -0
- /package/templates/{base → react/base}/package.json +0 -0
- /package/templates/{base → react/base}/package.ts.json +0 -0
- /package/templates/{base → react/base}/package.tw.json +0 -0
- /package/templates/{base → react/base}/public/favicon.ico +0 -0
- /package/templates/{base → react/base}/public/logo192.png +0 -0
- /package/templates/{base → react/base}/public/logo512.png +0 -0
- /package/templates/{base → react/base}/public/manifest.json +0 -0
- /package/templates/{base → react/base}/public/robots.txt +0 -0
- /package/templates/{base → react/base}/src/App.css +0 -0
- /package/templates/{base → react/base}/src/App.test.tsx.ejs +0 -0
- /package/templates/{base → react/base}/src/App.tsx.ejs +0 -0
- /package/templates/{base → react/base}/src/logo.svg +0 -0
- /package/templates/{base → react/base}/src/reportWebVitals.ts.ejs +0 -0
- /package/templates/{base → react/base}/src/styles.css.ejs +0 -0
- /package/templates/{file-router → react/file-router}/package.fr.json +0 -0
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
appendFile,
|
|
5
|
+
copyFile,
|
|
6
|
+
mkdir,
|
|
7
|
+
readFile,
|
|
8
|
+
writeFile,
|
|
9
|
+
} from 'node:fs/promises'
|
|
10
|
+
import { existsSync, readdirSync, statSync } from 'node:fs'
|
|
11
|
+
import { basename, dirname, resolve } from 'node:path'
|
|
12
|
+
import { fileURLToPath } from 'node:url'
|
|
13
|
+
import { log, outro, spinner } from '@clack/prompts'
|
|
14
|
+
import { execa } from 'execa'
|
|
15
|
+
import { render } from 'ejs'
|
|
16
|
+
import { format } from 'prettier'
|
|
17
|
+
import chalk from 'chalk'
|
|
18
|
+
|
|
19
|
+
import { CODE_ROUTER, FILE_ROUTER } from './constants.js'
|
|
20
|
+
|
|
21
|
+
import type { Options } from './types.js'
|
|
22
|
+
|
|
23
|
+
function sortObject(obj: Record<string, string>): Record<string, string> {
|
|
24
|
+
return Object.keys(obj)
|
|
25
|
+
.sort()
|
|
26
|
+
.reduce<Record<string, string>>((acc, key) => {
|
|
27
|
+
acc[key] = obj[key]
|
|
28
|
+
return acc
|
|
29
|
+
}, {})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function createCopyFiles(targetDir: string) {
|
|
33
|
+
return async function copyFiles(templateDir: string, files: Array<string>) {
|
|
34
|
+
for (const file of files) {
|
|
35
|
+
const targetFileName = file.replace('.tw', '')
|
|
36
|
+
await copyFile(
|
|
37
|
+
resolve(templateDir, file),
|
|
38
|
+
resolve(targetDir, targetFileName),
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function jsSafeName(name: string) {
|
|
45
|
+
return name
|
|
46
|
+
.split(/[^a-zA-Z0-9]/)
|
|
47
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
48
|
+
.join('')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function createTemplateFile(
|
|
52
|
+
projectName: string,
|
|
53
|
+
options: Required<Options>,
|
|
54
|
+
targetDir: string,
|
|
55
|
+
) {
|
|
56
|
+
return async function templateFile(
|
|
57
|
+
templateDir: string,
|
|
58
|
+
file: string,
|
|
59
|
+
targetFileName?: string,
|
|
60
|
+
extraTemplateValues?: Record<string, any>,
|
|
61
|
+
) {
|
|
62
|
+
const templateValues = {
|
|
63
|
+
packageManager: options.packageManager,
|
|
64
|
+
projectName: projectName,
|
|
65
|
+
typescript: options.typescript,
|
|
66
|
+
tailwind: options.tailwind,
|
|
67
|
+
js: options.typescript ? 'ts' : 'js',
|
|
68
|
+
jsx: options.typescript ? 'tsx' : 'jsx',
|
|
69
|
+
fileRouter: options.mode === FILE_ROUTER,
|
|
70
|
+
codeRouter: options.mode === CODE_ROUTER,
|
|
71
|
+
addOnEnabled: options.chosenAddOns.reduce<Record<string, boolean>>(
|
|
72
|
+
(acc, addOn) => {
|
|
73
|
+
acc[addOn.id] = true
|
|
74
|
+
return acc
|
|
75
|
+
},
|
|
76
|
+
{},
|
|
77
|
+
),
|
|
78
|
+
addOns: options.chosenAddOns,
|
|
79
|
+
...extraTemplateValues,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const template = await readFile(resolve(templateDir, file), 'utf-8')
|
|
83
|
+
let content = ''
|
|
84
|
+
try {
|
|
85
|
+
content = render(template, templateValues)
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.error(chalk.red(`EJS error in file ${file}`))
|
|
88
|
+
console.error(error)
|
|
89
|
+
process.exit(1)
|
|
90
|
+
}
|
|
91
|
+
const target = targetFileName ?? file.replace('.ejs', '')
|
|
92
|
+
|
|
93
|
+
if (target.endsWith('.ts') || target.endsWith('.tsx')) {
|
|
94
|
+
content = await format(content, {
|
|
95
|
+
semi: false,
|
|
96
|
+
singleQuote: true,
|
|
97
|
+
trailingComma: 'all',
|
|
98
|
+
parser: 'typescript',
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
await mkdir(dirname(resolve(targetDir, target)), {
|
|
103
|
+
recursive: true,
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
await writeFile(resolve(targetDir, target), content)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function createPackageJSON(
|
|
111
|
+
projectName: string,
|
|
112
|
+
options: Required<Options>,
|
|
113
|
+
templateDir: string,
|
|
114
|
+
routerDir: string,
|
|
115
|
+
targetDir: string,
|
|
116
|
+
addOns: Array<{
|
|
117
|
+
dependencies?: Record<string, string>
|
|
118
|
+
devDependencies?: Record<string, string>
|
|
119
|
+
scripts?: Record<string, string>
|
|
120
|
+
}>,
|
|
121
|
+
) {
|
|
122
|
+
let packageJSON = JSON.parse(
|
|
123
|
+
await readFile(resolve(templateDir, 'package.json'), 'utf8'),
|
|
124
|
+
)
|
|
125
|
+
packageJSON.name = projectName
|
|
126
|
+
if (options.typescript) {
|
|
127
|
+
const tsPackageJSON = JSON.parse(
|
|
128
|
+
await readFile(resolve(templateDir, 'package.ts.json'), 'utf8'),
|
|
129
|
+
)
|
|
130
|
+
packageJSON = {
|
|
131
|
+
...packageJSON,
|
|
132
|
+
devDependencies: {
|
|
133
|
+
...packageJSON.devDependencies,
|
|
134
|
+
...tsPackageJSON.devDependencies,
|
|
135
|
+
},
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (options.tailwind) {
|
|
139
|
+
const twPackageJSON = JSON.parse(
|
|
140
|
+
await readFile(resolve(templateDir, 'package.tw.json'), 'utf8'),
|
|
141
|
+
)
|
|
142
|
+
packageJSON = {
|
|
143
|
+
...packageJSON,
|
|
144
|
+
dependencies: {
|
|
145
|
+
...packageJSON.dependencies,
|
|
146
|
+
...twPackageJSON.dependencies,
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (options.mode === FILE_ROUTER) {
|
|
151
|
+
const frPackageJSON = JSON.parse(
|
|
152
|
+
await readFile(resolve(routerDir, 'package.fr.json'), 'utf8'),
|
|
153
|
+
)
|
|
154
|
+
packageJSON = {
|
|
155
|
+
...packageJSON,
|
|
156
|
+
dependencies: {
|
|
157
|
+
...packageJSON.dependencies,
|
|
158
|
+
...frPackageJSON.dependencies,
|
|
159
|
+
},
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
for (const addOn of addOns) {
|
|
164
|
+
packageJSON = {
|
|
165
|
+
...packageJSON,
|
|
166
|
+
dependencies: {
|
|
167
|
+
...packageJSON.dependencies,
|
|
168
|
+
...addOn.dependencies,
|
|
169
|
+
},
|
|
170
|
+
devDependencies: {
|
|
171
|
+
...packageJSON.devDependencies,
|
|
172
|
+
...addOn.devDependencies,
|
|
173
|
+
},
|
|
174
|
+
scripts: {
|
|
175
|
+
...packageJSON.scripts,
|
|
176
|
+
...addOn.scripts,
|
|
177
|
+
},
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
packageJSON.dependencies = sortObject(
|
|
182
|
+
packageJSON.dependencies as Record<string, string>,
|
|
183
|
+
)
|
|
184
|
+
packageJSON.devDependencies = sortObject(
|
|
185
|
+
packageJSON.devDependencies as Record<string, string>,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
await writeFile(
|
|
189
|
+
resolve(targetDir, 'package.json'),
|
|
190
|
+
JSON.stringify(packageJSON, null, 2),
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async function copyFilesRecursively(
|
|
195
|
+
source: string,
|
|
196
|
+
target: string,
|
|
197
|
+
copyFile: (source: string, target: string) => Promise<void>,
|
|
198
|
+
templateFile: (file: string, targetFileName?: string) => Promise<void>,
|
|
199
|
+
) {
|
|
200
|
+
const sourceStat = statSync(source)
|
|
201
|
+
if (sourceStat.isDirectory()) {
|
|
202
|
+
const files = readdirSync(source)
|
|
203
|
+
for (const file of files) {
|
|
204
|
+
const sourceChild = resolve(source, file)
|
|
205
|
+
const targetChild = resolve(target, file)
|
|
206
|
+
await copyFilesRecursively(
|
|
207
|
+
sourceChild,
|
|
208
|
+
targetChild,
|
|
209
|
+
copyFile,
|
|
210
|
+
templateFile,
|
|
211
|
+
)
|
|
212
|
+
}
|
|
213
|
+
} else {
|
|
214
|
+
let targetFile = basename(target).replace(/_dot_/, '.')
|
|
215
|
+
let isTemplate = false
|
|
216
|
+
if (targetFile.endsWith('.ejs')) {
|
|
217
|
+
targetFile = targetFile.replace('.ejs', '')
|
|
218
|
+
isTemplate = true
|
|
219
|
+
}
|
|
220
|
+
let isAppend = false
|
|
221
|
+
if (targetFile.endsWith('.append')) {
|
|
222
|
+
targetFile = targetFile.replace('.append', '')
|
|
223
|
+
isAppend = true
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const targetPath = resolve(dirname(target), targetFile)
|
|
227
|
+
|
|
228
|
+
await mkdir(dirname(targetPath), {
|
|
229
|
+
recursive: true,
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
if (isTemplate) {
|
|
233
|
+
await templateFile(source, targetPath)
|
|
234
|
+
} else {
|
|
235
|
+
if (isAppend) {
|
|
236
|
+
await appendFile(targetPath, (await readFile(source)).toString())
|
|
237
|
+
} else {
|
|
238
|
+
await copyFile(source, targetPath)
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export async function createApp(
|
|
245
|
+
options: Required<Options>,
|
|
246
|
+
{
|
|
247
|
+
silent = false,
|
|
248
|
+
}: {
|
|
249
|
+
silent?: boolean
|
|
250
|
+
} = {},
|
|
251
|
+
) {
|
|
252
|
+
const templateDirBase = fileURLToPath(
|
|
253
|
+
new URL(`../templates/${options.framework}/base`, import.meta.url),
|
|
254
|
+
)
|
|
255
|
+
const templateDirRouter = fileURLToPath(
|
|
256
|
+
new URL(
|
|
257
|
+
`../templates/${options.framework}/${options.mode}`,
|
|
258
|
+
import.meta.url,
|
|
259
|
+
),
|
|
260
|
+
)
|
|
261
|
+
const targetDir = resolve(process.cwd(), options.projectName)
|
|
262
|
+
|
|
263
|
+
if (existsSync(targetDir)) {
|
|
264
|
+
if (!silent) {
|
|
265
|
+
log.error(`Directory "${options.projectName}" already exists`)
|
|
266
|
+
}
|
|
267
|
+
return
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const copyFiles = createCopyFiles(targetDir)
|
|
271
|
+
const templateFile = createTemplateFile(
|
|
272
|
+
options.projectName,
|
|
273
|
+
options,
|
|
274
|
+
targetDir,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
const isAddOnEnabled = (id: string) =>
|
|
278
|
+
options.chosenAddOns.find((a) => a.id === id)
|
|
279
|
+
|
|
280
|
+
// Make the root directory
|
|
281
|
+
await mkdir(targetDir, { recursive: true })
|
|
282
|
+
|
|
283
|
+
// Setup the .vscode directory
|
|
284
|
+
await mkdir(resolve(targetDir, '.vscode'), { recursive: true })
|
|
285
|
+
await copyFile(
|
|
286
|
+
resolve(templateDirBase, '_dot_vscode/settings.json'),
|
|
287
|
+
resolve(targetDir, '.vscode/settings.json'),
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
// Fill the public directory
|
|
291
|
+
await mkdir(resolve(targetDir, 'public'), { recursive: true })
|
|
292
|
+
copyFiles(templateDirBase, [
|
|
293
|
+
'./public/robots.txt',
|
|
294
|
+
'./public/favicon.ico',
|
|
295
|
+
'./public/manifest.json',
|
|
296
|
+
'./public/logo192.png',
|
|
297
|
+
'./public/logo512.png',
|
|
298
|
+
])
|
|
299
|
+
|
|
300
|
+
// Make the src directory
|
|
301
|
+
await mkdir(resolve(targetDir, 'src'), { recursive: true })
|
|
302
|
+
if (options.mode === FILE_ROUTER) {
|
|
303
|
+
await mkdir(resolve(targetDir, 'src/routes'), { recursive: true })
|
|
304
|
+
await mkdir(resolve(targetDir, 'src/components'), { recursive: true })
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Check for a .cursorrules file
|
|
308
|
+
if (existsSync(resolve(templateDirBase, '.cursorrules'))) {
|
|
309
|
+
await copyFile(
|
|
310
|
+
resolve(templateDirBase, '.cursorrules'),
|
|
311
|
+
resolve(targetDir, '.cursorrules'),
|
|
312
|
+
)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Copy in Vite and Tailwind config and CSS
|
|
316
|
+
if (!options.tailwind) {
|
|
317
|
+
await copyFiles(templateDirBase, ['./src/App.css'])
|
|
318
|
+
}
|
|
319
|
+
await templateFile(templateDirBase, './vite.config.js.ejs')
|
|
320
|
+
await templateFile(templateDirBase, './src/styles.css.ejs')
|
|
321
|
+
|
|
322
|
+
copyFiles(templateDirBase, ['./src/logo.svg'])
|
|
323
|
+
|
|
324
|
+
// Setup the main, reportWebVitals and index.html files
|
|
325
|
+
if (!isAddOnEnabled('start') && options.framework === 'react') {
|
|
326
|
+
if (options.typescript) {
|
|
327
|
+
await templateFile(templateDirBase, './src/reportWebVitals.ts.ejs')
|
|
328
|
+
} else {
|
|
329
|
+
await templateFile(
|
|
330
|
+
templateDirBase,
|
|
331
|
+
'./src/reportWebVitals.ts.ejs',
|
|
332
|
+
'./src/reportWebVitals.js',
|
|
333
|
+
)
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (!isAddOnEnabled('start')) {
|
|
337
|
+
await templateFile(templateDirBase, './index.html.ejs')
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Setup tsconfig
|
|
341
|
+
if (options.typescript) {
|
|
342
|
+
await templateFile(
|
|
343
|
+
templateDirBase,
|
|
344
|
+
'./tsconfig.json.ejs',
|
|
345
|
+
'./tsconfig.json',
|
|
346
|
+
)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Setup the package.json file, optionally with typescript and tailwind
|
|
350
|
+
await createPackageJSON(
|
|
351
|
+
options.projectName,
|
|
352
|
+
options,
|
|
353
|
+
templateDirBase,
|
|
354
|
+
templateDirRouter,
|
|
355
|
+
targetDir,
|
|
356
|
+
options.chosenAddOns.map((addOn) => addOn.packageAdditions),
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
// Copy all the asset files from the addons
|
|
360
|
+
const s = silent ? null : spinner()
|
|
361
|
+
for (const phase of ['setup', 'add-on', 'example']) {
|
|
362
|
+
for (const addOn of options.chosenAddOns.filter(
|
|
363
|
+
(addOn) => addOn.phase === phase,
|
|
364
|
+
)) {
|
|
365
|
+
s?.start(`Setting up ${addOn.name}...`)
|
|
366
|
+
const addOnDir = resolve(addOn.directory, 'assets')
|
|
367
|
+
if (existsSync(addOnDir)) {
|
|
368
|
+
await copyFilesRecursively(
|
|
369
|
+
addOnDir,
|
|
370
|
+
targetDir,
|
|
371
|
+
copyFile,
|
|
372
|
+
async (file: string, targetFileName?: string) =>
|
|
373
|
+
templateFile(addOnDir, file, targetFileName),
|
|
374
|
+
)
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (addOn.command) {
|
|
378
|
+
await execa(addOn.command.command, addOn.command.args || [], {
|
|
379
|
+
cwd: targetDir,
|
|
380
|
+
})
|
|
381
|
+
}
|
|
382
|
+
s?.stop(`${addOn.name} setup complete`)
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (isAddOnEnabled('shadcn')) {
|
|
387
|
+
const shadcnComponents = new Set<string>()
|
|
388
|
+
for (const addOn of options.chosenAddOns) {
|
|
389
|
+
if (addOn.shadcnComponents) {
|
|
390
|
+
for (const component of addOn.shadcnComponents) {
|
|
391
|
+
shadcnComponents.add(component)
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (shadcnComponents.size > 0) {
|
|
397
|
+
s?.start(
|
|
398
|
+
`Installing shadcn components (${Array.from(shadcnComponents).join(', ')})...`,
|
|
399
|
+
)
|
|
400
|
+
await execa('npx', ['shadcn@canary', 'add', ...shadcnComponents], {
|
|
401
|
+
cwd: targetDir,
|
|
402
|
+
})
|
|
403
|
+
s?.stop(`Installed shadcn components`)
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const integrations: Array<{
|
|
408
|
+
type: 'layout' | 'provider' | 'header-user'
|
|
409
|
+
name: string
|
|
410
|
+
path: string
|
|
411
|
+
}> = []
|
|
412
|
+
if (existsSync(resolve(targetDir, 'src/integrations'))) {
|
|
413
|
+
for (const integration of readdirSync(
|
|
414
|
+
resolve(targetDir, 'src/integrations'),
|
|
415
|
+
)) {
|
|
416
|
+
const integrationName = jsSafeName(integration)
|
|
417
|
+
if (
|
|
418
|
+
existsSync(
|
|
419
|
+
resolve(targetDir, 'src/integrations', integration, 'layout.tsx'),
|
|
420
|
+
)
|
|
421
|
+
) {
|
|
422
|
+
integrations.push({
|
|
423
|
+
type: 'layout',
|
|
424
|
+
name: `${integrationName}Layout`,
|
|
425
|
+
path: `integrations/${integration}/layout`,
|
|
426
|
+
})
|
|
427
|
+
}
|
|
428
|
+
if (
|
|
429
|
+
existsSync(
|
|
430
|
+
resolve(targetDir, 'src/integrations', integration, 'provider.tsx'),
|
|
431
|
+
)
|
|
432
|
+
) {
|
|
433
|
+
integrations.push({
|
|
434
|
+
type: 'provider',
|
|
435
|
+
name: `${integrationName}Provider`,
|
|
436
|
+
path: `integrations/${integration}/provider`,
|
|
437
|
+
})
|
|
438
|
+
}
|
|
439
|
+
if (
|
|
440
|
+
existsSync(
|
|
441
|
+
resolve(
|
|
442
|
+
targetDir,
|
|
443
|
+
'src/integrations',
|
|
444
|
+
integration,
|
|
445
|
+
'header-user.tsx',
|
|
446
|
+
),
|
|
447
|
+
)
|
|
448
|
+
) {
|
|
449
|
+
integrations.push({
|
|
450
|
+
type: 'header-user',
|
|
451
|
+
name: `${integrationName}Header`,
|
|
452
|
+
path: `integrations/${integration}/header-user`,
|
|
453
|
+
})
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const routes: Array<{
|
|
459
|
+
path: string
|
|
460
|
+
name: string
|
|
461
|
+
}> = []
|
|
462
|
+
if (existsSync(resolve(targetDir, 'src/routes'))) {
|
|
463
|
+
for (const file of readdirSync(resolve(targetDir, 'src/routes'))) {
|
|
464
|
+
const name = file.replace(/\.tsx?|\.jsx?/, '')
|
|
465
|
+
const safeRouteName = jsSafeName(name)
|
|
466
|
+
routes.push({
|
|
467
|
+
path: `./routes/${name}`,
|
|
468
|
+
name: safeRouteName,
|
|
469
|
+
})
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Create the main entry point
|
|
474
|
+
if (!isAddOnEnabled('start')) {
|
|
475
|
+
if (options.typescript) {
|
|
476
|
+
await templateFile(
|
|
477
|
+
templateDirRouter,
|
|
478
|
+
'./src/main.tsx.ejs',
|
|
479
|
+
'./src/main.tsx',
|
|
480
|
+
{
|
|
481
|
+
routes,
|
|
482
|
+
integrations,
|
|
483
|
+
},
|
|
484
|
+
)
|
|
485
|
+
} else {
|
|
486
|
+
await templateFile(
|
|
487
|
+
templateDirRouter,
|
|
488
|
+
'./src/main.tsx.ejs',
|
|
489
|
+
'./src/main.jsx',
|
|
490
|
+
{
|
|
491
|
+
routes,
|
|
492
|
+
integrations,
|
|
493
|
+
},
|
|
494
|
+
)
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Setup the app component. There are four variations, typescript/javascript and tailwind/non-tailwind.
|
|
499
|
+
if (options.mode === FILE_ROUTER) {
|
|
500
|
+
await templateFile(
|
|
501
|
+
templateDirRouter,
|
|
502
|
+
'./src/routes/__root.tsx.ejs',
|
|
503
|
+
'./src/routes/__root.tsx',
|
|
504
|
+
{
|
|
505
|
+
integrations,
|
|
506
|
+
},
|
|
507
|
+
)
|
|
508
|
+
await templateFile(
|
|
509
|
+
templateDirBase,
|
|
510
|
+
'./src/App.tsx.ejs',
|
|
511
|
+
'./src/routes/index.tsx',
|
|
512
|
+
)
|
|
513
|
+
} else {
|
|
514
|
+
await templateFile(
|
|
515
|
+
templateDirBase,
|
|
516
|
+
'./src/App.tsx.ejs',
|
|
517
|
+
options.typescript ? undefined : './src/App.jsx',
|
|
518
|
+
)
|
|
519
|
+
if (options.framework === 'react') {
|
|
520
|
+
await templateFile(
|
|
521
|
+
templateDirBase,
|
|
522
|
+
'./src/App.test.tsx.ejs',
|
|
523
|
+
options.typescript ? undefined : './src/App.test.jsx',
|
|
524
|
+
)
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if (routes.length > 0) {
|
|
529
|
+
await templateFile(
|
|
530
|
+
templateDirBase,
|
|
531
|
+
'./src/components/Header.tsx.ejs',
|
|
532
|
+
'./src/components/Header.tsx',
|
|
533
|
+
{
|
|
534
|
+
integrations,
|
|
535
|
+
},
|
|
536
|
+
)
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const warnings: Array<string> = []
|
|
540
|
+
for (const addOn of options.chosenAddOns) {
|
|
541
|
+
if (addOn.warning) {
|
|
542
|
+
warnings.push(addOn.warning)
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Add .gitignore
|
|
547
|
+
await copyFile(
|
|
548
|
+
resolve(templateDirBase, '_dot_gitignore'),
|
|
549
|
+
resolve(targetDir, '.gitignore'),
|
|
550
|
+
)
|
|
551
|
+
|
|
552
|
+
// Create the README.md
|
|
553
|
+
await templateFile(templateDirBase, 'README.md.ejs')
|
|
554
|
+
|
|
555
|
+
// Install dependencies
|
|
556
|
+
s?.start(`Installing dependencies via ${options.packageManager}...`)
|
|
557
|
+
await execa(options.packageManager, ['install'], { cwd: targetDir })
|
|
558
|
+
s?.stop(`Installed dependencies`)
|
|
559
|
+
|
|
560
|
+
if (warnings.length > 0) {
|
|
561
|
+
if (!silent) {
|
|
562
|
+
log.warn(chalk.red(warnings.join('\n')))
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
if (options.git) {
|
|
567
|
+
s?.start(`Initializing git repository...`)
|
|
568
|
+
await execa('git', ['init'], { cwd: targetDir })
|
|
569
|
+
s?.stop(`Initialized git repository`)
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (!silent) {
|
|
573
|
+
outro(`Created your new TanStack app in '${basename(targetDir)}'.
|
|
574
|
+
|
|
575
|
+
Use the following commands to start your app:
|
|
576
|
+
% cd ${options.projectName}
|
|
577
|
+
% ${options.packageManager === 'deno' ? 'deno start' : options.packageManager} ${isAddOnEnabled('start') ? 'dev' : 'start'}
|
|
578
|
+
|
|
579
|
+
Please read README.md for more information on testing, styling, adding routes, react-query, etc.
|
|
580
|
+
`)
|
|
581
|
+
}
|
|
582
|
+
}
|