create-start-app 0.1.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.
Files changed (44) hide show
  1. package/.gitattributes +2 -0
  2. package/.github/FUNDING.yml +1 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.yml +94 -0
  4. package/.github/ISSUE_TEMPLATE/config.yml +11 -0
  5. package/.github/workflows/auto.yml +46 -0
  6. package/.github/workflows/ci.yml +43 -0
  7. package/.nvmrc +1 -0
  8. package/.prettierignore +3 -0
  9. package/CONTRIBUTING.md +34 -0
  10. package/LICENSE +21 -0
  11. package/README.md +51 -0
  12. package/dist/index.js +199 -0
  13. package/dist/utils/getPackageManager.js +15 -0
  14. package/eslint.config.js +35 -0
  15. package/package.json +48 -0
  16. package/prettier.config.js +10 -0
  17. package/scripts/publish.js +33 -0
  18. package/src/index.ts +335 -0
  19. package/src/utils/getPackageManager.ts +22 -0
  20. package/templates/base/.vscode/settings.json +11 -0
  21. package/templates/base/README.md.ejs +530 -0
  22. package/templates/base/gitignore +5 -0
  23. package/templates/base/index.html.ejs +20 -0
  24. package/templates/base/package.json +28 -0
  25. package/templates/base/package.ts.json +7 -0
  26. package/templates/base/package.tw.json +6 -0
  27. package/templates/base/public/favicon.ico +0 -0
  28. package/templates/base/public/logo192.png +0 -0
  29. package/templates/base/public/logo512.png +0 -0
  30. package/templates/base/public/manifest.json +25 -0
  31. package/templates/base/public/robots.txt +3 -0
  32. package/templates/base/src/App.css +38 -0
  33. package/templates/base/src/App.test.tsx.ejs +10 -0
  34. package/templates/base/src/App.tsx.ejs +74 -0
  35. package/templates/base/src/logo.svg +43 -0
  36. package/templates/base/src/reportWebVitals.ts.ejs +28 -0
  37. package/templates/base/src/styles.css.ejs +15 -0
  38. package/templates/base/tsconfig.json +24 -0
  39. package/templates/base/vite.config.js.ejs +15 -0
  40. package/templates/code-router/src/main.tsx.ejs +61 -0
  41. package/templates/file-router/package.fr.json +5 -0
  42. package/templates/file-router/src/main.tsx.ejs +35 -0
  43. package/templates/file-router/src/routes/__root.tsx +11 -0
  44. package/tsconfig.json +15 -0
