Skip to content

Commit f85f6b6

Browse files
committed
chore: strengthen registry support
1 parent 3c92032 commit f85f6b6

File tree

12 files changed

+195
-84
lines changed

12 files changed

+195
-84
lines changed

examples/react-cra/legend-state-add-on/.add-on/info.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"id": "legend-state-add-on-add-on",
33
"name": "Legend State",
44
"version": "0.0.1",
5-
"description": "Add-on",
5+
"description": "Legend State management for React",
66
"author": "Jane Smith <[email protected]>",
77
"license": "MIT",
88
"link": "https://github.com/jane-smith/legend-state-add-on-add-on",

examples/react-cra/legend-state-add-on/add-on.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"id": "legend-state-add-on-add-on",
33
"name": "Legend State",
44
"version": "0.0.1",
5-
"description": "Add-on",
5+
"description": "Legend State management for React",
66
"author": "Jane Smith <[email protected]>",
77
"license": "MIT",
88
"link": "https://github.com/jane-smith/legend-state-add-on-add-on",

examples/react-cra/mui-add-on/.add-on/info.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
2-
"id": "mui-add-on-add-on",
3-
"name": "mui-add-on-add-on",
2+
"id": "mui-add-on",
3+
"name": "MUI",
44
"version": "0.0.1",
5-
"description": "Add-on",
5+
"description": "Material UI components",
66
"author": "Jane Smith <[email protected]>",
77
"license": "MIT",
88
"link": "https://github.com/jane-smith/mui-add-on-add-on",

examples/react-cra/mui-add-on/add-on.json

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
{
2-
"id": "mui-add-on-add-on",
3-
"name": "mui-add-on-add-on",
2+
"id": "mui-add-on",
3+
"name": "MUI",
44
"version": "0.0.1",
5-
"description": "Add-on",
5+
"description": "Material UI components",
66
"author": "Jane Smith <[email protected]>",
77
"license": "MIT",
88
"link": "https://github.com/jane-smith/mui-add-on-add-on",
99
"shadcnComponents": [],
1010
"framework": "react-cra",
11-
"modes": [
12-
"file-router"
13-
],
11+
"modes": ["file-router"],
1412
"routes": [
1513
{
1614
"url": "/demo/mui",
@@ -45,4 +43,4 @@
4543
"./src/routes/demo.mui.tsx.ejs": "import { <% if (fileRouter) { %>createFileRoute<% } else { %>createRoute<% } %> } from '@tanstack/react-router'\n\nimport Button from '@mui/material/Button'\nimport Box from '@mui/material/Box'\n\n<% if (codeRouter) { %>\nimport type { RootRoute } from '@tanstack/react-router'\n<% } else { %>\nexport const Route = createFileRoute('/demo/mui')({\n component: MUIDemo,\n})\n<% } %>\n\nfunction MUIDemo() {\n return (\n <Box sx={{ px: 2, py: 4 }}>\n <Button variant=\"contained\">Hello world</Button>\n </Box>\n )\n}\n\n<% if (codeRouter) { %>\nexport default (parentRoute: RootRoute) => createRoute({\n path: '/demo/mui',\n \n component: MUIDemo,\n\n getParentRoute: () => parentRoute,\n})\n<% } %>\n"
4644
},
4745
"deletedFiles": []
48-
}
46+
}

examples/react-cra/registry.json

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,41 @@
44
"name": "e-Commerce",
55
"description": "A starter project for React with CRA",
66
"url": "./ecommerce-starter/starter.json",
7-
"banner": "./ecommerce-starter/.starter/banner.png"
7+
"banner": "./ecommerce-starter/.starter/banner.png",
8+
"modes": "file-router",
9+
"framework": "react-cra"
810
},
911
{
1012
"name": "Blog",
1113
"description": "A blog starter for Tanstack React Start",
1214
"url": "./blog-starter/starter.json",
13-
"banner": "./blog-starter/.starter/banner.png"
15+
"banner": "./blog-starter/.starter/banner.png",
16+
"modes": "file-router",
17+
"framework": "react-cra"
1418
},
1519
{
1620
"name": "Resume",
1721
"description": "A resume starter for Tanstack React Start",
1822
"url": "./resume-starter/starter.json",
19-
"banner": "./resume-starter/.starter/banner.png"
23+
"banner": "./resume-starter/.starter/banner.png",
24+
"modes": "file-router",
25+
"framework": "react-cra"
2026
}
2127
],
2228
"add-ons": [
2329
{
2430
"name": "Legend State",
2531
"description": "A Legend State add-on for Tanstack React Start",
26-
"url": "./legend-state-add-on/add-on.json"
32+
"url": "./legend-state-add-on/add-on.json",
33+
"modes": ["code-router", "file-router"],
34+
"framework": "react-cra"
2735
},
2836
{
2937
"name": "MUI",
3038
"description": "A MUI add-on for Tanstack React Start",
31-
"url": "./mui-add-on/add-on.json"
39+
"url": "./mui-add-on/add-on.json",
40+
"modes": ["code-router", "file-router"],
41+
"framework": "react-cra"
3242
}
3343
]
3444
}

