@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
@@ -0,0 +1,175 @@
1
+ import { cleanUpFileArray, cleanUpFiles } from './edge-file-helpers.js'
2
+ import {
3
+ basenamePath,
4
+ dirnamePath,
5
+ joinPaths,
6
+ normalizePath,
7
+ } from './edge-path.js'
8
+
9
+ import type { Environment } from './types.js'
10
+
11
+ export interface MemoryEnvironmentOutput {
12
+ files: Record<string, string>
13
+ deletedFiles: Array<string>
14
+ commands: Array<{ command: string; args: Array<string> }>
15
+ }
16
+
17
+ function hasDirectory(files: Record<string, string>, path: string) {
18
+ const directory = normalizePath(path)
19
+ const prefix = directory.endsWith('/') ? directory : `${directory}/`
20
+ return Object.keys(files).some((file) => file.startsWith(prefix))
21
+ }
22
+
23
+ function createMissingDirectoryError(path: string) {
24
+ return new Error(`Directory not found: ${path}`)
25
+ }
26
+
27
+ export function createMemoryEnvironment(returnPathsRelativeTo: string = '') {
28
+ const output: MemoryEnvironmentOutput = {
29
+ files: {},
30
+ commands: [],
31
+ deletedFiles: [],
32
+ }
33
+ const files: Record<string, string> = {}
34
+ let errors: Array<string> = []
35
+
36
+ const environment: Environment = {
37
+ startRun: () => {
38
+ errors = []
39
+ output.files = {}
40
+ output.commands = []
41
+ output.deletedFiles = []
42
+ },
43
+ finishRun: () => {
44
+ output.files = Object.keys(files).reduce<Record<string, string>>(
45
+ (acc, file) => {
46
+ acc[file] = files[file]
47
+ return acc
48
+ },
49
+ {},
50
+ )
51
+
52
+ if (returnPathsRelativeTo.length) {
53
+ output.files = cleanUpFiles(output.files, returnPathsRelativeTo)
54
+ output.deletedFiles = cleanUpFileArray(
55
+ output.deletedFiles,
56
+ returnPathsRelativeTo,
57
+ )
58
+ }
59
+ },
60
+ getErrors: () => errors,
61
+
62
+ appendFile: (path: string, contents: string) => {
63
+ const normalized = normalizePath(path)
64
+ files[normalized] = `${files[normalized] ?? ''}${contents}`
65
+ return Promise.resolve()
66
+ },
67
+ copyFile: (from: string, to: string) => {
68
+ const normalizedFrom = normalizePath(from)
69
+ const normalizedTo = normalizePath(to)
70
+ if (!(normalizedFrom in files)) {
71
+ throw new Error(`File not found: ${from}`)
72
+ }
73
+ files[normalizedTo] = files[normalizedFrom]
74
+ return Promise.resolve()
75
+ },
76
+ writeFile: (path: string, contents: string) => {
77
+ files[normalizePath(path)] = contents
78
+ return Promise.resolve()
79
+ },
80
+ writeFileBase64: (path: string, base64Contents: string) => {
81
+ files[normalizePath(path)] = base64Contents
82
+ return Promise.resolve()
83
+ },
84
+ execute: (command: string, args: Array<string>) => {
85
+ output.commands.push({
86
+ command,
87
+ args,
88
+ })
89
+ return Promise.resolve({ stdout: '' })
90
+ },
91
+ deleteFile: (path: string) => {
92
+ const normalized = normalizePath(path)
93
+ output.deletedFiles.push(normalized)
94
+ delete files[normalized]
95
+ return Promise.resolve()
96
+ },
97
+
98
+ exists: (path: string) => {
99
+ const normalized = normalizePath(path)
100
+ return normalized in files || hasDirectory(files, normalized)
101
+ },
102
+ isDirectory: (path: string) => hasDirectory(files, path),
103
+ readFile: (path: string) => {
104
+ const normalized = normalizePath(path)
105
+ if (!(normalized in files)) {
106
+ throw new Error(`File not found: ${path}`)
107
+ }
108
+ return Promise.resolve(files[normalized])
109
+ },
110
+ readdir: (path: string) => {
111
+ const normalized = normalizePath(path)
112
+ const directory = normalized === '.' ? '' : normalized
113
+ const prefix = directory ? `${directory}/` : ''
114
+
115
+ if (directory && !hasDirectory(files, directory)) {
116
+ throw createMissingDirectoryError(path)
117
+ }
118
+
119
+ const entries = new Set<string>()
120
+ for (const file of Object.keys(files)) {
121
+ if (!file.startsWith(prefix)) {
122
+ continue
123
+ }
124
+
125
+ const rest = file.slice(prefix.length)
126
+ const entry = rest.split('/')[0]
127
+ if (entry) {
128
+ entries.add(entry)
129
+ }
130
+ }
131
+
132
+ return Promise.resolve(Array.from(entries))
133
+ },
134
+ rimraf: (path: string) => {
135
+ const normalized = normalizePath(path)
136
+ const prefix = normalized.endsWith('/') ? normalized : `${normalized}/`
137
+ for (const file of Object.keys(files)) {
138
+ if (file === normalized || file.startsWith(prefix)) {
139
+ delete files[file]
140
+ }
141
+ }
142
+ return Promise.resolve()
143
+ },
144
+
145
+ appName: 'TanStack',
146
+
147
+ startStep: () => {},
148
+ finishStep: () => {},
149
+
150
+ intro: () => {},
151
+ outro: () => {},
152
+ info: () => {},
153
+ error: (_title?: string, message?: string) => {
154
+ if (message) {
155
+ errors.push(message)
156
+ }
157
+ },
158
+ warn: () => {},
159
+ confirm: () => Promise.resolve(true),
160
+ spinner: () => ({
161
+ start: () => {},
162
+ stop: () => {},
163
+ }),
164
+ }
165
+
166
+ return {
167
+ environment,
168
+ output,
169
+ paths: {
170
+ basename: basenamePath,
171
+ dirname: dirnamePath,
172
+ join: joinPaths,
173
+ },
174
+ }
175
+ }
@@ -0,0 +1,112 @@
1
+ import { basenamePath, extnamePath } from './edge-path.js'
2
+ import { hasDrive, stripDrive } from './utils.js'
3
+
4
+ const BINARY_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico']
5
+
6
+ export function isBinaryFile(path: string): boolean {
7
+ return BINARY_EXTENSIONS.includes(extnamePath(path))
8
+ }
9
+
10
+ export function isBase64(content: string): boolean {
11
+ return content.startsWith('base64::')
12
+ }
13
+
14
+ export function toCleanPath(absolutePath: string, baseDir: string): string {
15
+ const normalizedPath = absolutePath.replace(/\\/g, '/')
16
+ const normalizedBase = baseDir.replace(/\\/g, '/')
17
+ let cleanPath = normalizedPath
18
+ if (normalizedPath.startsWith(normalizedBase)) {
19
+ cleanPath = normalizedPath.slice(normalizedBase.length)
20
+ } else if (hasDrive(normalizedPath) !== hasDrive(normalizedBase)) {
21
+ const pathNoDrive = stripDrive(normalizedPath)
22
+ const baseNoDrive = stripDrive(normalizedBase)
23
+ if (pathNoDrive.startsWith(baseNoDrive)) {
24
+ cleanPath = pathNoDrive.slice(baseNoDrive.length)
25
+ }
26
+ }
27
+ if (cleanPath.startsWith('/')) {
28
+ cleanPath = cleanPath.slice(1)
29
+ }
30
+ return cleanPath
31
+ }
32
+
33
+ export function relativePath(
34
+ from: string,
35
+ to: string,
36
+ stripExtension: boolean = false,
37
+ ) {
38
+ const normalized = from.replace(/\\/g, '/')
39
+ const cleanedFrom = normalized.startsWith('./')
40
+ ? normalized.slice(2)
41
+ : normalized
42
+ const cleanedTo = to.startsWith('./') ? to.slice(2) : to
43
+
44
+ const fromSegments = cleanedFrom.split('/')
45
+ const toSegments = cleanedTo.split('/')
46
+
47
+ fromSegments.pop()
48
+ toSegments.pop()
49
+
50
+ let commonIndex = 0
51
+ while (
52
+ commonIndex < fromSegments.length &&
53
+ commonIndex < toSegments.length &&
54
+ fromSegments[commonIndex] === toSegments[commonIndex]
55
+ ) {
56
+ commonIndex++
57
+ }
58
+
59
+ const upLevels = fromSegments.length - commonIndex
60
+ const downLevels = toSegments.slice(commonIndex)
61
+ const target = stripExtension ? to.replace(extnamePath(to), '') : to
62
+
63
+ if (upLevels === 0 && downLevels.length === 0) {
64
+ return `./${basenamePath(target)}`
65
+ } else if (upLevels === 0 && downLevels.length > 0) {
66
+ return `./${downLevels.join('/')}/${basenamePath(target)}`
67
+ } else {
68
+ const relative = [...Array(upLevels).fill('..'), ...downLevels].join('/')
69
+ return `${relative}/${basenamePath(target)}`
70
+ }
71
+ }
72
+
73
+ export function isDemoFilePath(path?: string): boolean {
74
+ if (!path) return false
75
+ const normalized = path.replace(/\\/g, '/')
76
+
77
+ if (
78
+ normalized.includes('/routes/demo/') ||
79
+ normalized.includes('/routes/example/')
80
+ ) {
81
+ return true
82
+ }
83
+
84
+ const filename = normalized.split('/').pop() || ''
85
+ return (
86
+ filename.startsWith('demo.') ||
87
+ filename.startsWith('demo-') ||
88
+ filename.startsWith('example.') ||
89
+ filename.startsWith('example-')
90
+ )
91
+ }
92
+
93
+ export function cleanUpFiles(
94
+ files: Record<string, string>,
95
+ targetDir?: string,
96
+ ) {
97
+ return Object.keys(files).reduce<Record<string, string>>((acc, file) => {
98
+ if (basenamePath(file) !== '.cta.json') {
99
+ acc[targetDir ? toCleanPath(file, targetDir) : file] = files[file]
100
+ }
101
+ return acc
102
+ }, {})
103
+ }
104
+
105
+ export function cleanUpFileArray(files: Array<string>, targetDir?: string) {
106
+ return files.reduce<Array<string>>((acc, file) => {
107
+ if (basenamePath(file) !== '.cta.json') {
108
+ acc.push(targetDir ? toCleanPath(file, targetDir) : file)
109
+ }
110
+ return acc
111
+ }, [])
112
+ }
@@ -0,0 +1,54 @@
1
+ import { createManifestFrameworks } from './generated/create-manifest.js'
2
+
3
+ import type {
4
+ AddOn,
5
+ AddOnCompiled,
6
+ Framework,
7
+ FrameworkDefinition,
8
+ } from './types.js'
9
+
10
+ function createAddOn(addOn: AddOnCompiled): AddOn {
11
+ return {
12
+ ...addOn,
13
+ getFiles: () => Promise.resolve(Object.keys(addOn.files)),
14
+ getFileContents: (path: string) => Promise.resolve(addOn.files[path]),
15
+ getDeletedFiles: () => Promise.resolve(addOn.deletedFiles),
16
+ }
17
+ }
18
+
19
+ export function createFrameworkFromManifest(
20
+ framework: Omit<FrameworkDefinition, 'addOns'> & {
21
+ addOns: Array<AddOnCompiled>
22
+ },
23
+ ): Framework {
24
+ const addOns = framework.addOns.map(createAddOn)
25
+ const { addOns: _addOns, base, ...rest } = framework
26
+
27
+ return {
28
+ ...rest,
29
+ getFiles: () => Promise.resolve(Object.keys(base)),
30
+ getFileContents: (path: string) => Promise.resolve(base[path]),
31
+ getDeletedFiles: () => Promise.resolve([]),
32
+ getAddOns: () => addOns,
33
+ }
34
+ }
35
+
36
+ const frameworks = createManifestFrameworks().map(createFrameworkFromManifest)
37
+
38
+ export function getFrameworkById(id: string) {
39
+ if (id === 'react-cra') {
40
+ return frameworks.find((framework) => framework.id === 'react')
41
+ }
42
+
43
+ return frameworks.find((framework) => framework.id === id)
44
+ }
45
+
46
+ export function getFrameworkByName(name: string) {
47
+ return frameworks.find(
48
+ (framework) => framework.name.toLowerCase() === name.toLowerCase(),
49
+ )
50
+ }
51
+
52
+ export function getFrameworks() {
53
+ return frameworks
54
+ }
@@ -0,0 +1,212 @@
1
+ import { render } from './edge-render.js'
2
+ import { formatCommand, sortObject } from './utils.js'
3
+ import { getPackageManagerExecuteCommand } from './package-manager.js'
4
+
5
+ import type { Options } from './types.js'
6
+
7
+ export function mergePackageJSON(
8
+ packageJSON: Record<string, unknown>,
9
+ overlayPackageJSON?: Record<string, unknown>,
10
+ ) {
11
+ const packageDependencies =
12
+ packageJSON.dependencies && typeof packageJSON.dependencies === 'object'
13
+ ? (packageJSON.dependencies as Record<string, string>)
14
+ : {}
15
+ const overlayDependencies =
16
+ overlayPackageJSON?.dependencies &&
17
+ typeof overlayPackageJSON.dependencies === 'object'
18
+ ? (overlayPackageJSON.dependencies as Record<string, string>)
19
+ : {}
20
+ const packageDevDependencies =
21
+ packageJSON.devDependencies && typeof packageJSON.devDependencies === 'object'
22
+ ? (packageJSON.devDependencies as Record<string, string>)
23
+ : {}
24
+ const overlayDevDependencies =
25
+ overlayPackageJSON?.devDependencies &&
26
+ typeof overlayPackageJSON.devDependencies === 'object'
27
+ ? (overlayPackageJSON.devDependencies as Record<string, string>)
28
+ : {}
29
+ const packageScripts =
30
+ packageJSON.scripts && typeof packageJSON.scripts === 'object'
31
+ ? (packageJSON.scripts as Record<string, string>)
32
+ : {}
33
+ const overlayScripts =
34
+ overlayPackageJSON?.scripts && typeof overlayPackageJSON.scripts === 'object'
35
+ ? (overlayPackageJSON.scripts as Record<string, string>)
36
+ : {}
37
+
38
+ const mergedPackageJSON: Record<string, unknown> = {
39
+ ...packageJSON,
40
+ ...(overlayPackageJSON || {}),
41
+ dependencies: {
42
+ ...packageDependencies,
43
+ ...overlayDependencies,
44
+ },
45
+ devDependencies: {
46
+ ...packageDevDependencies,
47
+ ...overlayDevDependencies,
48
+ },
49
+ scripts: {
50
+ ...packageScripts,
51
+ ...overlayScripts,
52
+ },
53
+ }
54
+
55
+ const packagePnpm =
56
+ packageJSON.pnpm && typeof packageJSON.pnpm === 'object'
57
+ ? (packageJSON.pnpm as Record<string, unknown>)
58
+ : undefined
59
+ const overlayPnpm =
60
+ overlayPackageJSON?.pnpm && typeof overlayPackageJSON.pnpm === 'object'
61
+ ? (overlayPackageJSON.pnpm as Record<string, unknown>)
62
+ : undefined
63
+
64
+ const baseOnlyBuiltDependencies = Array.isArray(
65
+ packagePnpm?.onlyBuiltDependencies,
66
+ )
67
+ ? packagePnpm.onlyBuiltDependencies
68
+ : []
69
+ const overlayOnlyBuiltDependencies = Array.isArray(
70
+ overlayPnpm?.onlyBuiltDependencies,
71
+ )
72
+ ? overlayPnpm.onlyBuiltDependencies
73
+ : []
74
+
75
+ const onlyBuiltDependencies = [
76
+ ...new Set([
77
+ ...baseOnlyBuiltDependencies,
78
+ ...overlayOnlyBuiltDependencies,
79
+ ]),
80
+ ]
81
+
82
+ if (packagePnpm || overlayPnpm) {
83
+ mergedPackageJSON.pnpm = {
84
+ ...packagePnpm,
85
+ ...overlayPnpm,
86
+ }
87
+
88
+ if (onlyBuiltDependencies.length) {
89
+ const mergedPnpm = mergedPackageJSON.pnpm as Record<string, unknown>
90
+ mergedPnpm.onlyBuiltDependencies = onlyBuiltDependencies
91
+ }
92
+ }
93
+
94
+ return mergedPackageJSON
95
+ }
96
+
97
+ export function createPackageJSON(options: Options) {
98
+ const packageManager = options.packageManager
99
+
100
+ function getPackageManagerExecuteScript(
101
+ pkg: string,
102
+ args: Array<string> = [],
103
+ ) {
104
+ return formatCommand(getPackageManagerExecuteCommand(packageManager, pkg, args))
105
+ }
106
+
107
+ let packageJSON: Record<string, unknown> = {
108
+ ...(JSON.parse(
109
+ JSON.stringify(options.framework.basePackageJSON),
110
+ ) as Record<string, unknown>),
111
+ name: options.projectName,
112
+ }
113
+
114
+ const additions: Array<Record<string, unknown> | undefined> = [
115
+ options.framework.optionalPackages.typescript,
116
+ options.framework.optionalPackages.tailwindcss,
117
+ options.mode ? options.framework.optionalPackages[options.mode] : undefined,
118
+ ]
119
+ for (const addition of additions.filter(
120
+ (addition): addition is Record<string, unknown> => Boolean(addition),
121
+ )) {
122
+ packageJSON = mergePackageJSON(packageJSON, addition)
123
+ }
124
+
125
+ for (const addOn of options.chosenAddOns) {
126
+ let addOnPackageJSON = addOn.packageAdditions as
127
+ | Record<string, unknown>
128
+ | undefined
129
+
130
+ if (addOn.packageTemplate) {
131
+ const templateValues = {
132
+ packageManager: options.packageManager,
133
+ projectName: options.projectName,
134
+ typescript: true,
135
+ tailwind: true,
136
+ js: 'ts',
137
+ jsx: 'tsx',
138
+ fileRouter: options.mode === 'file-router',
139
+ codeRouter: options.mode === 'code-router',
140
+ routerOnly: options.routerOnly === true,
141
+ addOnEnabled: options.chosenAddOns.reduce<Record<string, boolean>>(
142
+ (acc, addon) => {
143
+ acc[addon.id] = true
144
+ return acc
145
+ },
146
+ {},
147
+ ),
148
+ addOnOption: options.addOnOptions,
149
+ addOns: options.chosenAddOns,
150
+ getPackageManagerExecuteScript,
151
+ }
152
+
153
+ try {
154
+ addOnPackageJSON = JSON.parse(render(addOn.packageTemplate, templateValues))
155
+ } catch (error) {
156
+ console.error(
157
+ `Error processing package.json.ejs for add-on ${addOn.id}:`,
158
+ error,
159
+ )
160
+ }
161
+ }
162
+
163
+ packageJSON = mergePackageJSON(packageJSON, addOnPackageJSON)
164
+ }
165
+
166
+ if (options.starter) {
167
+ packageJSON = mergePackageJSON(
168
+ packageJSON,
169
+ options.starter.packageAdditions as Record<string, unknown> | undefined,
170
+ )
171
+ }
172
+
173
+ const dependencies = packageJSON.dependencies as
174
+ | Record<string, string>
175
+ | undefined
176
+ const devDependencies = packageJSON.devDependencies as
177
+ | Record<string, string>
178
+ | undefined
179
+ const scripts = packageJSON.scripts as Record<string, string> | undefined
180
+
181
+ if (options.routerOnly) {
182
+ if (options.framework.id === 'react') {
183
+ delete dependencies?.['@tanstack/react-start']
184
+ delete dependencies?.['@tanstack/react-router-ssr-query']
185
+ packageJSON.devDependencies = {
186
+ ...(devDependencies ?? {}),
187
+ '@tanstack/router-plugin':
188
+ devDependencies?.['@tanstack/router-plugin'] ?? 'latest',
189
+ }
190
+ }
191
+
192
+ if (options.framework.id === 'solid') {
193
+ delete dependencies?.['@tanstack/solid-start']
194
+ delete dependencies?.['@tanstack/solid-router-ssr-query']
195
+ delete scripts?.start
196
+ packageJSON.devDependencies = {
197
+ ...(devDependencies ?? {}),
198
+ '@tanstack/router-plugin':
199
+ devDependencies?.['@tanstack/router-plugin'] ?? 'latest',
200
+ }
201
+ }
202
+ }
203
+
204
+ packageJSON.dependencies = sortObject(
205
+ (packageJSON.dependencies ?? {}) as Record<string, string>,
206
+ )
207
+ packageJSON.devDependencies = sortObject(
208
+ (packageJSON.devDependencies ?? {}) as Record<string, string>,
209
+ )
210
+
211
+ return packageJSON
212
+ }
@@ -0,0 +1,77 @@
1
+ export function normalizePath(path: string): string {
2
+ const normalized = path.replace(/\\/g, '/')
3
+ const isAbsolute = normalized.startsWith('/')
4
+ const parts: Array<string> = []
5
+
6
+ for (const part of normalized.split('/')) {
7
+ if (!part || part === '.') {
8
+ continue
9
+ }
10
+ if (part === '..') {
11
+ if (parts.length && parts[parts.length - 1] !== '..') {
12
+ parts.pop()
13
+ } else if (!isAbsolute) {
14
+ parts.push(part)
15
+ }
16
+ continue
17
+ }
18
+ parts.push(part)
19
+ }
20
+
21
+ const joined = parts.join('/')
22
+ if (isAbsolute) {
23
+ return joined ? `/${joined}` : '/'
24
+ }
25
+
26
+ return joined || '.'
27
+ }
28
+
29
+ export function joinPaths(...paths: Array<string | undefined>): string {
30
+ const filtered = paths.filter(
31
+ (path): path is string => typeof path === 'string' && path.length > 0,
32
+ )
33
+ if (!filtered.length) {
34
+ return '.'
35
+ }
36
+
37
+ return normalizePath(filtered.join('/'))
38
+ }
39
+
40
+ export function basenamePath(path: string): string {
41
+ const normalized = normalizePath(path)
42
+ if (normalized === '/') {
43
+ return ''
44
+ }
45
+
46
+ return normalized.split('/').pop() ?? ''
47
+ }
48
+
49
+ export function dirnamePath(path: string): string {
50
+ const normalized = normalizePath(path)
51
+ if (normalized === '/') {
52
+ return '/'
53
+ }
54
+
55
+ const parts = normalized.split('/')
56
+ parts.pop()
57
+
58
+ if (!parts.length) {
59
+ return '.'
60
+ }
61
+
62
+ if (parts.length === 1 && parts[0] === '') {
63
+ return '/'
64
+ }
65
+
66
+ return parts.join('/') || '.'
67
+ }
68
+
69
+ export function extnamePath(path: string): string {
70
+ const basename = basenamePath(path)
71
+ const index = basename.lastIndexOf('.')
72
+ if (index <= 0) {
73
+ return ''
74
+ }
75
+
76
+ return basename.slice(index)
77
+ }
@@ -0,0 +1,32 @@
1
+ import { renderManifestTemplate } from './generated/create-manifest.js'
2
+
3
+ export function render(template: string, data?: Record<string, any>) {
4
+ return renderManifestTemplate(template, {
5
+ packageManager: undefined,
6
+ projectName: undefined,
7
+ typescript: undefined,
8
+ tailwind: undefined,
9
+ js: undefined,
10
+ jsx: undefined,
11
+ fileRouter: undefined,
12
+ codeRouter: undefined,
13
+ routerOnly: undefined,
14
+ includeExamples: undefined,
15
+ addOnEnabled: {},
16
+ addOnOption: {},
17
+ addOns: [],
18
+ integrations: [],
19
+ routes: [],
20
+ getPackageManagerAddScript: () => '',
21
+ getPackageManagerRunScript: () => '',
22
+ getPackageManagerExecuteScript: () => '',
23
+ relativePath: () => '',
24
+ integrationImportContent: () => '',
25
+ integrationImportCode: () => '',
26
+ renderTemplate: () => '',
27
+ ignoreFile: () => {
28
+ throw new Error('ignoreFile')
29
+ },
30
+ ...(data ?? {}),
31
+ })
32
+ }