package/.gitattributes ADDED
@@ -0,0 +1,2 @@
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
@@ -0,0 +1 @@
1
+ github: tannerlinsley
@@ -0,0 +1,94 @@
1
+ name: '🐛 Bug report'
2
+ description: Create a report to help us improve
3
+ body:
4
+ - type: markdown
5
+ attributes:
6
+ value: |
7
+ Thank you for reporting an issue :pray:.
8
+
9
+ This issue tracker is for reporting bugs found in `create-tsrouter-app` (https://github.com/tanstack/create-tsrouter-app).
10
+ If you have a question about how to achieve something and are struggling, please post a question
11
+ inside of `create-tsrouter-app` Discussions tab: https://github.com/tanstack/create-tsrouter-app/discussions
12
+
13
+ Before submitting a new bug/issue, please check the links below to see if there is a solution or question posted there already:
14
+ - `create-tsrouter-app` Issues tab: https://github.com/tanstack/create-tsrouter-app/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc
15
+ - `create-tsrouter-app` closed issues tab: https://github.com/tanstack/create-tsrouter-app/issues?q=is%3Aissue+sort%3Aupdated-desc+is%3Aclosed
16
+ - `create-tsrouter-app` Discussions tab: https://github.com/tanstack/create-tsrouter-app/discussions
17
+
18
+ The more information you fill in, the better the community can help you.
19
+ - type: dropdown
20
+ id: project
21
+ attributes:
22
+ label: Which project does this relate to?
23
+ description: If you are unsure, please leave this as "Create Tanstack App".
24
+ options:
25
+ - Create Tanstack App
26
+ validations:
27
+ required: true
28
+ - type: textarea
29
+ id: description
30
+ attributes:
31
+ label: Describe the bug
32
+ description: Provide a clear and concise description of the challenge you are running into.
33
+ validations:
34
+ required: true
35
+ - type: input
36
+ id: link
37
+ attributes:
38
+ label: Your Example Website or App
39
+ description: |
40
+ Which website or app were you using when the bug happened?
41
+ Note:
42
+ - Please provide a link via our pre-configured Stackblitz project ([file-based routes](https://stackblitz.com/github/tanstack/router/tree/main/examples/react/quickstart-file-based?file=src%2Fmain.tsx)|[code-based routes](https://stackblitz.com/github/tanstack/router/tree/main/examples/react/quickstart?file=src%2Fmain.tsx)) or a link to a repo that can reproduce the issue.
43
+ - Your bug will may get fixed much faster if we can run your code and it doesn't have dependencies other than the `router` npm package / dependency.
44
+ - To create a shareable code example you can use Stackblitz. Please no localhost URLs.
45
+ - Please read these tips for providing a minimal example: https://stackoverflow.com/help/mcve.
46
+ placeholder: reproduction URL
47
+ validations:
48
+ required: true
49
+ - type: textarea
50
+ id: steps
51
+ attributes:
52
+ label: Steps to Reproduce the Bug or Issue
53
+ description: Describe the steps we have to take to reproduce the behavior.
54
+ placeholder: |
55
+ 1. Go to '...'
56
+ 2. Click on '....'
57
+ 3. Scroll down to '....'
58
+ 4. See error
59
+ validations:
60
+ required: true
61
+ - type: textarea
62
+ id: expected
63
+ attributes:
64
+ label: Expected behavior
65
+ description: Provide a clear and concise description of what you expected to happen.
66
+ placeholder: |
67
+ As a user, I expected ___ behavior but i am seeing ___
68
+ validations:
69
+ required: true
70
+ - type: textarea
71
+ id: screenshots_or_videos
72
+ attributes:
73
+ label: Screenshots or Videos
74
+ description: |
75
+ If applicable, add screenshots or a video to help explain your problem.
76
+ For more information on the supported file image/file types and the file size limits, please refer
77
+ to the following link: https://docs.github.com/en/github/writing-on-github/working-with-advanced-formatting/attaching-files
78
+ placeholder: |
79
+ You can drag your video or image files inside of this editor ↓
80
+ - type: textarea
81
+ id: platform
82
+ attributes:
83
+ label: Platform
84
+ value: |
85
+ - OS: [e.g. macOS, Windows, Linux]
86
+ - Browser: [e.g. Chrome, Safari, Firefox]
87
+ - Version: [e.g. 91.1]
88
+ validations:
89
+ required: true
90
+ - type: textarea
91
+ id: additional
92
+ attributes:
93
+ label: Additional context
94
+ description: Add any other context about the problem here.
@@ -0,0 +1,11 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: 🤔 Feature Requests & Questions
4
+ url: https://github.com/tanstack/router/discussions
5
+ about: Please ask and answer questions here.
6
+ - name: 💬 Community Chat
7
+ url: https://discord.gg/mQd7egN
8
+ about: A dedicated discord server hosted by Tanner Linsley
9
+ - name: 💬 Tanstack Twitter
10
+ url: https://twitter.com/tan_stack
11
+ about: Stay up to date with new releases of our libraries
@@ -0,0 +1,46 @@
1
+ name: auto
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ schedule:
8
+ - cron: '0 0 * * *'
9
+
10
+ concurrency: ${{ github.workflow }}-${{ github.ref }}
11
+
12
+ permissions:
13
+ contents: write
14
+
15
+ jobs:
16
+ update-templates:
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - name: Checkout Repo
20
+ uses: actions/checkout@v4
21
+ with:
22
+ fetch-depth: 0
23
+
24
+ - name: Setup Bun
25
+ uses: oven-sh/setup-bun@v2
26
+ with:
27
+ bun-version: latest
28
+
29
+ - name: Setup Node
30
+ uses: actions/setup-node@v4
31
+ with:
32
+ node-version: 22
33
+
34
+ - name: Publish Packages
35
+ run: |
36
+ bunx gitpick@latest https://github.com/TanStack/create-tsrouter-app . --overwrite
37
+ bun i
38
+ bun run build
39
+ rm -rf bun.lock
40
+ npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
41
+ bunx json -I -f package.json -e 'this.name="create-router-app"'
42
+ npm publish || true
43
+ bunx json -I -f package.json -e 'this.name="create-start-app"'
44
+ npm publish || true
45
+ env:
46
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -0,0 +1,43 @@
1
+ name: ci
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ tag:
7
+ description: override release tag
8
+ required: false
9
+ push:
10
+ branches: [main, alpha, beta]
11
+
12
+ concurrency:
13
+ group: ${{ github.workflow }}-${{ github.event.number || github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ permissions:
17
+ contents: write
18
+ id-token: write
19
+
20
+ jobs:
21
+ test-and-publish:
22
+ name: Test & Publish
23
+ if: github.repository_owner == 'TanStack'
24
+ runs-on: ubuntu-latest
25
+ steps:
26
+ - name: Checkout
27
+ uses: actions/checkout@v4.2.2
28
+ with:
29
+ fetch-depth: 0
30
+ - name: Setup Tools
31
+ uses: tanstack/config/.github/setup@main
32
+ - name: Build
33
+ run: pnpm build
34
+ - name: Publish
35
+ run: |
36
+ git config --global user.name 'Tanner Linsley'
37
+ git config --global user.email 'tannerlinsley@users.noreply.github.com'
38
+ npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
39
+ pnpm run cipublish
40
+ env:
41
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
42
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
43
+ TAG: ${{ inputs.tag }}
package/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ 20.17.0
@@ -0,0 +1,3 @@
1
+ dist
2
+ project-template
3
+ node_modules
@@ -0,0 +1,34 @@
1
+ # Contributing
2
+
3
+ - Clone the repo
4
+ - `gh repo clone TanStack/create-tsrouter-app`
5
+ - Ensure `node` is installed
6
+ - https://nodejs.org/en/
7
+ - Ensure `pnpm` is installed
8
+ - https://pnpm.io/installation
9
+ - Why? We use `pnpm` to manage workspace dependencies. It's easily the best monorepo/workspace experience available as of when this was written.
10
+ - Install dependencies
11
+ - `pnpm install`
12
+ - This installs dependencies for all of the packages in the monorepo, even examples!
13
+ - Dependencies inside of the packages and examples are automatically linked together as local/dynamic dependencies.
14
+ - Run the build
15
+ - `pnpm build`
16
+ - Build an example app with the builder
17
+ - `pnpm start app-js`
18
+ - Run the `app-js` app just to make sure it works
19
+ - Make changes to the code
20
+ - Re-run `pnpm build` and `pnpm start` (in all its configurations) to make sure the changes work
21
+ - Check your work and PR
22
+
23
+ # Testing combinations
24
+
25
+ These must all product running applications that can be built (`pnpm build`) and tested (`pnpm test`).
26
+
27
+ | Command | Description |
28
+ | -------------------------------------------------------- | ------------------------------------------------------------------ |
29
+ | `pnpm start app-js` | Creates a JavaScript app |
30
+ | `pnpm start app-ts --template typescript` | Creates a TypeScript app |
31
+ | `pnpm start app-js-tw --tailwind` | Creates a JavaScript app with Tailwind CSS |
32
+ | `pnpm start app-ts-tw --template typescript --tailwind` | Creates a TypeScript app with Tailwind CSS |
33
+ | `pnpm start app-fr --template file-router` | Creates a TypeScript app with File Based Routing |
34
+ | `pnpm start app-fr-tw --template file-router --tailwind` | Creates a TypeScript app with File Based Routing with Tailwind CSS |
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021-present Tanner Linsley
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # Create React App for TanStack Router
2
+
3
+ This CLI applications builds Tanstack Start applications that are the functional equivalent of [Create React App](https://create-react-app.dev/).
4
+
5
+ To help accelerate the migration away from `create-react-app` we created the `create-tsrouter-app` CLI which is a plug-n-play replacement for CRA.
6
+
7
+ Instead of:
8
+
9
+ ```bash
10
+ npx create-react-app my-app
11
+ ```
12
+
13
+ You can now run:
14
+
15
+ ```bash
16
+ npx create-tsrouter-app@latest my-app
17
+ ```
18
+
19
+ Instead of using:
20
+
21
+ ```bash
22
+ npx create-react-app my-app --template typescript
23
+ ```
24
+
25
+ To create a SPA application using TypeScript. You can now run:
26
+
27
+ ```bash
28
+ npx create-tsrouter-app@latest my-app --template typescript
29
+ ```
30
+
31
+ What you'll get is a Vite application that uses TanStack Router. All the files will still be in the same place as in CRA, but you'll get a fully functional Router setup under in `app/main.tsx`.
32
+
33
+ `create-tsrouter-app` is everything you loved about CRA but implemented with modern tools and best practices, on top of the popular TanStack set of libraries. Which includes [@tanstack/react-query](https://tanstack.com/query/latest) and [@tanstack/react-router](https://tanstack.com/router/latest).
34
+
35
+ If you want Tailwind then just add `--tailwind` and that will automatically configure [Tailwind V4](https://tailwindcss.com/).
36
+
37
+ You can also specify your preferred package manager with `--package-manager` such as `npm`, `bun`, `yarn`, or `pnpm`.
38
+
39
+ Extensive documentation on using the TanStack Router, migrating to a File Base Routing approach, as well as integrating [@tanstack/react-query](https://tanstack.com/query/latest) and [@tanstack/store](https://tanstack.com/store/latest) be found in the generated `README.md` for your project.
40
+
41
+ ## File Based Routing
42
+
43
+ By default `create-tsrouter-app` will create a Code Based Routing application. If you want to use File Based Routing then you can specify `--template file-router`. The location of the home page will be `app/routes/index.tsx`.
44
+
45
+ # Contributing
46
+
47
+ Check out the [Contributing](CONTRIBUTING.md) guide.
48
+
49
+ # License
50
+
51
+ MIT
package/dist/index.js ADDED
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env node
2
+ import { copyFile, mkdir, readFile, writeFile } from 'node:fs/promises';
3
+ import { existsSync } from 'node:fs';
4
+ import { resolve } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { Command, InvalidArgumentError } from 'commander';
7
+ import { intro, outro, spinner, log } from '@clack/prompts';
8
+ import { execa } from 'execa';
9
+ import { render } from 'ejs';
10
+ import { SUPPORTED_PACKAGE_MANAGERS, getPackageManager, } from './utils/getPackageManager.js';
11
+ const program = new Command();
12
+ const CODE_ROUTER = 'code-router';
13
+ const FILE_ROUTER = 'file-router';
14
+ function sortObject(obj) {
15
+ return Object.keys(obj)
16
+ .sort()
17
+ .reduce((acc, key) => {
18
+ acc[key] = obj[key];
19
+ return acc;
20
+ }, {});
21
+ }
22
+ function createCopyFile(targetDir) {
23
+ return async function copyFiles(templateDir, files) {
24
+ for (const file of files) {
25
+ const targetFileName = file.replace('.tw', '');
26
+ await copyFile(resolve(templateDir, file), resolve(targetDir, targetFileName));
27
+ }
28
+ };
29
+ }
30
+ function createTemplateFile(projectName, options, targetDir) {
31
+ return async function templateFile(templateDir, file, targetFileName) {
32
+ const templateValues = {
33
+ packageManager: options.packageManager,
34
+ projectName: projectName,
35
+ typescript: options.typescript,
36
+ tailwind: options.tailwind,
37
+ js: options.typescript ? 'ts' : 'js',
38
+ jsx: options.typescript ? 'tsx' : 'jsx',
39
+ fileRouter: options.mode === FILE_ROUTER,
40
+ codeRouter: options.mode === CODE_ROUTER,
41
+ };
42
+ const template = await readFile(resolve(templateDir, file), 'utf-8');
43
+ const content = render(template, templateValues);
44
+ const target = targetFileName ?? file.replace('.ejs', '');
45
+ await writeFile(resolve(targetDir, target), content);
46
+ };
47
+ }
48
+ async function createPackageJSON(projectName, options, templateDir, routerDir, targetDir) {
49
+ let packageJSON = JSON.parse(await readFile(resolve(templateDir, 'package.json'), 'utf8'));
50
+ packageJSON.name = projectName;
51
+ if (options.typescript) {
52
+ const tsPackageJSON = JSON.parse(await readFile(resolve(templateDir, 'package.ts.json'), 'utf8'));
53
+ packageJSON = {
54
+ ...packageJSON,
55
+ devDependencies: {
56
+ ...packageJSON.devDependencies,
57
+ ...tsPackageJSON.devDependencies,
58
+ },
59
+ };
60
+ }
61
+ if (options.tailwind) {
62
+ const twPackageJSON = JSON.parse(await readFile(resolve(templateDir, 'package.tw.json'), 'utf8'));
63
+ packageJSON = {
64
+ ...packageJSON,
65
+ dependencies: {
66
+ ...packageJSON.dependencies,
67
+ ...twPackageJSON.dependencies,
68
+ },
69
+ };
70
+ }
71
+ if (options.mode === FILE_ROUTER) {
72
+ const frPackageJSON = JSON.parse(await readFile(resolve(routerDir, 'package.fr.json'), 'utf8'));
73
+ packageJSON = {
74
+ ...packageJSON,
75
+ dependencies: {
76
+ ...packageJSON.dependencies,
77
+ ...frPackageJSON.dependencies,
78
+ },
79
+ };
80
+ }
81
+ packageJSON.dependencies = sortObject(packageJSON.dependencies);
82
+ packageJSON.devDependencies = sortObject(packageJSON.devDependencies);
83
+ await writeFile(resolve(targetDir, 'package.json'), JSON.stringify(packageJSON, null, 2));
84
+ }
85
+ async function createApp(projectName, options) {
86
+ const templateDirBase = fileURLToPath(new URL('../templates/base', import.meta.url));
87
+ const templateDirRouter = fileURLToPath(new URL(`../templates/${options.mode}`, import.meta.url));
88
+ const targetDir = resolve(process.cwd(), projectName);
89
+ if (existsSync(targetDir)) {
90
+ log.error(`Directory "${projectName}" already exists`);
91
+ return;
92
+ }
93
+ const copyFiles = createCopyFile(targetDir);
94
+ const templateFile = createTemplateFile(projectName, options, targetDir);
95
+ intro(`Creating a new TanStack app in ${targetDir}...`);
96
+ // Make the root directory
97
+ await mkdir(targetDir, { recursive: true });
98
+ // Setup the .vscode directory
99
+ await mkdir(resolve(targetDir, '.vscode'), { recursive: true });
100
+ await copyFile(resolve(templateDirBase, '.vscode/settings.json'), resolve(targetDir, '.vscode/settings.json'));
101
+ // Fill the public directory
102
+ await mkdir(resolve(targetDir, 'public'), { recursive: true });
103
+ copyFiles(templateDirBase, [
104
+ './public/robots.txt',
105
+ './public/favicon.ico',
106
+ './public/manifest.json',
107
+ './public/logo192.png',
108
+ './public/logo512.png',
109
+ ]);
110
+ // Make the src directory
111
+ await mkdir(resolve(targetDir, 'src'), { recursive: true });
112
+ if (options.mode === FILE_ROUTER) {
113
+ await mkdir(resolve(targetDir, 'src/routes'), { recursive: true });
114
+ }
115
+ // Copy in Vite and Tailwind config and CSS
116
+ if (!options.tailwind) {
117
+ await copyFiles(templateDirBase, ['./src/App.css']);
118
+ }
119
+ await templateFile(templateDirBase, './vite.config.js.ejs');
120
+ await templateFile(templateDirBase, './src/styles.css.ejs');
121
+ copyFiles(templateDirBase, ['./src/logo.svg']);
122
+ // Setup the app component. There are four variations, typescript/javascript and tailwind/non-tailwind.
123
+ if (options.mode === FILE_ROUTER) {
124
+ copyFiles(templateDirRouter, ['./src/routes/__root.tsx']);
125
+ await templateFile(templateDirBase, './src/App.tsx.ejs', './src/routes/index.tsx');
126
+ }
127
+ else {
128
+ await templateFile(templateDirBase, './src/App.tsx.ejs', options.typescript ? undefined : './src/App.jsx');
129
+ await templateFile(templateDirBase, './src/App.test.tsx.ejs', options.typescript ? undefined : './src/App.test.jsx');
130
+ }
131
+ // Create the main entry point
132
+ if (options.typescript) {
133
+ await templateFile(templateDirRouter, './src/main.tsx.ejs');
134
+ }
135
+ else {
136
+ await templateFile(templateDirRouter, './src/main.tsx.ejs', './src/main.jsx');
137
+ }
138
+ // Setup the main, reportWebVitals and index.html files
139
+ if (options.typescript) {
140
+ await templateFile(templateDirBase, './src/reportWebVitals.ts.ejs');
141
+ }
142
+ else {
143
+ await templateFile(templateDirBase, './src/reportWebVitals.ts.ejs', './src/reportWebVitals.js');
144
+ }
145
+ await templateFile(templateDirBase, './index.html.ejs');
146
+ // Setup tsconfig
147
+ if (options.typescript) {
148
+ await copyFiles(templateDirBase, ['./tsconfig.json']);
149
+ }
150
+ // Setup the package.json file, optionally with typescript and tailwind
151
+ await createPackageJSON(projectName, options, templateDirBase, templateDirRouter, targetDir);
152
+ // Add .gitignore
153
+ await copyFile(resolve(templateDirBase, 'gitignore'), resolve(targetDir, '.gitignore'));
154
+ // Create the README.md
155
+ await templateFile(templateDirBase, 'README.md.ejs');
156
+ // Install dependencies
157
+ const s = spinner();
158
+ s.start(`Installing dependencies via ${options.packageManager}...`);
159
+ await execa(options.packageManager, ['install'], { cwd: targetDir });
160
+ s.stop(`Installed dependencies`);
161
+ outro(`Created your new TanStack app in ${targetDir}.
162
+
163
+ Use the following commands to start your app:
164
+
165
+ % cd ${projectName}
166
+ % ${options.packageManager} start
167
+
168
+ Please read README.md for more information on testing, styling, adding routes, react-query, etc.
169
+ `);
170
+ }
171
+ program
172
+ .name('create-tsrouter-app')
173
+ .description('CLI to create a new TanStack application')
174
+ .argument('<project-name>', 'name of the project')
175
+ .option('--template <type>', 'project template (typescript, javascript, file-router)', (value) => {
176
+ if (value !== 'typescript' &&
177
+ value !== 'javascript' &&
178
+ value !== 'file-router') {
179
+ throw new InvalidArgumentError(`Invalid template: ${value}. Only the following are allowed: typescript, javascript, file-router`);
180
+ }
181
+ return value;
182
+ }, 'javascript')
183
+ .option(`--package-manager <${SUPPORTED_PACKAGE_MANAGERS.join('|')}>`, `Explicitly tell the CLI to use this package manager`, (value) => {
184
+ if (!SUPPORTED_PACKAGE_MANAGERS.includes(value)) {
185
+ throw new InvalidArgumentError(`Invalid package manager: ${value}. Only the following are allowed: ${SUPPORTED_PACKAGE_MANAGERS.join(', ')}`);
186
+ }
187
+ return value;
188
+ }, getPackageManager())
189
+ .option('--tailwind', 'add Tailwind CSS', false)
190
+ .action((projectName, options) => {
191
+ const typescript = options.template === 'typescript' || options.template === 'file-router';
192
+ createApp(projectName, {
193
+ typescript,
194
+ tailwind: options.tailwind,
195
+ packageManager: options.packageManager,
196
+ mode: options.template === 'file-router' ? FILE_ROUTER : CODE_ROUTER,
197
+ });
198
+ });
199
+ program.parse();
@@ -0,0 +1,15 @@
1
+ export const SUPPORTED_PACKAGE_MANAGERS = [
2
+ 'npm',
3
+ 'yarn',
4
+ 'pnpm',
5
+ 'bun',
6
+ ];
7
+ export const DEFAULT_PACKAGE_MANAGER = 'npm';
8
+ export function getPackageManager() {
9
+ const userAgent = process.env.npm_config_user_agent;
10
+ if (userAgent === undefined) {
11
+ return DEFAULT_PACKAGE_MANAGER;
12
+ }
13
+ const packageManager = SUPPORTED_PACKAGE_MANAGERS.find((manager) => userAgent.startsWith(manager));
14
+ return packageManager || DEFAULT_PACKAGE_MANAGER;
15
+ }
@@ -0,0 +1,35 @@
1
+ // @ts-check
2
+
3
+ // @ts-expect-error
4
+ import { tanstackConfig } from '@tanstack/config/eslint'
5
+
6
+ import unusedImports from 'eslint-plugin-unused-imports'
7
+
8
+ export default [
9
+ ...tanstackConfig,
10
+ {
11
+ name: 'tanstack/temp',
12
+ rules: {
13
+ '@typescript-eslint/no-unsafe-function-type': 'off',
14
+ 'no-shadow': 'off',
15
+ },
16
+ },
17
+ {
18
+ plugins: {
19
+ 'unused-imports': unusedImports,
20
+ },
21
+ rules: {
22
+ '@typescript-eslint/no-unused-vars': 'off',
23
+ 'unused-imports/no-unused-imports': 'error',
24
+ 'unused-imports/no-unused-vars': [
25
+ 'warn',
26
+ {
27
+ vars: 'all',
28
+ varsIgnorePattern: '^_',
29
+ args: 'after-used',
30
+ argsIgnorePattern: '^_',
31
+ },
32
+ ],
33
+ },
34
+ },
35
+ ]
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "create-start-app",
3
+ "version": "0.1.2",
4
+ "description": "Tanstack Application Builder",
5
+ "bin": "./dist/index.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "start": "tsc && node dist/index.js",
10
+ "test": "echo \"Error: no test specified\" && exit 0",
11
+ "cipublish": "node scripts/publish.js",
12
+ "test:lint": "eslint ./src"
13
+ },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/TanStack/create-tsrouter-app.git"
17
+ },
18
+ "homepage": "https://tanstack.com/router",
19
+ "funding": {
20
+ "type": "github",
21
+ "url": "https://github.com/sponsors/tannerlinsley"
22
+ },
23
+ "keywords": [
24
+ "react",
25
+ "tanstack",
26
+ "router",
27
+ "create-react-app"
28
+ ],
29
+ "author": "Jack Herrington <jherr@pobox.com>",
30
+ "license": "MIT",
31
+ "packageManager": "pnpm@9.15.5",
32
+ "dependencies": {
33
+ "@clack/prompts": "^0.10.0",
34
+ "commander": "^13.1.0",
35
+ "ejs": "^3.1.10",
36
+ "execa": "^9.5.2"
37
+ },
38
+ "devDependencies": {
39
+ "@tanstack/config": "^0.16.2",
40
+ "@types/ejs": "^3.1.5",
41
+ "@types/node": "^22.13.4",
42
+ "eslint": "^9.20.0",
43
+ "eslint-plugin-react-hooks": "^5.1.0",
44
+ "eslint-plugin-unused-imports": "^4.1.4",
45
+ "prettier": "^3.5.0",
46
+ "typescript": "^5.6.3"
47
+ }
48
+ }
@@ -0,0 +1,10 @@
1
+ // @ts-check
2
+
3
+ /** @type {import('prettier').Config} */
4
+ const config = {
5
+ semi: false,
6
+ singleQuote: true,
7
+ trailingComma: "all",
8
+ };
9
+
10
+ export default config;
@@ -0,0 +1,33 @@
1
+ // @ts-check
2
+
3
+ import { resolve } from 'node:path'
4
+ import { fileURLToPath } from 'node:url'
5
+ import { publish } from '@tanstack/config/publish'
6
+
7
+ const __dirname = fileURLToPath(new URL('.', import.meta.url))
8
+
9
+ await publish({
10
+ packages: [
11
+ {
12
+ name: 'create-tsrouter-app',
13
+ packageDir: '.',
14
+ },
15
+ ],
16
+ branchConfigs: {
17
+ main: {
18
+ prerelease: false,
19
+ },
20
+ alpha: {
21
+ prerelease: true,
22
+ },
23
+ beta: {
24
+ prerelease: true,
25
+ },
26
+ },
27
+ rootDir: resolve(__dirname, '..'),
28
+ branch: process.env.BRANCH,
29
+ tag: process.env.TAG,
30
+ ghToken: process.env.GH_TOKEN,
31
+ })
32
+
33
+ process.exit(0)