create-vite-taro 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/LICENSE +21 -0
- package/README.md +55 -0
- package/index.js +196 -0
- package/package.json +40 -0
- package/templates/default/README.md +38 -0
- package/templates/default/_env.local +1 -0
- package/templates/default/_gitignore +24 -0
- package/templates/default/index.html +11 -0
- package/templates/default/package.json +26 -0
- package/templates/default/src/app.css +11 -0
- package/templates/default/src/app.ts +15 -0
- package/templates/default/src/components/navigation-bar/navigation-bar.tsx +32 -0
- package/templates/default/src/components/navigation-bar/use-navigation-bar.ts +85 -0
- package/templates/default/src/pages/index/index.tsx +29 -0
- package/templates/default/tsconfig.app.json +25 -0
- package/templates/default/tsconfig.json +4 -0
- package/templates/default/tsconfig.node.json +24 -0
- package/templates/default/vite.config.ts +82 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 sep2
|
|
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,55 @@
|
|
|
1
|
+
# create-vite-taro
|
|
2
|
+
|
|
3
|
+
Create a Vite 8 + React 19 + Taro app for WeChat Mini Program and H5 targets.
|
|
4
|
+
|
|
5
|
+
## Quick start
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
# Create a new app from the default template
|
|
9
|
+
pnpm create vite-taro my-app
|
|
10
|
+
|
|
11
|
+
# Enter the project and install dependencies
|
|
12
|
+
cd my-app
|
|
13
|
+
pnpm install
|
|
14
|
+
|
|
15
|
+
# WeChat Mini Program: rebuild dist/wx in watch mode
|
|
16
|
+
pnpm dev:wx
|
|
17
|
+
|
|
18
|
+
# H5: start the Vite dev server
|
|
19
|
+
pnpm dev:h5
|
|
20
|
+
|
|
21
|
+
# Then open the standard Vite dev URL in your browser
|
|
22
|
+
# http://localhost:5173
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
You can keep `pnpm dev:wx` and `pnpm dev:h5` running at the same time in separate terminals.
|
|
26
|
+
|
|
27
|
+
Note: Because of WeChat DevTools and Mini Program runtime limitations, hot reload/fast rebuilds for the WeChat target may not always apply cleanly. For day-to-day iteration, prefer the H5 Vite dev server for fast feedback, and periodically verify the Mini Program result in WeChat DevTools.
|
|
28
|
+
|
|
29
|
+
Set `VITE_PLUGIN_TARO_WECHAT_APP_ID` in the generated `.env.local` to your WeChat App ID, then open `dist/wx` with WeChat DevTools for Mini Program development.
|
|
30
|
+
|
|
31
|
+
## Other package managers
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
npm create vite-taro@latest my-app
|
|
35
|
+
yarn create vite-taro my-app
|
|
36
|
+
bun create vite-taro my-app
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Scripts
|
|
40
|
+
|
|
41
|
+
| Script | Description |
|
|
42
|
+
| --- | --- |
|
|
43
|
+
| `pnpm dev:wx` | Build the WeChat Mini Program in watch mode. |
|
|
44
|
+
| `pnpm dev:h5` | Start the H5 dev server with Vite 8 hot reload. |
|
|
45
|
+
| `pnpm build:wx` | Build the WeChat Mini Program output into `dist/wx`. |
|
|
46
|
+
| `pnpm build:h5` | Build the H5 output into `dist/h5`. |
|
|
47
|
+
| `pnpm preview:h5` | Preview the built H5 output. |
|
|
48
|
+
| `pnpm typecheck` | Typecheck with `tsgo`. |
|
|
49
|
+
|
|
50
|
+
## Troubleshooting
|
|
51
|
+
|
|
52
|
+
| Problem | Check |
|
|
53
|
+
| --- | --- |
|
|
54
|
+
| `pnpm install` says dependency build scripts were ignored | Run `pnpm approve-builds`, approve the requested dependency build scripts, then rerun `pnpm install` if needed. |
|
|
55
|
+
|
package/index.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { randomBytes } from 'node:crypto'
|
|
3
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import process from 'node:process'
|
|
6
|
+
import readline from 'node:readline/promises'
|
|
7
|
+
import { fileURLToPath } from 'node:url'
|
|
8
|
+
|
|
9
|
+
const packageRoot = path.dirname(fileURLToPath(import.meta.url))
|
|
10
|
+
const defaultProjectName = 'vite-taro-app'
|
|
11
|
+
const defaultProjectTitle = 'Vite Taro App'
|
|
12
|
+
const defaultWechatAppId = 'wx0000000000000000'
|
|
13
|
+
const renamedFiles = {
|
|
14
|
+
'_env.local': '.env.local',
|
|
15
|
+
_gitignore: '.gitignore'
|
|
16
|
+
}
|
|
17
|
+
const helpText = `Usage:
|
|
18
|
+
pnpm create vite-taro [project-name]
|
|
19
|
+
npm create vite-taro@latest [project-name]
|
|
20
|
+
yarn create vite-taro [project-name]
|
|
21
|
+
bun create vite-taro [project-name]
|
|
22
|
+
|
|
23
|
+
Options:
|
|
24
|
+
-f, --force Create files even when the target directory is not empty.
|
|
25
|
+
-h, --help Show this help.
|
|
26
|
+
`
|
|
27
|
+
|
|
28
|
+
const options = parseArgs(process.argv.slice(2))
|
|
29
|
+
|
|
30
|
+
if (options.help) {
|
|
31
|
+
console.log(helpText)
|
|
32
|
+
process.exit(0)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const targetDirectory = options.targetDirectory ?? (await promptProjectName())
|
|
36
|
+
const projectPath = path.resolve(targetDirectory)
|
|
37
|
+
const projectName = toValidPackageName(path.basename(projectPath))
|
|
38
|
+
const projectTitle = toTitle(projectName)
|
|
39
|
+
const wechatAppId = createWechatAppId()
|
|
40
|
+
|
|
41
|
+
assertCanCreateProject(projectPath, options.force)
|
|
42
|
+
mkdirSync(projectPath, { recursive: true })
|
|
43
|
+
copyTemplate(path.join(packageRoot, 'templates/default'), projectPath, projectName, projectTitle, wechatAppId)
|
|
44
|
+
|
|
45
|
+
const packageManager = getPackageManager()
|
|
46
|
+
const displayProjectPath = getDisplayProjectPath(projectPath)
|
|
47
|
+
const cdCommand = displayProjectPath === '.' ? undefined : `cd ${quotePath(displayProjectPath)}`
|
|
48
|
+
|
|
49
|
+
console.log(`\nCreated ${projectName} in ${projectPath}\n`)
|
|
50
|
+
console.log('Next steps:')
|
|
51
|
+
if (cdCommand) console.log(` ${cdCommand}`)
|
|
52
|
+
console.log(` ${packageManager} install`)
|
|
53
|
+
console.log(` ${packageManager} dev:wx`)
|
|
54
|
+
console.log(` ${packageManager} dev:h5`)
|
|
55
|
+
|
|
56
|
+
function parseArgs(args) {
|
|
57
|
+
const options = {
|
|
58
|
+
force: false,
|
|
59
|
+
help: false,
|
|
60
|
+
targetDirectory: undefined
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (const arg of args) {
|
|
64
|
+
if (arg === '--help' || arg === '-h') {
|
|
65
|
+
options.help = true
|
|
66
|
+
continue
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (arg === '--force' || arg === '-f') {
|
|
70
|
+
options.force = true
|
|
71
|
+
continue
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (arg.startsWith('-')) {
|
|
75
|
+
fail(`Unknown option: ${arg}\n\n${helpText}`)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (options.targetDirectory) {
|
|
79
|
+
fail(`Unexpected argument: ${arg}\n\n${helpText}`)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
options.targetDirectory = arg
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return options
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function promptProjectName() {
|
|
89
|
+
if (!process.stdin.isTTY) return defaultProjectName
|
|
90
|
+
|
|
91
|
+
const prompt = readline.createInterface({ input: process.stdin, output: process.stdout })
|
|
92
|
+
try {
|
|
93
|
+
const answer = await prompt.question(`Project name (${defaultProjectName}): `)
|
|
94
|
+
return answer.trim() || defaultProjectName
|
|
95
|
+
} finally {
|
|
96
|
+
prompt.close()
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function assertCanCreateProject(projectPath, force) {
|
|
101
|
+
if (!existsSync(projectPath)) return
|
|
102
|
+
|
|
103
|
+
const stats = statSync(projectPath)
|
|
104
|
+
if (!stats.isDirectory()) {
|
|
105
|
+
fail(`Target path exists and is not a directory: ${projectPath}`)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!force && readdirSync(projectPath).length > 0) {
|
|
109
|
+
fail(`Target directory is not empty: ${projectPath}\nUse --force to write template files anyway.`)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function copyTemplate(sourceDirectory, targetDirectory, projectName, projectTitle, wechatAppId) {
|
|
114
|
+
for (const entry of readdirSync(sourceDirectory, { withFileTypes: true })) {
|
|
115
|
+
const sourcePath = path.join(sourceDirectory, entry.name)
|
|
116
|
+
const targetName = renamedFiles[entry.name] ?? entry.name
|
|
117
|
+
const targetPath = path.join(targetDirectory, targetName)
|
|
118
|
+
|
|
119
|
+
if (entry.isDirectory()) {
|
|
120
|
+
mkdirSync(targetPath, { recursive: true })
|
|
121
|
+
copyTemplate(sourcePath, targetPath, projectName, projectTitle, wechatAppId)
|
|
122
|
+
continue
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const source = readFileSync(sourcePath, 'utf8')
|
|
126
|
+
writeFileSync(targetPath, transformTemplateFile(targetName, source, projectName, projectTitle, wechatAppId))
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function transformTemplateFile(fileName, source, projectName, projectTitle, wechatAppId) {
|
|
131
|
+
if (fileName === 'package.json') {
|
|
132
|
+
const packageJson = JSON.parse(source)
|
|
133
|
+
packageJson.name = projectName
|
|
134
|
+
return `${JSON.stringify(packageJson, null, 4)}\n`
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (fileName === 'index.html') {
|
|
138
|
+
return source.replace(/<title>.*?<\/title>/, `<title>${projectTitle}</title>`)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return source
|
|
142
|
+
.replaceAll(defaultProjectName, projectName)
|
|
143
|
+
.replaceAll(defaultProjectTitle, projectTitle)
|
|
144
|
+
.replaceAll(defaultWechatAppId, wechatAppId)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function createWechatAppId() {
|
|
148
|
+
return `wx${randomBytes(8).toString('hex')}`
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function toValidPackageName(name) {
|
|
152
|
+
const normalizedName = name
|
|
153
|
+
.trim()
|
|
154
|
+
.toLowerCase()
|
|
155
|
+
.replace(/\s+/g, '-')
|
|
156
|
+
.replace(/^[._]/, '')
|
|
157
|
+
.replace(/[^a-z0-9-~]+/g, '-')
|
|
158
|
+
.replace(/^-+|-+$/g, '')
|
|
159
|
+
|
|
160
|
+
return isValidPackageName(normalizedName) ? normalizedName : defaultProjectName
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function isValidPackageName(name) {
|
|
164
|
+
return /^(?:@[a-z\d-*~][a-z\d-*._~]*\/)?[a-z\d-~][a-z\d-._~]*$/.test(name)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function toTitle(name) {
|
|
168
|
+
return name
|
|
169
|
+
.split(/[-_\s]+/)
|
|
170
|
+
.filter(Boolean)
|
|
171
|
+
.map((word) => `${word.slice(0, 1).toUpperCase()}${word.slice(1)}`)
|
|
172
|
+
.join(' ')
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function getPackageManager() {
|
|
176
|
+
const userAgent = process.env.npm_config_user_agent ?? ''
|
|
177
|
+
if (userAgent.startsWith('pnpm')) return 'pnpm'
|
|
178
|
+
if (userAgent.startsWith('yarn')) return 'yarn'
|
|
179
|
+
if (userAgent.startsWith('bun')) return 'bun'
|
|
180
|
+
if (userAgent.startsWith('npm')) return 'npm'
|
|
181
|
+
return 'pnpm'
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function getDisplayProjectPath(projectPath) {
|
|
185
|
+
const relativePath = path.relative(process.cwd(), projectPath) || '.'
|
|
186
|
+
return relativePath.startsWith('..') ? projectPath : relativePath
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function quotePath(value) {
|
|
190
|
+
return value.includes(' ') ? JSON.stringify(value) : value
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function fail(message) {
|
|
194
|
+
console.error(message)
|
|
195
|
+
process.exit(1)
|
|
196
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-vite-taro",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Create Vite 8 + React 19 + Taro apps for H5 and WeChat Mini Program targets.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-vite-taro": "./index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"index.js",
|
|
11
|
+
"templates",
|
|
12
|
+
"LICENSE",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/sep2/vite-plugin-taro.git",
|
|
18
|
+
"directory": "packages/create-vite-taro"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/sep2/vite-plugin-taro/issues"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/sep2/vite-plugin-taro/tree/main/packages/create-vite-taro#readme",
|
|
24
|
+
"keywords": [
|
|
25
|
+
"vite",
|
|
26
|
+
"taro",
|
|
27
|
+
"create",
|
|
28
|
+
"react",
|
|
29
|
+
"wechat",
|
|
30
|
+
"mini-program"
|
|
31
|
+
],
|
|
32
|
+
"author": "sep2",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=22"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Vite Taro App
|
|
2
|
+
|
|
3
|
+
Vite 8 + React 19 + Taro app generated by `create-vite-taro`.
|
|
4
|
+
|
|
5
|
+
Docs and source: <https://github.com/sep2/vite-plugin-taro>
|
|
6
|
+
|
|
7
|
+
## Quick start
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
# Install dependencies after scaffolding
|
|
11
|
+
pnpm install
|
|
12
|
+
|
|
13
|
+
# WeChat Mini Program: rebuild dist/wx in watch mode
|
|
14
|
+
pnpm dev:wx
|
|
15
|
+
|
|
16
|
+
# H5: start the Vite dev server
|
|
17
|
+
pnpm dev:h5
|
|
18
|
+
|
|
19
|
+
# Then open the standard Vite dev URL in your browser
|
|
20
|
+
# http://localhost:5173
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
You can keep `pnpm dev:wx` and `pnpm dev:h5` running at the same time in separate terminals.
|
|
24
|
+
|
|
25
|
+
Note: Because of WeChat DevTools and Mini Program runtime limitations, hot reload/fast rebuilds for the WeChat target may not always apply cleanly. For day-to-day iteration, prefer the H5 Vite dev server for fast feedback, and periodically verify the Mini Program result in WeChat DevTools.
|
|
26
|
+
|
|
27
|
+
Set `VITE_PLUGIN_TARO_WECHAT_APP_ID` in `.env.local` to your WeChat App ID, then open `dist/wx` with WeChat DevTools for Mini Program development.
|
|
28
|
+
|
|
29
|
+
## Scripts
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
pnpm dev:wx # Build the WeChat Mini Program in watch mode
|
|
33
|
+
pnpm dev:h5 # Start the H5 dev server
|
|
34
|
+
pnpm build:wx # Build dist/wx
|
|
35
|
+
pnpm build:h5 # Build dist/h5
|
|
36
|
+
pnpm preview:h5 # Preview dist/h5
|
|
37
|
+
pnpm typecheck # Typecheck with tsgo
|
|
38
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
VITE_PLUGIN_TARO_WECHAT_APP_ID=wx0000000000000000
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Logs
|
|
2
|
+
logs
|
|
3
|
+
*.log
|
|
4
|
+
npm-debug.log*
|
|
5
|
+
yarn-debug.log*
|
|
6
|
+
yarn-error.log*
|
|
7
|
+
pnpm-debug.log*
|
|
8
|
+
lerna-debug.log*
|
|
9
|
+
|
|
10
|
+
node_modules
|
|
11
|
+
dist
|
|
12
|
+
dist-ssr
|
|
13
|
+
*.local
|
|
14
|
+
|
|
15
|
+
# Editor directories and files
|
|
16
|
+
.vscode/*
|
|
17
|
+
!.vscode/extensions.json
|
|
18
|
+
.idea
|
|
19
|
+
.DS_Store
|
|
20
|
+
*.suo
|
|
21
|
+
*.ntvs*
|
|
22
|
+
*.njsproj
|
|
23
|
+
*.sln
|
|
24
|
+
*.sw?
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vite-taro-app",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev:wx": "NODE_ENV=development VITE_PLUGIN_TARO_TARGET=wx vite build --watch",
|
|
8
|
+
"dev:h5": "NODE_ENV=development VITE_PLUGIN_TARO_TARGET=h5 vite",
|
|
9
|
+
"build:wx": "NODE_ENV=production VITE_PLUGIN_TARO_TARGET=wx vite build",
|
|
10
|
+
"build:h5": "NODE_ENV=production VITE_PLUGIN_TARO_TARGET=h5 vite build",
|
|
11
|
+
"preview:h5": "NODE_ENV=production VITE_PLUGIN_TARO_TARGET=h5 vite preview --outDir dist/h5",
|
|
12
|
+
"typecheck": "tsgo -b"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"react": "^19.2.7",
|
|
16
|
+
"react-dom": "^19.2.7"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/node": "^26.0.0",
|
|
20
|
+
"@types/react": "^19.2.17",
|
|
21
|
+
"@types/react-dom": "^19.2.3",
|
|
22
|
+
"@typescript/native-preview": "latest",
|
|
23
|
+
"vite": "^8.0.16",
|
|
24
|
+
"vite-plugin-taro": "^0.1.2"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useLaunch } from 'virtual:taro/api'
|
|
2
|
+
import type { PropsWithChildren } from 'react'
|
|
3
|
+
import './app.css'
|
|
4
|
+
import { initNavigationBar } from './components/navigation-bar/use-navigation-bar.ts'
|
|
5
|
+
|
|
6
|
+
function App({ children }: PropsWithChildren) {
|
|
7
|
+
useLaunch(() => {
|
|
8
|
+
console.log('App launched')
|
|
9
|
+
initNavigationBar()
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
return children
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default App
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { View } from 'virtual:taro/components'
|
|
2
|
+
import type { CSSProperties } from 'react'
|
|
3
|
+
import { useNavigationBar } from './use-navigation-bar.ts'
|
|
4
|
+
|
|
5
|
+
interface NavigationBarProps {
|
|
6
|
+
title: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function px(value: number): string {
|
|
10
|
+
return `${Math.round(value)}px`
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function NavigationBar({ title }: NavigationBarProps) {
|
|
14
|
+
const { navBar, menuInfo } = useNavigationBar()
|
|
15
|
+
const containerStyle: CSSProperties = { height: px(navBar.height) }
|
|
16
|
+
const statusBarStyle: CSSProperties = { height: px(navBar.top) }
|
|
17
|
+
const contentStyle: CSSProperties = { padding: `${px(navBar.py)} ${px(navBar.px)}` }
|
|
18
|
+
const sideStyle: CSSProperties = { flexBasis: px(menuInfo.width), width: px(menuInfo.width) }
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<View className="flex w-full flex-col text-white bg-blue-600" style={containerStyle}>
|
|
22
|
+
<View className="shrink-0" style={statusBarStyle} />
|
|
23
|
+
<View className="flex w-full flex-1 flex-row items-center" style={contentStyle}>
|
|
24
|
+
<View className="flex h-full shrink-0" style={sideStyle} />
|
|
25
|
+
<View className="flex h-full min-w-0 flex-1 flex-row items-center justify-center text-center text-base font-medium">
|
|
26
|
+
{title}
|
|
27
|
+
</View>
|
|
28
|
+
<View className="flex h-full shrink-0" style={sideStyle} />
|
|
29
|
+
</View>
|
|
30
|
+
</View>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import Taro from 'virtual:taro/api'
|
|
2
|
+
import { useState } from 'react'
|
|
3
|
+
|
|
4
|
+
interface NavigationBarNavInfo {
|
|
5
|
+
height: number
|
|
6
|
+
top: number
|
|
7
|
+
py: number
|
|
8
|
+
px: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface NavigationBarMenuInfo {
|
|
12
|
+
bottom: number
|
|
13
|
+
height: number
|
|
14
|
+
left: number
|
|
15
|
+
right: number
|
|
16
|
+
top: number
|
|
17
|
+
width: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface NavigationBarInfo {
|
|
21
|
+
navBar: NavigationBarNavInfo
|
|
22
|
+
menuInfo: NavigationBarMenuInfo
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let navigationBarInfo: NavigationBarInfo = {
|
|
26
|
+
navBar: { height: 0, top: 0, py: 0, px: 0 },
|
|
27
|
+
menuInfo: { bottom: 0, height: 0, left: 0, right: 0, top: 0, width: 0 }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function createFallbackMenuInfo(screenWidth: number): NavigationBarMenuInfo {
|
|
31
|
+
const width = 88
|
|
32
|
+
const height = 32
|
|
33
|
+
const top = 6
|
|
34
|
+
const right = Math.max(width, screenWidth - 16)
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
bottom: top + height,
|
|
38
|
+
height,
|
|
39
|
+
left: right - width,
|
|
40
|
+
right,
|
|
41
|
+
top,
|
|
42
|
+
width
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getMenuInfo(screenWidth: number): NavigationBarMenuInfo {
|
|
47
|
+
if (IS_WEAPP) {
|
|
48
|
+
const menuButtonInfo = Taro.getMenuButtonBoundingClientRect()
|
|
49
|
+
if (menuButtonInfo.width > 0 && menuButtonInfo.height > 0) return menuButtonInfo
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return createFallbackMenuInfo(screenWidth)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function initNavigationBar(): NavigationBarInfo {
|
|
56
|
+
const windowInfo = Taro.getWindowInfo()
|
|
57
|
+
const screenWidth = windowInfo.screenWidth || 375
|
|
58
|
+
const statusBarHeight = IS_WEAPP ? (windowInfo.statusBarHeight ?? 44) : 0
|
|
59
|
+
const menuInfo = getMenuInfo(screenWidth)
|
|
60
|
+
const menuButtonStatusBarGap = Math.max(0, menuInfo.top - statusBarHeight)
|
|
61
|
+
const navBarHeight = menuButtonStatusBarGap * 2 + menuInfo.height + statusBarHeight
|
|
62
|
+
const paddingX = Math.max(0, screenWidth - menuInfo.right)
|
|
63
|
+
|
|
64
|
+
navigationBarInfo = {
|
|
65
|
+
navBar: {
|
|
66
|
+
height: navBarHeight,
|
|
67
|
+
top: statusBarHeight,
|
|
68
|
+
py: menuButtonStatusBarGap,
|
|
69
|
+
px: paddingX
|
|
70
|
+
},
|
|
71
|
+
menuInfo
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return navigationBarInfo
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function getNavigationBarInfo(): NavigationBarInfo {
|
|
78
|
+
if (navigationBarInfo.navBar.height > 0) return navigationBarInfo
|
|
79
|
+
return initNavigationBar()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function useNavigationBar(): NavigationBarInfo {
|
|
83
|
+
const [currentNavigationBarInfo] = useState(getNavigationBarInfo)
|
|
84
|
+
return currentNavigationBarInfo
|
|
85
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import Taro from 'virtual:taro/api'
|
|
2
|
+
import { Button, Text, View } from 'virtual:taro/components'
|
|
3
|
+
import { NavigationBar } from "../../components/navigation-bar/navigation-bar.tsx"
|
|
4
|
+
|
|
5
|
+
function IndexPage() {
|
|
6
|
+
return (
|
|
7
|
+
<View className="flex flex-col h-screen overflow-hidden">
|
|
8
|
+
<NavigationBar title={'Vite Taro App'}/>
|
|
9
|
+
<View className="flex-1 flex items-center justify-center bg-slate-50 px-6 py-12">
|
|
10
|
+
<View className="rounded-3xl bg-white p-6 shadow-sm">
|
|
11
|
+
<Text className="block text-2xl font-semibold text-slate-950">Vite + Taro</Text>
|
|
12
|
+
<Text className="mt-3 block text-base leading-7 text-slate-600">
|
|
13
|
+
Build H5 and WeChat Mini Program targets from one React/Taro codebase.
|
|
14
|
+
</Text>
|
|
15
|
+
<Button
|
|
16
|
+
className="mt-6 rounded-xl bg-blue-600 px-4 py-2 text-white"
|
|
17
|
+
onClick={() => {
|
|
18
|
+
Taro.showToast({title: 'Hello Taro'})
|
|
19
|
+
}}
|
|
20
|
+
>
|
|
21
|
+
Show toast
|
|
22
|
+
</Button>
|
|
23
|
+
</View>
|
|
24
|
+
</View>
|
|
25
|
+
</View>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default IndexPage
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
4
|
+
"target": "es2023",
|
|
5
|
+
"lib": ["ES2023", "DOM"],
|
|
6
|
+
"module": "esnext",
|
|
7
|
+
"types": ["vite/client", "vite-plugin-taro/client"],
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
|
|
10
|
+
/* Bundler mode */
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"allowImportingTsExtensions": true,
|
|
13
|
+
"verbatimModuleSyntax": true,
|
|
14
|
+
"moduleDetection": "force",
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
"jsx": "react-jsx",
|
|
17
|
+
|
|
18
|
+
/* Linting */
|
|
19
|
+
"noUnusedLocals": true,
|
|
20
|
+
"noUnusedParameters": true,
|
|
21
|
+
"erasableSyntaxOnly": true,
|
|
22
|
+
"noFallthroughCasesInSwitch": true
|
|
23
|
+
},
|
|
24
|
+
"include": ["src"]
|
|
25
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
4
|
+
"target": "es2023",
|
|
5
|
+
"lib": ["ES2023"],
|
|
6
|
+
"module": "esnext",
|
|
7
|
+
"types": ["node"],
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
|
|
10
|
+
/* Bundler mode */
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"allowImportingTsExtensions": true,
|
|
13
|
+
"verbatimModuleSyntax": true,
|
|
14
|
+
"moduleDetection": "force",
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
|
|
17
|
+
/* Linting */
|
|
18
|
+
"noUnusedLocals": true,
|
|
19
|
+
"noUnusedParameters": true,
|
|
20
|
+
"erasableSyntaxOnly": true,
|
|
21
|
+
"noFallthroughCasesInSwitch": true
|
|
22
|
+
},
|
|
23
|
+
"include": ["vite.config.ts"]
|
|
24
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
import { defineConfig, loadEnv } from 'vite'
|
|
4
|
+
import vitePluginTaro, { type VitePluginTaroTarget } from 'vite-plugin-taro'
|
|
5
|
+
|
|
6
|
+
const targetEnvName = 'VITE_PLUGIN_TARO_TARGET'
|
|
7
|
+
const projectRoot = fileURLToPath(new URL('.', import.meta.url))
|
|
8
|
+
|
|
9
|
+
function getTarget(env: Record<string, string>): VitePluginTaroTarget {
|
|
10
|
+
const target = env[targetEnvName]
|
|
11
|
+
if (target === 'wx' || target === 'h5') return target
|
|
12
|
+
throw new Error(`${targetEnvName} must be "wx" or "h5".`)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function fromRoot(...segments: string[]): string {
|
|
16
|
+
return path.resolve(projectRoot, ...segments)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default defineConfig(({ mode }) => {
|
|
20
|
+
const env = loadEnv(mode, process.cwd(), 'VITE_PLUGIN_TARO_')
|
|
21
|
+
const target = getTarget(env)
|
|
22
|
+
const wechatAppId = env.VITE_PLUGIN_TARO_WECHAT_APP_ID || 'touristappid'
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
build: {
|
|
26
|
+
outDir: fromRoot('dist', target)
|
|
27
|
+
},
|
|
28
|
+
plugins: [
|
|
29
|
+
vitePluginTaro({
|
|
30
|
+
target,
|
|
31
|
+
app: 'src/app.ts',
|
|
32
|
+
pages: [
|
|
33
|
+
{
|
|
34
|
+
path: 'pages/index/index',
|
|
35
|
+
config: {
|
|
36
|
+
navigationStyle: 'custom'
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
appJson: {
|
|
41
|
+
lazyCodeLoading: 'requiredComponents',
|
|
42
|
+
window: {
|
|
43
|
+
navigationStyle: 'custom'
|
|
44
|
+
},
|
|
45
|
+
renderer: 'skyline',
|
|
46
|
+
componentFramework: 'glass-easel',
|
|
47
|
+
rendererOptions: {
|
|
48
|
+
skyline: {
|
|
49
|
+
defaultDisplayBlock: true,
|
|
50
|
+
defaultContentBox: true
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
projectConfigJson: {
|
|
55
|
+
appid: wechatAppId,
|
|
56
|
+
projectname: 'vite-taro-app',
|
|
57
|
+
description: '',
|
|
58
|
+
compileType: 'miniprogram',
|
|
59
|
+
setting: {
|
|
60
|
+
skylineRenderEnable: true,
|
|
61
|
+
urlCheck: true,
|
|
62
|
+
es6: false,
|
|
63
|
+
postcss: false,
|
|
64
|
+
minified: false,
|
|
65
|
+
enhance: false,
|
|
66
|
+
uglifyFileName: false,
|
|
67
|
+
preloadBackgroundData: false,
|
|
68
|
+
newFeature: true,
|
|
69
|
+
autoAudits: false,
|
|
70
|
+
coverView: true,
|
|
71
|
+
showShadowRootInWxmlPanel: false,
|
|
72
|
+
scopeDataCheck: false,
|
|
73
|
+
useCompilerModule: false
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
sitemapJson: {
|
|
77
|
+
rules: [{ action: 'allow', page: '*' }]
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
})
|