Skip to content

Commit 571eeaf

Browse files
committed
chore: adding another example of a customized CLI
1 parent 9cc371e commit 571eeaf

File tree

16 files changed

+311
-114
lines changed

16 files changed

+311
-114
lines changed

examples/custom-cli/README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
## Scenarios
2+
3+
There are three customizing scenarios we should support:
4+
5+
- Augmenting an exsting CTA framework - Take an existing framework like `react-cra` then allow for base template customization, removal of add-ons, and addition of new custom add-ons.
6+
- Customized UI - Build up a new UI from components of the base UI.
7+
- New framework - Build up a new framework from with base and add-ons as well as a new CLI.
8+
9+
## Customers
10+
11+
There are two types of customers for this work:
12+
13+
- Infrastructure teams - Infrastructure teams at companies who want to create a template for new applications as well as optional add-ons with a friendly CLI and UI.
14+
- OSS framework authors - OSS framework authors who want to build a friendly CLI and UI for their framework.
15+
16+
## Examples
17+
18+
| Project | Description |
19+
| -------------------------------------- | --------------------------------------------------------------------- |
20+
| [customized-react](./customized-react) | Shows a small customization of the existing `react-cra` framework. |
21+
| [create-qwik-app](./create-qwik-app) | Show support for an entire framework (Qwik) with a custom CLI and UI. |

examples/custom-cli/create-qwik-app/src/index.ts

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,36 @@
22
import { dirname, join } from 'node:path'
33
import { fileURLToPath } from 'node:url'
44

5-
import { registerFramework } from '@tanstack/cta-engine'
5+
import {
6+
registerFramework,
7+
scanAddOnDirectories,
8+
scanProjectDirectory,
9+
} from '@tanstack/cta-engine'
610
import { cli } from '@tanstack/cta-cli'
711