packages/cta-engine/src/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ export {
5252

5353
export { createSerializedOptions } from './options.js'
5454

55+
export {
56+
getRawRegistry,
57+
getRegistry,
58+
getRegistryAddOns,
59+
getRegistryStarters,
60+
} from './registry.js'
61+
5562
export {
5663
StarterCompiledSchema,
5764
StatusEvent,

packages/cta-engine/src/registry.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { loadRemoteAddOn } from './custom-add-ons/add-on.js'
2+
import { loadStarter } from './custom-add-ons/starter.js'
3+
4+
import type { AddOn, Mode, Starter } from './types'
5+
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+
}
23+
24+
function absolutizeUrl(originalUrl: string, relativeUrl: string) {
25+
if (relativeUrl.startsWith('http') || relativeUrl.startsWith('https')) {
26+
return relativeUrl
27+
}
28+
const baseUrl = originalUrl.replace(/registry.json$/, '')
29+
return `${baseUrl}${relativeUrl.replace(/^\.\//, '')}`
30+
}
31+
32+
export async function getRawRegistry(
33+
registryUrl?: string,
34+
): Promise<Registry | undefined> {
35+
const regUrl = registryUrl || process.env.CTA_REGISTRY
36+
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)
40+
}
41+
for (const starter of registry.starters) {
42+
starter.url = absolutizeUrl(regUrl, starter.url)
43+
if (starter.banner) {
44+
starter.banner = absolutizeUrl(regUrl, starter.banner)
45+
}
46+
}
47+
return registry
48+
}
49+
}
50+
51+
async function getAddOns(registry: Registry): Promise<Array<AddOn>> {
52+
const addOns: Array<AddOn> = []
53+
for (const addOnInfo of registry['add-ons']) {
54+
const addOn = await loadRemoteAddOn(addOnInfo.url)
55+
addOns.push(addOn)
56+
}
57+
return addOns
58+
}
59+
60+
export async function getRegistryAddOns(
61+
registryUrl?: string,
62+
): Promise<Array<AddOn>> {
63+
const registry = await getRawRegistry(registryUrl)
64+
return registry ? await getAddOns(registry) : []
65+
}
66+
67+
async function getStarters(registry: Registry): Promise<Array<Starter>> {
68+
const starters: Array<Starter> = []
69+
for (const starterInfo of registry.starters) {
70+
const starter = await loadStarter(starterInfo.url)
71+
starters.push(starter)
72+
}
73+
return starters
74+
}
75+
76+
export async function getRegistryStarters(
77+
registryUrl?: string,
78+
): Promise<Array<Starter>> {
79+
const registry = await getRawRegistry(registryUrl)
80+
return registry ? await getStarters(registry) : []
81+
}
82+
83+
export async function getRegistry(registryUrl?: string): Promise<{
84+
addOns: Array<AddOn>
85+
starters: Array<Starter>
86+
}> {
87+
const registry = await getRawRegistry(registryUrl)
88+
return {
89+
addOns: registry ? await getAddOns(registry) : [],
90+
starters: registry ? await getStarters(registry) : [],
91+
}
92+
}

packages/cta-ui/lib/engine-handling/generate-initial-payload.ts

Lines changed: 48 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import {
44
createSerializedOptionsFromPersisted,
55
getAllAddOns,
66
getFrameworkById,
7+
getRawRegistry,
8+
getRegistryAddOns,
79
readConfigFile,
810
recursivelyGatherFiles,
911
} from '@tanstack/cta-engine'
@@ -17,18 +19,24 @@ import {
1719
getForcedRouterMode,
1820
getProjectOptions,
1921
getProjectPath,
20-
getRegistry,
22+
getRegistry as getRegistryURL,
2123
} from './server-environment.js'
2224

23-
import type { SerializedOptions } from '@tanstack/cta-engine'
24-
import type { Registry } from '../types.js'
25+
import type { AddOn, SerializedOptions } from '@tanstack/cta-engine'
26+
import type { AddOnInfo } from '../types.js'
2527

