@tanstack/cta-ui 0.10.0-alpha.26 → 0.10.0-alpha.27
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 +20 -0
- package/dist/assets/index-DSKioOfX.css +1 -0
- package/dist/assets/index-DWTDdndE.js +213 -0
- package/dist/assets/index-DWTDdndE.js.map +1 -0
- package/dist/index.html +9 -3
- package/dist/logo-color-100w.png +0 -0
- package/index.html +7 -1
- package/lib/engine-handling/add-to-app-wrapper.ts +39 -15
- package/lib/engine-handling/create-app-wrapper.ts +17 -6
- package/lib/engine-handling/file-helpers.ts +4 -2
- package/lib/engine-handling/generate-initial-payload.ts +58 -51
- package/lib/index.ts +11 -1
- package/lib/types.d.ts +18 -11
- package/lib-dist/engine-handling/add-to-app-wrapper.d.ts +2 -0
- package/lib-dist/engine-handling/add-to-app-wrapper.js +14 -9
- package/lib-dist/engine-handling/create-app-wrapper.d.ts +2 -1
- package/lib-dist/engine-handling/create-app-wrapper.js +6 -4
- package/lib-dist/engine-handling/file-helpers.js +3 -2
- package/lib-dist/engine-handling/generate-initial-payload.d.ts +14 -22
- package/lib-dist/engine-handling/generate-initial-payload.js +44 -49
- package/lib-dist/index.d.ts +2 -0
- package/lib-dist/index.js +6 -1
- package/package.json +6 -4
- package/public/logo-color-100w.png +0 -0
- package/src/components/background-animation.tsx +229 -0
- package/src/components/cta-sidebar.tsx +28 -33
- package/src/components/file-navigator.tsx +72 -74
- package/src/components/header.tsx +31 -0
- package/src/components/sidebar-items/add-ons.tsx +48 -45
- package/src/components/sidebar-items/mode-selector.tsx +6 -4
- package/src/components/sidebar-items/project-name.tsx +4 -5
- package/src/components/sidebar-items/typescript-switch.tsx +3 -3
- package/src/components/startup-dialog.tsx +4 -6
- package/src/components/ui/switch.tsx +6 -6
- package/src/hooks/use-mounted.ts +9 -0
- package/src/hooks/use-preferred-reduced-motion.ts +27 -0
- package/src/index.tsx +24 -20
- package/src/store/project.ts +36 -20
- package/src/styles.css +90 -18
- package/src/types.d.ts +1 -1
- package/tailwind.config.cjs +47 -0
- package/dist/assets/index-D0-fpgzI.js +0 -223
- package/dist/assets/index-D0-fpgzI.js.map +0 -1
- package/dist/assets/index-D5brMzJg.css +0 -1
package/dist/index.html
CHANGED
|
@@ -4,10 +4,16 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>TanStack CTA</title>
|
|
7
|
-
<
|
|
8
|
-
<link rel="
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="" />
|
|
9
|
+
<link
|
|
10
|
+
rel="stylesheet"
|
|
11
|
+
href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap"
|
|
12
|
+
/>
|
|
13
|
+
<script type="module" crossorigin src="/assets/index-DWTDdndE.js"></script>
|
|
14
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DSKioOfX.css">
|
|
9
15
|
</head>
|
|
10
|
-
<body class="dark
|
|
16
|
+
<body class="dark">
|
|
11
17
|
<div id="root"></div>
|
|
12
18
|
</body>
|
|
13
19
|
</html>
|
|
Binary file
|
package/index.html
CHANGED
|
@@ -4,8 +4,14 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>TanStack CTA</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="" />
|
|
9
|
+
<link
|
|
10
|
+
rel="stylesheet"
|
|
11
|
+
href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap"
|
|
12
|
+
/>
|
|
7
13
|
</head>
|
|
8
|
-
<body class="dark
|
|
14
|
+
<body class="dark">
|
|
9
15
|
<div id="root"></div>
|
|
10
16
|
<script type="module" src="/src/main.tsx"></script>
|
|
11
17
|
</body>
|
|
@@ -1,52 +1,63 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs'
|
|
2
1
|
import { resolve } from 'node:path'
|
|
3
2
|
|
|
4
3
|
import {
|
|
4
|
+
CONFIG_FILE,
|
|
5
5
|
addToApp,
|
|
6
|
+
createAppOptionsFromPersisted,
|
|
6
7
|
createDefaultEnvironment,
|
|
7
8
|
createMemoryEnvironment,
|
|
9
|
+
createSerializedOptionsFromPersisted,
|
|
10
|
+
readConfigFile,
|
|
8
11
|
recursivelyGatherFiles,
|
|
12
|
+
writeConfigFileToEnvironment,
|
|
9
13
|
} from '@tanstack/cta-engine'
|
|
10
14
|
|
|
11
15
|
import { cleanUpFileArray, cleanUpFiles } from './file-helpers.js'
|
|
12
16
|
import { getProjectPath } from './server-environment.js'
|
|
13
17
|
import { createAppWrapper } from './create-app-wrapper.js'
|
|
14
18
|
|
|
19
|
+
import type { Environment } from '@tanstack/cta-engine'
|
|
15
20
|
import type { Response } from 'express'
|
|
21
|
+
import type { DryRunOutput } from '../types.js'
|
|
16
22
|
|
|
17
23
|
export async function addToAppWrapper(
|
|
18
24
|
addOns: Array<string>,
|
|
19
25
|
opts: {
|
|
20
26
|
dryRun?: boolean
|
|
21
27
|
response?: Response
|
|
28
|
+
environmentFactory?: () => Environment
|
|
22
29
|
},
|
|
23
30
|
) {
|
|
24
31
|
const projectPath = getProjectPath()
|
|
25
32
|
|
|
26
|
-
const persistedOptions =
|
|
27
|
-
readFileSync(resolve(projectPath, '.cta.json')).toString(),
|
|
28
|
-
)
|
|
33
|
+
const persistedOptions = await readConfigFile(projectPath)
|
|
29
34
|
|
|
30
|
-
persistedOptions
|
|
35
|
+
if (!persistedOptions) {
|
|
36
|
+
throw new Error('No config file found')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const options = await createAppOptionsFromPersisted(persistedOptions)
|
|
40
|
+
options.targetDir = projectPath
|
|
31
41
|
|
|
32
42
|
const newAddons: Array<string> = []
|
|
33
43
|
for (const addOn of addOns) {
|
|
34
|
-
if (!
|
|
44
|
+
if (!options.chosenAddOns.some((a) => a.id === addOn)) {
|
|
35
45
|
newAddons.push(addOn)
|
|
36
46
|
}
|
|
37
47
|
}
|
|
38
48
|
|
|
39
49
|
if (newAddons.length === 0) {
|
|
40
|
-
|
|
50
|
+
const serializedOptions =
|
|
51
|
+
createSerializedOptionsFromPersisted(persistedOptions)
|
|
52
|
+
return await createAppWrapper(serializedOptions, opts)
|
|
41
53
|
}
|
|
42
54
|
|
|
43
|
-
async function createEnvironment() {
|
|
55
|
+
async function createEnvironment(): Promise<{
|
|
56
|
+
environment: Environment
|
|
57
|
+
output: DryRunOutput
|
|
58
|
+
}> {
|
|
44
59
|
if (opts.dryRun) {
|
|
45
60
|
const { environment, output } = createMemoryEnvironment(projectPath)
|
|
46
|
-
environment.writeFile(
|
|
47
|
-
resolve(projectPath, '.cta.json'),
|
|
48
|
-
JSON.stringify(persistedOptions, null, 2),
|
|
49
|
-
)
|
|
50
61
|
|
|
51
62
|
const localFiles = await cleanUpFiles(
|
|
52
63
|
await recursivelyGatherFiles(projectPath, false),
|
|
@@ -57,7 +68,7 @@ export async function addToAppWrapper(
|
|
|
57
68
|
return { environment, output }
|
|
58
69
|
}
|
|
59
70
|
return {
|
|
60
|
-
environment: createDefaultEnvironment(),
|
|
71
|
+
environment: opts.environmentFactory?.() ?? createDefaultEnvironment(),
|
|
61
72
|
output: { files: {}, deletedFiles: [], commands: [] },
|
|
62
73
|
}
|
|
63
74
|
}
|
|
@@ -70,7 +81,15 @@ export async function addToAppWrapper(
|
|
|
70
81
|
'Transfer-Encoding': 'chunked',
|
|
71
82
|
})
|
|
72
83
|
|
|
73
|
-
environment.startStep = ({
|
|
84
|
+
environment.startStep = ({
|
|
85
|
+
id,
|
|
86
|
+
type,
|
|
87
|
+
message,
|
|
88
|
+
}: {
|
|
89
|
+
id: string
|
|
90
|
+
type: string
|
|
91
|
+
message: string
|
|
92
|
+
}) => {
|
|
74
93
|
opts.response!.write(
|
|
75
94
|
JSON.stringify({
|
|
76
95
|
msgType: 'start',
|
|
@@ -80,7 +99,7 @@ export async function addToAppWrapper(
|
|
|
80
99
|
}) + '\n',
|
|
81
100
|
)
|
|
82
101
|
}
|
|
83
|
-
environment.finishStep = (id, message) => {
|
|
102
|
+
environment.finishStep = (id: string, message: string) => {
|
|
84
103
|
opts.response!.write(
|
|
85
104
|
JSON.stringify({
|
|
86
105
|
msgType: 'finish',
|
|
@@ -98,9 +117,14 @@ export async function addToAppWrapper(
|
|
|
98
117
|
opts.response.end()
|
|
99
118
|
} else {
|
|
100
119
|
environment.startRun()
|
|
120
|
+
environment.writeFile(
|
|
121
|
+
resolve(projectPath, CONFIG_FILE),
|
|
122
|
+
JSON.stringify(persistedOptions, null, 2),
|
|
123
|
+
)
|
|
101
124
|
await addToApp(environment, newAddons, projectPath, {
|
|
102
125
|
forced: true,
|
|
103
126
|
})
|
|
127
|
+
writeConfigFileToEnvironment(environment, options)
|
|
104
128
|
environment.finishRun()
|
|
105
129
|
|
|
106
130
|
output.files = cleanUpFiles(output.files, projectPath)
|
|
@@ -14,24 +14,35 @@ import { registerFrameworks } from './framework-registration.js'
|
|
|
14
14
|
import { cleanUpFileArray, cleanUpFiles } from './file-helpers.js'
|
|
15
15
|
import { getApplicationMode, getProjectPath } from './server-environment.js'
|
|
16
16
|
|
|
17
|
-
import type {
|
|
17
|
+
import type {
|
|
18
|
+
Environment,
|
|
19
|
+
Options,
|
|
20
|
+
SerializedOptions,
|
|
21
|
+
Starter,
|
|
22
|
+
} from '@tanstack/cta-engine'
|
|
18
23
|
|
|
19
24
|
import type { Response } from 'express'
|
|
20
25
|
|
|
21
26
|
export async function createAppWrapper(
|
|
22
27
|
projectOptions: SerializedOptions,
|
|
23
|
-
opts: {
|
|
28
|
+
opts: {
|
|
29
|
+
dryRun?: boolean
|
|
30
|
+
response?: Response
|
|
31
|
+
environmentFactory?: () => Environment
|
|
32
|
+
},
|
|
24
33
|
) {
|
|
25
34
|
registerFrameworks()
|
|
26
35
|
|
|
27
36
|
const framework = getFrameworkById(projectOptions.framework)!
|
|
28
37
|
|
|
29
38
|
let starter: Starter | undefined
|
|
30
|
-
const addOns: Array<string> = [...
|
|
39
|
+
const addOns: Array<string> = [...projectOptions.chosenAddOns]
|
|
31
40
|
if (projectOptions.starter) {
|
|
32
41
|
starter = await loadStarter(projectOptions.starter)
|
|
33
|
-
|
|
34
|
-
|
|
42
|
+
if (starter) {
|
|
43
|
+
for (const addOn of starter.dependsOn ?? []) {
|
|
44
|
+
addOns.push(addOn)
|
|
45
|
+
}
|
|
35
46
|
}
|
|
36
47
|
}
|
|
37
48
|
const chosenAddOns = await finalizeAddOns(
|
|
@@ -59,7 +70,7 @@ export async function createAppWrapper(
|
|
|
59
70
|
return createMemoryEnvironment(targetDir)
|
|
60
71
|
}
|
|
61
72
|
return {
|
|
62
|
-
environment: createDefaultEnvironment(),
|
|
73
|
+
environment: opts.environmentFactory?.() ?? createDefaultEnvironment(),
|
|
63
74
|
output: { files: {}, deletedFiles: [], commands: [] },
|
|
64
75
|
}
|
|
65
76
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { basename } from 'node:path'
|
|
2
2
|
|
|
3
|
+
import { CONFIG_FILE } from '@tanstack/cta-engine'
|
|
4
|
+
|
|
3
5
|
export function cleanUpFiles(
|
|
4
6
|
files: Record<string, string>,
|
|
5
7
|
targetDir?: string,
|
|
@@ -8,7 +10,7 @@ export function cleanUpFiles(
|
|
|
8
10
|
const content = files[file].startsWith('base64::')
|
|
9
11
|
? '<binary file>'
|
|
10
12
|
: files[file]
|
|
11
|
-
if (basename(file) !==
|
|
13
|
+
if (basename(file) !== CONFIG_FILE) {
|
|
12
14
|
acc[targetDir ? file.replace(targetDir, '.') : file] = content
|
|
13
15
|
}
|
|
14
16
|
return acc
|
|
@@ -17,7 +19,7 @@ export function cleanUpFiles(
|
|
|
17
19
|
|
|
18
20
|
export function cleanUpFileArray(files: Array<string>, targetDir?: string) {
|
|
19
21
|
return files.reduce<Array<string>>((acc, file) => {
|
|
20
|
-
if (basename(file) !==
|
|
22
|
+
if (basename(file) !== CONFIG_FILE) {
|
|
21
23
|
acc.push(targetDir ? file.replace(targetDir, '.') : file)
|
|
22
24
|
}
|
|
23
25
|
return acc
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs'
|
|
2
1
|
import { basename, resolve } from 'node:path'
|
|
3
2
|
|
|
4
3
|
import {
|
|
5
4
|
createSerializedOptionsFromPersisted,
|
|
6
5
|
getAllAddOns,
|
|
7
6
|
getFrameworkById,
|
|
7
|
+
getRawRegistry,
|
|
8
|
+
getRegistryAddOns,
|
|
9
|
+
readConfigFile,
|
|
8
10
|
recursivelyGatherFiles,
|
|
9
11
|
} from '@tanstack/cta-engine'
|
|
10
12
|
|
|
@@ -17,18 +19,24 @@ import {
|
|
|
17
19
|
getForcedRouterMode,
|
|
18
20
|
getProjectOptions,
|
|
19
21
|
getProjectPath,
|
|
20
|
-
getRegistry,
|
|
22
|
+
getRegistry as getRegistryURL,
|
|
21
23
|
} from './server-environment.js'
|
|
22
24
|
|
|
23
|
-
import type { SerializedOptions } from '@tanstack/cta-engine'
|
|
24
|
-
import type {
|
|
25
|
+
import type { AddOn, SerializedOptions } from '@tanstack/cta-engine'
|
|
26
|
+
import type { AddOnInfo } from '../types.js'
|
|
25
27
|
|
|
26
|
-
function
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
function convertAddOnToAddOnInfo(addOn: AddOn): AddOnInfo {
|
|
29
|
+
return {
|
|
30
|
+
id: addOn.id,
|
|
31
|
+
name: addOn.name,
|
|
32
|
+
description: addOn.description,
|
|
33
|
+
modes: addOn.modes as Array<'code-router' | 'file-router'>,
|
|
34
|
+
type: addOn.type,
|
|
35
|
+
smallLogo: addOn.smallLogo,
|
|
36
|
+
logo: addOn.logo,
|
|
37
|
+
link: addOn.link!,
|
|
38
|
+
dependsOn: addOn.dependsOn,
|
|
29
39
|
}
|
|
30
|
-
const baseUrl = originalUrl.replace(/registry.json$/, '')
|
|
31
|
-
return `${baseUrl}${relativeUrl.replace(/^\.\//, '')}`
|
|
32
40
|
}
|
|
33
41
|
|
|
34
42
|
export async function generateInitialPayload() {
|
|
@@ -44,7 +52,7 @@ export async function generateInitialPayload() {
|
|
|
44
52
|
|
|
45
53
|
const forcedRouterMode = getForcedRouterMode()
|
|
46
54
|
|
|
47
|
-
function getSerializedOptions() {
|
|
55
|
+
async function getSerializedOptions() {
|
|
48
56
|
if (applicationMode === 'setup') {
|
|
49
57
|
const projectOptions = getProjectOptions()
|
|
50
58
|
return {
|
|
@@ -55,31 +63,23 @@ export async function generateInitialPayload() {
|
|
|
55
63
|
typescript: projectOptions.typescript || true,
|
|
56
64
|
tailwind: projectOptions.tailwind || true,
|
|
57
65
|
git: projectOptions.git || true,
|
|
66
|
+
targetDir:
|
|
67
|
+
projectOptions.targetDir ||
|
|
68
|
+
resolve(projectPath, projectOptions.projectName),
|
|
58
69
|
} as SerializedOptions
|
|
59
70
|
} else {
|
|
60
|
-
const persistedOptions =
|
|
61
|
-
|
|
62
|
-
|
|
71
|
+
const persistedOptions = await readConfigFile(projectPath)
|
|
72
|
+
if (!persistedOptions) {
|
|
73
|
+
throw new Error('No config file found')
|
|
74
|
+
}
|
|
63
75
|
return createSerializedOptionsFromPersisted(persistedOptions)
|
|
64
76
|
}
|
|
65
77
|
}
|
|
66
78
|
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
if (registryUrl) {
|
|
70
|
-
registry = (await fetch(registryUrl).then((res) => res.json())) as Registry
|
|
71
|
-
for (const addOn of registry['add-ons']) {
|
|
72
|
-
addOn.url = absolutizeUrl(registryUrl, addOn.url)
|
|
73
|
-
}
|
|
74
|
-
for (const starter of registry.starters) {
|
|
75
|
-
starter.url = absolutizeUrl(registryUrl, starter.url)
|
|
76
|
-
if (starter.banner) {
|
|
77
|
-
starter.banner = absolutizeUrl(registryUrl, starter.banner)
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
79
|
+
const rawRegistry = await getRawRegistry(getRegistryURL())
|
|
80
|
+
const registryAddOns = await getRegistryAddOns(getRegistryURL())
|
|
81
81
|
|
|
82
|
-
const serializedOptions = getSerializedOptions()
|
|
82
|
+
const serializedOptions = await getSerializedOptions()
|
|
83
83
|
|
|
84
84
|
const output = await createAppWrapper(serializedOptions, {
|
|
85
85
|
dryRun: true,
|
|
@@ -87,39 +87,46 @@ export async function generateInitialPayload() {
|
|
|
87
87
|
|
|
88
88
|
const framework = await getFrameworkById(serializedOptions.framework)
|
|
89
89
|
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
90
|
+
const codeRouterAddOns = getAllAddOns(framework!, 'code-router').map(
|
|
91
|
+
convertAddOnToAddOnInfo,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
const fileRouterAddOns = getAllAddOns(framework!, 'file-router').map(
|
|
95
|
+
convertAddOnToAddOnInfo,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
for (const addOnInfo of registryAddOns || []) {
|
|
99
|
+
const addOnFramework = rawRegistry?.['add-ons'].find(
|
|
100
|
+
(addOn) => addOn.url === addOnInfo.id,
|
|
101
|
+
)
|
|
102
|
+
if (addOnFramework?.framework === serializedOptions.framework) {
|
|
103
|
+
if (addOnInfo.modes.includes('code-router')) {
|
|
104
|
+
codeRouterAddOns.push(convertAddOnToAddOnInfo(addOnInfo))
|
|
105
|
+
}
|
|
106
|
+
if (addOnInfo.modes.includes('file-router')) {
|
|
107
|
+
fileRouterAddOns.push(convertAddOnToAddOnInfo(addOnInfo))
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
100
111
|
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
logo: addOn.logo,
|
|
108
|
-
link: addOn.link,
|
|
109
|
-
dependsOn: addOn.dependsOn,
|
|
110
|
-
}))
|
|
112
|
+
const serializedRegistry = {
|
|
113
|
+
['add-ons']: [],
|
|
114
|
+
starters: (rawRegistry?.starters || []).filter(
|
|
115
|
+
(starter) => starter.framework === serializedOptions.framework,
|
|
116
|
+
),
|
|
117
|
+
}
|
|
111
118
|
|
|
112
119
|
return {
|
|
113
120
|
applicationMode,
|
|
114
121
|
localFiles,
|
|
115
122
|
addOns: {
|
|
116
|
-
'code-router':
|
|
117
|
-
'file-router':
|
|
123
|
+
'code-router': codeRouterAddOns,
|
|
124
|
+
'file-router': fileRouterAddOns,
|
|
118
125
|
},
|
|
119
126
|
options: serializedOptions,
|
|
120
127
|
output,
|
|
121
128
|
forcedRouterMode,
|
|
122
129
|
forcedAddOns: getForcedAddOns(),
|
|
123
|
-
registry,
|
|
130
|
+
registry: serializedRegistry,
|
|
124
131
|
}
|
|
125
132
|
}
|
package/lib/index.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { dirname, resolve } from 'node:path'
|
|
|
2
2
|
import { fileURLToPath } from 'node:url'
|
|
3
3
|
import express from 'express'
|
|
4
4
|
import cors from 'cors'
|
|
5
|
+
import chalk from 'chalk'
|
|
6
|
+
|
|
5
7
|
import {
|
|
6
8
|
AddOnCompiledSchema,
|
|
7
9
|
StarterCompiledSchema,
|
|
@@ -13,10 +15,12 @@ import { generateInitialPayload } from './engine-handling/generate-initial-paylo
|
|
|
13
15
|
import { setServerEnvironment } from './engine-handling/server-environment.js'
|
|
14
16
|
|
|
15
17
|
import type { ServerEnvironment } from './engine-handling/server-environment.js'
|
|
18
|
+
import type { Environment } from '@tanstack/cta-engine'
|
|
16
19
|
|
|
17
20
|
export function launchUI(
|
|
18
21
|
options: Partial<ServerEnvironment> & {
|
|
19
22
|
port?: number
|
|
23
|
+
environmentFactory?: () => Environment
|
|
20
24
|
},
|
|
21
25
|
) {
|
|
22
26
|
const { port: requestedPort, ...rest } = options
|
|
@@ -37,12 +41,14 @@ export function launchUI(
|
|
|
37
41
|
app.post('/api/add-to-app', async (req, res) => {
|
|
38
42
|
await addToAppWrapper(req.body.addOns, {
|
|
39
43
|
response: res,
|
|
44
|
+
environmentFactory: options.environmentFactory,
|
|
40
45
|
})
|
|
41
46
|
})
|
|
42
47
|
|
|
43
48
|
app.post('/api/create-app', async (req, res) => {
|
|
44
49
|
await createAppWrapper(req.body.options, {
|
|
45
50
|
response: res,
|
|
51
|
+
environmentFactory: options.environmentFactory,
|
|
46
52
|
})
|
|
47
53
|
})
|
|
48
54
|
|
|
@@ -50,6 +56,7 @@ export function launchUI(
|
|
|
50
56
|
res.send(
|
|
51
57
|
await addToAppWrapper(req.body.addOns, {
|
|
52
58
|
dryRun: true,
|
|
59
|
+
environmentFactory: options.environmentFactory,
|
|
53
60
|
}),
|
|
54
61
|
)
|
|
55
62
|
})
|
|
@@ -58,6 +65,7 @@ export function launchUI(
|
|
|
58
65
|
res.send(
|
|
59
66
|
await createAppWrapper(req.body.options, {
|
|
60
67
|
dryRun: true,
|
|
68
|
+
environmentFactory: options.environmentFactory,
|
|
61
69
|
}),
|
|
62
70
|
)
|
|
63
71
|
})
|
|
@@ -143,7 +151,9 @@ export function launchUI(
|
|
|
143
151
|
const port = requestedPort || process.env.PORT || 8080
|
|
144
152
|
app.listen(port, () => {
|
|
145
153
|
console.log(
|
|
146
|
-
`Create TanStack ${launchUI ? 'App' : 'API'} is running on
|
|
154
|
+
`🔥 ${chalk.blueBright(`Create TanStack ${launchUI ? 'App' : 'API'}`)} is running on ${chalk.underline(
|
|
155
|
+
`http://localhost:${port}`,
|
|
156
|
+
)}`,
|
|
147
157
|
)
|
|
148
158
|
})
|
|
149
159
|
}
|
package/lib/types.d.ts
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
export type
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
banner?: string
|
|
7
|
-
}>
|
|
8
|
-
'add-ons': Array<{
|
|
9
|
-
name: string
|
|
10
|
-
description: string
|
|
11
|
-
url: string
|
|
1
|
+
export type DryRunOutput = {
|
|
2
|
+
files: Record<string, string>
|
|
3
|
+
commands: Array<{
|
|
4
|
+
command: string
|
|
5
|
+
args: Array<string>
|
|
12
6
|
}>
|
|
7
|
+
deletedFiles: Array<string>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type AddOnInfo = {
|
|
11
|
+
id: string
|
|
12
|
+
name: string
|
|
13
|
+
description: string
|
|
14
|
+
type: 'add-on' | 'example' | 'starter' | 'toolchain'
|
|
15
|
+
modes: Array<'code-router' | 'file-router'>
|
|
16
|
+
smallLogo?: string
|
|
17
|
+
logo?: string
|
|
18
|
+
link: string
|
|
19
|
+
dependsOn?: Array<string>
|
|
13
20
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import type { Environment } from '@tanstack/cta-engine';
|
|
1
2
|
import type { Response } from 'express';
|
|
2
3
|
export declare function addToAppWrapper(addOns: Array<string>, opts: {
|
|
3
4
|
dryRun?: boolean;
|
|
4
5
|
response?: Response;
|
|
6
|
+
environmentFactory?: () => Environment;
|
|
5
7
|
}): Promise<{
|
|
6
8
|
files: Record<string, string>;
|
|
7
9
|
deletedFiles: Array<string>;
|
|
@@ -1,26 +1,29 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
2
1
|
import { resolve } from 'node:path';
|
|
3
|
-
import { addToApp, createDefaultEnvironment, createMemoryEnvironment, recursivelyGatherFiles, } from '@tanstack/cta-engine';
|
|
2
|
+
import { CONFIG_FILE, addToApp, createAppOptionsFromPersisted, createDefaultEnvironment, createMemoryEnvironment, createSerializedOptionsFromPersisted, readConfigFile, recursivelyGatherFiles, writeConfigFileToEnvironment, } from '@tanstack/cta-engine';
|
|
4
3
|
import { cleanUpFileArray, cleanUpFiles } from './file-helpers.js';
|
|
5
4
|
import { getProjectPath } from './server-environment.js';
|
|
6
5
|
import { createAppWrapper } from './create-app-wrapper.js';
|
|
7
6
|
export async function addToAppWrapper(addOns, opts) {
|
|
8
7
|
const projectPath = getProjectPath();
|
|
9
|
-
const persistedOptions =
|
|
10
|
-
persistedOptions
|
|
8
|
+
const persistedOptions = await readConfigFile(projectPath);
|
|
9
|
+
if (!persistedOptions) {
|
|
10
|
+
throw new Error('No config file found');
|
|
11
|
+
}
|
|
12
|
+
const options = await createAppOptionsFromPersisted(persistedOptions);
|
|
13
|
+
options.targetDir = projectPath;
|
|
11
14
|
const newAddons = [];
|
|
12
15
|
for (const addOn of addOns) {
|
|
13
|
-
if (!
|
|
16
|
+
if (!options.chosenAddOns.some((a) => a.id === addOn)) {
|
|
14
17
|
newAddons.push(addOn);
|
|
15
18
|
}
|
|
16
19
|
}
|
|
17
20
|
if (newAddons.length === 0) {
|
|
18
|
-
|
|
21
|
+
const serializedOptions = createSerializedOptionsFromPersisted(persistedOptions);
|
|
22
|
+
return await createAppWrapper(serializedOptions, opts);
|
|
19
23
|
}
|
|
20
24
|
async function createEnvironment() {
|
|
21
25
|
if (opts.dryRun) {
|
|
22
26
|
const { environment, output } = createMemoryEnvironment(projectPath);
|
|
23
|
-
environment.writeFile(resolve(projectPath, '.cta.json'), JSON.stringify(persistedOptions, null, 2));
|
|
24
27
|
const localFiles = await cleanUpFiles(await recursivelyGatherFiles(projectPath, false));
|
|
25
28
|
for (const file of Object.keys(localFiles)) {
|
|
26
29
|
environment.writeFile(resolve(projectPath, file), localFiles[file]);
|
|
@@ -28,7 +31,7 @@ export async function addToAppWrapper(addOns, opts) {
|
|
|
28
31
|
return { environment, output };
|
|
29
32
|
}
|
|
30
33
|
return {
|
|
31
|
-
environment: createDefaultEnvironment(),
|
|
34
|
+
environment: opts.environmentFactory?.() ?? createDefaultEnvironment(),
|
|
32
35
|
output: { files: {}, deletedFiles: [], commands: [] },
|
|
33
36
|
};
|
|
34
37
|
}
|
|
@@ -38,7 +41,7 @@ export async function addToAppWrapper(addOns, opts) {
|
|
|
38
41
|
'Content-Type': 'text/plain',
|
|
39
42
|
'Transfer-Encoding': 'chunked',
|
|
40
43
|
});
|
|
41
|
-
environment.startStep = ({ id, type, message }) => {
|
|
44
|
+
environment.startStep = ({ id, type, message, }) => {
|
|
42
45
|
opts.response.write(JSON.stringify({
|
|
43
46
|
msgType: 'start',
|
|
44
47
|
id,
|
|
@@ -62,9 +65,11 @@ export async function addToAppWrapper(addOns, opts) {
|
|
|
62
65
|
}
|
|
63
66
|
else {
|
|
64
67
|
environment.startRun();
|
|
68
|
+
environment.writeFile(resolve(projectPath, CONFIG_FILE), JSON.stringify(persistedOptions, null, 2));
|
|
65
69
|
await addToApp(environment, newAddons, projectPath, {
|
|
66
70
|
forced: true,
|
|
67
71
|
});
|
|
72
|
+
writeConfigFileToEnvironment(environment, options);
|
|
68
73
|
environment.finishRun();
|
|
69
74
|
output.files = cleanUpFiles(output.files, projectPath);
|
|
70
75
|
output.deletedFiles = cleanUpFileArray(output.deletedFiles, projectPath);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type { SerializedOptions } from '@tanstack/cta-engine';
|
|
1
|
+
import type { Environment, SerializedOptions } from '@tanstack/cta-engine';
|
|
2
2
|
import type { Response } from 'express';
|
|
3
3
|
export declare function createAppWrapper(projectOptions: SerializedOptions, opts: {
|
|
4
4
|
dryRun?: boolean;
|
|
5
5
|
response?: Response;
|
|
6
|
+
environmentFactory?: () => Environment;
|
|
6
7
|
}): Promise<{
|
|
7
8
|
files: Record<string, string>;
|
|
8
9
|
deletedFiles: Array<string>;
|
|
@@ -7,11 +7,13 @@ export async function createAppWrapper(projectOptions, opts) {
|
|
|
7
7
|
registerFrameworks();
|
|
8
8
|
const framework = getFrameworkById(projectOptions.framework);
|
|
9
9
|
let starter;
|
|
10
|
-
const addOns = [...
|
|
10
|
+
const addOns = [...projectOptions.chosenAddOns];
|
|
11
11
|
if (projectOptions.starter) {
|
|
12
12
|
starter = await loadStarter(projectOptions.starter);
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
if (starter) {
|
|
14
|
+
for (const addOn of starter.dependsOn ?? []) {
|
|
15
|
+
addOns.push(addOn);
|
|
16
|
+
}
|
|
15
17
|
}
|
|
16
18
|
}
|
|
17
19
|
const chosenAddOns = await finalizeAddOns(framework, projectOptions.mode, addOns);
|
|
@@ -31,7 +33,7 @@ export async function createAppWrapper(projectOptions, opts) {
|
|
|
31
33
|
return createMemoryEnvironment(targetDir);
|
|
32
34
|
}
|
|
33
35
|
return {
|
|
34
|
-
environment: createDefaultEnvironment(),
|
|
36
|
+
environment: opts.environmentFactory?.() ?? createDefaultEnvironment(),
|
|
35
37
|
output: { files: {}, deletedFiles: [], commands: [] },
|
|
36
38
|
};
|
|
37
39
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { basename } from 'node:path';
|
|
2
|
+
import { CONFIG_FILE } from '@tanstack/cta-engine';
|
|
2
3
|
export function cleanUpFiles(files, targetDir) {
|
|
3
4
|
return Object.keys(files).reduce((acc, file) => {
|
|
4
5
|
const content = files[file].startsWith('base64::')
|
|
5
6
|
? '<binary file>'
|
|
6
7
|
: files[file];
|
|
7
|
-
if (basename(file) !==
|
|
8
|
+
if (basename(file) !== CONFIG_FILE) {
|
|
8
9
|
acc[targetDir ? file.replace(targetDir, '.') : file] = content;
|
|
9
10
|
}
|
|
10
11
|
return acc;
|
|
@@ -12,7 +13,7 @@ export function cleanUpFiles(files, targetDir) {
|
|
|
12
13
|
}
|
|
13
14
|
export function cleanUpFileArray(files, targetDir) {
|
|
14
15
|
return files.reduce((acc, file) => {
|
|
15
|
-
if (basename(file) !==
|
|
16
|
+
if (basename(file) !== CONFIG_FILE) {
|
|
16
17
|
acc.push(targetDir ? file.replace(targetDir, '.') : file);
|
|
17
18
|
}
|
|
18
19
|
return acc;
|