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.
- package/.gitattributes +2 -0
- package/.github/FUNDING.yml +1 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +94 -0
- package/.github/ISSUE_TEMPLATE/config.yml +11 -0
- package/.github/workflows/auto.yml +46 -0
- package/.github/workflows/ci.yml +43 -0
- package/.nvmrc +1 -0
- package/.prettierignore +3 -0
- package/CONTRIBUTING.md +34 -0
- package/LICENSE +21 -0
- package/README.md +51 -0
- package/dist/index.js +199 -0
- package/dist/utils/getPackageManager.js +15 -0
- package/eslint.config.js +35 -0
- package/package.json +48 -0
- package/prettier.config.js +10 -0
- package/scripts/publish.js +33 -0
- package/src/index.ts +335 -0
- package/src/utils/getPackageManager.ts +22 -0
- package/templates/base/.vscode/settings.json +11 -0
- package/templates/base/README.md.ejs +530 -0
- package/templates/base/gitignore +5 -0
- package/templates/base/index.html.ejs +20 -0
- package/templates/base/package.json +28 -0
- package/templates/base/package.ts.json +7 -0
- package/templates/base/package.tw.json +6 -0
- package/templates/base/public/favicon.ico +0 -0
- package/templates/base/public/logo192.png +0 -0
- package/templates/base/public/logo512.png +0 -0
- package/templates/base/public/manifest.json +25 -0
- package/templates/base/public/robots.txt +3 -0
- package/templates/base/src/App.css +38 -0
- package/templates/base/src/App.test.tsx.ejs +10 -0
- package/templates/base/src/App.tsx.ejs +74 -0
- package/templates/base/src/logo.svg +43 -0
- package/templates/base/src/reportWebVitals.ts.ejs +28 -0
- package/templates/base/src/styles.css.ejs +15 -0
- package/templates/base/tsconfig.json +24 -0
- package/templates/base/vite.config.js.ejs +15 -0
- package/templates/code-router/src/main.tsx.ejs +61 -0
- package/templates/file-router/package.fr.json +5 -0
- package/templates/file-router/src/main.tsx.ejs +35 -0
- package/templates/file-router/src/routes/__root.tsx +11 -0
- package/tsconfig.json +15 -0
package/.gitattributes
ADDED
|
@@ -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
|
package/.prettierignore
ADDED
package/CONTRIBUTING.md
ADDED
|
@@ -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
|
+
}
|
package/eslint.config.js
ADDED
|
@@ -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,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)
|