26-
function absolutizeUrl(originalUrl: string, relativeUrl: string) {
27-
if (relativeUrl.startsWith('http') || relativeUrl.startsWith('https')) {
28-
return relativeUrl
28+
function convertAddOnToAddOnInfo(addOn: AddOn): AddOnInfo {
29+
return {
30+
id: addOn.id,
31+
name: addOn.name,
32+
description: addOn.description,
33+
modes: addOn.modes as Array<'code-router' | 'file-router'>,
34+
type: addOn.type,
35+
smallLogo: addOn.smallLogo,
36+
logo: addOn.logo,
37+
link: addOn.link!,
38+
dependsOn: addOn.dependsOn,
2939
}
30-
const baseUrl = originalUrl.replace(/registry.json$/, '')
31-
return `${baseUrl}${relativeUrl.replace(/^\.\//, '')}`
3240
}
3341

3442
export async function generateInitialPayload() {
@@ -68,20 +76,8 @@ export async function generateInitialPayload() {
6876
}
6977
}
7078

71-
const registryUrl = getRegistry()
72-
let registry: Registry | undefined
73-
if (registryUrl) {
74-
registry = (await fetch(registryUrl).then((res) => res.json())) as Registry
75-
for (const addOn of registry['add-ons']) {
76-
addOn.url = absolutizeUrl(registryUrl, addOn.url)
77-
}
78-
for (const starter of registry.starters) {
79-
starter.url = absolutizeUrl(registryUrl, starter.url)
80-
if (starter.banner) {
81-
starter.banner = absolutizeUrl(registryUrl, starter.banner)
82-
}
83-
}
84-
}
79+
const rawRegistry = await getRawRegistry(getRegistryURL())
80+
const registryAddOns = await getRegistryAddOns(getRegistryURL())
8581

8682
const serializedOptions = await getSerializedOptions()
8783

@@ -91,39 +87,46 @@ export async function generateInitialPayload() {
9187

9288
const framework = await getFrameworkById(serializedOptions.framework)
9389

94-
const codeRouter = getAllAddOns(framework!, 'code-router').map((addOn) => ({
95-
id: addOn.id,
96-
name: addOn.name,
97-
description: addOn.description,
98-
type: addOn.type,
99-
smallLogo: addOn.smallLogo,
100-
logo: addOn.logo,
101-
link: addOn.link,
102-
dependsOn: addOn.dependsOn,
103-
}))
90+
const codeRouterAddOns = getAllAddOns(framework!, 'code-router').map(
91+
convertAddOnToAddOnInfo,
92+
)
93+
94+
const fileRouterAddOns = getAllAddOns(framework!, 'file-router').map(
95+
convertAddOnToAddOnInfo,
96+
)
97+
98+
for (const addOnInfo of registryAddOns || []) {
99+
const addOnFramework = rawRegistry?.['add-ons'].find(
100+
(addOn) => addOn.url === addOnInfo.id,
101+
)
102+
if (addOnFramework?.framework === serializedOptions.framework) {
103+
if (addOnInfo.modes.includes('code-router')) {
104+
codeRouterAddOns.push(convertAddOnToAddOnInfo(addOnInfo))
105+
}
106+
if (addOnInfo.modes.includes('file-router')) {
107+
fileRouterAddOns.push(convertAddOnToAddOnInfo(addOnInfo))
108+
}
109+
}
110+
}
104111

105-
const fileRouter = getAllAddOns(framework!, 'file-router').map((addOn) => ({
106-
id: addOn.id,
107-
name: addOn.name,
108-
description: addOn.description,
109-
type: addOn.type,
110-
smallLogo: addOn.smallLogo,
111-
logo: addOn.logo,
112-
link: addOn.link,
113-
dependsOn: addOn.dependsOn,
114-
}))
112+
const serializedRegistry = {
113+
['add-ons']: [],
114+
starters: (rawRegistry?.starters || []).filter(
115+
(starter) => starter.framework === serializedOptions.framework,
116+
),
117+
}
115118

116119
return {
117120
applicationMode,
118121
localFiles,
119122
addOns: {
120-
'code-router': codeRouter,
121-
'file-router': fileRouter,
123+
'code-router': codeRouterAddOns,
124+
'file-router': fileRouterAddOns,
122125
},
123126
options: serializedOptions,
124127
output,
125128
forcedRouterMode,
126129
forcedAddOns: getForcedAddOns(),
127-
registry,
130+
registry: serializedRegistry,
128131
}
129132
}

packages/cta-ui/lib/types.d.ts

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,3 @@
1-
export type Registry = {
2-
starters: Array<{
3-
name: string
4-
description: string
5-
url: string
6-
banner?: string
7-
}>
8-
'add-ons': Array<{
9-
name: string
10-
description: string
11-
url: string
12-
}>
13-
}
14-
151
export type DryRunOutput = {
162
files: Record<string, string>
173
commands: Array<{
@@ -20,3 +6,15 @@ export type DryRunOutput = {
206
}>
217
deletedFiles: Array<string>
228
}
9+
10+
export type AddOnInfo = {
11+
id: string
12+
name: string
13+
description: string
14+
type: 'add-on' | 'example' | 'starter' | 'toolchain'
15+
modes: Array<'code-router' | 'file-router'>
16+
smallLogo?: string
17+
logo?: string
18+
link: string
19+
dependsOn?: Array<string>
20+
}

0 commit comments

Comments
 (0)