Skip to content

Commit 0730a69

Browse files
committed
fix: not corrupting binary files and supporting examples
1 parent b36aed4 commit 0730a69

File tree

6 files changed

+71
-36
lines changed

6 files changed

+71
-36
lines changed

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

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

66
import { getTemplatesRoot } from './templates.js'
77
import { DEFAULT_FRAMEWORK } from './constants.js'
8+
import { readFileHelper } from './file-helper.js'
89

910
import type { AddOn, CliOptions, Framework, TemplateOptions } from './types.js'
1011

@@ -19,7 +20,7 @@ function findFilesRecursively(path: string, files: Record<string, string>) {
1920
if (isDirectory(filePath)) {
2021
findFilesRecursively(filePath, files)
2122
} else {
22-
files[filePath] = readFileSync(filePath, 'utf-8').toString()
23+
files[filePath] = readFileHelper(filePath)
2324
}
2425
}
2526
}

packages/cta-engine/src/cli.ts

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,27 @@ export function cli({
5454
// await initAddOn('overlay')
5555
// })
5656

57+
program.argument('[project-name]', 'name of the project')
58+
59+
if (!forcedMode) {
60+
program.option<'typescript' | 'javascript' | 'file-router'>(
61+
'--template <type>',
62+
'project template (typescript, javascript, file-router)',
63+
(value) => {
64+
if (
65+
value !== 'typescript' &&
66+
value !== 'javascript' &&
67+
value !== 'file-router'
68+
) {
69+
throw new InvalidArgumentError(
70+
`Invalid template: ${value}. Only the following are allowed: typescript, javascript, file-router`,
71+
)
72+
}
73+
return value
74+
},
75+
)
76+
}
5777
program
58-
.argument('[project-name]', 'name of the project')
59-
.option('--no-git', 'do not create a git repository')
60-
.option('--target-dir <path>', 'the directory to create the project in')
6178
.option<Framework>(
6279
'--framework <type>',
6380
'project framework (solid, react)',
@@ -73,6 +90,7 @@ export function cli({
7390
},
7491
DEFAULT_FRAMEWORK,
7592
)
93+
// .option('--overlay [url]', 'add an overlay from a URL', false)
7694
.option<PackageManager>(
7795
`--package-manager <${SUPPORTED_PACKAGE_MANAGERS.join('|')}>`,
7896
`Explicitly tell the CLI to use this package manager`,
@@ -114,29 +132,11 @@ export function cli({
114132
},
115133
)
116134
.option('--list-add-ons', 'list all available add-ons', false)
117-
// .option('--overlay [url]', 'add an overlay from a URL', false)
135+
.option('--no-git', 'do not create a git repository')
136+
.option('--target-dir <path>', 'the directory to create the project in')
118137
.option('--mcp', 'run the MCP server', false)
119138
.option('--mcp-sse', 'run the MCP server in SSE mode', false)
120139

121-
if (!forcedMode) {
122-
program.option<'typescript' | 'javascript' | 'file-router'>(
123-
'--template <type>',
124-
'project template (typescript, javascript, file-router)',
125-
(value) => {
126-
if (
127-
value !== 'typescript' &&
128-
value !== 'javascript' &&
129-
value !== 'file-router'
130-
) {
131-
throw new InvalidArgumentError(
132-
`Invalid template: ${value}. Only the following are allowed: typescript, javascript, file-router`,
133-
)
134-
}
135-
return value
136-
},
137-
)
138-
}
139-
140140
program.action(async (projectName: string, options: CliOptions) => {
141141
if (options.listAddOns) {
142142
await listAddOns(options, {

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { CODE_ROUTER, FILE_ROUTER } from './constants.js'
99
import { sortObject } from './utils.js'
1010
import { writeConfigFile } from './config-file.js'
1111
import { packageManagerExecute } from './package-manager.js'
12+
import { getBinaryFile } from './file-helper.js'
1213

1314
import type { AddOn, Environment, Options } from './types.js'
1415

@@ -287,6 +288,15 @@ async function copyAddOnFile(
287288

288289
const finalTargetPath = resolve(dirname(targetPath), targetFile)
289290

291+
const binaryContent = getBinaryFile(content)
292+
if (binaryContent) {
293+
await environment.writeFile(
294+
finalTargetPath,
295+
binaryContent as unknown as string,
296+
)
297+
return
298+
}
299+
290300
if (isTemplate) {
291301
await templateFile(content, finalTargetPath)
292302
} else {
@@ -499,7 +509,7 @@ export async function createApp(
499509
// Copy all the asset files from the addons
500510
const s = silent ? null : spinner()
501511
for (const type of ['add-on', 'example']) {
502-
for (const phase of ['setup', 'add-on']) {
512+
for (const phase of ['setup', 'add-on', 'example']) {
503513
for (const addOn of options.chosenAddOns.filter(
504514
(addOn) => addOn.phase === phase && addOn.type === type,
505515
)) {

packages/cta-engine/src/custom-add-on.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { createMemoryEnvironment } from './environment.js'
77
import { createApp } from './create-app.js'
88
import { readConfigFile } from './config-file.js'
99
import { finalizeAddOns } from './add-ons.js'
10+
import { readFileHelper } from './file-helper.js'
1011

1112
import type { Framework, Options } from './types.js'
1213
import type { PersistedOptions } from './config-file.js'
@@ -134,9 +135,7 @@ async function recursivelyGatherFiles(
134135
if (file.isDirectory()) {
135136
await recursivelyGatherFiles(resolve(path, file.name), files)
136137
} else {
137-
files[resolve(path, file.name)] = (
138-
await readFile(resolve(path, file.name))
139-
).toString()
138+
files[resolve(path, file.name)] = readFileHelper(resolve(path, file.name))
140139
}
141140
}
142141
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { readFileSync } from 'node:fs'
2+
import { extname } from 'node:path'
3+
4+
const BINARY_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico']
5+
6+
export function readFileHelper(path: string): string {
7+
if (BINARY_EXTENSIONS.includes(extname(path))) {
8+
return `base64::${readFileSync(path).toString('base64')}`
9+
} else {
10+
return readFileSync(path, 'utf-8').toString()
11+
}
12+
}
13+
14+
export function getBinaryFile(content: string): string | null {
15+
if (content.startsWith('base64::')) {
16+
const binaryContent = Buffer.from(content.replace('base64::', ''), 'base64')
17+
return binaryContent as unknown as string
18+
}
19+
return null
20+
}

packages/cta-engine/src/options.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,6 @@ export async function normalizeOptions(
3131
if (parseSeparatedArgs.length > 1) {
3232
cliOptions.addOns = parseSeparatedArgs
3333
}
34-
if (forcedAddOns) {
35-
cliOptions.addOns = [...cliOptions.addOns, ...forcedAddOns]
36-
}
37-
} else if (forcedAddOns) {
38-
cliOptions.addOns = [...forcedAddOns]
3934
}
4035

4136
if (cliOptions.projectName) {
@@ -65,11 +60,21 @@ export async function normalizeOptions(
6560

6661
let addOns = false
6762
let chosenAddOns: Array<AddOn> = []
68-
if (Array.isArray(cliOptions.addOns) || overlay?.dependsOn) {
63+
if (
64+
Array.isArray(cliOptions.addOns) ||
65+
overlay?.dependsOn ||
66+
forcedAddOns
67+
) {
6968
addOns = true
7069
let finalAddOns = [...(overlay?.dependsOn || [])]
7170
if (cliOptions.addOns && Array.isArray(cliOptions.addOns)) {
72-
finalAddOns = [...finalAddOns, ...cliOptions.addOns]
71+
finalAddOns = Array.from(
72+
new Set([
73+
...(forcedAddOns || []),
74+
...finalAddOns,
75+
...cliOptions.addOns,
76+
]),
77+
)
7378
}
7479
chosenAddOns = await finalizeAddOns(
7580
cliOptions.framework || DEFAULT_FRAMEWORK,

0 commit comments

Comments
 (0)