create-tsrouter-app 0.3.0 → 0.4.0
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 +41 -9
- package/dist/add-ons.js +69 -0
- package/dist/cli.js +78 -0
- package/dist/constants.js +4 -0
- package/dist/create-app.js +371 -0
- package/dist/index.js +2 -347
- package/dist/mcp.js +169 -0
- package/dist/options.js +260 -0
- package/dist/{utils/getPackageManager.js → package-manager.js} +1 -0
- package/dist/types.js +1 -0
- package/package.json +6 -3
- package/src/add-ons.ts +156 -0
- package/src/cli.ts +114 -0
- package/src/constants.ts +7 -0
- package/src/create-app.ts +582 -0
- package/src/index.ts +2 -507
- package/src/mcp.ts +205 -0
- package/src/options.ts +308 -0
- package/src/{utils/getPackageManager.ts → package-manager.ts} +1 -0
- package/src/types.ts +30 -0
- package/templates/react/add-on/clerk/README.md +3 -0
- package/templates/react/add-on/clerk/assets/_dot_env.local.append +2 -0
- package/templates/react/add-on/clerk/assets/src/integrations/clerk/header-user.tsx +19 -0
- package/templates/react/add-on/clerk/assets/src/integrations/clerk/provider.tsx +18 -0
- package/templates/react/add-on/clerk/assets/src/routes/demo.clerk.tsx +20 -0
- package/templates/react/add-on/clerk/info.json +13 -0
- package/templates/react/add-on/clerk/package.json +5 -0
- package/templates/react/add-on/convex/README.md +4 -0
- package/templates/react/add-on/convex/assets/_dot_cursorrules.append +93 -0
- package/templates/react/add-on/convex/assets/_dot_env.local.append +3 -0
- package/templates/react/add-on/convex/assets/convex/products.ts +8 -0
- package/templates/react/add-on/convex/assets/convex/schema.ts +10 -0
- package/templates/react/add-on/convex/assets/src/integrations/convex/provider.tsx +20 -0
- package/templates/react/add-on/convex/assets/src/routes/demo.convex.tsx +33 -0
- package/templates/react/add-on/convex/info.json +13 -0
- package/templates/react/add-on/convex/package.json +6 -0
- package/templates/react/add-on/form/assets/src/routes/demo.form.tsx.ejs +62 -0
- package/templates/react/add-on/form/info.json +13 -0
- package/templates/react/add-on/form/package.json +5 -0
- package/templates/react/add-on/module-federation/assets/module-federation.config.js.ejs +31 -0
- package/templates/react/add-on/module-federation/assets/src/demo-mf-component.tsx +3 -0
- package/templates/react/add-on/module-federation/assets/src/demo-mf-self-contained.tsx +11 -0
- package/templates/react/add-on/module-federation/info.json +7 -0
- package/templates/react/add-on/module-federation/package.json +5 -0
- package/templates/react/add-on/netlify/README.md +11 -0
- package/templates/react/add-on/netlify/info.json +7 -0
- package/templates/react/add-on/sentry/assets/_dot_cursorrules.append +22 -0
- package/templates/react/add-on/sentry/assets/_dot_env.local.append +2 -0
- package/templates/react/add-on/sentry/assets/src/app/global-middleware.ts +25 -0
- package/templates/react/add-on/sentry/assets/src/routes/demo.sentry.testing.tsx +480 -0
- package/templates/react/add-on/sentry/info.json +14 -0
- package/templates/react/add-on/sentry/package.json +7 -0
- package/templates/react/add-on/shadcn/README.md +7 -0
- package/templates/react/add-on/shadcn/assets/_dot_cursorrules.append +7 -0
- package/templates/react/add-on/shadcn/info.json +11 -0
- package/templates/react/add-on/start/assets/app.config.ts +16 -0
- package/templates/react/add-on/start/assets/postcss.config.ts +5 -0
- package/templates/react/add-on/start/assets/src/api.ts +6 -0
- package/templates/react/add-on/start/assets/src/client.tsx +10 -0
- package/templates/react/add-on/start/assets/src/router.tsx.ejs +51 -0
- package/templates/react/add-on/start/assets/src/routes/api.demo-names.ts +11 -0
- package/templates/react/add-on/start/assets/src/routes/demo.start.api-request.tsx.ejs +33 -0
- package/templates/react/add-on/start/assets/src/routes/demo.start.server-funcs.tsx +49 -0
- package/templates/react/add-on/start/assets/src/ssr.tsx +12 -0
- package/templates/react/add-on/start/info.json +19 -0
- package/templates/react/add-on/start/package.json +14 -0
- package/templates/react/add-on/store/assets/src/lib/demo-store.ts +13 -0
- package/templates/react/add-on/store/assets/src/routes/demo.store.tsx.ejs +75 -0
- package/templates/react/add-on/store/info.json +13 -0
- package/templates/react/add-on/store/package.json +6 -0
- package/templates/react/add-on/tanstack-query/assets/src/integrations/tanstack-query/layout.tsx +5 -0
- package/templates/react/add-on/tanstack-query/assets/src/integrations/tanstack-query/provider.tsx +9 -0
- package/templates/react/add-on/tanstack-query/assets/src/routes/demo.tanstack-query.tsx.ejs +38 -0
- package/templates/react/add-on/tanstack-query/info.json +13 -0
- package/templates/react/add-on/tanstack-query/package.json +6 -0
- package/templates/{base → react/base}/README.md.ejs +9 -0
- package/templates/react/base/_dot_vscode/settings.json +11 -0
- package/templates/react/base/src/components/Header.tsx.ejs +25 -0
- package/templates/{base/tsconfig.json → react/base/tsconfig.json.ejs} +5 -1
- package/templates/react/base/vite.config.js.ejs +24 -0
- package/templates/{code-router → react/code-router}/src/main.tsx.ejs +17 -1
- package/templates/react/example/tanchat/README.md +37 -0
- package/templates/react/example/tanchat/assets/_dot_env.local.append +2 -0
- package/templates/react/example/tanchat/assets/src/components/demo.SettingsDialog.tsx +148 -0
- package/templates/react/example/tanchat/assets/src/demo.index.css +220 -0
- package/templates/react/example/tanchat/assets/src/routes/example.chat.tsx.ejs +375 -0
- package/templates/react/example/tanchat/assets/src/store/demo.hooks.ts +21 -0
- package/templates/react/example/tanchat/assets/src/store/demo.store.ts +133 -0
- package/templates/react/example/tanchat/assets/src/utils/demo.ai.ts +108 -0
- package/templates/react/example/tanchat/info.json +15 -0
- package/templates/react/example/tanchat/package.json +10 -0
- package/templates/react/file-router/src/routes/__root.tsx.ejs +71 -0
- package/templates/solid/add-on/form/assets/src/routes/demo.form.tsx.ejs +148 -0
- package/templates/solid/add-on/form/info.json +13 -0
- package/templates/solid/add-on/form/package.json +5 -0
- package/templates/solid/add-on/module-federation/assets/module-federation.config.js.ejs +27 -0
- package/templates/solid/add-on/module-federation/assets/src/demo-mf-component.tsx +3 -0
- package/templates/solid/add-on/module-federation/assets/src/demo-mf-self-contained.tsx +9 -0
- package/templates/solid/add-on/module-federation/info.json +7 -0
- package/templates/solid/add-on/module-federation/package.json +5 -0
- package/templates/solid/add-on/sentry/assets/_dot_cursorrules.append +22 -0
- package/templates/solid/add-on/sentry/assets/_dot_env.local.append +2 -0
- package/templates/solid/add-on/sentry/assets/src/routes/demo.sentry.bad-event-handler.tsx +20 -0
- package/templates/solid/add-on/sentry/info.json +13 -0
- package/templates/solid/add-on/sentry/package.json +5 -0
- package/templates/solid/add-on/solid-ui/README.md +9 -0
- package/templates/solid/add-on/solid-ui/assets/src/lib/utils.ts +6 -0
- package/templates/solid/add-on/solid-ui/assets/src/styles.css +138 -0
- package/templates/solid/add-on/solid-ui/assets/ui.config.json +13 -0
- package/templates/solid/add-on/solid-ui/info.json +11 -0
- package/templates/solid/add-on/solid-ui/package.json +9 -0
- package/templates/solid/add-on/store/assets/src/lib/demo-store.ts +13 -0
- package/templates/solid/add-on/store/assets/src/routes/demo.store.tsx.ejs +77 -0
- package/templates/solid/add-on/store/info.json +13 -0
- package/templates/solid/add-on/store/package.json +6 -0
- package/templates/solid/add-on/tanstack-query/assets/src/integrations/tanstack-query/header-user.tsx +5 -0
- package/templates/solid/add-on/tanstack-query/assets/src/integrations/tanstack-query/provider.tsx +15 -0
- package/templates/solid/add-on/tanstack-query/assets/src/routes/demo.tanstack-query.tsx +30 -0
- package/templates/solid/add-on/tanstack-query/info.json +13 -0
- package/templates/solid/add-on/tanstack-query/package.json +6 -0
- package/templates/solid/base/README.md.ejs +200 -0
- package/templates/solid/base/_dot_cursorrules.append +35 -0
- package/templates/solid/base/_dot_gitignore +5 -0
- package/templates/solid/base/_dot_vscode/settings.json +11 -0
- package/templates/solid/base/index.html.ejs +20 -0
- package/templates/solid/base/package.json +22 -0
- package/templates/solid/base/package.ts.json +5 -0
- package/templates/solid/base/package.tw.json +6 -0
- package/templates/solid/base/public/favicon.ico +0 -0
- package/templates/solid/base/public/logo192.png +0 -0
- package/templates/solid/base/public/logo512.png +0 -0
- package/templates/solid/base/public/manifest.json +25 -0
- package/templates/solid/base/public/robots.txt +3 -0
- package/templates/solid/base/src/App.css +0 -0
- package/templates/solid/base/src/App.tsx.ejs +47 -0
- package/templates/solid/base/src/components/Header.tsx.ejs +26 -0
- package/templates/solid/base/src/logo.svg +120 -0
- package/templates/solid/base/src/styles.css.ejs +15 -0
- package/templates/solid/base/tsconfig.json.ejs +30 -0
- package/templates/solid/base/vite.config.js.ejs +22 -0
- package/templates/solid/code-router/src/main.tsx.ejs +69 -0
- package/templates/solid/file-router/package.fr.json +5 -0
- package/templates/solid/file-router/src/main.tsx.ejs +44 -0
- package/templates/solid/file-router/src/routes/__root.tsx.ejs +41 -0
- package/templates/solid/file-router/src/routes/index.tsx +43 -0
- package/templates/base/vite.config.js.ejs +0 -15
- package/templates/file-router/src/routes/__root.tsx +0 -11
- /package/templates/{base/gitignore → react/base/_dot_gitignore} +0 -0
- /package/templates/{base → react/base}/index.html.ejs +0 -0
- /package/templates/{base → react/base}/package.json +0 -0
- /package/templates/{base → react/base}/package.ts.json +0 -0
- /package/templates/{base → react/base}/package.tw.json +0 -0
- /package/templates/{base → react/base}/public/favicon.ico +0 -0
- /package/templates/{base → react/base}/public/logo192.png +0 -0
- /package/templates/{base → react/base}/public/logo512.png +0 -0
- /package/templates/{base → react/base}/public/manifest.json +0 -0
- /package/templates/{base → react/base}/public/robots.txt +0 -0
- /package/templates/{base → react/base}/src/App.css +0 -0
- /package/templates/{base → react/base}/src/App.test.tsx.ejs +0 -0
- /package/templates/{base → react/base}/src/App.tsx.ejs +0 -0
- /package/templates/{base → react/base}/src/logo.svg +0 -0
- /package/templates/{base → react/base}/src/reportWebVitals.ts.ejs +0 -0
- /package/templates/{base → react/base}/src/styles.css.ejs +0 -0
- /package/templates/{file-router → react/file-router}/package.fr.json +0 -0
- /package/templates/{file-router → react/file-router}/src/main.tsx.ejs +0 -0
package/src/options.ts
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cancel,
|
|
3
|
+
confirm,
|
|
4
|
+
isCancel,
|
|
5
|
+
multiselect,
|
|
6
|
+
select,
|
|
7
|
+
text,
|
|
8
|
+
} from '@clack/prompts'
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
DEFAULT_PACKAGE_MANAGER,
|
|
12
|
+
SUPPORTED_PACKAGE_MANAGERS,
|
|
13
|
+
getPackageManager,
|
|
14
|
+
} from './package-manager.js'
|
|
15
|
+
import { CODE_ROUTER, DEFAULT_FRAMEWORK, FILE_ROUTER } from './constants.js'
|
|
16
|
+
import { finalizeAddOns, getAllAddOns } from './add-ons.js'
|
|
17
|
+
import type { AddOn, Variable } from './add-ons.js'
|
|
18
|
+
|
|
19
|
+
import type { CliOptions, Options } from './types.js'
|
|
20
|
+
|
|
21
|
+
// If all CLI options are provided, use them directly
|
|
22
|
+
export async function normalizeOptions(
|
|
23
|
+
cliOptions: CliOptions,
|
|
24
|
+
): Promise<Required<Options> | undefined> {
|
|
25
|
+
if (cliOptions.projectName) {
|
|
26
|
+
const typescript =
|
|
27
|
+
cliOptions.template === 'typescript' ||
|
|
28
|
+
cliOptions.template === 'file-router' ||
|
|
29
|
+
cliOptions.framework === 'solid'
|
|
30
|
+
|
|
31
|
+
let tailwind = !!cliOptions.tailwind
|
|
32
|
+
if (cliOptions.framework === 'solid') {
|
|
33
|
+
tailwind = true
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let addOns = false
|
|
37
|
+
let chosenAddOns: Array<AddOn> = []
|
|
38
|
+
if (Array.isArray(cliOptions.addOns)) {
|
|
39
|
+
addOns = true
|
|
40
|
+
chosenAddOns = await finalizeAddOns(
|
|
41
|
+
cliOptions.framework || DEFAULT_FRAMEWORK,
|
|
42
|
+
cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER,
|
|
43
|
+
cliOptions.addOns,
|
|
44
|
+
)
|
|
45
|
+
tailwind = true
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
framework: cliOptions.framework || 'react',
|
|
50
|
+
projectName: cliOptions.projectName,
|
|
51
|
+
typescript,
|
|
52
|
+
tailwind,
|
|
53
|
+
packageManager: cliOptions.packageManager || DEFAULT_PACKAGE_MANAGER,
|
|
54
|
+
mode: cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER,
|
|
55
|
+
git: !!cliOptions.git,
|
|
56
|
+
addOns,
|
|
57
|
+
chosenAddOns,
|
|
58
|
+
variableValues: {},
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function collectVariables(
|
|
64
|
+
variables: Array<Variable>,
|
|
65
|
+
): Promise<Record<string, string | number | boolean>> {
|
|
66
|
+
const responses: Record<string, string | number | boolean> = {}
|
|
67
|
+
for (const variable of variables) {
|
|
68
|
+
if (variable.type === 'string') {
|
|
69
|
+
const response = await text({
|
|
70
|
+
message: variable.description,
|
|
71
|
+
initialValue: variable.default,
|
|
72
|
+
})
|
|
73
|
+
if (isCancel(response)) {
|
|
74
|
+
cancel('Operation cancelled.')
|
|
75
|
+
process.exit(0)
|
|
76
|
+
}
|
|
77
|
+
responses[variable.name] = response
|
|
78
|
+
} else if (variable.type === 'number') {
|
|
79
|
+
const response = await text({
|
|
80
|
+
message: variable.description,
|
|
81
|
+
initialValue: variable.default.toString(),
|
|
82
|
+
})
|
|
83
|
+
if (isCancel(response)) {
|
|
84
|
+
cancel('Operation cancelled.')
|
|
85
|
+
process.exit(0)
|
|
86
|
+
}
|
|
87
|
+
responses[variable.name] = Number(response)
|
|
88
|
+
} else {
|
|
89
|
+
const response = await confirm({
|
|
90
|
+
message: variable.description,
|
|
91
|
+
initialValue: variable.default === true,
|
|
92
|
+
})
|
|
93
|
+
if (isCancel(response)) {
|
|
94
|
+
cancel('Operation cancelled.')
|
|
95
|
+
process.exit(0)
|
|
96
|
+
}
|
|
97
|
+
responses[variable.name] = response
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return responses
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export async function promptForOptions(
|
|
104
|
+
cliOptions: CliOptions,
|
|
105
|
+
): Promise<Required<Options>> {
|
|
106
|
+
const options = {} as Required<Options>
|
|
107
|
+
|
|
108
|
+
options.framework = cliOptions.framework || DEFAULT_FRAMEWORK
|
|
109
|
+
if (options.framework === 'solid') {
|
|
110
|
+
options.typescript = true
|
|
111
|
+
options.tailwind = true
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (cliOptions.addOns) {
|
|
115
|
+
options.typescript = true
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (!cliOptions.projectName) {
|
|
119
|
+
const value = await text({
|
|
120
|
+
message: 'What would you like to name your project?',
|
|
121
|
+
defaultValue: 'my-app',
|
|
122
|
+
validate(value) {
|
|
123
|
+
if (!value) {
|
|
124
|
+
return 'Please enter a name'
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
})
|
|
128
|
+
if (isCancel(value)) {
|
|
129
|
+
cancel('Operation cancelled.')
|
|
130
|
+
process.exit(0)
|
|
131
|
+
}
|
|
132
|
+
options.projectName = value
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Router type selection
|
|
136
|
+
if (!cliOptions.template) {
|
|
137
|
+
const routerType = await select({
|
|
138
|
+
message: 'Select the router type:',
|
|
139
|
+
options: [
|
|
140
|
+
{
|
|
141
|
+
value: FILE_ROUTER,
|
|
142
|
+
label: 'File Router - File-based routing structure',
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
value: CODE_ROUTER,
|
|
146
|
+
label: 'Code Router - Traditional code-based routing',
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
initialValue: FILE_ROUTER,
|
|
150
|
+
})
|
|
151
|
+
if (isCancel(routerType)) {
|
|
152
|
+
cancel('Operation cancelled.')
|
|
153
|
+
process.exit(0)
|
|
154
|
+
}
|
|
155
|
+
options.mode = routerType as typeof CODE_ROUTER | typeof FILE_ROUTER
|
|
156
|
+
} else {
|
|
157
|
+
options.mode = cliOptions.template as
|
|
158
|
+
| typeof CODE_ROUTER
|
|
159
|
+
| typeof FILE_ROUTER
|
|
160
|
+
if (options.mode === FILE_ROUTER) {
|
|
161
|
+
options.typescript = true
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// TypeScript selection (if using Code Router)
|
|
166
|
+
if (!options.typescript) {
|
|
167
|
+
if (options.mode === CODE_ROUTER) {
|
|
168
|
+
const typescriptEnable = await confirm({
|
|
169
|
+
message: 'Would you like to use TypeScript?',
|
|
170
|
+
initialValue: true,
|
|
171
|
+
})
|
|
172
|
+
if (isCancel(typescriptEnable)) {
|
|
173
|
+
cancel('Operation cancelled.')
|
|
174
|
+
process.exit(0)
|
|
175
|
+
}
|
|
176
|
+
options.typescript = typescriptEnable
|
|
177
|
+
} else {
|
|
178
|
+
options.typescript = true
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Tailwind selection
|
|
183
|
+
if (cliOptions.tailwind === undefined && options.framework === 'react') {
|
|
184
|
+
const tailwind = await confirm({
|
|
185
|
+
message: 'Would you like to use Tailwind CSS?',
|
|
186
|
+
initialValue: true,
|
|
187
|
+
})
|
|
188
|
+
if (isCancel(tailwind)) {
|
|
189
|
+
cancel('Operation cancelled.')
|
|
190
|
+
process.exit(0)
|
|
191
|
+
}
|
|
192
|
+
options.tailwind = tailwind
|
|
193
|
+
} else {
|
|
194
|
+
options.tailwind = options.framework === 'solid' || !!cliOptions.tailwind
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Package manager selection
|
|
198
|
+
if (cliOptions.packageManager === undefined) {
|
|
199
|
+
const detectedPackageManager = getPackageManager()
|
|
200
|
+
if (!detectedPackageManager) {
|
|
201
|
+
const pm = await select({
|
|
202
|
+
message: 'Select package manager:',
|
|
203
|
+
options: SUPPORTED_PACKAGE_MANAGERS.map((pm) => ({
|
|
204
|
+
value: pm,
|
|
205
|
+
label: pm,
|
|
206
|
+
})),
|
|
207
|
+
initialValue: DEFAULT_PACKAGE_MANAGER,
|
|
208
|
+
})
|
|
209
|
+
if (isCancel(pm)) {
|
|
210
|
+
cancel('Operation cancelled.')
|
|
211
|
+
process.exit(0)
|
|
212
|
+
}
|
|
213
|
+
options.packageManager = pm
|
|
214
|
+
} else {
|
|
215
|
+
options.packageManager = detectedPackageManager
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
options.packageManager = cliOptions.packageManager
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
options.chosenAddOns = []
|
|
222
|
+
if (Array.isArray(cliOptions.addOns)) {
|
|
223
|
+
options.chosenAddOns = await finalizeAddOns(
|
|
224
|
+
options.framework,
|
|
225
|
+
options.mode,
|
|
226
|
+
cliOptions.addOns,
|
|
227
|
+
)
|
|
228
|
+
options.tailwind = true
|
|
229
|
+
} else if (cliOptions.addOns) {
|
|
230
|
+
// Select any add-ons
|
|
231
|
+
const allAddOns = await getAllAddOns(options.framework, options.mode)
|
|
232
|
+
const addOns = allAddOns.filter((addOn) => addOn.type === 'add-on')
|
|
233
|
+
let selectedAddOns: Array<string> = []
|
|
234
|
+
if (options.typescript && addOns.length > 0) {
|
|
235
|
+
const value = await multiselect({
|
|
236
|
+
message: 'What add-ons would you like for your project:',
|
|
237
|
+
options: addOns.map((addOn) => ({
|
|
238
|
+
value: addOn.id,
|
|
239
|
+
label: addOn.name,
|
|
240
|
+
hint: addOn.description,
|
|
241
|
+
})),
|
|
242
|
+
required: false,
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
if (isCancel(value)) {
|
|
246
|
+
cancel('Operation cancelled.')
|
|
247
|
+
process.exit(0)
|
|
248
|
+
}
|
|
249
|
+
selectedAddOns = value
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Select any examples
|
|
253
|
+
const selectedExamples: Array<string> = []
|
|
254
|
+
// const examples = allAddOns.filter((addOn) => addOn.type === 'example')
|
|
255
|
+
// if (options.typescript && examples.length > 0) {
|
|
256
|
+
// const value = await multiselect({
|
|
257
|
+
// message: 'Would you like any examples?',
|
|
258
|
+
// options: examples.map((addOn) => ({
|
|
259
|
+
// value: addOn.id,
|
|
260
|
+
// label: addOn.name,
|
|
261
|
+
// hint: addOn.description,
|
|
262
|
+
// })),
|
|
263
|
+
// required: false,
|
|
264
|
+
// })
|
|
265
|
+
|
|
266
|
+
// if (isCancel(value)) {
|
|
267
|
+
// cancel('Operation cancelled.')
|
|
268
|
+
// process.exit(0)
|
|
269
|
+
// }
|
|
270
|
+
// selectedExamples = value
|
|
271
|
+
// }
|
|
272
|
+
|
|
273
|
+
if (selectedAddOns.length > 0 || selectedExamples.length > 0) {
|
|
274
|
+
options.chosenAddOns = await finalizeAddOns(
|
|
275
|
+
options.framework,
|
|
276
|
+
options.mode,
|
|
277
|
+
[...selectedAddOns, ...selectedExamples],
|
|
278
|
+
)
|
|
279
|
+
options.tailwind = true
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Collect variables
|
|
284
|
+
const variables: Array<Variable> = []
|
|
285
|
+
for (const addOn of options.chosenAddOns) {
|
|
286
|
+
for (const variable of addOn.variables ?? []) {
|
|
287
|
+
variables.push(variable)
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
options.variableValues = await collectVariables(variables)
|
|
291
|
+
|
|
292
|
+
// Git selection
|
|
293
|
+
if (cliOptions.git === undefined) {
|
|
294
|
+
const git = await confirm({
|
|
295
|
+
message: 'Would you like to initialize a new git repository?',
|
|
296
|
+
initialValue: true,
|
|
297
|
+
})
|
|
298
|
+
if (isCancel(git)) {
|
|
299
|
+
cancel('Operation cancelled.')
|
|
300
|
+
process.exit(0)
|
|
301
|
+
}
|
|
302
|
+
options.git = git
|
|
303
|
+
} else {
|
|
304
|
+
options.git = !!cliOptions.git
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return options
|
|
308
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { AddOn } from './add-ons.js'
|
|
2
|
+
import type { CODE_ROUTER, FILE_ROUTER } from './constants.js'
|
|
3
|
+
import type { PackageManager } from './package-manager.js'
|
|
4
|
+
|
|
5
|
+
export type Framework = 'solid' | 'react'
|
|
6
|
+
|
|
7
|
+
export interface Options {
|
|
8
|
+
framework: Framework
|
|
9
|
+
projectName: string
|
|
10
|
+
typescript: boolean
|
|
11
|
+
tailwind: boolean
|
|
12
|
+
packageManager: PackageManager
|
|
13
|
+
mode: typeof CODE_ROUTER | typeof FILE_ROUTER
|
|
14
|
+
addOns: boolean
|
|
15
|
+
chosenAddOns: Array<AddOn>
|
|
16
|
+
git: boolean
|
|
17
|
+
variableValues: Record<string, string | number | boolean>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface CliOptions {
|
|
21
|
+
template?: 'typescript' | 'javascript' | 'file-router'
|
|
22
|
+
framework?: Framework
|
|
23
|
+
tailwind?: boolean
|
|
24
|
+
packageManager?: PackageManager
|
|
25
|
+
projectName?: string
|
|
26
|
+
git?: boolean
|
|
27
|
+
addOns?: Array<string> | boolean
|
|
28
|
+
listAddOns?: boolean
|
|
29
|
+
mcp?: boolean
|
|
30
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SignedIn,
|
|
3
|
+
SignInButton,
|
|
4
|
+
SignedOut,
|
|
5
|
+
UserButton,
|
|
6
|
+
} from '@clerk/clerk-react'
|
|
7
|
+
|
|
8
|
+
export default function HeaderUser() {
|
|
9
|
+
return (
|
|
10
|
+
<>
|
|
11
|
+
<SignedIn>
|
|
12
|
+
<UserButton />
|
|
13
|
+
</SignedIn>
|
|
14
|
+
<SignedOut>
|
|
15
|
+
<SignInButton />
|
|
16
|
+
</SignedOut>
|
|
17
|
+
</>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ClerkProvider } from '@clerk/clerk-react'
|
|
2
|
+
|
|
3
|
+
const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
|
|
4
|
+
if (!PUBLISHABLE_KEY) {
|
|
5
|
+
throw new Error('Add your Clerk Publishable Key to the .env.local file')
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export default function AppClerkProvider({
|
|
9
|
+
children,
|
|
10
|
+
}: {
|
|
11
|
+
children: React.ReactNode
|
|
12
|
+
}) {
|
|
13
|
+
return (
|
|
14
|
+
<ClerkProvider publishableKey={PUBLISHABLE_KEY} afterSignOutUrl="/">
|
|
15
|
+
{children}
|
|
16
|
+
</ClerkProvider>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
2
|
+
import { useUser } from '@clerk/clerk-react'
|
|
3
|
+
|
|
4
|
+
export const Route = createFileRoute('/demo/clerk')({
|
|
5
|
+
component: App,
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
function App() {
|
|
9
|
+
const { isSignedIn, user, isLoaded } = useUser()
|
|
10
|
+
|
|
11
|
+
if (!isLoaded) {
|
|
12
|
+
return <div className="p-4">Loading...</div>
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (!isSignedIn) {
|
|
16
|
+
return <div className="p-4">Sign in to view this page</div>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return <div className="p-4">Hello {user.firstName}!</div>
|
|
20
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
This document serves as some special instructions when working with Convex.
|
|
2
|
+
|
|
3
|
+
# Schemas
|
|
4
|
+
|
|
5
|
+
When designing the schema please see this page on built in System fields and data types available: https://docs.convex.dev/database/types
|
|
6
|
+
|
|
7
|
+
Here are some specifics that are often mishandled:
|
|
8
|
+
|
|
9
|
+
## v (https://docs.convex.dev/api/modules/values#v)
|
|
10
|
+
|
|
11
|
+
The validator builder.
|
|
12
|
+
|
|
13
|
+
This builder allows you to build validators for Convex values.
|
|
14
|
+
|
|
15
|
+
Validators can be used in schema definitions and as input validators for Convex functions.
|
|
16
|
+
|
|
17
|
+
Type declaration
|
|
18
|
+
Name Type
|
|
19
|
+
id <TableName>(tableName: TableName) => VId<GenericId<TableName>, "required">
|
|
20
|
+
null () => VNull<null, "required">
|
|
21
|
+
number () => VFloat64<number, "required">
|
|
22
|
+
float64 () => VFloat64<number, "required">
|
|
23
|
+
bigint () => VInt64<bigint, "required">
|
|
24
|
+
int64 () => VInt64<bigint, "required">
|
|
25
|
+
boolean () => VBoolean<boolean, "required">
|
|
26
|
+
string () => VString<string, "required">
|
|
27
|
+
bytes () => VBytes<ArrayBuffer, "required">
|
|
28
|
+
literal <T>(literal: T) => VLiteral<T, "required">
|
|
29
|
+
array <T>(element: T) => VArray<T["type"][], T, "required">
|
|
30
|
+
object <T>(fields: T) => VObject<Expand<{ [Property in string | number | symbol]?: Exclude<Infer<T[Property]>, undefined> } & { [Property in string | number | symbol]: Infer<T[Property]> }>, T, "required", { [Property in string | number | symbol]: Property | `${Property & string}.${T[Property]["fieldPaths"]}` }[keyof T] & string>
|
|
31
|
+
record <Key, Value>(keys: Key, values: Value) => VRecord<Record<Infer<Key>, Value["type"]>, Key, Value, "required", string>
|
|
32
|
+
union <T>(...members: T) => VUnion<T[number]["type"], T, "required", T[number]["fieldPaths"]>
|
|
33
|
+
any () => VAny<any, "required", string>
|
|
34
|
+
optional <T>(value: T) => VOptional<T>
|
|
35
|
+
|
|
36
|
+
## System fields (https://docs.convex.dev/database/types#system-fields)
|
|
37
|
+
|
|
38
|
+
Every document in Convex has two automatically-generated system fields:
|
|
39
|
+
|
|
40
|
+
_id: The document ID of the document.
|
|
41
|
+
_creationTime: The time this document was created, in milliseconds since the Unix epoch.
|
|
42
|
+
|
|
43
|
+
You do not need to add indices as these are added automatically.
|
|
44
|
+
|
|
45
|
+
## Example Schema
|
|
46
|
+
|
|
47
|
+
This is an example of a well crafted schema.
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { defineSchema, defineTable } from "convex/server";
|
|
51
|
+
import { v } from "convex/values";
|
|
52
|
+
|
|
53
|
+
export default defineSchema(
|
|
54
|
+
{
|
|
55
|
+
users: defineTable({
|
|
56
|
+
name: v.string(),
|
|
57
|
+
}),
|
|
58
|
+
|
|
59
|
+
sessions: defineTable({
|
|
60
|
+
userId: v.id("users"),
|
|
61
|
+
sessionId: v.string(),
|
|
62
|
+
}).index("sessionId", ["sessionId"]),
|
|
63
|
+
|
|
64
|
+
threads: defineTable({
|
|
65
|
+
uuid: v.string(),
|
|
66
|
+
summary: v.optional(v.string()),
|
|
67
|
+
summarizer: v.optional(v.id("_scheduled_functions")),
|
|
68
|
+
}).index("uuid", ["uuid"]),
|
|
69
|
+
|
|
70
|
+
messages: defineTable({
|
|
71
|
+
message: v.string(),
|
|
72
|
+
threadId: v.id("threads"),
|
|
73
|
+
author: v.union(
|
|
74
|
+
v.object({
|
|
75
|
+
role: v.literal("system"),
|
|
76
|
+
}),
|
|
77
|
+
v.object({
|
|
78
|
+
role: v.literal("assistant"),
|
|
79
|
+
context: v.array(v.id("messages")),
|
|
80
|
+
model: v.optional(v.string()),
|
|
81
|
+
}),
|
|
82
|
+
v.object({
|
|
83
|
+
role: v.literal("user"),
|
|
84
|
+
userId: v.id("users"),
|
|
85
|
+
}),
|
|
86
|
+
),
|
|
87
|
+
})
|
|
88
|
+
.index("threadId", ["threadId"]),
|
|
89
|
+
},
|
|
90
|
+
);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Sourced from: https://github.com/PatrickJS/awesome-cursorrules/blob/main/rules/convex-cursorrules-prompt-file/.cursorrules
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ConvexProvider } from 'convex/react'
|
|
2
|
+
import { ConvexQueryClient } from '@convex-dev/react-query'
|
|
3
|
+
|
|
4
|
+
const CONVEX_URL = (import.meta as any).env.VITE_CONVEX_URL
|
|
5
|
+
if (!CONVEX_URL) {
|
|
6
|
+
console.error('missing envar CONVEX_URL')
|
|
7
|
+
}
|
|
8
|
+
const convexQueryClient = new ConvexQueryClient(CONVEX_URL)
|
|
9
|
+
|
|
10
|
+
export default function AppConvexProvider({
|
|
11
|
+
children,
|
|
12
|
+
}: {
|
|
13
|
+
children: React.ReactNode
|
|
14
|
+
}) {
|
|
15
|
+
return (
|
|
16
|
+
<ConvexProvider client={convexQueryClient.convexClient}>
|
|
17
|
+
{children}
|
|
18
|
+
</ConvexProvider>
|
|
19
|
+
)
|
|
20
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Suspense } from 'react'
|
|
2
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
3
|
+
import { useQuery } from 'convex/react'
|
|
4
|
+
|
|
5
|
+
import { api } from '../../convex/_generated/api'
|
|
6
|
+
|
|
7
|
+
export const Route = createFileRoute('/demo/convex')({
|
|
8
|
+
component: App,
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
function Products() {
|
|
12
|
+
const products = useQuery(api.products.get)
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<ul>
|
|
16
|
+
{(products || []).map((p) => (
|
|
17
|
+
<li key={p._id}>
|
|
18
|
+
{p.title} - {p.price}
|
|
19
|
+
</li>
|
|
20
|
+
))}
|
|
21
|
+
</ul>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function App() {
|
|
26
|
+
return (
|
|
27
|
+
<div className="p-4">
|
|
28
|
+
<Suspense fallback={<div>Loading...</div>}>
|
|
29
|
+
<Products />
|
|
30
|
+
</Suspense>
|
|
31
|
+
</div>
|
|
32
|
+
)
|
|
33
|
+
}
|