@tanstack/cta-engine 0.15.0 → 0.15.2

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/index.js CHANGED
@@ -9,7 +9,7 @@ export { DEFAULT_PACKAGE_MANAGER, SUPPORTED_PACKAGE_MANAGERS, getPackageManager,
9
9
  export { registerFramework, getFrameworkById, getFrameworkByName, getFrameworks, } from './frameworks.js';
10
10
  export { writeConfigFileToEnvironment, readConfigFileFromEnvironment, readConfigFile, } from './config-file.js';
11
11
  export { cleanUpFiles, cleanUpFileArray, readFileHelper, getBinaryFile, recursivelyGatherFiles, relativePath, } from './file-helpers.js';
12
- export { formatCommand } from './utils.js';
12
+ export { formatCommand, handleSpecialURL } from './utils.js';
13
13
  export { initStarter, compileStarter } from './custom-add-ons/starter.js';
14
14
  export { initAddOn, compileAddOn } from './custom-add-ons/add-on.js';
15
15
  export { createAppOptionsFromPersisted, createSerializedOptionsFromPersisted, } from './custom-add-ons/shared.js';
package/dist/registry.js CHANGED
@@ -1,5 +1,28 @@
1
+ import { z } from 'zod';
1
2
  import { loadRemoteAddOn } from './custom-add-ons/add-on.js';
2
3
  import { loadStarter } from './custom-add-ons/starter.js';
4
+ import { handleSpecialURL } from './utils.js';
5
+ const registrySchema = z.object({
6
+ starters: z
7
+ .array(z.object({
8
+ name: z.string(),
9
+ description: z.string(),
10
+ url: z.string(),
11
+ banner: z.string().optional(),
12
+ mode: z.enum(['code-router', 'file-router']),
13
+ framework: z.string(),
14
+ }))
15
+ .optional(),
16
+ 'add-ons': z
17
+ .array(z.object({
18
+ name: z.string(),
19
+ description: z.string(),
20
+ url: z.string(),
21
+ modes: z.array(z.enum(['code-router', 'file-router'])),
22
+ framework: z.string(),
23
+ }))
24
+ .optional(),
25
+ });
3
26
  function absolutizeUrl(originalUrl, relativeUrl) {
4
27
  if (relativeUrl.startsWith('http') || relativeUrl.startsWith('https')) {
5
28
  return relativeUrl;
@@ -10,22 +33,24 @@ function absolutizeUrl(originalUrl, relativeUrl) {
10
33
  export async function getRawRegistry(registryUrl) {
11
34
  const regUrl = registryUrl || process.env.CTA_REGISTRY;
12
35
  if (regUrl) {
13
- const registry = (await fetch(regUrl).then((res) => res.json()));
14
- for (const addOn of registry['add-ons']) {
15
- addOn.url = absolutizeUrl(regUrl, addOn.url);
36
+ const url = handleSpecialURL(regUrl);
37
+ const registry = (await fetch(url).then((res) => res.json()));
38
+ const parsedRegistry = registrySchema.parse(registry);
39
+ for (const addOn of parsedRegistry['add-ons'] || []) {
40
+ addOn.url = absolutizeUrl(url, addOn.url);
16
41
  }
17
- for (const starter of registry.starters) {
18
- starter.url = absolutizeUrl(regUrl, starter.url);
42
+ for (const starter of parsedRegistry.starters || []) {
43
+ starter.url = absolutizeUrl(url, starter.url);
19
44
  if (starter.banner) {
20
- starter.banner = absolutizeUrl(regUrl, starter.banner);
45
+ starter.banner = absolutizeUrl(url, starter.banner);
21
46
  }
22
47
  }
23
- return registry;
48
+ return parsedRegistry;
24
49
  }
25
50
  }
26
51
  async function getAddOns(registry) {
27
52
  const addOns = [];
28
- for (const addOnInfo of registry['add-ons']) {
53
+ for (const addOnInfo of registry['add-ons'] || []) {
29
54
  const addOn = await loadRemoteAddOn(addOnInfo.url);
30
55
  addOns.push(addOn);
31
56
  }
@@ -37,7 +62,7 @@ export async function getRegistryAddOns(registryUrl) {
37
62
  }
38
63
  async function getStarters(registry) {
39
64
  const starters = [];
40
- for (const starterInfo of registry.starters) {
65
+ for (const starterInfo of registry.starters || []) {
41
66
  const starter = await loadStarter(starterInfo.url);
42
67
  starters.push(starter);
43
68
  }
@@ -9,7 +9,7 @@ export { DEFAULT_PACKAGE_MANAGER, SUPPORTED_PACKAGE_MANAGERS, getPackageManager,
9
9
  export { registerFramework, getFrameworkById, getFrameworkByName, getFrameworks, } from './frameworks.js';
10
10
  export { writeConfigFileToEnvironment, readConfigFileFromEnvironment, readConfigFile, } from './config-file.js';
11
11
  export { cleanUpFiles, cleanUpFileArray, readFileHelper, getBinaryFile, recursivelyGatherFiles, relativePath, } from './file-helpers.js';
12
- export { formatCommand } from './utils.js';
12
+ export { formatCommand, handleSpecialURL } from './utils.js';
13
13
  export { initStarter, compileStarter } from './custom-add-ons/starter.js';
14
14
  export { initAddOn, compileAddOn } from './custom-add-ons/add-on.js';
15
15
  export { createAppOptionsFromPersisted, createSerializedOptionsFromPersisted, } from './custom-add-ons/shared.js';
@@ -1,21 +1,81 @@
1
- import type { AddOn, Mode, Starter } from './types';
2
- export type Registry = {
3
- starters: Array<{
1
+ import { z } from 'zod';
2
+ import type { AddOn, Starter } from './types';
3
+ declare const registrySchema: z.ZodObject<{
4
+ starters: z.ZodOptional<z.ZodArray<z.ZodObject<{
5
+ name: z.ZodString;
6
+ description: z.ZodString;
7
+ url: z.ZodString;
8
+ banner: z.ZodOptional<z.ZodString>;
9
+ mode: z.ZodEnum<["code-router", "file-router"]>;
10
+ framework: z.ZodString;
11
+ }, "strip", z.ZodTypeAny, {
4
12
  name: string;
5
13
  description: string;
6
14
  url: string;
7
- banner?: string;
8
- mode: Mode;
9
15
  framework: string;
10
- }>;
11
- 'add-ons': Array<{
16
+ mode: "code-router" | "file-router";
17
+ banner?: string | undefined;
18
+ }, {
12
19
  name: string;
13
20
  description: string;
14
21
  url: string;
15
- modes: Array<Mode>;
16
22
  framework: string;
17
- }>;
18
- };
23
+ mode: "code-router" | "file-router";
24
+ banner?: string | undefined;
25
+ }>, "many">>;
26
+ 'add-ons': z.ZodOptional<z.ZodArray<z.ZodObject<{
27
+ name: z.ZodString;
28
+ description: z.ZodString;
29
+ url: z.ZodString;
30
+ modes: z.ZodArray<z.ZodEnum<["code-router", "file-router"]>, "many">;
31
+ framework: z.ZodString;
32
+ }, "strip", z.ZodTypeAny, {
33
+ name: string;
34
+ description: string;
35
+ url: string;
36
+ framework: string;
37
+ modes: ("code-router" | "file-router")[];
38
+ }, {
39
+ name: string;
40
+ description: string;
41
+ url: string;
42
+ framework: string;
43
+ modes: ("code-router" | "file-router")[];
44
+ }>, "many">>;
45
+ }, "strip", z.ZodTypeAny, {
46
+ starters?: {
47
+ name: string;
48
+ description: string;
49
+ url: string;
50
+ framework: string;
51
+ mode: "code-router" | "file-router";
52
+ banner?: string | undefined;
53
+ }[] | undefined;
54
+ 'add-ons'?: {
55
+ name: string;
56
+ description: string;
57
+ url: string;
58
+ framework: string;
59
+ modes: ("code-router" | "file-router")[];
60
+ }[] | undefined;
61
+ }, {
62
+ starters?: {
63
+ name: string;
64
+ description: string;
65
+ url: string;
66
+ framework: string;
67
+ mode: "code-router" | "file-router";
68
+ banner?: string | undefined;
69
+ }[] | undefined;
70
+ 'add-ons'?: {
71
+ name: string;
72
+ description: string;
73
+ url: string;
74
+ framework: string;
75
+ modes: ("code-router" | "file-router")[];
76
+ }[] | undefined;
77
+ }>;
78
+ export type Registry = z.infer<typeof registrySchema>;
19
79
  export declare function getRawRegistry(registryUrl?: string): Promise<Registry | undefined>;
20
80
  export declare function getRegistryAddOns(registryUrl?: string): Promise<Array<AddOn>>;
21
81
  export declare function getRegistryStarters(registryUrl?: string): Promise<Array<Starter>>;
@@ -23,3 +83,4 @@ export declare function getRegistry(registryUrl?: string): Promise<{
23
83
  addOns: Array<AddOn>;
24
84
  starters: Array<Starter>;
25
85
  }>;
86
+ export {};
@@ -4,3 +4,4 @@ export declare function formatCommand({ command, args, }: {
4
4
  command: string;
5
5
  args: Array<string>;
6
6
  }): string;
7
+ export declare function handleSpecialURL(url: string): string;
package/dist/utils.js CHANGED
@@ -15,3 +15,17 @@ export function jsSafeName(name) {
15
15
  export function formatCommand({ command, args, }) {
16
16
  return `${command} ${args.join(' ')}`.trim();
17
17
  }
18
+ // Turn GitHub URLs into raw URLs
19
+ export function handleSpecialURL(url) {
20
+ if (url.startsWith('https://github.com/') && url.includes('blob')) {
21
+ return url
22
+ .replace('https://github.com/', 'https://raw.githubusercontent.com/')
23
+ .replace('/blob/', '/refs/heads/');
24
+ }
25
+ if (url.startsWith('https://github.com/') && url.includes('tree')) {
26
+ return url
27
+ .replace('https://github.com/', 'https://raw.githubusercontent.com/')
28
+ .replace('/tree/', '/refs/heads/');
29
+ }
30
+ return url;
31
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/cta-engine",
3
- "version": "0.15.0",
3
+ "version": "0.15.2",
4
4
  "description": "Tanstack Application Builder Engine",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/index.ts CHANGED
@@ -41,7 +41,7 @@ export {
41
41
  relativePath,
42
42
  } from './file-helpers.js'
43
43
 
44
- export { formatCommand } from './utils.js'
44
+ export { formatCommand, handleSpecialURL } from './utils.js'
45
45
 
46
46
  export { initStarter, compileStarter } from './custom-add-ons/starter.js'
47
47
  export { initAddOn, compileAddOn } from './custom-add-ons/add-on.js'
package/src/registry.ts CHANGED
@@ -1,25 +1,37 @@
1
+ import { z } from 'zod'
1
2
  import { loadRemoteAddOn } from './custom-add-ons/add-on.js'
2
3
  import { loadStarter } from './custom-add-ons/starter.js'
4
+ import { handleSpecialURL } from './utils.js'
3
5
 
4
- import type { AddOn, Mode, Starter } from './types'
6
+ import type { AddOn, Starter } from './types'
5
7
 
6
- export type Registry = {
7
- starters: Array<{
8
- name: string
9
- description: string
10
- url: string
11
- banner?: string
12
- mode: Mode
13
- framework: string
14
- }>
15
- 'add-ons': Array<{
16
- name: string
17
- description: string
18
- url: string
19
- modes: Array<Mode>
20
- framework: string
21
- }>
22
- }
8
+ const registrySchema = z.object({
9
+ starters: z
10
+ .array(
11
+ z.object({
12
+ name: z.string(),
13
+ description: z.string(),
14
+ url: z.string(),
15
+ banner: z.string().optional(),
16
+ mode: z.enum(['code-router', 'file-router']),
17
+ framework: z.string(),
18
+ }),
19
+ )
20
+ .optional(),
21
+ 'add-ons': z
22
+ .array(
23
+ z.object({
24
+ name: z.string(),
25
+ description: z.string(),
26
+ url: z.string(),
27
+ modes: z.array(z.enum(['code-router', 'file-router'])),
28
+ framework: z.string(),
29
+ }),
30
+ )
31
+ .optional(),
32
+ })
33
+
34
+ export type Registry = z.infer<typeof registrySchema>
23
35
 
24
36
  function absolutizeUrl(originalUrl: string, relativeUrl: string) {
25
37
  if (relativeUrl.startsWith('http') || relativeUrl.startsWith('https')) {
@@ -34,23 +46,25 @@ export async function getRawRegistry(
34
46
  ): Promise<Registry | undefined> {
35
47
  const regUrl = registryUrl || process.env.CTA_REGISTRY
36
48
  if (regUrl) {
37
- const registry = (await fetch(regUrl).then((res) => res.json())) as Registry
38
- for (const addOn of registry['add-ons']) {
39
- addOn.url = absolutizeUrl(regUrl, addOn.url)
49
+ const url = handleSpecialURL(regUrl)
50
+ const registry = (await fetch(url).then((res) => res.json())) as Registry
51
+ const parsedRegistry = registrySchema.parse(registry)
52
+ for (const addOn of parsedRegistry['add-ons'] || []) {
53
+ addOn.url = absolutizeUrl(url, addOn.url)
40
54
  }
41
- for (const starter of registry.starters) {
42
- starter.url = absolutizeUrl(regUrl, starter.url)
55
+ for (const starter of parsedRegistry.starters || []) {
56
+ starter.url = absolutizeUrl(url, starter.url)
43
57
  if (starter.banner) {
44
- starter.banner = absolutizeUrl(regUrl, starter.banner)
58
+ starter.banner = absolutizeUrl(url, starter.banner)
45
59
  }
46
60
  }
47
- return registry
61
+ return parsedRegistry
48
62
  }
49
63
  }
50
64
 
51
65
  async function getAddOns(registry: Registry): Promise<Array<AddOn>> {
52
66
  const addOns: Array<AddOn> = []
53
- for (const addOnInfo of registry['add-ons']) {
67
+ for (const addOnInfo of registry['add-ons'] || []) {
54
68
  const addOn = await loadRemoteAddOn(addOnInfo.url)
55
69
  addOns.push(addOn)
56
70
  }
@@ -66,7 +80,7 @@ export async function getRegistryAddOns(
66
80
 
67
81
  async function getStarters(registry: Registry): Promise<Array<Starter>> {
68
82
  const starters: Array<Starter> = []
69
- for (const starterInfo of registry.starters) {
83
+ for (const starterInfo of registry.starters || []) {
70
84
  const starter = await loadStarter(starterInfo.url)
71
85
  starters.push(starter)
72
86
  }
package/src/utils.ts CHANGED
@@ -25,3 +25,18 @@ export function formatCommand({
25
25
  }) {
26
26
  return `${command} ${args.join(' ')}`.trim()
27
27
  }
28
+
29
+ // Turn GitHub URLs into raw URLs
30
+ export function handleSpecialURL(url: string) {
31
+ if (url.startsWith('https://github.com/') && url.includes('blob')) {
32
+ return url
33
+ .replace('https://github.com/', 'https://raw.githubusercontent.com/')
34
+ .replace('/blob/', '/refs/heads/')
35
+ }
36
+ if (url.startsWith('https://github.com/') && url.includes('tree')) {
37
+ return url
38
+ .replace('https://github.com/', 'https://raw.githubusercontent.com/')
39
+ .replace('/tree/', '/refs/heads/')
40
+ }
41
+ return url
42
+ }
@@ -1,6 +1,11 @@
1
1
  import { describe, expect, it } from 'vitest'
2
2
 
3
- import { formatCommand, jsSafeName, sortObject } from '../src/utils.js'
3
+ import {
4
+ formatCommand,
5
+ handleSpecialURL,
6
+ jsSafeName,
7
+ sortObject,
8
+ } from '../src/utils.js'
4
9
 
5
10
  describe('formatCommand', () => {
6
11
  it('should format a command', () => {
@@ -21,3 +26,23 @@ describe('sortObject', () => {
21
26
  expect(sortObject({ b: 'b', a: 'a' })).toEqual({ a: 'a', b: 'b' })
22
27
  })
23
28
  })
29
+
30
+ describe('handleSpecialURL', () => {
31
+ it('should handle special URLs', () => {
32
+ expect(
33
+ handleSpecialURL(
34
+ 'https://github.com/TanStack/create-tsrouter-app/blob/main/examples/react-cra/registry.json',
35
+ ),
36
+ ).toEqual(
37
+ 'https://raw.githubusercontent.com/TanStack/create-tsrouter-app/refs/heads/main/examples/react-cra/registry.json',
38
+ )
39
+
40
+ expect(
41
+ handleSpecialURL(
42
+ 'https://github.com/TanStack/create-tsrouter-app/tree/alpha/packages/cta-cli/tsconfig.json',
43
+ ),
44
+ ).toEqual(
45
+ 'https://raw.githubusercontent.com/TanStack/create-tsrouter-app/refs/heads/alpha/packages/cta-cli/tsconfig.json',
46
+ )
47
+ })
48
+ })