12+
const projectDirectory = join(
13+
dirname(dirname(fileURLToPath(import.meta.url))),
14+
'project',
15+
)
16+
17+
const addOns = scanAddOnDirectories([
18+
join(dirname(dirname(fileURLToPath(import.meta.url))), 'add-ons'),
19+
])
20+
21+
const { files, basePackageJSON, optionalPackages } = scanProjectDirectory(
22+
projectDirectory,
23+
join(dirname(dirname(fileURLToPath(import.meta.url))), 'project'),
24+
)
25+
826
registerFramework({
927
id: 'qwik',
1028
name: 'qwik',
1129
description: 'Templates for Qwik',
1230
version: '0.1.0',
13-
baseDirectory: join(
14-
dirname(dirname(fileURLToPath(import.meta.url))),
15-
'project',
16-
),
17-
addOnsDirectories: [
18-
join(dirname(dirname(fileURLToPath(import.meta.url))), 'add-ons'),
19-
],
31+
base: files,
32+
addOns,
33+
basePackageJSON,
34+
optionalPackages,
2035
supportedModes: {
2136
default: {
2237
displayName: 'Default',
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"name": "customized-react-app",
3+
"version": "0.15.2",
4+
"description": "CTA Qwik Builder",
5+
"bin": "./dist/index.js",
6+
"type": "module",
7+
"scripts": {
8+
"build": "tsc",
9+
"dev": "tsc --watch",
10+
"start": "node dist/index.js"
11+
},
12+
"repository": {
13+
"type": "git",
14+
"url": "https://github.com/TanStack/create-router-app.git"
15+
},
16+
"homepage": "https://tanstack.com/router",
17+
"funding": {
18+
"type": "github",
19+
"url": "https://github.com/sponsors/tannerlinsley"
20+
},
21+
"keywords": [
22+
"qwik",
23+
"create-tanstack-app",
24+
"tanstack",
25+
"create-qwik-app"
26+
],
27+
"author": "Jack Herrington <jherr@pobox.com>",
28+
"license": "MIT",
29+
"packageManager": "pnpm@9.15.5",
30+
"dependencies": {
31+
"@tanstack/cta-cli": "workspace:*",
32+
"@tanstack/cta-engine": "workspace:*",
33+
"@tanstack/cta-framework-react-cra": "1.96.1"
34+
},
35+
"devDependencies": {
36+
"@types/node": "^22.13.4",
37+
"typescript": "^5.6.3"
38+
}
39+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<% if (fileRouter) { ignoreFile() } %><% if (!tailwind) { %>import "./App.css";<% } %>
2+
3+
function App() {
4+
return (<% if (tailwind) { %>
5+
<div className="text-center">
6+
<h1>I'm a customized setup</h1>
7+
</div>
8+
<% } else { %>
9+
<div className="App">
10+
<h1>I'm a customized setup</h1>
11+
</div>
12+
<% } %> );
13+
}
14+
15+
export default App;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<% if (codeRouter) { ignoreFile() } %>
2+
import { createFileRoute } from "@tanstack/react-router";
3+
<% if (!tailwind) { %>import "../App.css";<% } %>
4+
5+
export const Route = createFileRoute("/")({
6+
component: App,
7+
});
8+
9+
function App() {
10+
return (<% if (tailwind) { %>
11+
<div className="text-center">
12+
<h1>I'm a customized setup</h1>
13+
</div>
14+
<% } else { %>
15+
<div className="App">
16+
<h1>I'm a customized setup</h1>
17+
</div>
18+
<% } %> );
19+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/env node
2+
import { dirname, join } from 'node:path'
3+
import { fileURLToPath } from 'node:url'
4+
5+
import { cli } from '@tanstack/cta-cli'
6+
import { registerFramework, scanProjectDirectory } from '@tanstack/cta-engine'
7+
import { createFrameworkDefinition } from '@tanstack/cta-framework-react-cra'
8+
9+
const frameworkDefinition = createFrameworkDefinition()
10+
11+
frameworkDefinition.id = 'react'
12+
frameworkDefinition.name = 'react'
13+
frameworkDefinition.description = 'Customized React Setup'
14+
frameworkDefinition.version = '0.1.0'
15+
16+
const baseDirectory = dirname(dirname(fileURLToPath(import.meta.url)))
17+
const { files } = scanProjectDirectory(
18+
join(baseDirectory, 'project'),
19+
join(baseDirectory, 'project/base'),
20+
)
21+
22+
frameworkDefinition.base = {
23+
...frameworkDefinition.base,
24+
...files,
25+
}
26+
27+
registerFramework(frameworkDefinition)
28+
29+
cli({
30+
name: 'customized-react-app',
31+
appName: 'React',
32+
defaultFramework: 'react',
33+
})
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2020",
4+
"module": "ES2020",
5+
"outDir": "./dist",
6+
"rootDir": "./src",
7+
"strict": true,
8+
"esModuleInterop": true,
9+
"skipLibCheck": true,
10+
"forceConsistentCasingInFileNames": true,
11+
"moduleResolution": "node",
12+
"declaration": true,
13+
"declarationDir": "./dist/types"
14+
},
15+
"include": ["./src/**/*.ts"],
16+
"exclude": ["node_modules", "dist"]
17+
}

frameworks/react-cra/src/index.ts

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,36 @@
11
import { dirname, join } from 'node:path'
22
import { fileURLToPath } from 'node:url'
33

4-
import { registerFramework } from '@tanstack/cta-engine'
4+
import {
5+
registerFramework,
6+
scanAddOnDirectories,
7+
scanProjectDirectory,
8+
} from '@tanstack/cta-engine'
9+
import type { FrameworkDefinition } from '@tanstack/cta-engine'
510

6-
export function register() {
7-
registerFramework({
11+
export function createFrameworkDefinition(): FrameworkDefinition {
12+
const baseDirectory = dirname(dirname(fileURLToPath(import.meta.url)))
13+
14+
const addOns = scanAddOnDirectories([
15+
join(baseDirectory, 'add-ons'),
16+
join(baseDirectory, 'toolchains'),
17+
join(baseDirectory, 'examples'),
18+
])
19+
20+
const { files, basePackageJSON, optionalPackages } = scanProjectDirectory(
21+
join(baseDirectory, 'project'),
22+
join(baseDirectory, 'project/base'),
23+
)
24+
25+
return {
826
id: 'react-cra',
927
name: 'react',
1028
description: 'Templates for React CRA',
1129
version: '0.1.0',
12-
baseDirectory: join(
13-
dirname(dirname(fileURLToPath(import.meta.url))),
14-
'project',
15-
),
16-
addOnsDirectories: [
17-
join(dirname(dirname(fileURLToPath(import.meta.url))), 'add-ons'),
18-
join(dirname(dirname(fileURLToPath(import.meta.url))), 'toolchains'),
19-
join(dirname(dirname(fileURLToPath(import.meta.url))), 'examples'),
20-
],
30+
base: files,
31+
addOns,
32+
basePackageJSON,
33+
optionalPackages,
2134
supportedModes: {
2235
'code-router': {
2336
displayName: 'Code Router',
@@ -30,5 +43,9 @@ export function register() {
3043
forceTypescript: true,
3144
},
3245
},
33-
})
46+
}
47+
}
48+
49+
export function register() {
50+
registerFramework(createFrameworkDefinition())
3451
}

frameworks/solid/src/index.ts

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,36 @@
11
import { dirname, join } from 'node:path'
22
import { fileURLToPath } from 'node:url'
33

4-
import { registerFramework } from '@tanstack/cta-engine'
4+
import {
5+
registerFramework,
6+
scanAddOnDirectories,
7+
scanProjectDirectory,
8+
} from '@tanstack/cta-engine'
9+
import type { FrameworkDefinition } from '@tanstack/cta-engine'
510

6-
export function register() {
7-
registerFramework({
11+
export function createFrameworkDefinition(): FrameworkDefinition {
12+
const baseDirectory = dirname(dirname(fileURLToPath(import.meta.url)))
13+
14+
const addOns = scanAddOnDirectories([
15+
join(baseDirectory, 'add-ons'),
16+
join(baseDirectory, 'toolchains'),
17+
join(baseDirectory, 'examples'),
18+
])
19+
20+
const { files, basePackageJSON, optionalPackages } = scanProjectDirectory(
21+
join(baseDirectory, 'project'),
22+
join(baseDirectory, 'project/base'),
23+
)
24+
25+
return {
826
id: 'solid',
927
name: 'solid',
1028
description: 'Solid templates for Tanstack Router Applications',
1129
version: '0.1.0',
12-
baseDirectory: join(
13-
dirname(dirname(fileURLToPath(import.meta.url))),
14-
'project',
15-
),
16-
addOnsDirectories: [
17-
join(dirname(dirname(fileURLToPath(import.meta.url))), 'add-ons'),
18-
join(dirname(dirname(fileURLToPath(import.meta.url))), 'toolchains'),
19-
join(dirname(dirname(fileURLToPath(import.meta.url))), 'examples'),
20-
],
30+
base: files,
31+
addOns,
32+
basePackageJSON,
33+
optionalPackages,
2134
supportedModes: {
2235
'code-router': {
2336
displayName: 'Code Router',
@@ -30,5 +43,9 @@ export function register() {
3043
forceTypescript: true,
3144
},
3245
},
33-
})
46+
}
47+
}
48+
49+
export function register() {
50+
registerFramework(createFrameworkDefinition())
3451
}

frameworks/solid/tests/solid.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
getFrameworkById,
88
} from '@tanstack/cta-engine'
99

10-
import type { AddOn, Mode, Options } from '@tanstack/cta-engine'
10+
import type { AddOn, Options } from '@tanstack/cta-engine'
1111

1212
import { cleanupOutput } from './test-utilities.js'
1313

@@ -77,7 +77,7 @@ test('file router on npm', async () => {
7777
const { environment, output } = createMemoryEnvironment()
7878
const options = {
7979
...(await createSolidOptions(projectName)),
80-
mode: 'file-router' as Mode,
80+
mode: 'file-router',
8181
typescript: true,
8282
}
8383
await createApp(environment, options)
@@ -92,7 +92,7 @@ test('file router with tailwind on npm', async () => {
9292
const { environment, output } = createMemoryEnvironment()
9393
const options = {
9494
...(await createSolidOptions(projectName)),
95-
mode: 'file-router' as Mode,
95+
mode: 'file-router',
9696
typescript: true,
9797
tailwind: true,
9898
}

0 commit comments

Comments
 (0)