@tanstack/create 0.68.2 → 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.
Files changed (83) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/edge-add-ons.js +106 -0
  3. package/dist/edge-config-file.js +15 -0
  4. package/dist/edge-create-app.js +438 -0
  5. package/dist/edge-environment.js +141 -0
  6. package/dist/edge-file-helpers.js +88 -0
  7. package/dist/edge-frameworks.js +33 -0
  8. package/dist/edge-package-json.js +146 -0
  9. package/dist/edge-path.js +62 -0
  10. package/dist/edge-render.js +31 -0
  11. package/dist/edge-template-file.js +141 -0
  12. package/dist/edge.js +7 -0
  13. package/dist/frameworks/react/add-ons/storybook/info.json +5 -10
  14. package/dist/generated/create-manifest.js +4683 -0
  15. package/dist/manifest-types.js +1 -0
  16. package/dist/manifest.js +1 -0
  17. package/dist/types/custom-add-ons/add-on.d.ts +5 -3
  18. package/dist/types/edge-add-ons.d.ts +5 -0
  19. package/dist/types/edge-config-file.d.ts +8 -0
  20. package/dist/types/edge-create-app.d.ts +2 -0
  21. package/dist/types/edge-environment.d.ts +19 -0
  22. package/dist/types/edge-file-helpers.d.ts +7 -0
  23. package/dist/types/edge-frameworks.d.ts +7 -0
  24. package/dist/types/edge-package-json.d.ts +3 -0
  25. package/dist/types/edge-path.d.ts +5 -0
  26. package/dist/types/edge-render.d.ts +1 -0
  27. package/dist/types/edge-template-file.d.ts +2 -0
  28. package/dist/types/edge.d.ts +9 -0
  29. package/dist/types/generated/create-manifest.d.ts +36 -0
  30. package/dist/types/manifest-types.d.ts +4 -0
  31. package/dist/types/manifest.d.ts +1 -0
  32. package/dist/types/types.d.ts +96 -56
  33. package/dist/types.js +5 -3
  34. package/package.json +25 -5
  35. package/scripts/generate-manifest.mjs +407 -0
  36. package/src/edge-add-ons.ts +138 -0
  37. package/src/edge-config-file.ts +35 -0
  38. package/src/edge-create-app.ts +594 -0
  39. package/src/edge-environment.ts +175 -0
  40. package/src/edge-file-helpers.ts +112 -0
  41. package/src/edge-frameworks.ts +54 -0
  42. package/src/edge-package-json.ts +212 -0
  43. package/src/edge-path.ts +77 -0
  44. package/src/edge-render.ts +32 -0
  45. package/src/edge-template-file.ts +204 -0
  46. package/src/edge.ts +43 -0
  47. package/src/frameworks/react/add-ons/storybook/info.json +5 -10
  48. package/src/generated/create-manifest.ts +6490 -0
  49. package/src/manifest-types.ts +8 -0
  50. package/src/manifest.ts +1 -0
  51. package/src/types.ts +5 -3
  52. package/tests/edge-import.test.ts +31 -0
  53. package/tests/edge-manifest.test.ts +168 -0
  54. package/dist/frameworks/react/add-ons/storybook/assets/_dot_storybook/main.ts +0 -17
  55. package/dist/frameworks/react/add-ons/storybook/assets/_dot_storybook/preview.ts +0 -15
  56. package/dist/frameworks/react/add-ons/storybook/assets/src/components/storybook/button.stories.ts +0 -67
  57. package/dist/frameworks/react/add-ons/storybook/assets/src/components/storybook/button.tsx +0 -47
  58. package/dist/frameworks/react/add-ons/storybook/assets/src/components/storybook/dialog.stories.tsx +0 -92
  59. package/dist/frameworks/react/add-ons/storybook/assets/src/components/storybook/dialog.tsx +0 -29
  60. package/dist/frameworks/react/add-ons/storybook/assets/src/components/storybook/index.ts +0 -14
  61. package/dist/frameworks/react/add-ons/storybook/assets/src/components/storybook/input.stories.ts +0 -43
  62. package/dist/frameworks/react/add-ons/storybook/assets/src/components/storybook/input.tsx +0 -39
  63. package/dist/frameworks/react/add-ons/storybook/assets/src/components/storybook/radio-group.stories.ts +0 -53
  64. package/dist/frameworks/react/add-ons/storybook/assets/src/components/storybook/radio-group.tsx +0 -52
  65. package/dist/frameworks/react/add-ons/storybook/assets/src/components/storybook/slider.stories.ts +0 -55
  66. package/dist/frameworks/react/add-ons/storybook/assets/src/components/storybook/slider.tsx +0 -57
  67. package/dist/frameworks/react/add-ons/storybook/assets/src/routes/demo/storybook.tsx +0 -93
  68. package/dist/frameworks/react/add-ons/storybook/package.json +0 -10
  69. package/src/frameworks/react/add-ons/storybook/assets/_dot_storybook/main.ts +0 -17
  70. package/src/frameworks/react/add-ons/storybook/assets/_dot_storybook/preview.ts +0 -15
  71. package/src/frameworks/react/add-ons/storybook/assets/src/components/storybook/button.stories.ts +0 -67
  72. package/src/frameworks/react/add-ons/storybook/assets/src/components/storybook/button.tsx +0 -47
  73. package/src/frameworks/react/add-ons/storybook/assets/src/components/storybook/dialog.stories.tsx +0 -92
  74. package/src/frameworks/react/add-ons/storybook/assets/src/components/storybook/dialog.tsx +0 -29
  75. package/src/frameworks/react/add-ons/storybook/assets/src/components/storybook/index.ts +0 -14
  76. package/src/frameworks/react/add-ons/storybook/assets/src/components/storybook/input.stories.ts +0 -43
  77. package/src/frameworks/react/add-ons/storybook/assets/src/components/storybook/input.tsx +0 -39
  78. package/src/frameworks/react/add-ons/storybook/assets/src/components/storybook/radio-group.stories.ts +0 -53
  79. package/src/frameworks/react/add-ons/storybook/assets/src/components/storybook/radio-group.tsx +0 -52
  80. package/src/frameworks/react/add-ons/storybook/assets/src/components/storybook/slider.stories.ts +0 -55
  81. package/src/frameworks/react/add-ons/storybook/assets/src/components/storybook/slider.tsx +0 -57
  82. package/src/frameworks/react/add-ons/storybook/assets/src/routes/demo/storybook.tsx +0 -93
  83. package/src/frameworks/react/add-ons/storybook/package.json +0 -10
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @tanstack/create
2
2
 
