Skip to content

Commit e54fd08

Browse files
committed
adding solid support
1 parent 97a8d8c commit e54fd08

File tree

104 files changed

+783
-45
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

104 files changed

+783
-45
lines changed

.gitignore

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
node_modules
22
dist
3-
app-js
4-
app-ts
5-
app-js-tw
6-
app-ts-tw
7-
app-fr
8-
app-fr-tw
3+
app-*
94
my-app
105
.DS_Store

src/add-ons.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { existsSync, readdirSync, statSync } from 'node:fs'
33
import { resolve } from 'node:path'
44
import { fileURLToPath } from 'node:url'
55

6+
import type { Framework } from './types.js'
7+
68
type BooleanVariable = {
79
name: string
810
default: boolean
@@ -74,12 +76,14 @@ function isDirectory(path: string): boolean {
7476
return statSync(path).isDirectory()
7577
}
7678

77-
export async function getAllAddOns(): Promise<Array<AddOn>> {
79+
export async function getAllAddOns(
80+
framework: Framework,
81+
): Promise<Array<AddOn>> {
7882
const addOns: Array<AddOn> = []
7983

8084
for (const type of ['add-on', 'example']) {
8185
const addOnsBase = fileURLToPath(
82-
new URL(`../templates/${type}`, import.meta.url),
86+
new URL(`../templates/${framework}/${type}`, import.meta.url),
8387
)
8488

8589
for (const dir of await readdirSync(addOnsBase).filter((file) =>
@@ -116,11 +120,12 @@ export async function getAllAddOns(): Promise<Array<AddOn>> {
116120

117121
// Turn the list of chosen add-on IDs into a final list of add-ons by resolving dependencies
118122
export async function finalizeAddOns(
123+
framework: Framework,
119124
chosenAddOnIDs: Array<string>,
120125
): Promise<Array<AddOn>> {
121126
const finalAddOnIDs = new Set(chosenAddOnIDs)
122127

123-
const addOns = await getAllAddOns()
128+
const addOns = await getAllAddOns(framework)
124129

125130
for (const addOnID of finalAddOnIDs) {
126131
const addOn = addOns.find((a) => a.id === addOnID)

src/cli.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import { createApp } from './create-app.js'
55
import { normalizeOptions, promptForOptions } from './options.js'
66
import { SUPPORTED_PACKAGE_MANAGERS } from './package-manager.js'
77

8+
import { DEFAULT_FRAMEWORK, SUPPORTED_FRAMEWORKS } from './constants.js'
89
import type { PackageManager } from './package-manager.js'
9-
import type { CliOptions } from './types.js'
10+
import type { CliOptions, Framework } from './types.js'
1011

1112
export function cli() {
1213
const program = new Command()
@@ -16,6 +17,21 @@ export function cli() {
1617
.description('CLI to create a new TanStack application')
1718
.argument('[project-name]', 'name of the project')
1819
.option('--no-git', 'do not create a git repository')
20+
.option<Framework>(
21+
'--framework <type>',
22+
'project framework (solid, react)',
23+
(value) => {
24+
if (!SUPPORTED_FRAMEWORKS.includes(value as Framework)) {
25+
throw new InvalidArgumentError(
26+
`Invalid framework: ${value}. Only the following are allowed: ${SUPPORTED_FRAMEWORKS.join(
27+
', ',
28+
)}`,
29+
)
30+
}
31+
return value as Framework
32+
},
33+
DEFAULT_FRAMEWORK,
34+
)
1935
.option<'typescript' | 'javascript' | 'file-router'>(
2036
'--template <type>',
2137
'project template (typescript, javascript, file-router)',

src/constants.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
1+
import type { Framework } from './types.js'
2+
13
export const CODE_ROUTER = 'code-router'
24
export const FILE_ROUTER = 'file-router'
5+
6+
export const SUPPORTED_FRAMEWORKS: Array<Framework> = ['solid', 'react']
7+
export const DEFAULT_FRAMEWORK: Framework = 'react'

src/create-app.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -219,10 +219,13 @@ async function copyFilesRecursively(
219219

220220
export async function createApp(options: Required<Options>) {
221221
const templateDirBase = fileURLToPath(
222-
new URL('../templates/base', import.meta.url),
222+
new URL(`../templates/${options.framework}/base`, import.meta.url),
223223
)
224224
const templateDirRouter = fileURLToPath(
225-
new URL(`../templates/${options.mode}`, import.meta.url),
225+
new URL(
226+
`../templates/${options.framework}/${options.mode}`,
227+
import.meta.url,
228+
),
226229
)
227230
const targetDir = resolve(process.cwd(), options.projectName)
228231

@@ -302,11 +305,13 @@ export async function createApp(options: Required<Options>) {
302305
'./src/App.tsx.ejs',
303306
options.typescript ? undefined : './src/App.jsx',
304307
)
305-
await templateFile(
306-
templateDirBase,
307-
'./src/App.test.tsx.ejs',
308-
options.typescript ? undefined : './src/App.test.jsx',
309-
)
308+
if (options.framework === 'react') {
309+
await templateFile(
310+
templateDirBase,
311+
'./src/App.test.tsx.ejs',
312+
options.typescript ? undefined : './src/App.test.jsx',
313+
)
314+
}
310315
}
311316

312317
// Create the main entry point
@@ -323,7 +328,7 @@ export async function createApp(options: Required<Options>) {
323328
}
324329

325330
// Setup the main, reportWebVitals and index.html files
326-
if (!isAddOnEnabled('start')) {
331+
if (!isAddOnEnabled('start') && options.framework === 'react') {
327332
if (options.typescript) {
328333
await templateFile(templateDirBase, './src/reportWebVitals.ts.ejs')
329334
} else {
@@ -333,6 +338,8 @@ export async function createApp(options: Required<Options>) {
333338
'./src/reportWebVitals.js',
334339
)
335340
}
341+
}
342+
if (!isAddOnEnabled('start')) {
336343
await templateFile(templateDirBase, './index.html.ejs')
337344
}
338345

src/options.ts

Lines changed: 49 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
SUPPORTED_PACKAGE_MANAGERS,
1313
getPackageManager,
1414
} from './package-manager.js'
15-
import { CODE_ROUTER, FILE_ROUTER } from './constants.js'
15+
import { CODE_ROUTER, DEFAULT_FRAMEWORK, FILE_ROUTER } from './constants.js'
1616
import { finalizeAddOns, getAllAddOns } from './add-ons.js'
1717
import type { Variable } from './add-ons.js'
1818

@@ -25,12 +25,19 @@ export function normalizeOptions(
2525
if (cliOptions.projectName) {
2626
const typescript =
2727
cliOptions.template === 'typescript' ||
28-
cliOptions.template === 'file-router'
28+
cliOptions.template === 'file-router' ||
29+
cliOptions.framework === 'solid'
30+
31+
const tailwind =
32+
cliOptions.tailwind === undefined
33+
? cliOptions.framework === 'solid'
34+
: cliOptions.tailwind
2935

3036
return {
37+
framework: cliOptions.framework || 'react',
3138
projectName: cliOptions.projectName,
3239
typescript,
33-
tailwind: !!cliOptions.tailwind,
40+
tailwind: !!tailwind,
3441
packageManager: cliOptions.packageManager || DEFAULT_PACKAGE_MANAGER,
3542
mode: cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER,
3643
git: !!cliOptions.git,
@@ -86,6 +93,12 @@ export async function promptForOptions(
8693
): Promise<Required<Options>> {
8794
const options = {} as Required<Options>
8895

96+
options.framework = cliOptions.framework || DEFAULT_FRAMEWORK
97+
if (options.framework === 'solid') {
98+
options.typescript = true
99+
options.tailwind = true
100+
}
101+
89102
if (!cliOptions.projectName) {
90103
const value = await text({
91104
message: 'What would you like to name your project?',
@@ -151,7 +164,7 @@ export async function promptForOptions(
151164
}
152165

153166
// Tailwind selection
154-
if (cliOptions.tailwind === undefined) {
167+
if (cliOptions.tailwind === undefined && options.framework === 'react') {
155168
const tailwind = await confirm({
156169
message: 'Would you like to use Tailwind CSS?',
157170
initialValue: true,
@@ -162,7 +175,7 @@ export async function promptForOptions(
162175
}
163176
options.tailwind = tailwind
164177
} else {
165-
options.tailwind = cliOptions.tailwind
178+
options.tailwind = options.framework === 'solid' || !!cliOptions.tailwind
166179
}
167180

168181
// Package manager selection
@@ -190,44 +203,54 @@ export async function promptForOptions(
190203
}
191204

192205
// Select any add-ons
193-
if (options.mode === FILE_ROUTER && cliOptions.addOns) {
194-
const addOns = await getAllAddOns()
195-
196-
const selectedAddOns = await multiselect({
206+
const allAddOns = await getAllAddOns(options.framework)
207+
const addOns = allAddOns.filter((addOn) => addOn.type === 'add-on')
208+
let selectedAddOns: Array<string> = []
209+
if (options.mode === FILE_ROUTER && cliOptions.addOns && addOns.length > 0) {
210+
const value = await multiselect({
197211
message: 'What add-ons would you like for your project:',
198-
options: addOns
199-
.filter((addOn) => addOn.type === 'add-on')
200-
.map((addOn) => ({
201-
value: addOn.id,
202-
label: addOn.name,
203-
hint: addOn.description,
204-
})),
212+
options: addOns.map((addOn) => ({
213+
value: addOn.id,
214+
label: addOn.name,
215+
hint: addOn.description,
216+
})),
205217
required: false,
206218
})
207219

208220
if (isCancel(selectedAddOns)) {
209221
cancel('Operation cancelled.')
210222
process.exit(0)
211223
}
224+
selectedAddOns = value as Array<string>
225+
}
212226

213-
const selectedExamples = await multiselect({
227+
// Select any examples
228+
const examples = allAddOns.filter((addOn) => addOn.type === 'example')
229+
let selectedExamples: Array<string> = []
230+
if (
231+
options.mode === FILE_ROUTER &&
232+
cliOptions.addOns &&
233+
examples.length > 0
234+
) {
235+
const value = await multiselect({
214236
message: 'Would you like any examples?',
215-
options: addOns
216-
.filter((addOn) => addOn.type === 'example')
217-
.map((addOn) => ({
218-
value: addOn.id,
219-
label: addOn.name,
220-
hint: addOn.description,
221-
})),
237+
options: examples.map((addOn) => ({
238+
value: addOn.id,
239+
label: addOn.name,
240+
hint: addOn.description,
241+
})),
222242
required: false,
223243
})
224244

225-
if (isCancel(selectedExamples)) {
245+
if (isCancel(value)) {
226246
cancel('Operation cancelled.')
227247
process.exit(0)
228248
}
249+
selectedExamples = value
250+
}
229251

230-
options.chosenAddOns = await finalizeAddOns([
252+
if (selectedAddOns.length > 0 || selectedExamples.length > 0) {
253+
options.chosenAddOns = await finalizeAddOns(options.framework, [
231254
...selectedAddOns,
232255
...selectedExamples,
233256
])

src/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ import type { AddOn } from './add-ons.js'
22
import type { CODE_ROUTER, FILE_ROUTER } from './constants.js'
33
import type { PackageManager } from './package-manager.js'
44

5+
export type Framework = 'solid' | 'react'
6+
57
export interface Options {
8+
framework: Framework
69
projectName: string
710
typescript: boolean
811
tailwind: boolean
@@ -16,6 +19,7 @@ export interface Options {
1619

1720
export interface CliOptions {
1821
template?: 'typescript' | 'javascript' | 'file-router'
22+
framework?: Framework
1923
tailwind?: boolean
2024
packageManager?: PackageManager
2125
projectName?: string

0 commit comments

Comments
 (0)