@tanstack/create 0.68.1 → 0.68.3
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/CHANGELOG.md +12 -0
- package/dist/create-app.js +22 -0
- package/dist/edge-add-ons.js +106 -0
- package/dist/edge-config-file.js +15 -0
- package/dist/edge-create-app.js +438 -0
- package/dist/edge-environment.js +141 -0
- package/dist/edge-file-helpers.js +88 -0
- package/dist/edge-frameworks.js +33 -0
- package/dist/edge-package-json.js +146 -0
- package/dist/edge-path.js +62 -0
- package/dist/edge-render.js +31 -0
- package/dist/edge-template-file.js +141 -0
- package/dist/edge.js +7 -0
- package/dist/frameworks/react/add-ons/storybook/info.json +5 -10
- package/dist/frameworks/react/project/base/_dot_gitignore +0 -1
- package/dist/frameworks/react/project/base/package.json +1 -0
- package/dist/frameworks/react/project/base/tsr.config.json +3 -0
- package/dist/frameworks/react/project/packages.json +3 -0
- package/dist/frameworks/solid/project/base/_dot_gitignore +0 -1
- package/dist/frameworks/solid/project/base/package.json +1 -0
- package/dist/frameworks/solid/project/base/tsr.config.json +3 -0
- package/dist/frameworks/solid/project/packages.json +3 -0
- package/dist/generated/create-manifest.js +4683 -0
- package/dist/manifest-types.js +1 -0
- package/dist/manifest.js +1 -0
- package/dist/types/custom-add-ons/add-on.d.ts +5 -3
- package/dist/types/edge-add-ons.d.ts +5 -0
- package/dist/types/edge-config-file.d.ts +8 -0
- package/dist/types/edge-create-app.d.ts +2 -0
- package/dist/types/edge-environment.d.ts +19 -0
- package/dist/types/edge-file-helpers.d.ts +7 -0
- package/dist/types/edge-frameworks.d.ts +7 -0
- package/dist/types/edge-package-json.d.ts +3 -0
- package/dist/types/edge-path.d.ts +5 -0
- package/dist/types/edge-render.d.ts +1 -0
- package/dist/types/edge-template-file.d.ts +2 -0
- package/dist/types/edge.d.ts +9 -0
- package/dist/types/generated/create-manifest.d.ts +36 -0
- package/dist/types/manifest-types.d.ts +4 -0
- package/dist/types/manifest.d.ts +1 -0
- package/dist/types/types.d.ts +96 -56
- package/dist/types.js +5 -3
- package/package.json +25 -5
- package/scripts/generate-manifest.mjs +407 -0
- package/src/create-app.ts +32 -1
- package/src/edge-add-ons.ts +138 -0
- package/src/edge-config-file.ts +35 -0
- package/src/edge-create-app.ts +594 -0
- package/src/edge-environment.ts +175 -0
- package/src/edge-file-helpers.ts +112 -0
- package/src/edge-frameworks.ts +54 -0
- package/src/edge-package-json.ts +212 -0
- package/src/edge-path.ts +77 -0
- package/src/edge-render.ts +32 -0
- package/src/edge-template-file.ts +204 -0
- package/src/edge.ts +43 -0
- package/src/frameworks/react/add-ons/storybook/info.json +5 -10
- package/src/frameworks/react/project/base/_dot_gitignore +0 -1
- package/src/frameworks/react/project/base/package.json +1 -0
- package/src/frameworks/react/project/base/tsr.config.json +3 -0
- package/src/frameworks/react/project/packages.json +3 -0
- package/src/frameworks/solid/project/base/_dot_gitignore +0 -1
- package/src/frameworks/solid/project/base/package.json +1 -0
- package/src/frameworks/solid/project/base/tsr.config.json +3 -0
- package/src/frameworks/solid/project/packages.json +3 -0
- package/src/generated/create-manifest.ts +6490 -0
- package/src/manifest-types.ts +8 -0
- package/src/manifest.ts +1 -0
- package/src/types.ts +5 -3
- package/tests/create-app.test.ts +39 -3
- package/tests/edge-import.test.ts +31 -0
- package/tests/edge-manifest.test.ts +168 -0
- package/tests/framework-template.test.ts +21 -2
- package/dist/frameworks/react/add-ons/storybook/assets/_dot_storybook/main.ts +0 -17
- package/dist/frameworks/react/add-ons/storybook/assets/_dot_storybook/preview.ts +0 -15
- package/dist/frameworks/react/add-ons/storybook/assets/src/components/storybook/button.stories.ts +0 -67
- package/dist/frameworks/react/add-ons/storybook/assets/src/components/storybook/button.tsx +0 -47
- package/dist/frameworks/react/add-ons/storybook/assets/src/components/storybook/dialog.stories.tsx +0 -92
- package/dist/frameworks/react/add-ons/storybook/assets/src/components/storybook/dialog.tsx +0 -29
- package/dist/frameworks/react/add-ons/storybook/assets/src/components/storybook/index.ts +0 -14
- package/dist/frameworks/react/add-ons/storybook/assets/src/components/storybook/input.stories.ts +0 -43
- package/dist/frameworks/react/add-ons/storybook/assets/src/components/storybook/input.tsx +0 -39
- package/dist/frameworks/react/add-ons/storybook/assets/src/components/storybook/radio-group.stories.ts +0 -53
- package/dist/frameworks/react/add-ons/storybook/assets/src/components/storybook/radio-group.tsx +0 -52
- package/dist/frameworks/react/add-ons/storybook/assets/src/components/storybook/slider.stories.ts +0 -55
- package/dist/frameworks/react/add-ons/storybook/assets/src/components/storybook/slider.tsx +0 -57
- package/dist/frameworks/react/add-ons/storybook/assets/src/routes/demo/storybook.tsx +0 -93
- package/dist/frameworks/react/add-ons/storybook/package.json +0 -10
- package/src/frameworks/react/add-ons/storybook/assets/_dot_storybook/main.ts +0 -17
- package/src/frameworks/react/add-ons/storybook/assets/_dot_storybook/preview.ts +0 -15
- package/src/frameworks/react/add-ons/storybook/assets/src/components/storybook/button.stories.ts +0 -67
- package/src/frameworks/react/add-ons/storybook/assets/src/components/storybook/button.tsx +0 -47
- package/src/frameworks/react/add-ons/storybook/assets/src/components/storybook/dialog.stories.tsx +0 -92
- package/src/frameworks/react/add-ons/storybook/assets/src/components/storybook/dialog.tsx +0 -29
- package/src/frameworks/react/add-ons/storybook/assets/src/components/storybook/index.ts +0 -14
- package/src/frameworks/react/add-ons/storybook/assets/src/components/storybook/input.stories.ts +0 -43
- package/src/frameworks/react/add-ons/storybook/assets/src/components/storybook/input.tsx +0 -39
- package/src/frameworks/react/add-ons/storybook/assets/src/components/storybook/radio-group.stories.ts +0 -53
- package/src/frameworks/react/add-ons/storybook/assets/src/components/storybook/radio-group.tsx +0 -52
- package/src/frameworks/react/add-ons/storybook/assets/src/components/storybook/slider.stories.ts +0 -55
- package/src/frameworks/react/add-ons/storybook/assets/src/components/storybook/slider.tsx +0 -57
- package/src/frameworks/react/add-ons/storybook/assets/src/routes/demo/storybook.tsx +0 -93
- package/src/frameworks/react/add-ons/storybook/package.json +0 -10
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
import { createPackageJSON } from './edge-package-json.js'
|
|
2
|
+
import { createTemplateFile } from './edge-template-file.js'
|
|
3
|
+
import { formatCommand } from './utils.js'
|
|
4
|
+
import { isBase64, isDemoFilePath } from './edge-file-helpers.js'
|
|
5
|
+
import { basenamePath, joinPaths, normalizePath } from './edge-path.js'
|
|
6
|
+
import { resolvePackageJSONLatest } from './npm-resolver.js'
|
|
7
|
+
import { writeConfigFileToEnvironment } from './edge-config-file.js'
|
|
8
|
+
import {
|
|
9
|
+
getPackageManagerExecuteCommand,
|
|
10
|
+
getPackageManagerScriptCommand,
|
|
11
|
+
packageManagerInstall,
|
|
12
|
+
translateExecuteCommand,
|
|
13
|
+
} from './package-manager.js'
|
|
14
|
+
|
|
15
|
+
import type { Environment, FileBundleHandler, Options } from './types.js'
|
|
16
|
+
|
|
17
|
+
function stripExamplesFromOptions(options: Options): Options {
|
|
18
|
+
if (options.includeExamples !== false) {
|
|
19
|
+
return options
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const chosenAddOns = options.chosenAddOns
|
|
23
|
+
.filter((addOn) => addOn.type !== 'example')
|
|
24
|
+
.map((addOn) => {
|
|
25
|
+
const filteredRoutes = (addOn.routes || []).filter(
|
|
26
|
+
(route) =>
|
|
27
|
+
!isDemoFilePath(route.path) &&
|
|
28
|
+
!(route.url && route.url.startsWith('/demo')),
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
const filteredIntegrations = (addOn.integrations || []).filter(
|
|
32
|
+
(integration) => !isDemoFilePath(integration.path),
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
...addOn,
|
|
37
|
+
routes: filteredRoutes,
|
|
38
|
+
integrations: filteredIntegrations,
|
|
39
|
+
getFiles: async () => {
|
|
40
|
+
const files = await addOn.getFiles()
|
|
41
|
+
return files.filter((file) => !isDemoFilePath(file))
|
|
42
|
+
},
|
|
43
|
+
getDeletedFiles: async () => {
|
|
44
|
+
const deletedFiles = await addOn.getDeletedFiles()
|
|
45
|
+
return deletedFiles.filter((file) => !isDemoFilePath(file))
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
...options,
|
|
52
|
+
chosenAddOns,
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function writeFiles(environment: Environment, options: Options) {
|
|
57
|
+
const templateFileFromContent = createTemplateFile(environment, options)
|
|
58
|
+
|
|
59
|
+
async function writeFileBundle(bundle: FileBundleHandler) {
|
|
60
|
+
const files = await bundle.getFiles()
|
|
61
|
+
|
|
62
|
+
for (const file of files) {
|
|
63
|
+
const contents = await bundle.getFileContents(file)
|
|
64
|
+
|
|
65
|
+
if (isBase64(contents)) {
|
|
66
|
+
await environment.writeFileBase64(
|
|
67
|
+
joinPaths(options.targetDir, file),
|
|
68
|
+
contents,
|
|
69
|
+
)
|
|
70
|
+
} else {
|
|
71
|
+
await templateFileFromContent(file, contents)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const deletedFiles = await bundle.getDeletedFiles()
|
|
76
|
+
for (const file of deletedFiles) {
|
|
77
|
+
await environment.deleteFile(joinPaths(options.targetDir, file))
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
environment.startStep({
|
|
82
|
+
id: 'write-framework-files',
|
|
83
|
+
type: 'file',
|
|
84
|
+
message: 'Writing framework files...',
|
|
85
|
+
})
|
|
86
|
+
await writeFileBundle(options.framework)
|
|
87
|
+
environment.finishStep('write-framework-files', 'Framework files written')
|
|
88
|
+
|
|
89
|
+
let wroteAddonFiles = false
|
|
90
|
+
for (const type of ['add-on', 'example', 'toolchain', 'deployment']) {
|
|
91
|
+
for (const phase of ['setup', 'add-on', 'example']) {
|
|
92
|
+
for (const addOn of options.chosenAddOns.filter(
|
|
93
|
+
(addOn) => addOn.phase === phase && addOn.type === type,
|
|
94
|
+
)) {
|
|
95
|
+
environment.startStep({
|
|
96
|
+
id: 'write-addon-files',
|
|
97
|
+
type: 'file',
|
|
98
|
+
message: `Writing ${addOn.name} files...`,
|
|
99
|
+
})
|
|
100
|
+
await writeFileBundle(addOn)
|
|
101
|
+
wroteAddonFiles = true
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (wroteAddonFiles) {
|
|
106
|
+
environment.finishStep('write-addon-files', 'Add-on files written')
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (options.starter) {
|
|
110
|
+
environment.startStep({
|
|
111
|
+
id: 'write-starter-files',
|
|
112
|
+
type: 'file',
|
|
113
|
+
message: 'Writing starter files...',
|
|
114
|
+
})
|
|
115
|
+
await writeFileBundle(options.starter)
|
|
116
|
+
environment.finishStep('write-starter-files', 'Starter files written')
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
environment.startStep({
|
|
120
|
+
id: 'write-package-json',
|
|
121
|
+
type: 'file',
|
|
122
|
+
message: 'Writing package.json...',
|
|
123
|
+
})
|
|
124
|
+
const packageJSON = await resolvePackageJSONLatest(createPackageJSON(options))
|
|
125
|
+
await environment.writeFile(
|
|
126
|
+
joinPaths(options.targetDir, './package.json'),
|
|
127
|
+
JSON.stringify(packageJSON, null, 2),
|
|
128
|
+
)
|
|
129
|
+
environment.finishStep('write-package-json', 'Package.json written')
|
|
130
|
+
|
|
131
|
+
environment.startStep({
|
|
132
|
+
id: 'write-config-file',
|
|
133
|
+
type: 'file',
|
|
134
|
+
message: 'Writing config file...',
|
|
135
|
+
})
|
|
136
|
+
await writeConfigFileToEnvironment(environment, options)
|
|
137
|
+
environment.finishStep('write-config-file', 'Config file written')
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function runSpecialSteps(
|
|
141
|
+
environment: Environment,
|
|
142
|
+
options: Options,
|
|
143
|
+
specialSteps: Array<string>,
|
|
144
|
+
) {
|
|
145
|
+
if (!specialSteps.length) {
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
environment.startStep({
|
|
150
|
+
id: 'special-steps',
|
|
151
|
+
type: 'command',
|
|
152
|
+
message: 'Running special steps...',
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
for (const step of specialSteps) {
|
|
156
|
+
if (step === 'rimraf-node-modules') {
|
|
157
|
+
await environment.rimraf(joinPaths(options.targetDir, 'node_modules'))
|
|
158
|
+
for (const lockFile of [
|
|
159
|
+
'package-lock.json',
|
|
160
|
+
'yarn.lock',
|
|
161
|
+
'pnpm-lock.yaml',
|
|
162
|
+
]) {
|
|
163
|
+
const lockFilePath = joinPaths(options.targetDir, lockFile)
|
|
164
|
+
if (environment.exists(lockFilePath)) {
|
|
165
|
+
await environment.deleteFile(lockFilePath)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
continue
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (step === 'post-init-script') {
|
|
172
|
+
const packageJsonPath = joinPaths(options.targetDir, 'package.json')
|
|
173
|
+
if (!environment.exists(packageJsonPath)) {
|
|
174
|
+
environment.warn(
|
|
175
|
+
'Warning',
|
|
176
|
+
'No package.json found, skipping post-create-init script',
|
|
177
|
+
)
|
|
178
|
+
continue
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const packageJson = JSON.parse(await environment.readFile(packageJsonPath))
|
|
182
|
+
const postCreateInit = packageJson.scripts?.['post-create-init']
|
|
183
|
+
if (postCreateInit) {
|
|
184
|
+
const { command, args } = getPackageManagerScriptCommand(
|
|
185
|
+
options.packageManager,
|
|
186
|
+
['post-create-init'],
|
|
187
|
+
)
|
|
188
|
+
await environment.execute(command, args, options.targetDir, {
|
|
189
|
+
inherit: true,
|
|
190
|
+
})
|
|
191
|
+
}
|
|
192
|
+
continue
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
environment.error(`Special step ${step} not found`)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
environment.finishStep('special-steps', 'Special steps complete')
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function installShadcnComponents(
|
|
202
|
+
environment: Environment,
|
|
203
|
+
targetDir: string,
|
|
204
|
+
options: Options,
|
|
205
|
+
) {
|
|
206
|
+
if (!options.chosenAddOns.find((a) => a.id === 'shadcn')) {
|
|
207
|
+
return
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const shadcnComponents = new Set<string>()
|
|
211
|
+
for (const addOn of options.chosenAddOns) {
|
|
212
|
+
if (addOn.shadcnComponents) {
|
|
213
|
+
for (const component of addOn.shadcnComponents) {
|
|
214
|
+
shadcnComponents.add(component)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (options.starter?.shadcnComponents) {
|
|
219
|
+
for (const component of options.starter.shadcnComponents) {
|
|
220
|
+
shadcnComponents.add(component)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (shadcnComponents.size > 0) {
|
|
225
|
+
environment.startStep({
|
|
226
|
+
id: 'install-shadcn-components',
|
|
227
|
+
type: 'command',
|
|
228
|
+
message: `Installing shadcn components (${Array.from(shadcnComponents).join(', ')})...`,
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
const { command, args } = getPackageManagerExecuteCommand(
|
|
232
|
+
options.packageManager,
|
|
233
|
+
'shadcn@latest',
|
|
234
|
+
['add', '--silent', '--yes', ...Array.from(shadcnComponents)],
|
|
235
|
+
)
|
|
236
|
+
await environment.execute(command, args, normalizePath(targetDir))
|
|
237
|
+
|
|
238
|
+
environment.finishStep(
|
|
239
|
+
'install-shadcn-components',
|
|
240
|
+
'Shadcn components installed',
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async function setupIntent(
|
|
246
|
+
environment: Environment,
|
|
247
|
+
targetDir: string,
|
|
248
|
+
options: Options,
|
|
249
|
+
) {
|
|
250
|
+
if (!options.intent) {
|
|
251
|
+
return
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
environment.startStep({
|
|
255
|
+
id: 'setup-intent',
|
|
256
|
+
type: 'command',
|
|
257
|
+
message: 'Setting up TanStack Intent skill mappings...',
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
const { command, args } = getPackageManagerExecuteCommand(
|
|
261
|
+
options.packageManager,
|
|
262
|
+
'@tanstack/intent',
|
|
263
|
+
['install', '--map'],
|
|
264
|
+
)
|
|
265
|
+
await environment.execute(command, args, normalizePath(targetDir))
|
|
266
|
+
environment.finishStep('setup-intent', 'TanStack Intent configured')
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function runCommandsAndInstallDependencies(
|
|
270
|
+
environment: Environment,
|
|
271
|
+
options: Options,
|
|
272
|
+
) {
|
|
273
|
+
const s = environment.spinner()
|
|
274
|
+
|
|
275
|
+
if (options.git) {
|
|
276
|
+
s.start('Initializing git repository...')
|
|
277
|
+
environment.startStep({
|
|
278
|
+
id: 'initialize-git-repository',
|
|
279
|
+
type: 'command',
|
|
280
|
+
message: 'Initializing git repository...',
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
await environment.execute('git', ['init'], normalizePath(options.targetDir))
|
|
284
|
+
|
|
285
|
+
environment.finishStep(
|
|
286
|
+
'initialize-git-repository',
|
|
287
|
+
'Initialized git repository',
|
|
288
|
+
)
|
|
289
|
+
s.stop('Initialized git repository')
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const specialSteps = new Set<string>()
|
|
293
|
+
for (const addOn of options.chosenAddOns) {
|
|
294
|
+
for (const step of addOn.createSpecialSteps || []) {
|
|
295
|
+
specialSteps.add(step)
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (specialSteps.size) {
|
|
299
|
+
await runSpecialSteps(environment, options, Array.from(specialSteps))
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (options.install !== false) {
|
|
303
|
+
s.start(`Installing dependencies via ${options.packageManager}...`)
|
|
304
|
+
environment.startStep({
|
|
305
|
+
id: 'install-dependencies',
|
|
306
|
+
type: 'package-manager',
|
|
307
|
+
message: `Installing dependencies via ${options.packageManager}...`,
|
|
308
|
+
})
|
|
309
|
+
await packageManagerInstall(
|
|
310
|
+
environment,
|
|
311
|
+
options.targetDir,
|
|
312
|
+
options.packageManager,
|
|
313
|
+
)
|
|
314
|
+
environment.finishStep('install-dependencies', 'Installed dependencies')
|
|
315
|
+
s.stop('Installed dependencies')
|
|
316
|
+
} else {
|
|
317
|
+
s.start('Skipping dependency installation...')
|
|
318
|
+
environment.startStep({
|
|
319
|
+
id: 'skip-dependencies',
|
|
320
|
+
type: 'info',
|
|
321
|
+
message: 'Skipping dependency installation...',
|
|
322
|
+
})
|
|
323
|
+
environment.finishStep('skip-dependencies', 'Dependency installation skipped')
|
|
324
|
+
s.stop('Dependency installation skipped')
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const postInitSpecialSteps = new Set<string>()
|
|
328
|
+
for (const addOn of options.chosenAddOns) {
|
|
329
|
+
for (const step of addOn.postInitSpecialSteps || []) {
|
|
330
|
+
postInitSpecialSteps.add(step)
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (postInitSpecialSteps.size) {
|
|
334
|
+
await runSpecialSteps(
|
|
335
|
+
environment,
|
|
336
|
+
options,
|
|
337
|
+
Array.from(postInitSpecialSteps),
|
|
338
|
+
)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
for (const phase of ['setup', 'add-on', 'example']) {
|
|
342
|
+
for (const addOn of options.chosenAddOns.filter(
|
|
343
|
+
(addOn) =>
|
|
344
|
+
addOn.phase === phase && addOn.command && addOn.command.command,
|
|
345
|
+
)) {
|
|
346
|
+
s.start(`Running commands for ${addOn.name}...`)
|
|
347
|
+
const translated = translateExecuteCommand(options.packageManager, {
|
|
348
|
+
command: addOn.command!.command,
|
|
349
|
+
args: addOn.command!.args || [],
|
|
350
|
+
})
|
|
351
|
+
const cmd = formatCommand(translated)
|
|
352
|
+
environment.startStep({
|
|
353
|
+
id: 'run-commands',
|
|
354
|
+
type: 'command',
|
|
355
|
+
message: cmd,
|
|
356
|
+
})
|
|
357
|
+
await environment.execute(
|
|
358
|
+
translated.command,
|
|
359
|
+
translated.args,
|
|
360
|
+
options.targetDir,
|
|
361
|
+
{ inherit: true },
|
|
362
|
+
)
|
|
363
|
+
environment.finishStep('run-commands', 'Setup commands complete')
|
|
364
|
+
s.stop(`${addOn.name} commands complete`)
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (
|
|
369
|
+
options.starter &&
|
|
370
|
+
options.starter.command &&
|
|
371
|
+
options.starter.command.command
|
|
372
|
+
) {
|
|
373
|
+
s.start(`Setting up starter ${options.starter.name}...`)
|
|
374
|
+
const starterTranslated = translateExecuteCommand(options.packageManager, {
|
|
375
|
+
command: options.starter.command.command,
|
|
376
|
+
args: options.starter.command.args || [],
|
|
377
|
+
})
|
|
378
|
+
const cmd = formatCommand(starterTranslated)
|
|
379
|
+
environment.startStep({
|
|
380
|
+
id: 'run-starter-command',
|
|
381
|
+
type: 'command',
|
|
382
|
+
message: cmd,
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
await environment.execute(
|
|
386
|
+
starterTranslated.command,
|
|
387
|
+
starterTranslated.args,
|
|
388
|
+
options.targetDir,
|
|
389
|
+
{ inherit: true },
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
environment.finishStep('run-starter-command', 'Starter command complete')
|
|
393
|
+
s.stop(`${options.starter.name} commands complete`)
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
await installShadcnComponents(environment, options.targetDir, options)
|
|
397
|
+
await setupIntent(environment, options.targetDir, options)
|
|
398
|
+
|
|
399
|
+
if (shouldGenerateRoutes(options)) {
|
|
400
|
+
s.start('Generating route tree...')
|
|
401
|
+
const command = getPackageManagerScriptCommand(options.packageManager, [
|
|
402
|
+
'generate-routes',
|
|
403
|
+
])
|
|
404
|
+
const cmd = formatCommand(command)
|
|
405
|
+
environment.startStep({
|
|
406
|
+
id: 'generate-routes',
|
|
407
|
+
type: 'command',
|
|
408
|
+
message: cmd,
|
|
409
|
+
})
|
|
410
|
+
await environment.execute(
|
|
411
|
+
command.command,
|
|
412
|
+
command.args,
|
|
413
|
+
options.targetDir,
|
|
414
|
+
{
|
|
415
|
+
inherit: true,
|
|
416
|
+
},
|
|
417
|
+
)
|
|
418
|
+
environment.finishStep('generate-routes', 'Route tree generated')
|
|
419
|
+
s.stop('Route tree generated')
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function shouldGenerateRoutes(options: Options) {
|
|
424
|
+
return (
|
|
425
|
+
options.install !== false &&
|
|
426
|
+
options.mode === 'file-router' &&
|
|
427
|
+
(options.framework.id === 'react' || options.framework.id === 'solid')
|
|
428
|
+
)
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
async function seedEnvValues(environment: Environment, options: Options) {
|
|
432
|
+
const envVarValues = options.envVarValues || {}
|
|
433
|
+
const entries = Object.entries(envVarValues)
|
|
434
|
+
if (entries.length === 0) return
|
|
435
|
+
|
|
436
|
+
const envLocalPath = joinPaths(options.targetDir, '.env.local')
|
|
437
|
+
if (!environment.exists(envLocalPath)) {
|
|
438
|
+
return
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
let envContents = await environment.readFile(envLocalPath)
|
|
442
|
+
for (const [key, value] of entries) {
|
|
443
|
+
const escapedValue = value.replace(/\n/g, '\\n')
|
|
444
|
+
const nextLine = `${key}=${escapedValue}`
|
|
445
|
+
const pattern = new RegExp(`^${key}=.*$`, 'm')
|
|
446
|
+
|
|
447
|
+
if (pattern.test(envContents)) {
|
|
448
|
+
envContents = envContents.replace(pattern, nextLine)
|
|
449
|
+
} else {
|
|
450
|
+
envContents += `${envContents.endsWith('\n') ? '' : '\n'}${nextLine}\n`
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
await environment.writeFile(envLocalPath, envContents)
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
async function writeEnvExample(environment: Environment, options: Options) {
|
|
458
|
+
const envExamplePath = joinPaths(options.targetDir, '.env.example')
|
|
459
|
+
const existing = environment.exists(envExamplePath)
|
|
460
|
+
? await environment.readFile(envExamplePath)
|
|
461
|
+
: ''
|
|
462
|
+
|
|
463
|
+
const declared = new Set<string>()
|
|
464
|
+
for (const match of existing.matchAll(/^([A-Z_][A-Z0-9_]*)=/gm)) {
|
|
465
|
+
declared.add(match[1])
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const sections: Array<string> = []
|
|
469
|
+
for (const addOn of options.chosenAddOns) {
|
|
470
|
+
const lines: Array<string> = []
|
|
471
|
+
for (const envVar of addOn.envVars || []) {
|
|
472
|
+
if (declared.has(envVar.name)) continue
|
|
473
|
+
declared.add(envVar.name)
|
|
474
|
+
if (envVar.description) {
|
|
475
|
+
const required = envVar.required ? ' (required)' : ''
|
|
476
|
+
lines.push(`# ${envVar.description}${required}`)
|
|
477
|
+
}
|
|
478
|
+
lines.push(`${envVar.name}=`)
|
|
479
|
+
}
|
|
480
|
+
if (lines.length > 0) {
|
|
481
|
+
sections.push(`# ${addOn.name}\n${lines.join('\n')}`)
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
if (sections.length === 0) return
|
|
486
|
+
|
|
487
|
+
const additions = sections.join('\n\n')
|
|
488
|
+
const newContent = existing
|
|
489
|
+
? `${existing.trimEnd()}\n\n${additions}\n`
|
|
490
|
+
: `${additions}\n`
|
|
491
|
+
|
|
492
|
+
await environment.writeFile(envExamplePath, newContent)
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const SHIPPING_CATEGORIES = new Set(['auth', 'database', 'orm', 'deploy'])
|
|
496
|
+
|
|
497
|
+
function buildNextSteps(options: Options): string {
|
|
498
|
+
const collectedEnv = new Set(Object.keys(options.envVarValues || {}))
|
|
499
|
+
const listedEnvVars = new Set<string>()
|
|
500
|
+
const envVarLines: Array<string> = []
|
|
501
|
+
const docLines: Array<string> = []
|
|
502
|
+
|
|
503
|
+
for (const addOn of options.chosenAddOns) {
|
|
504
|
+
if (addOn.link && addOn.category && SHIPPING_CATEGORIES.has(addOn.category)) {
|
|
505
|
+
docLines.push(` - ${addOn.name} (${addOn.category}): ${addOn.link}`)
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
for (const envVar of addOn.envVars || []) {
|
|
509
|
+
if (listedEnvVars.has(envVar.name)) continue
|
|
510
|
+
listedEnvVars.add(envVar.name)
|
|
511
|
+
const required = envVar.required ? ' (required)' : ''
|
|
512
|
+
const status = collectedEnv.has(envVar.name)
|
|
513
|
+
? ' - already set from your input'
|
|
514
|
+
: ' - needs a value'
|
|
515
|
+
const desc = envVar.description ? ` - ${envVar.description}` : ''
|
|
516
|
+
envVarLines.push(` - ${envVar.name}${required}${desc}${status}`)
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
const sections: Array<string> = []
|
|
521
|
+
if (envVarLines.length > 0) {
|
|
522
|
+
sections.push(
|
|
523
|
+
`Environment variables (review/fill in .env.local before deploying):\n${envVarLines.join('\n')}`,
|
|
524
|
+
)
|
|
525
|
+
}
|
|
526
|
+
if (docLines.length > 0) {
|
|
527
|
+
sections.push(`Docs for the integrations you picked:\n${docLines.join('\n')}`)
|
|
528
|
+
}
|
|
529
|
+
if (options.intent) {
|
|
530
|
+
sections.push(
|
|
531
|
+
`Working with an AI agent? Your agent config (AGENTS.md / CLAUDE.md) was wired up by TanStack Intent\nwith explicit skill mappings for the libraries you installed. Try asking your agent:\n - "migrate this Next.js page to TanStack Start"\n - "add a protected /dashboard route"\n - "show me how to use TanStack Router search params"`,
|
|
532
|
+
)
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
return sections.length > 0 ? `\nNext steps:\n\n${sections.join('\n\n')}\n` : ''
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
function report(environment: Environment, options: Options) {
|
|
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
|
+
if (warnings.length > 0) {
|
|
547
|
+
environment.warn('Warnings', warnings.join('\n'))
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
let errorStatement = ''
|
|
551
|
+
if (environment.getErrors().length) {
|
|
552
|
+
errorStatement = `
|
|
553
|
+
|
|
554
|
+
Errors were encountered during the creation of your app:
|
|
555
|
+
|
|
556
|
+
${environment.getErrors().join('\n')}`
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const targetDir = normalizePath(options.targetDir)
|
|
560
|
+
const isCurrentDirectory = targetDir === '.'
|
|
561
|
+
const locationMessage = isCurrentDirectory
|
|
562
|
+
? `Your ${environment.appName} app is ready.`
|
|
563
|
+
: `Your ${environment.appName} app is ready in '${basenamePath(targetDir)}'.`
|
|
564
|
+
const cdInstruction = isCurrentDirectory
|
|
565
|
+
? ''
|
|
566
|
+
: `% cd ${options.projectName}
|
|
567
|
+
`
|
|
568
|
+
|
|
569
|
+
const nextSteps = buildNextSteps(options)
|
|
570
|
+
|
|
571
|
+
environment.outro(
|
|
572
|
+
`${locationMessage}
|
|
573
|
+
|
|
574
|
+
Use the following commands to start your app:
|
|
575
|
+
${cdInstruction}% ${formatCommand(
|
|
576
|
+
getPackageManagerScriptCommand(options.packageManager, ['dev']),
|
|
577
|
+
)}
|
|
578
|
+
${nextSteps}
|
|
579
|
+
Please read the README.md file for information on testing, styling, adding routes, etc.${errorStatement}`,
|
|
580
|
+
)
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
export async function createApp(environment: Environment, options: Options) {
|
|
584
|
+
const effectiveOptions = stripExamplesFromOptions(options)
|
|
585
|
+
|
|
586
|
+
environment.startRun()
|
|
587
|
+
await writeFiles(environment, effectiveOptions)
|
|
588
|
+
await seedEnvValues(environment, effectiveOptions)
|
|
589
|
+
await writeEnvExample(environment, effectiveOptions)
|
|
590
|
+
await runCommandsAndInstallDependencies(environment, effectiveOptions)
|
|
591
|
+
environment.finishRun()
|
|
592
|
+
|
|
593
|
+
report(environment, effectiveOptions)
|
|
594
|
+
}
|