3
+ ## 0.68.3
4
+
5
+ ### Patch Changes
6
+
7
+ - Add a Worker-safe edge export backed by a build-time generated template and add-on manifest. ([#475](https://github.com/TanStack/cli/pull/475))
8
+
3
9
  ## 0.68.2
4
10
 
5
11
  ### Patch Changes
@@ -0,0 +1,106 @@
1
+ import { AddOnCompiledSchema } from './types.js';
2
+ export function getAllAddOns(framework, mode) {
3
+ return framework
4
+ .getAddOns()
5
+ .filter((a) => a.modes.includes(mode))
6
+ .sort((a, b) => {
7
+ const aPriority = a.priority ?? 0;
8
+ const bPriority = b.priority ?? 0;
9
+ return bPriority - aPriority;
10
+ });
11
+ }
12
+ export async function finalizeAddOns(framework, mode, chosenAddOnIDs) {
13
+ const finalAddOnIDs = new Set(chosenAddOnIDs);
14
+ const addOns = getAllAddOns(framework, mode);
15
+ for (const addOnID of finalAddOnIDs) {
16
+ let addOn;
17
+ const localAddOn = addOns.find((a) => a.id === addOnID) ??
18
+ addOns.find((a) => a.id.toLowerCase() === addOnID.toLowerCase());
19
+ if (localAddOn) {
20
+ addOn = localAddOn;
21
+ if (localAddOn.id !== addOnID) {
22
+ finalAddOnIDs.delete(addOnID);
23
+ finalAddOnIDs.add(localAddOn.id);
24
+ }
25
+ }
26
+ else if (addOnID.startsWith('http')) {
27
+ addOn = await loadRemoteAddOn(addOnID);
28
+ addOns.push(addOn);
29
+ }
30
+ else {
31
+ const suggestion = findClosestAddOn(addOnID, addOns);
32
+ throw new Error(`Add-on ${addOnID} not found${suggestion ? `. Did you mean "${suggestion}"?` : ''}`);
33
+ }
34
+ for (const dependsOn of addOn.dependsOn || []) {
35
+ const dep = addOns.find((a) => a.id === dependsOn);
36
+ if (!dep) {
37
+ throw new Error(`Dependency ${dependsOn} not found`);
38
+ }
39
+ finalAddOnIDs.add(dep.id);
40
+ }
41
+ }
42
+ return [...finalAddOnIDs].map((id) => addOns.find((a) => a.id === id));
43
+ }
44
+ export function populateAddOnOptionsDefaults(chosenAddOns) {
45
+ const addOnOptions = {};
46
+ for (const addOn of chosenAddOns) {
47
+ if (addOn.options) {
48
+ const defaults = {};
49
+ for (const [optionKey, optionDef] of Object.entries(addOn.options)) {
50
+ defaults[optionKey] = optionDef.default;
51
+ }
52
+ addOnOptions[addOn.id] = defaults;
53
+ }
54
+ }
55
+ return addOnOptions;
56
+ }
57
+ export async function loadRemoteAddOn(url) {
58
+ const response = await fetch(url);
59
+ const jsonContent = await response.json();
60
+ const checked = AddOnCompiledSchema.safeParse(jsonContent);
61
+ if (!checked.success) {
62
+ throw new Error(`Invalid add-on: ${url}`);
63
+ }
64
+ const addOn = {
65
+ ...checked.data,
66
+ id: url,
67
+ };
68
+ return {
69
+ ...addOn,
70
+ getFiles: () => Promise.resolve(Object.keys(addOn.files)),
71
+ getFileContents: (path) => Promise.resolve(addOn.files[path]),
72
+ getDeletedFiles: () => Promise.resolve(addOn.deletedFiles),
73
+ };
74
+ }
75
+ function findClosestAddOn(input, addOns) {
76
+ const inputLower = input.toLowerCase();
77
+ let bestMatch;
78
+ let bestDistance = Infinity;
79
+ for (const addOn of addOns) {
80
+ const distance = levenshtein(inputLower, addOn.id.toLowerCase());
81
+ if (distance < bestDistance) {
82
+ bestDistance = distance;
83
+ bestMatch = addOn.id;
84
+ }
85
+ }
86
+ if (bestMatch && bestDistance <= Math.max(Math.floor(input.length / 2), 2)) {
87
+ return bestMatch;
88
+ }
89
+ return undefined;
90
+ }
91
+ function levenshtein(a, b) {
92
+ const m = a.length;
93
+ const n = b.length;
94
+ let prev = Array.from({ length: n + 1 }, (_, j) => j);
95
+ for (let i = 1; i <= m; i++) {
96
+ const curr = [i];
97
+ for (let j = 1; j <= n; j++) {
98
+ curr[j] =
99
+ a[i - 1] === b[j - 1]
100
+ ? prev[j - 1]
101
+ : 1 + Math.min(prev[j], curr[j - 1], prev[j - 1]);
102
+ }
103
+ prev = curr;
104
+ }
105
+ return prev[n];
106
+ }
@@ -0,0 +1,15 @@
1
+ import { CONFIG_FILE } from './constants.js';
2
+ import { joinPaths } from './edge-path.js';
3
+ function createPersistedOptions(options) {
4
+ const { chosenAddOns, framework, targetDir: _targetDir, ...rest } = options;
5
+ return {
6
+ ...rest,
7
+ version: 1,
8
+ framework: framework.id,
9
+ chosenAddOns: chosenAddOns.map((addOn) => addOn.id),
10
+ starter: options.starter?.id ?? undefined,
11
+ };
12
+ }
13
+ export async function writeConfigFileToEnvironment(environment, options) {
14
+ await environment.writeFile(joinPaths(options.targetDir, CONFIG_FILE), JSON.stringify(createPersistedOptions(options), null, 2));
15
+ }
@@ -0,0 +1,438 @@
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 { getPackageManagerExecuteCommand, getPackageManagerScriptCommand, packageManagerInstall, translateExecuteCommand, } from './package-manager.js';
9
+ function stripExamplesFromOptions(options) {
10
+ if (options.includeExamples !== false) {
11
+ return options;
12
+ }
13
+ const chosenAddOns = options.chosenAddOns
14
+ .filter((addOn) => addOn.type !== 'example')
15
+ .map((addOn) => {
16
+ const filteredRoutes = (addOn.routes || []).filter((route) => !isDemoFilePath(route.path) &&
17
+ !(route.url && route.url.startsWith('/demo')));
18
+ const filteredIntegrations = (addOn.integrations || []).filter((integration) => !isDemoFilePath(integration.path));
19
+ return {
20
+ ...addOn,
21
+ routes: filteredRoutes,
22
+ integrations: filteredIntegrations,
23
+ getFiles: async () => {
24
+ const files = await addOn.getFiles();
25
+ return files.filter((file) => !isDemoFilePath(file));
26
+ },
27
+ getDeletedFiles: async () => {
28
+ const deletedFiles = await addOn.getDeletedFiles();
29
+ return deletedFiles.filter((file) => !isDemoFilePath(file));
30
+ },
31
+ };
32
+ });
33
+ return {
34
+ ...options,
35
+ chosenAddOns,
36
+ };
37
+ }
38
+ async function writeFiles(environment, options) {
39
+ const templateFileFromContent = createTemplateFile(environment, options);
40
+ async function writeFileBundle(bundle) {
41
+ const files = await bundle.getFiles();
42
+ for (const file of files) {
43
+ const contents = await bundle.getFileContents(file);
44
+ if (isBase64(contents)) {
45
+ await environment.writeFileBase64(joinPaths(options.targetDir, file), contents);
46
+ }
47
+ else {
48
+ await templateFileFromContent(file, contents);
49
+ }
50
+ }
51
+ const deletedFiles = await bundle.getDeletedFiles();
52
+ for (const file of deletedFiles) {
53
+ await environment.deleteFile(joinPaths(options.targetDir, file));
54
+ }
55
+ }
56
+ environment.startStep({
57
+ id: 'write-framework-files',
58
+ type: 'file',
59
+ message: 'Writing framework files...',
60
+ });
61
+ await writeFileBundle(options.framework);
62
+ environment.finishStep('write-framework-files', 'Framework files written');
63
+ let wroteAddonFiles = false;
64
+ for (const type of ['add-on', 'example', 'toolchain', 'deployment']) {
65
+ for (const phase of ['setup', 'add-on', 'example']) {
66
+ for (const addOn of options.chosenAddOns.filter((addOn) => addOn.phase === phase && addOn.type === type)) {
67
+ environment.startStep({
68
+ id: 'write-addon-files',
69
+ type: 'file',
70
+ message: `Writing ${addOn.name} files...`,
71
+ });
72
+ await writeFileBundle(addOn);
73
+ wroteAddonFiles = true;
74
+ }
75
+ }
76
+ }
77
+ if (wroteAddonFiles) {
78
+ environment.finishStep('write-addon-files', 'Add-on files written');
79
+ }
80
+ if (options.starter) {
81
+ environment.startStep({
82
+ id: 'write-starter-files',
83
+ type: 'file',
84
+ message: 'Writing starter files...',
85
+ });
86
+ await writeFileBundle(options.starter);
87
+ environment.finishStep('write-starter-files', 'Starter files written');
88
+ }
89
+ environment.startStep({
90
+ id: 'write-package-json',
91
+ type: 'file',
92
+ message: 'Writing package.json...',
93
+ });
94
+ const packageJSON = await resolvePackageJSONLatest(createPackageJSON(options));
95
+ await environment.writeFile(joinPaths(options.targetDir, './package.json'), JSON.stringify(packageJSON, null, 2));
96
+ environment.finishStep('write-package-json', 'Package.json written');
97
+ environment.startStep({
98
+ id: 'write-config-file',
99
+ type: 'file',
100
+ message: 'Writing config file...',
101
+ });
102
+ await writeConfigFileToEnvironment(environment, options);
103
+ environment.finishStep('write-config-file', 'Config file written');
104
+ }
105
+ async function runSpecialSteps(environment, options, specialSteps) {
106
+ if (!specialSteps.length) {
107
+ return;
108
+ }
109
+ environment.startStep({
110
+ id: 'special-steps',
111
+ type: 'command',
112
+ message: 'Running special steps...',
113
+ });
114
+ for (const step of specialSteps) {
115
+ if (step === 'rimraf-node-modules') {
116
+ await environment.rimraf(joinPaths(options.targetDir, 'node_modules'));
117
+ for (const lockFile of [
118
+ 'package-lock.json',
119
+ 'yarn.lock',
120
+ 'pnpm-lock.yaml',
121
+ ]) {
122
+ const lockFilePath = joinPaths(options.targetDir, lockFile);
123
+ if (environment.exists(lockFilePath)) {
124
+ await environment.deleteFile(lockFilePath);
125
+ }
126
+ }
127
+ continue;
128
+ }
129
+ if (step === 'post-init-script') {
130
+ const packageJsonPath = joinPaths(options.targetDir, 'package.json');
131
+ if (!environment.exists(packageJsonPath)) {
132
+ environment.warn('Warning', 'No package.json found, skipping post-create-init script');
133
+ continue;
134
+ }
135
+ const packageJson = JSON.parse(await environment.readFile(packageJsonPath));
136
+ const postCreateInit = packageJson.scripts?.['post-create-init'];
137
+ if (postCreateInit) {
138
+ const { command, args } = getPackageManagerScriptCommand(options.packageManager, ['post-create-init']);
139
+ await environment.execute(command, args, options.targetDir, {
140
+ inherit: true,
141
+ });
142
+ }
143
+ continue;
144
+ }
145
+ environment.error(`Special step ${step} not found`);
146
+ }
147
+ environment.finishStep('special-steps', 'Special steps complete');
148
+ }
149
+ async function installShadcnComponents(environment, targetDir, options) {
150
+ if (!options.chosenAddOns.find((a) => a.id === 'shadcn')) {
151
+ return;
152
+ }
153
+ const shadcnComponents = new Set();
154
+ for (const addOn of options.chosenAddOns) {
155
+ if (addOn.shadcnComponents) {
156
+ for (const component of addOn.shadcnComponents) {
157
+ shadcnComponents.add(component);
158
+ }
159
+ }
160
+ }
161
+ if (options.starter?.shadcnComponents) {
162
+ for (const component of options.starter.shadcnComponents) {
163
+ shadcnComponents.add(component);
164
+ }
165
+ }
166
+ if (shadcnComponents.size > 0) {
167
+ environment.startStep({
168
+ id: 'install-shadcn-components',
169
+ type: 'command',
170
+ message: `Installing shadcn components (${Array.from(shadcnComponents).join(', ')})...`,
171
+ });
172
+ const { command, args } = getPackageManagerExecuteCommand(options.packageManager, 'shadcn@latest', ['add', '--silent', '--yes', ...Array.from(shadcnComponents)]);
173
+ await environment.execute(command, args, normalizePath(targetDir));
174
+ environment.finishStep('install-shadcn-components', 'Shadcn components installed');
175
+ }
176
+ }
177
+ async function setupIntent(environment, targetDir, options) {
178
+ if (!options.intent) {
179
+ return;
180
+ }
181
+ environment.startStep({
182
+ id: 'setup-intent',
183
+ type: 'command',
184
+ message: 'Setting up TanStack Intent skill mappings...',
185
+ });
186
+ const { command, args } = getPackageManagerExecuteCommand(options.packageManager, '@tanstack/intent', ['install', '--map']);
187
+ await environment.execute(command, args, normalizePath(targetDir));
188
+ environment.finishStep('setup-intent', 'TanStack Intent configured');
189
+ }
190
+ async function runCommandsAndInstallDependencies(environment, options) {
191
+ const s = environment.spinner();
192
+ if (options.git) {
193
+ s.start('Initializing git repository...');
194
+ environment.startStep({
195
+ id: 'initialize-git-repository',
196
+ type: 'command',
197
+ message: 'Initializing git repository...',
198
+ });
199
+ await environment.execute('git', ['init'], normalizePath(options.targetDir));
200
+ environment.finishStep('initialize-git-repository', 'Initialized git repository');
201
+ s.stop('Initialized git repository');
202
+ }
203
+ const specialSteps = new Set();
204
+ for (const addOn of options.chosenAddOns) {
205
+ for (const step of addOn.createSpecialSteps || []) {
206
+ specialSteps.add(step);
207
+ }
208
+ }
209
+ if (specialSteps.size) {
210
+ await runSpecialSteps(environment, options, Array.from(specialSteps));
211
+ }
212
+ if (options.install !== false) {
213
+ s.start(`Installing dependencies via ${options.packageManager}...`);
214
+ environment.startStep({
215
+ id: 'install-dependencies',
216
+ type: 'package-manager',
217
+ message: `Installing dependencies via ${options.packageManager}...`,
218
+ });
219
+ await packageManagerInstall(environment, options.targetDir, options.packageManager);
220
+ environment.finishStep('install-dependencies', 'Installed dependencies');
221
+ s.stop('Installed dependencies');
222
+ }
223
+ else {
224
+ s.start('Skipping dependency installation...');
225
+ environment.startStep({
226
+ id: 'skip-dependencies',
227
+ type: 'info',
228
+ message: 'Skipping dependency installation...',
229
+ });
230
+ environment.finishStep('skip-dependencies', 'Dependency installation skipped');
231
+ s.stop('Dependency installation skipped');
232
+ }
233
+ const postInitSpecialSteps = new Set();
234
+ for (const addOn of options.chosenAddOns) {
235
+ for (const step of addOn.postInitSpecialSteps || []) {
236
+ postInitSpecialSteps.add(step);
237
+ }
238
+ }
239
+ if (postInitSpecialSteps.size) {
240
+ await runSpecialSteps(environment, options, Array.from(postInitSpecialSteps));
241
+ }
242
+ for (const phase of ['setup', 'add-on', 'example']) {
243
+ for (const addOn of options.chosenAddOns.filter((addOn) => addOn.phase === phase && addOn.command && addOn.command.command)) {
244
+ s.start(`Running commands for ${addOn.name}...`);
245
+ const translated = translateExecuteCommand(options.packageManager, {
246
+ command: addOn.command.command,
247
+ args: addOn.command.args || [],
248
+ });
249
+ const cmd = formatCommand(translated);
250
+ environment.startStep({
251
+ id: 'run-commands',
252
+ type: 'command',
253
+ message: cmd,
254
+ });
255
+ await environment.execute(translated.command, translated.args, options.targetDir, { inherit: true });
256
+ environment.finishStep('run-commands', 'Setup commands complete');
257
+ s.stop(`${addOn.name} commands complete`);
258
+ }
259
+ }
260
+ if (options.starter &&
261
+ options.starter.command &&
262
+ options.starter.command.command) {
263
+ s.start(`Setting up starter ${options.starter.name}...`);
264
+ const starterTranslated = translateExecuteCommand(options.packageManager, {
265
+ command: options.starter.command.command,
266
+ args: options.starter.command.args || [],
267
+ });
268
+ const cmd = formatCommand(starterTranslated);
269
+ environment.startStep({
270
+ id: 'run-starter-command',
271
+ type: 'command',
272
+ message: cmd,
273
+ });
274
+ await environment.execute(starterTranslated.command, starterTranslated.args, options.targetDir, { inherit: true });
275
+ environment.finishStep('run-starter-command', 'Starter command complete');
276
+ s.stop(`${options.starter.name} commands complete`);
277
+ }
278
+ await installShadcnComponents(environment, options.targetDir, options);
279
+ await setupIntent(environment, options.targetDir, options);
280
+ if (shouldGenerateRoutes(options)) {
281
+ s.start('Generating route tree...');
282
+ const command = getPackageManagerScriptCommand(options.packageManager, [
283
+ 'generate-routes',
284
+ ]);
285
+ const cmd = formatCommand(command);
286
+ environment.startStep({
287
+ id: 'generate-routes',
288
+ type: 'command',
289
+ message: cmd,
290
+ });
291
+ await environment.execute(command.command, command.args, options.targetDir, {
292
+ inherit: true,
293
+ });
294
+ environment.finishStep('generate-routes', 'Route tree generated');
295
+ s.stop('Route tree generated');
296
+ }
297
+ }
298
+ function shouldGenerateRoutes(options) {
299
+ return (options.install !== false &&
300
+ options.mode === 'file-router' &&
301
+ (options.framework.id === 'react' || options.framework.id === 'solid'));
302
+ }
303
+ async function seedEnvValues(environment, options) {
304
+ const envVarValues = options.envVarValues || {};
305
+ const entries = Object.entries(envVarValues);
306
+ if (entries.length === 0)
307
+ return;
308
+ const envLocalPath = joinPaths(options.targetDir, '.env.local');
309
+ if (!environment.exists(envLocalPath)) {
310
+ return;
311
+ }
312
+ let envContents = await environment.readFile(envLocalPath);
313
+ for (const [key, value] of entries) {
314
+ const escapedValue = value.replace(/\n/g, '\\n');
315
+ const nextLine = `${key}=${escapedValue}`;
316
+ const pattern = new RegExp(`^${key}=.*$`, 'm');
317
+ if (pattern.test(envContents)) {
318
+ envContents = envContents.replace(pattern, nextLine);
319
+ }
320
+ else {
321
+ envContents += `${envContents.endsWith('\n') ? '' : '\n'}${nextLine}\n`;
322
+ }
323
+ }
324
+ await environment.writeFile(envLocalPath, envContents);
325
+ }
326
+ async function writeEnvExample(environment, options) {
327
+ const envExamplePath = joinPaths(options.targetDir, '.env.example');
328
+ const existing = environment.exists(envExamplePath)
329
+ ? await environment.readFile(envExamplePath)
330
+ : '';
331
+ const declared = new Set();
332
+ for (const match of existing.matchAll(/^([A-Z_][A-Z0-9_]*)=/gm)) {
333
+ declared.add(match[1]);
334
+ }
335
+ const sections = [];
336
+ for (const addOn of options.chosenAddOns) {
337
+ const lines = [];
338
+ for (const envVar of addOn.envVars || []) {
339
+ if (declared.has(envVar.name))
340
+ continue;
341
+ declared.add(envVar.name);
342
+ if (envVar.description) {
343
+ const required = envVar.required ? ' (required)' : '';
344
+ lines.push(`# ${envVar.description}${required}`);
345
+ }
346
+ lines.push(`${envVar.name}=`);
347
+ }
348
+ if (lines.length > 0) {
349
+ sections.push(`# ${addOn.name}\n${lines.join('\n')}`);
350
+ }
351
+ }
352
+ if (sections.length === 0)
353
+ return;
354
+ const additions = sections.join('\n\n');
355
+ const newContent = existing
356
+ ? `${existing.trimEnd()}\n\n${additions}\n`
357
+ : `${additions}\n`;
358
+ await environment.writeFile(envExamplePath, newContent);
359
+ }
360
+ const SHIPPING_CATEGORIES = new Set(['auth', 'database', 'orm', 'deploy']);
361
+ function buildNextSteps(options) {
362
+ const collectedEnv = new Set(Object.keys(options.envVarValues || {}));
363
+ const listedEnvVars = new Set();
364
+ const envVarLines = [];
365
+ const docLines = [];
366
+ for (const addOn of options.chosenAddOns) {
367
+ if (addOn.link && addOn.category && SHIPPING_CATEGORIES.has(addOn.category)) {
368
+ docLines.push(` - ${addOn.name} (${addOn.category}): ${addOn.link}`);
369
+ }
370
+ for (const envVar of addOn.envVars || []) {
371
+ if (listedEnvVars.has(envVar.name))
372
+ continue;
373
+ listedEnvVars.add(envVar.name);
374
+ const required = envVar.required ? ' (required)' : '';
375
+ const status = collectedEnv.has(envVar.name)
376
+ ? ' - already set from your input'
377
+ : ' - needs a value';
378
+ const desc = envVar.description ? ` - ${envVar.description}` : '';
379
+ envVarLines.push(` - ${envVar.name}${required}${desc}${status}`);
380
+ }
381
+ }
382
+ const sections = [];
383
+ if (envVarLines.length > 0) {
384
+ sections.push(`Environment variables (review/fill in .env.local before deploying):\n${envVarLines.join('\n')}`);
385
+ }
386
+ if (docLines.length > 0) {
387
+ sections.push(`Docs for the integrations you picked:\n${docLines.join('\n')}`);
388
+ }
389
+ if (options.intent) {
390
+ sections.push(`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"`);
391
+ }
392
+ return sections.length > 0 ? `\nNext steps:\n\n${sections.join('\n\n')}\n` : '';
393
+ }
394
+ function report(environment, options) {
395
+ const warnings = [];
396
+ for (const addOn of options.chosenAddOns) {
397
+ if (addOn.warning) {
398
+ warnings.push(addOn.warning);
399
+ }
400
+ }
401
+ if (warnings.length > 0) {
402
+ environment.warn('Warnings', warnings.join('\n'));
403
+ }
404
+ let errorStatement = '';
405
+ if (environment.getErrors().length) {
406
+ errorStatement = `
407
+
408
+ Errors were encountered during the creation of your app:
409
+
410
+ ${environment.getErrors().join('\n')}`;
411
+ }
412
+ const targetDir = normalizePath(options.targetDir);
413
+ const isCurrentDirectory = targetDir === '.';
414
+ const locationMessage = isCurrentDirectory
415
+ ? `Your ${environment.appName} app is ready.`
416
+ : `Your ${environment.appName} app is ready in '${basenamePath(targetDir)}'.`;
417
+ const cdInstruction = isCurrentDirectory
418
+ ? ''
419
+ : `% cd ${options.projectName}
420
+ `;
421
+ const nextSteps = buildNextSteps(options);
422
+ environment.outro(`${locationMessage}
423
+
424
+ Use the following commands to start your app:
425
+ ${cdInstruction}% ${formatCommand(getPackageManagerScriptCommand(options.packageManager, ['dev']))}
426
+ ${nextSteps}
427
+ Please read the README.md file for information on testing, styling, adding routes, etc.${errorStatement}`);
428
+ }
429
+ export async function createApp(environment, options) {
430
+ const effectiveOptions = stripExamplesFromOptions(options);
431
+ environment.startRun();
432
+ await writeFiles(environment, effectiveOptions);
433
+ await seedEnvValues(environment, effectiveOptions);
434
+ await writeEnvExample(environment, effectiveOptions);
435
+ await runCommandsAndInstallDependencies(environment, effectiveOptions);
436
+ environment.finishRun();
437
+ report(environment, effectiveOptions);
438
+ }