Skip to content

Commit 5532010

Browse files
committed
chore: moving to file bundle architecture
1 parent 933cdc0 commit 5532010

File tree

12 files changed

+99
-100
lines changed

12 files changed

+99
-100
lines changed

packages/cta-core/src/add-ons.ts

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,12 @@
11
import { readFile } from 'node:fs/promises'
2-
import { existsSync, readdirSync, statSync } from 'node:fs'
2+
import { existsSync, readdirSync } from 'node:fs'
33
import { basename, dirname, resolve } from 'node:path'
44

5-
import { getBinaryFile, readFileHelper } from './file-helper.js'
5+
import { getBinaryFile } from './file-helper.js'
6+
import { findFilesRecursively, isDirectory } from './utils.js'
67

78
import type { AddOn, Environment, FrameworkDefinition } from './types.js'
89

9-
function isDirectory(path: string): boolean {
10-
return statSync(path).isDirectory()
11-
}
12-
13-
function findFilesRecursively(path: string, files: Record<string, string>) {
14-
const dirFiles = readdirSync(path)
15-
for (const file of dirFiles) {
16-
const filePath = resolve(path, file)
17-
if (isDirectory(filePath)) {
18-
findFilesRecursively(filePath, files)
19-
} else {
20-
files[filePath] = readFileHelper(filePath)
21-
}
22-
}
23-
}
24-
2510
export async function getAllAddOns(
2611
framework: FrameworkDefinition,
2712
template: string,

packages/cta-core/src/frameworks.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,31 @@
1-
import type { FrameworkDefinition } from './types.js'
1+
import { resolve } from 'node:path'
22

3-
const frameworks: Array<FrameworkDefinition> = []
3+
import { findFilesRecursively } from './utils.js'
4+
import { readFileHelper } from './file-helper.js'
5+
6+
import type { Framework, FrameworkDefinition } from './types.js'
7+
8+
const frameworks: Array<Framework> = []
49

510
export function registerFramework(framework: FrameworkDefinition) {
6-
frameworks.push(framework)
11+
const baseAssetsDirectory = resolve(framework.baseDirectory, 'base')
12+
const frameworkWithBundler: Framework = {
13+
...framework,
14+
getFiles: () => {
15+
const files: Record<string, string> = {}
16+
findFilesRecursively(baseAssetsDirectory, files)
17+
return Promise.resolve(
18+
Object.keys(files).map((path) =>
19+
path.replace(baseAssetsDirectory, '.'),
20+
),
21+
)
22+
},
23+
getFileContents: (path: string) => {
24+
return Promise.resolve(readFileHelper(resolve(baseAssetsDirectory, path)))
25+
},
26+
}
27+
28+
frameworks.push(frameworkWithBundler)
729
}
830

931
export function getFrameworkById(id: string) {

packages/cta-core/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@ export {
2424
} from './frameworks.js'
2525
export { jsSafeName, relativePath, sortObject } from './utils.js'
2626
export { writeConfigFile, readConfigFile } from './config-file.js'
27-
export { readFileHelper } from './file-helper.js'
27+
export { readFileHelper, getBinaryFile } from './file-helper.js'
2828

2929
export type {
3030
AddOn,
3131
Environment,
32+
FileBundleHandler,
3233
FrameworkDefinition,
3334
Mode,
3435
Options,

packages/cta-core/src/types.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ export type TemplateOptions = 'typescript' | 'javascript' | 'file-router'
66

77
export type Mode = typeof CODE_ROUTER | typeof FILE_ROUTER
88

9+
export type FileBundleHandler = {
10+
getFiles: () => Promise<Array<string>>
11+
getFileContents: (path: string) => Promise<string>
12+
}
13+
914
export type FrameworkDefinition = {
1015
id: string
1116
name: string
@@ -17,8 +22,10 @@ export type FrameworkDefinition = {
1722
examplesDirectory: string
1823
}
1924

25+
export type Framework = FrameworkDefinition & FileBundleHandler
26+
2027
export interface Options {
21-
framework: FrameworkDefinition
28+
framework: Framework
2229
projectName: string
2330
typescript: boolean
2431
tailwind: boolean

packages/cta-core/src/utils.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
import { basename } from 'node:path'
2-
;``
1+
import { readdirSync, statSync } from 'node:fs'
2+
import { basename, resolve } from 'node:path'
3+
4+
import { readFileHelper } from './file-helper.js'
5+
36
export function sortObject(
47
obj: Record<string, string>,
58
): Record<string, string> {
@@ -48,3 +51,22 @@ export function relativePath(from: string, to: string) {
4851
return `${relativePath}/${basename(to)}`
4952
}
5053
}
54+
55+
export function isDirectory(path: string): boolean {
56+
return statSync(path).isDirectory()
57+
}
58+
59+
export function findFilesRecursively(
60+
path: string,
61+
files: Record<string, string>,
62+
) {
63+
const dirFiles = readdirSync(path)
64+
for (const file of dirFiles) {
65+
const filePath = resolve(path, file)
66+
if (isDirectory(filePath)) {
67+
findFilesRecursively(filePath, files)
68+
} else {
69+
files[filePath] = readFileHelper(filePath)
70+
}
71+
}
72+
}

packages/cta-engine/src/create-app.ts

Lines changed: 20 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,20 @@ import { basename, resolve } from 'node:path'
22

33
import {
44
copyAddOnFile,
5+
getBinaryFile,
56
packageManagerExecute,
67
writeConfigFile,
78
} from '@tanstack/cta-core'
89

9-
import { createCopyFiles } from './copy-files.js'
1010
import { createPackageJSON } from './package-json.js'
1111
import { createTemplateFile } from './template-file.js'
1212

13-
import type { AddOn, Environment, Options } from '@tanstack/cta-core'
13+
import type {
14+
AddOn,
15+
Environment,
16+
FileBundleHandler,
17+
Options,
18+
} from '@tanstack/cta-core'
1419

1520
export async function createApp(
1621
options: Options,
@@ -44,7 +49,6 @@ export async function createApp(
4449
}
4550
}
4651

47-
const copyFiles = createCopyFiles(environment, targetDir)
4852
const templateFileFromContent = createTemplateFile(
4953
environment,
5054
options.projectName,
@@ -70,71 +74,19 @@ export async function createApp(
7074
)
7175
}
7276

73-
// Setup the base files
74-
await templateFile(templateDirBase, '_dot_vscode/settings.json.ejs')
75-
76-
copyFiles(templateDirBase, [
77-
'./public/robots.txt',
78-
'./public/favicon.ico',
79-
'./public/manifest.json',
80-
'./public/logo192.png',
81-
'./public/logo512.png',
82-
])
83-
84-
if (environment.exists(resolve(templateDirBase, '_dot_cursorrules'))) {
85-
await environment.copyFile(
86-
resolve(templateDirBase, '_dot_cursorrules'),
87-
resolve(targetDir, '.cursorrules'),
88-
)
89-
}
90-
91-
await templateFile(templateDirBase, './src/App.css.ejs')
92-
93-
await templateFile(templateDirBase, './vite.config.js.ejs')
94-
await templateFile(templateDirBase, './src/styles.css.ejs')
95-
96-
copyFiles(templateDirBase, ['./src/logo.svg'])
97-
98-
await templateFile(templateDirBase, 'biome.json.ejs')
99-
await templateFile(templateDirBase, '_dot_prettierignore.ejs')
100-
await templateFile(templateDirBase, 'eslint.config.js.ejs')
101-
await templateFile(templateDirBase, 'prettier.config.js.ejs')
102-
103-
if (
104-
environment.exists(resolve(templateDirBase, './src/reportWebVitals.ts.ejs'))
105-
) {
106-
await templateFile(templateDirBase, './src/reportWebVitals.ts.ejs')
107-
}
108-
await templateFile(templateDirBase, './index.html.ejs')
109-
110-
await environment.copyFile(
111-
resolve(templateDirBase, '_dot_gitignore'),
112-
resolve(targetDir, '.gitignore'),
113-
)
114-
115-
await templateFile(templateDirBase, './tsconfig.json.ejs')
116-
117-
await templateFile(templateDirBase, './src/main.tsx.ejs', './src/main.tsx')
118-
119-
await templateFile(
120-
templateDirBase,
121-
'./src/routes/__root.tsx.ejs',
122-
'./src/routes/__root.tsx',
123-
)
124-
125-
if (options.framework.id === 'react-cra') {
126-
await templateFile(templateDirBase, './src/App.test.tsx.ejs')
77+
async function writeFileBundle(bundle: FileBundleHandler) {
78+
const files = await bundle.getFiles()
79+
for (const file of files) {
80+
const binaryFile = getBinaryFile(file)
81+
if (binaryFile) {
82+
await environment.writeFile(resolve(targetDir, file), binaryFile)
83+
} else {
84+
await templateFile(templateDirBase, file)
85+
}
86+
}
12787
}
128-
await templateFile(templateDirBase, './src/App.tsx.ejs')
129-
await templateFile(templateDirBase, './src/routes/index.tsx.ejs')
130-
131-
await templateFile(
132-
templateDirBase,
133-
'./src/components/Header.tsx.ejs',
134-
'./src/components/Header.tsx',
135-
)
13688

137-
await templateFile(templateDirBase, 'README.md.ejs')
89+
await writeFileBundle(options.framework)
13890

13991
// Setup the package.json file, optionally with typescript, tailwind and formatter/linter
14092
await createPackageJSON(
@@ -146,6 +98,8 @@ export async function createApp(
14698
options.chosenAddOns.map((addOn) => addOn.packageAdditions),
14799
)
148100

101+
await writeConfigFile(environment, targetDir, options)
102+
149103
const s = silent ? null : environment.spinner()
150104

151105
// Setup the add-ons
@@ -165,11 +119,6 @@ export async function createApp(
165119
)
166120
}
167121
}
168-
if (addOn.deletedFiles) {
169-
for (const file of addOn.deletedFiles) {
170-
await environment.deleteFile(resolve(targetDir, file))
171-
}
172-
}
173122

174123
if (addOn.command && addOn.command.command) {
175124
await environment.execute(
@@ -293,8 +242,6 @@ export async function createApp(
293242
s?.stop(`Initialized git repository`)
294243
}
295244

296-
await writeConfigFile(environment, targetDir, options)
297-
298245
environment.finishRun()
299246

300247
let errorStatement = ''

packages/cta-engine/src/template-file.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { format } from 'prettier'
44

55
import { CODE_ROUTER, FILE_ROUTER, relativePath } from '@tanstack/cta-core'
66

7-
import type { Environment, Options } from '@tanstack/cta-core'
7+
import type { AddOn, Environment, Options } from '@tanstack/cta-core'
88

99
function convertDotFilesAndPaths(path: string) {
1010
return path
@@ -141,6 +141,12 @@ export function createTemplateFile(
141141
targetFileName ?? file.replace('.ejs', ''),
142142
)
143143

144+
let append = false
145+
if (target.endsWith('.append')) {
146+
append = true
147+
target = target.replace('.append', '')
148+
}
149+
144150
if (target.endsWith('.ts') || target.endsWith('.tsx')) {
145151
content = await format(content, {
146152
semi: false,
@@ -154,6 +160,10 @@ export function createTemplateFile(
154160
target = target.replace(/\.tsx?$/, '.jsx').replace(/\.ts$/, '.js')
155161
}
156162

157-
await environment.writeFile(resolve(targetDir, target), content)
163+
if (append) {
164+
await environment.appendFile(resolve(targetDir, target), content)
165+
} else {
166+
await environment.writeFile(resolve(targetDir, target), content)
167+
}
158168
}
159169
}

tests/integration-tests/snapshots/solid/solid-cr-js-npm.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"files": {
33
".cta.json": "{\n \"git\": true,\n \"mode\": \"code-router\",\n \"packageManager\": \"npm\",\n \"projectName\": \"TEST\",\n \"tailwind\": false,\n \"toolchain\": \"none\",\n \"typescript\": false,\n \"variableValues\": {},\n \"version\": 1,\n \"framework\": \"solid\",\n \"existingAddOns\": []\n}",
4+
".cursorrules": "// Solid.js with Tailwind CSS .cursorrules\n\n// Prefer functional components\n\nconst preferFunctionalComponents = true;\n\n// Solid.js and Tailwind CSS best practices\n\nconst solidjsTailwindBestPractices = [\n\"Use createSignal() for reactive state\",\n\"Implement Tailwind CSS classes for styling\",\n\"Utilize TypeScript's strict mode\",\n\"Utilize @apply directive in CSS files for reusable styles\",\n\"Implement responsive design using Tailwind's responsive classes\",\n\"Use Tailwind's CSS in /src/styles.css for global styles\",\n\"Implement dark mode using Tailwind's dark variant\",\n];\n\n// Additional instructions\n\nconst additionalInstructions = `\n\n1. Use .tsx extension for files with JSX\n2. Implement strict TypeScript checks\n3. Implement proper Tailwind CSS purging for production builds\n4. Utilize TanStack Router for routing when applicable\n5. Use type-safe context with createContext\n6. Implement proper typing for event handlers\n7. Follow TypeScript best practices and naming conventions\n8. Use type assertions sparingly and only when necessary\n9. Use Tailwind's @layer directive for custom styles\n10. Implement utility-first CSS approach\n11. Follow both Solid.js and Tailwind naming conventions\n12. Use JIT (Just-In-Time) mode for faster development\n `;\n",
45
".gitignore": "node_modules\n.DS_Store\ndist\ndist-ssr\n*.local\n",
56
"/.vscode/settings.json": "{\n \"files.watcherExclude\": {\n \"**/routeTree.gen.ts\": true\n },\n \"search.exclude\": {\n \"**/routeTree.gen.ts\": true\n },\n \"files.readonlyInclude\": {\n \"**/routeTree.gen.ts\": true\n }\n}\n",
67
"/public/manifest.json": "{\n \"short_name\": \"TanStack App\",\n \"name\": \"Create TanStack App Sample\",\n \"icons\": [\n {\n \"src\": \"favicon.ico\",\n \"sizes\": \"64x64 32x32 24x24 16x16\",\n \"type\": \"image/x-icon\"\n },\n {\n \"src\": \"logo192.png\",\n \"type\": \"image/png\",\n \"sizes\": \"192x192\"\n },\n {\n \"src\": \"logo512.png\",\n \"type\": \"image/png\",\n \"sizes\": \"512x512\"\n }\n ],\n \"start_url\": \".\",\n \"display\": \"standalone\",\n \"theme_color\": \"#000000\",\n \"background_color\": \"#ffffff\"\n}\n",

tests/integration-tests/snapshots/solid/solid-cr-ts-npm.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"files": {
33
".cta.json": "{\n \"git\": true,\n \"mode\": \"code-router\",\n \"packageManager\": \"npm\",\n \"projectName\": \"TEST\",\n \"tailwind\": false,\n \"toolchain\": \"none\",\n \"typescript\": true,\n \"variableValues\": {},\n \"version\": 1,\n \"framework\": \"solid\",\n \"existingAddOns\": []\n}",
4+
".cursorrules": "// Solid.js with Tailwind CSS .cursorrules\n\n// Prefer functional components\n\nconst preferFunctionalComponents = true;\n\n// Solid.js and Tailwind CSS best practices\n\nconst solidjsTailwindBestPractices = [\n\"Use createSignal() for reactive state\",\n\"Implement Tailwind CSS classes for styling\",\n\"Utilize TypeScript's strict mode\",\n\"Utilize @apply directive in CSS files for reusable styles\",\n\"Implement responsive design using Tailwind's responsive classes\",\n\"Use Tailwind's CSS in /src/styles.css for global styles\",\n\"Implement dark mode using Tailwind's dark variant\",\n];\n\n// Additional instructions\n\nconst additionalInstructions = `\n\n1. Use .tsx extension for files with JSX\n2. Implement strict TypeScript checks\n3. Implement proper Tailwind CSS purging for production builds\n4. Utilize TanStack Router for routing when applicable\n5. Use type-safe context with createContext\n6. Implement proper typing for event handlers\n7. Follow TypeScript best practices and naming conventions\n8. Use type assertions sparingly and only when necessary\n9. Use Tailwind's @layer directive for custom styles\n10. Implement utility-first CSS approach\n11. Follow both Solid.js and Tailwind naming conventions\n12. Use JIT (Just-In-Time) mode for faster development\n `;\n",
45
".gitignore": "node_modules\n.DS_Store\ndist\ndist-ssr\n*.local\n",
56
"/.vscode/settings.json": "{\n \"files.watcherExclude\": {\n \"**/routeTree.gen.ts\": true\n },\n \"search.exclude\": {\n \"**/routeTree.gen.ts\": true\n },\n \"files.readonlyInclude\": {\n \"**/routeTree.gen.ts\": true\n }\n}\n",
67
"/public/manifest.json": "{\n \"short_name\": \"TanStack App\",\n \"name\": \"Create TanStack App Sample\",\n \"icons\": [\n {\n \"src\": \"favicon.ico\",\n \"sizes\": \"64x64 32x32 24x24 16x16\",\n \"type\": \"image/x-icon\"\n },\n {\n \"src\": \"logo192.png\",\n \"type\": \"image/png\",\n \"sizes\": \"192x192\"\n },\n {\n \"src\": \"logo512.png\",\n \"type\": \"image/png\",\n \"sizes\": \"512x512\"\n }\n ],\n \"start_url\": \".\",\n \"display\": \"standalone\",\n \"theme_color\": \"#000000\",\n \"background_color\": \"#ffffff\"\n}\n",

tests/integration-tests/snapshots/solid/solid-cr-ts-start-npm.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"files": {
33
".cta.json": "{\n \"git\": true,\n \"mode\": \"file-router\",\n \"packageManager\": \"npm\",\n \"projectName\": \"TEST\",\n \"tailwind\": true,\n \"toolchain\": \"none\",\n \"typescript\": true,\n \"variableValues\": {},\n \"version\": 1,\n \"framework\": \"solid\",\n \"existingAddOns\": [\n \"start\"\n ]\n}",
4+
".cursorrules": "// Solid.js with Tailwind CSS .cursorrules\n\n// Prefer functional components\n\nconst preferFunctionalComponents = true;\n\n// Solid.js and Tailwind CSS best practices\n\nconst solidjsTailwindBestPractices = [\n\"Use createSignal() for reactive state\",\n\"Implement Tailwind CSS classes for styling\",\n\"Utilize TypeScript's strict mode\",\n\"Utilize @apply directive in CSS files for reusable styles\",\n\"Implement responsive design using Tailwind's responsive classes\",\n\"Use Tailwind's CSS in /src/styles.css for global styles\",\n\"Implement dark mode using Tailwind's dark variant\",\n];\n\n// Additional instructions\n\nconst additionalInstructions = `\n\n1. Use .tsx extension for files with JSX\n2. Implement strict TypeScript checks\n3. Implement proper Tailwind CSS purging for production builds\n4. Utilize TanStack Router for routing when applicable\n5. Use type-safe context with createContext\n6. Implement proper typing for event handlers\n7. Follow TypeScript best practices and naming conventions\n8. Use type assertions sparingly and only when necessary\n9. Use Tailwind's @layer directive for custom styles\n10. Implement utility-first CSS approach\n11. Follow both Solid.js and Tailwind naming conventions\n12. Use JIT (Just-In-Time) mode for faster development\n `;\n",
45
".gitignore": "node_modules\n.DS_Store\ndist\ndist-ssr\n*.local\n",
56
"/.vscode/settings.json": "{\n \"files.watcherExclude\": {\n \"**/routeTree.gen.ts\": true\n },\n \"search.exclude\": {\n \"**/routeTree.gen.ts\": true\n },\n \"files.readonlyInclude\": {\n \"**/routeTree.gen.ts\": true\n }\n}\n",
67
"/public/manifest.json": "{\n \"short_name\": \"TanStack App\",\n \"name\": \"Create TanStack App Sample\",\n \"icons\": [\n {\n \"src\": \"favicon.ico\",\n \"sizes\": \"64x64 32x32 24x24 16x16\",\n \"type\": \"image/x-icon\"\n },\n {\n \"src\": \"logo192.png\",\n \"type\": \"image/png\",\n \"sizes\": \"192x192\"\n },\n {\n \"src\": \"logo512.png\",\n \"type\": \"image/png\",\n \"sizes\": \"512x512\"\n }\n ],\n \"start_url\": \".\",\n \"display\": \"standalone\",\n \"theme_color\": \"#000000\",\n \"background_color\": \"#ffffff\"\n}\n",

0 commit comments

Comments
 (0)