create-lve 0.3.0 → 0.3.1

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 (2) hide show
  1. package/config.js +201 -0
  2. package/package.json +2 -1
package/config.js ADDED
@@ -0,0 +1,201 @@
1
+ import path from 'node:path'
2
+ import { fileURLToPath } from 'node:url'
3
+ import fs from 'fs-extra'
4
+ import pc from 'picocolors'
5
+ import * as p from '@clack/prompts'
6
+ import { execSync, spawn } from 'node:child_process'
7
+
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
9
+
10
+ const getReactAppTemplate = (isUno) => {
11
+ const logoClass = isUno
12
+ ? 'animate-spin animate-duration-20s animate-linear animate-infinite'
13
+ : 'animate-[spin_20s_linear_infinite]'
14
+
15
+ return `
16
+ import { useState } from 'react'
17
+ import reactLogo from './assets/react.svg'
18
+
19
+ export function App() {
20
+ const [count, setCount] = useState(0)
21
+
22
+ return (
23
+ <div className="max-w-7xl mx-auto p-8 text-center font-sans antialiased text-[#213547] dark:text-zinc-200 min-h-dvh flex flex-col justify-center items-center">
24
+ <div className="flex justify-center gap-12 mb-12">
25
+ <a href="https://viteplus.dev" target="_blank" rel="noreferrer" className="transition-all duration-300 hover:drop-shadow-[0_0_2em_#646cffaa]">
26
+ <img src="/favicon.svg" className="h-24 p-6" alt="VitePlus logo" />
27
+ </a>
28
+ <a href="https://react.dev" target="_blank" rel="noreferrer" className="transition-all duration-300 hover:drop-shadow-[0_0_2em_#61dafbaa]">
29
+ <img src={reactLogo} className="h-24 p-6 ${logoClass}" alt="React logo" />
30
+ </a>
31
+ </div>
32
+ <h1 className="text-5xl font-bold leading-[1.1] mb-8">VitePlus + React</h1>
33
+ <div className="p-8 space-y-4 flex flex-col items-center">
34
+ <button onClick={() => setCount((count) => count + 1)} className="rounded-lg border border-transparent px-5 py-2.5 text-base font-medium bg-[#f9f9f9] dark:bg-zinc-800 cursor-pointer transition-colors hover:border-[#646cff] outline-none">
35
+ count is {count}
36
+ </button>
37
+ </div>
38
+ </div>
39
+ )
40
+ }
41
+ `.trim()
42
+ }
43
+
44
+ const FRAMEWORK_CONFIG = {
45
+ next: {
46
+ deps: (isUno) => ({
47
+ devDependencies: isUno ? { unocss: 'latest' } : { tailwindcss: 'latest' },
48
+ }),
49
+ },
50
+ react: {
51
+ deps: (isUno) => ({
52
+ devDependencies: isUno
53
+ ? { unocss: 'latest' }
54
+ : { tailwindcss: 'latest', '@tailwindcss/vite': 'latest' },
55
+
56
+ pnpm: {
57
+ overrides: {
58
+ vite: 'npm:@voidzero-dev/vite-plus-core@latest',
59
+ vitest: 'npm:@voidzero-dev/vite-plus-test@latest',
60
+ },
61
+ },
62
+ }),
63
+ },
64
+ vue: {
65
+ deps: (isUno) => FRAMEWORK_CONFIG.react.deps(isUno),
66
+ },
67
+ }
68
+
69
+ const CSS_STRATEGIES = {
70
+ unocss: {
71
+ pluginImport: "import UnoCSS from 'unocss/vite'\n",
72
+ pluginCode: 'UnoCSS(), ',
73
+ entryImport: "import 'virtual:uno.css'\n",
74
+ async setup(ctx) {
75
+ const unoConfig = `
76
+ import { defineConfig, presetWind3, transformerCompileClass } from 'unocss'
77
+
78
+ export default defineConfig({
79
+ presets: [presetWind3()],
80
+ transformers: [
81
+ {
82
+ name: 'auto-uno-injector',
83
+ enforce: 'pre',
84
+ idFilter(id) { return /\\.[tj]sx$|\\.vue$/.test(id) },
85
+ async transform(code) {
86
+ const classRegex = /(?:class|className)=["']([^"']+)["']/g
87
+ let match
88
+ while ((match = classRegex.exec(code.original))) {
89
+ const content = match[1]
90
+ if (content.trim() && !content.includes(':uno:')) {
91
+ const insertPos = match.index + match[0].indexOf(content)
92
+ code.appendLeft(insertPos, ':uno: ')
93
+ }
94
+ }
95
+ },
96
+ },
97
+ transformerCompileClass({ classPrefix: 'kfc-' }),
98
+ ],
99
+ })`.trim()
100
+ await fs.writeFile(path.join(ctx.targetDir, 'uno.config.ts'), unoConfig + '\n')
101
+ const stylePath = path.join(ctx.targetDir, 'src/style.css')
102
+ if (fs.existsSync(stylePath)) await fs.remove(stylePath)
103
+ },
104
+ },
105
+ tailwind: {
106
+ pluginImport: "import tailwindcss from '@tailwindcss/vite'\n",
107
+ pluginCode: 'tailwindcss(), ',
108
+ entryImport: "import './style.css'\n",
109
+ async setup(ctx) {
110
+ const stylePath = path.join(ctx.targetDir, 'src/style.css')
111
+ await fs.ensureDir(path.dirname(stylePath))
112
+ await fs.writeFile(stylePath, `@import "tailwindcss";`)
113
+ },
114
+ },
115
+ }
116
+
117
+ async function runTask(command, args, cwd) {
118
+ return new Promise((resolve, reject) => {
119
+ const child = spawn(command, args, {
120
+ cwd,
121
+ stdio: 'ignore',
122
+ shell: process.platform === 'win32',
123
+ })
124
+
125
+ child.on('error', (err) => {
126
+ console.error(pc.red(`无法启动命令: ${command}`), err)
127
+ reject(err)
128
+ })
129
+
130
+ child.on('close', (code) => (code === 0 ? resolve() : reject()))
131
+ })
132
+ }
133
+
134
+ async function applyProjectTransform(ctx) {
135
+ const { targetDir, framework, css, isUno, isNext } = ctx
136
+
137
+ const pkgPath = path.join(targetDir, 'package.json')
138
+ const pkg = await fs.readJson(pkgPath)
139
+ const config = FRAMEWORK_CONFIG[framework]
140
+ pkg.name = ctx.name
141
+ const extraConfig = config.deps(isUno)
142
+ pkg.devDependencies = { ...pkg.devDependencies, ...extraConfig.devDependencies }
143
+ if (extraConfig.pnpm) pkg.pnpm = { ...pkg.pnpm, ...extraConfig.pnpm }
144
+ await fs.writeJson(pkgPath, pkg, { spaces: 2 })
145
+
146
+ if (isNext) return
147
+
148
+ const strategy = CSS_STRATEGIES[css]
149
+ const isVue = framework === 'vue'
150
+ const paths = {
151
+ main: path.join(targetDir, isVue ? 'src/main.ts' : 'src/main.tsx'),
152
+ vite: path.join(targetDir, 'vite.config.ts'),
153
+ app: path.join(targetDir, isVue ? 'src/App.vue' : 'src/App.tsx'),
154
+ }
155
+
156
+ if (framework === 'react') {
157
+ await fs.writeFile(paths.app, getReactAppTemplate(isUno))
158
+ }
159
+
160
+ let viteContent = await fs.readFile(paths.vite, 'utf-8')
161
+ viteContent = strategy.pluginImport + viteContent
162
+ viteContent = viteContent.replace('/* VITE_PLUS_PLUGINS */', strategy.pluginCode)
163
+ await fs.writeFile(paths.vite, viteContent)
164
+
165
+ let mainContent = await fs.readFile(paths.main, 'utf-8')
166
+ await fs.writeFile(paths.main, strategy.entryImport + mainContent)
167
+
168
+ await strategy.setup(ctx)
169
+ }
170
+
171
+ async function cleanupTemplate(ctx) {
172
+ const toRemove = ['pnpm-workspace.yaml', 'pnpm-lock.yaml', 'node_modules', 'dist']
173
+ await Promise.all(toRemove.map((file) => fs.remove(path.join(ctx.targetDir, file))))
174
+
175
+ try {
176
+ execSync('git init', { cwd: ctx.targetDir, stdio: 'ignore' })
177
+ } catch {}
178
+
179
+ const oldGit = path.join(ctx.targetDir, '_gitignore')
180
+ if (fs.existsSync(oldGit)) {
181
+ await fs.move(oldGit, path.join(ctx.targetDir, '.gitignore'))
182
+ }
183
+ }
184
+
185
+ async function installDependencies(ctx, s) {
186
+ s.stop(pc.cyan(`准备安装依赖...`))
187
+ try {
188
+ await runTask(ctx.pkgManager, ['install'], ctx.targetDir)
189
+
190
+ s.start(pc.green(`正在执行 ${ctx.fmtCmd} 优化代码结构...`))
191
+ await new Promise((r) => setTimeout(r, 300))
192
+
193
+ const [cmd, ...args] = ctx.fmtCmd.split(' ')
194
+ await runTask(cmd, args, ctx.targetDir)
195
+ } catch (err) {
196
+ p.log.warn('自动安装或格式化失败,请稍后手动尝试', err)
197
+ throw err
198
+ }
199
+ }
200
+
201
+ export { __dirname, runTask, applyProjectTransform, cleanupTemplate, installDependencies }
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "create-lve",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "bin": {
5
5
  "create-lve": "index.js"
6
6
  },
7
7
  "files": [
8
8
  "index.js",
9
+ "config.js",
9
10
  "template-react",
10
11
  "template-next",
11
12
  "template-vue"