@tanstack/cta-engine 0.10.0-alpha.11 → 0.10.0-alpha.12

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/dist/add-ons.js CHANGED
@@ -1,9 +1,10 @@
1
1
  import { readFile } from 'node:fs/promises';
2
- import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
2
+ import { existsSync, readdirSync, statSync } from 'node:fs';
3
3
  import { resolve } from 'node:path';
4
4
  import chalk from 'chalk';
5
5
  import { getTemplatesRoot } from './templates.js';
6
6
  import { DEFAULT_FRAMEWORK } from './constants.js';
7
+ import { readFileHelper } from './file-helper.js';
7
8
  function isDirectory(path) {
8
9
  return statSync(path).isDirectory();
9
10
  }
@@ -15,7 +16,7 @@ function findFilesRecursively(path, files) {
15
16
  findFilesRecursively(filePath, files);
16
17
  }
17
18
  else {
18
- files[filePath] = readFileSync(filePath, 'utf-8').toString();
19
+ files[filePath] = readFileHelper(filePath);
19
20
  }
20
21
  }
21
22
  }
package/dist/cli.js CHANGED
@@ -30,16 +30,25 @@ export function cli({ name, appName, forcedMode, forcedAddOns, }) {
30
30
  // .action(async () => {
31
31
  // await initAddOn('overlay')
32
32
  // })
33
+ program.argument('[project-name]', 'name of the project');
34
+ if (!forcedMode) {
35
+ program.option('--template <type>', 'project template (typescript, javascript, file-router)', (value) => {
36
+ if (value !== 'typescript' &&
37
+ value !== 'javascript' &&
38
+ value !== 'file-router') {
39
+ throw new InvalidArgumentError(`Invalid template: ${value}. Only the following are allowed: typescript, javascript, file-router`);
40
+ }
41
+ return value;
42
+ });
43
+ }
33
44
  program
34
- .argument('[project-name]', 'name of the project')
35
- .option('--no-git', 'do not create a git repository')
36
- .option('--target-dir <path>', 'the directory to create the project in')
37
45
  .option('--framework <type>', 'project framework (solid, react)', (value) => {
38
46
  if (!SUPPORTED_FRAMEWORKS.includes(value)) {
39
47
  throw new InvalidArgumentError(`Invalid framework: ${value}. Only the following are allowed: ${SUPPORTED_FRAMEWORKS.join(', ')}`);
40
48
  }
41
49
  return value;
42
50
  }, DEFAULT_FRAMEWORK)
51
+ // .option('--overlay [url]', 'add an overlay from a URL', false)
43
52
  .option(`--package-manager <${SUPPORTED_PACKAGE_MANAGERS.join('|')}>`, `Explicitly tell the CLI to use this package manager`, (value) => {
44
53
  if (!SUPPORTED_PACKAGE_MANAGERS.includes(value)) {
45
54
  throw new InvalidArgumentError(`Invalid package manager: ${value}. The following are allowed: ${SUPPORTED_PACKAGE_MANAGERS.join(', ')}`);
@@ -61,19 +70,10 @@ export function cli({ name, appName, forcedMode, forcedAddOns, }) {
61
70
  return addOns;
62
71
  })
63
72
  .option('--list-add-ons', 'list all available add-ons', false)
64
- // .option('--overlay [url]', 'add an overlay from a URL', false)
73
+ .option('--no-git', 'do not create a git repository')
74
+ .option('--target-dir <path>', 'the directory to create the project in')
65
75
  .option('--mcp', 'run the MCP server', false)
66
76
  .option('--mcp-sse', 'run the MCP server in SSE mode', false);
67
- if (!forcedMode) {
68
- program.option('--template <type>', 'project template (typescript, javascript, file-router)', (value) => {
69
- if (value !== 'typescript' &&
70
- value !== 'javascript' &&
71
- value !== 'file-router') {
72
- throw new InvalidArgumentError(`Invalid template: ${value}. Only the following are allowed: typescript, javascript, file-router`);
73
- }
74
- return value;
75
- });
76
- }
77
77
  program.action(async (projectName, options) => {
78
78
  if (options.listAddOns) {
79
79
  await listAddOns(options, {
@@ -8,6 +8,7 @@ import { CODE_ROUTER, FILE_ROUTER } from './constants.js';
8
8
  import { sortObject } from './utils.js';
9
9
  import { writeConfigFile } from './config-file.js';
10
10
  import { packageManagerExecute } from './package-manager.js';
11
+ import { getBinaryFile } from './file-helper.js';
11
12
  function createCopyFiles(environment, targetDir) {
12
13
  return async function copyFiles(templateDir, files,
13
14
  // optionally copy files from a folder to the root
@@ -197,6 +198,11 @@ async function copyAddOnFile(environment, content, target, targetPath, templateF
197
198
  isAppend = true;
198
199
  }
199
200
  const finalTargetPath = resolve(dirname(targetPath), targetFile);
201
+ const binaryContent = getBinaryFile(content);
202
+ if (binaryContent) {
203
+ await environment.writeFile(finalTargetPath, binaryContent);
204
+ return;
205
+ }
200
206
  if (isTemplate) {
201
207
  await templateFile(content, finalTargetPath);
202
208
  }
@@ -309,7 +315,7 @@ export async function createApp(options, { silent = false, environment, cwd, app
309
315
  // Copy all the asset files from the addons
310
316
  const s = silent ? null : spinner();
311
317
  for (const type of ['add-on', 'example']) {
312
- for (const phase of ['setup', 'add-on']) {
318
+ for (const phase of ['setup', 'add-on', 'example']) {
313
319
  for (const addOn of options.chosenAddOns.filter((addOn) => addOn.phase === phase && addOn.type === type)) {
314
320
  s?.start(`Setting up ${addOn.name}...`);
315
321
  await runAddOn(addOn);
@@ -6,6 +6,7 @@ import { createMemoryEnvironment } from './environment.js';
6
6
  import { createApp } from './create-app.js';
7
7
  import { readConfigFile } from './config-file.js';
8
8
  import { finalizeAddOns } from './add-ons.js';
9
+ import { readFileHelper } from './file-helper.js';
9
10
  const INFO_FILE = {
10
11
  'add-on': '.add-on/info.json',
11
12
  overlay: 'overlay-info.json',
@@ -98,7 +99,7 @@ async function recursivelyGatherFiles(path, files) {
98
99
  await recursivelyGatherFiles(resolve(path, file.name), files);
99
100
  }
100
101
  else {
101
- files[resolve(path, file.name)] = (await readFile(resolve(path, file.name))).toString();
102
+ files[resolve(path, file.name)] = readFileHelper(resolve(path, file.name));
102
103
  }
103
104
  }
104
105
  }
@@ -0,0 +1,18 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { extname } from 'node:path';
3
+ const BINARY_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico'];
4
+ export function readFileHelper(path) {
5
+ if (BINARY_EXTENSIONS.includes(extname(path))) {
6
+ return `base64::${readFileSync(path).toString('base64')}`;
7
+ }
8
+ else {
9
+ return readFileSync(path, 'utf-8').toString();
10
+ }
11
+ }
12
+ export function getBinaryFile(content) {
13
+ if (content.startsWith('base64::')) {
14
+ const binaryContent = Buffer.from(content.replace('base64::', ''), 'base64');
15
+ return binaryContent;
16
+ }
17
+ return null;
18
+ }
package/dist/options.js CHANGED
@@ -13,12 +13,6 @@ export async function normalizeOptions(cliOptions, forcedAddOns) {
13
13
  if (parseSeparatedArgs.length > 1) {
14
14
  cliOptions.addOns = parseSeparatedArgs;
15
15
  }
16
- if (forcedAddOns) {
17
- cliOptions.addOns = [...cliOptions.addOns, ...forcedAddOns];
18
- }
19
- }
20
- else if (forcedAddOns) {
21
- cliOptions.addOns = [...forcedAddOns];
22
16
  }
23
17
  if (cliOptions.projectName) {
24
18
  let typescript = cliOptions.template === 'typescript' ||
@@ -40,11 +34,17 @@ export async function normalizeOptions(cliOptions, forcedAddOns) {
40
34
  }
41
35
  let addOns = false;
42
36
  let chosenAddOns = [];
43
- if (Array.isArray(cliOptions.addOns) || overlay?.dependsOn) {
37
+ if (Array.isArray(cliOptions.addOns) ||
38
+ overlay?.dependsOn ||
39
+ forcedAddOns) {
44
40
  addOns = true;
45
41
  let finalAddOns = [...(overlay?.dependsOn || [])];
46
42
  if (cliOptions.addOns && Array.isArray(cliOptions.addOns)) {
47
- finalAddOns = [...finalAddOns, ...cliOptions.addOns];
43
+ finalAddOns = Array.from(new Set([
44
+ ...(forcedAddOns || []),
45
+ ...finalAddOns,
46
+ ...cliOptions.addOns,
47
+ ]));
48
48
  }
49
49
  chosenAddOns = await finalizeAddOns(cliOptions.framework || DEFAULT_FRAMEWORK, cliOptions.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER, finalAddOns);
50
50
  tailwind = true;
@@ -0,0 +1,2 @@
1
+ export declare function readFileHelper(path: string): string;
2
+ export declare function getBinaryFile(content: string): string | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/cta-engine",
3
- "version": "0.10.0-alpha.11",
3
+ "version": "0.10.0-alpha.12",
4
4
  "description": "Tanstack Application Builder Engine",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/add-ons.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  import { readFile } from 'node:fs/promises'
2
- import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs'
2
+ import { existsSync, readdirSync, statSync } from 'node:fs'
3
3
  import { resolve } from 'node:path'
4
4
  import chalk from 'chalk'
5
5
 
6
6
  import { getTemplatesRoot } from './templates.js'
7
7
  import { DEFAULT_FRAMEWORK } from './constants.js'
8
+ import { readFileHelper } from './file-helper.js'
8
9
 
9
10
  import type { AddOn, CliOptions, Framework, TemplateOptions } from './types.js'
10
11
 
@@ -19,7 +20,7 @@ function findFilesRecursively(path: string, files: Record<string, string>) {
19
20
  if (isDirectory(filePath)) {
20
21
  findFilesRecursively(filePath, files)
21
22
  } else {
22
- files[filePath] = readFileSync(filePath, 'utf-8').toString()
23
+ files[filePath] = readFileHelper(filePath)
23
24
  }
24
25
  }
25
26
  }
package/src/cli.ts CHANGED
@@ -54,10 +54,27 @@ export function cli({
54
54
  // await initAddOn('overlay')
55
55
  // })
56
56
 
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
+ }
57
77
  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')
61
78
  .option<Framework>(
62
79
  '--framework <type>',
63
80
  'project framework (solid, react)',
@@ -73,6 +90,7 @@ export function cli({
73
90
  },
74
91
  DEFAULT_FRAMEWORK,
75
92
  )
93
+ // .option('--overlay [url]', 'add an overlay from a URL', false)
76
94
  .option<PackageManager>(
77
95
  `--package-manager <${SUPPORTED_PACKAGE_MANAGERS.join('|')}>`,
78
96
  `Explicitly tell the CLI to use this package manager`,
@@ -114,29 +132,11 @@ export function cli({
114
132
  },
115
133
  )
116
134
  .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')
118
137
  .option('--mcp', 'run the MCP server', false)
119
138
  .option('--mcp-sse', 'run the MCP server in SSE mode', false)
120
139
 
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
-
140
140
  program.action(async (projectName: string, options: CliOptions) => {
141
141
  if (options.listAddOns) {
142
142
  await listAddOns(options, {
package/src/create-app.ts CHANGED
@@ -9,6 +9,7 @@ import { CODE_ROUTER, FILE_ROUTER } from './constants.js'
9
9
  import { sortObject } from './utils.js'
10
10
  import { writeConfigFile } from './config-file.js'
11
11
  import { packageManagerExecute } from './package-manager.js'
12
+ import { getBinaryFile } from './file-helper.js'
12
13
 
13
14
  import type { AddOn, Environment, Options } from './types.js'
14
15
 
@@ -287,6 +288,15 @@ async function copyAddOnFile(
287
288
 
288
289
  const finalTargetPath = resolve(dirname(targetPath), targetFile)
289
290
 
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
+
290
300
  if (isTemplate) {
291
301
  await templateFile(content, finalTargetPath)
292
302
  } else {
@@ -499,7 +509,7 @@ export async function createApp(
499
509
  // Copy all the asset files from the addons
500
510
  const s = silent ? null : spinner()
501
511
  for (const type of ['add-on', 'example']) {
502
- for (const phase of ['setup', 'add-on']) {
512
+ for (const phase of ['setup', 'add-on', 'example']) {
503
513
  for (const addOn of options.chosenAddOns.filter(
504
514
  (addOn) => addOn.phase === phase && addOn.type === type,
505
515
  )) {
@@ -7,6 +7,7 @@ import { createMemoryEnvironment } from './environment.js'
7
7
  import { createApp } from './create-app.js'
8
8
  import { readConfigFile } from './config-file.js'
9
9
  import { finalizeAddOns } from './add-ons.js'
10
+ import { readFileHelper } from './file-helper.js'
10
11
 
11
12
  import type { Framework, Options } from './types.js'
12
13
  import type { PersistedOptions } from './config-file.js'
@@ -134,9 +135,7 @@ async function recursivelyGatherFiles(
134
135
  if (file.isDirectory()) {
135
136
  await recursivelyGatherFiles(resolve(path, file.name), files)
136
137
  } 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))
140
139
  }
141
140
  }
142
141
  }
@@ -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
+ }
package/src/options.ts CHANGED
@@ -31,11 +31,6 @@ export async function normalizeOptions(
31
31
  if (parseSeparatedArgs.length > 1) {
32
32
  cliOptions.addOns = parseSeparatedArgs
33
33
  }
34
- if (forcedAddOns) {
35
- cliOptions.addOns = [...cliOptions.addOns, ...forcedAddOns]
36
- }
37
- } else if (forcedAddOns) {
38
- cliOptions.addOns = [...forcedAddOns]
39
34
  }
40
35
 
41
36
  if (cliOptions.projectName) {
@@ -65,11 +60,21 @@ export async function normalizeOptions(
65
60
 
66
61
  let addOns = false
67
62
  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
+ ) {
69
68
  addOns = true
70
69
  let finalAddOns = [...(overlay?.dependsOn || [])]
71
70
  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
+ )
73
78
  }
74
79
  chosenAddOns = await finalizeAddOns(
75
80
  cliOptions.framework || DEFAULT_FRAMEWORK,