create-lve 0.4.27 → 0.4.29

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/config.js CHANGED
@@ -7,38 +7,102 @@ import { execSync, spawn } from 'node:child_process'
7
7
 
8
8
  const __dirname = path.dirname(fileURLToPath(import.meta.url))
9
9
 
10
+ const BASE_STYLE = `@layer base {
11
+ /* Scrollbar */
12
+ * {
13
+ scrollbar-width: thin;
14
+ scrollbar-color: rgba(0, 0, 0, 0.2) transparent;
15
+ }
16
+ ::-webkit-scrollbar {
17
+ width: 6px;
18
+ height: 6px;
19
+ }
20
+ ::-webkit-scrollbar-thumb {
21
+ background: rgba(0, 0, 0, 0.2);
22
+ border-radius: 3px;
23
+ }
24
+ ::-webkit-scrollbar-track {
25
+ background: transparent;
26
+ }
27
+ .dark * {
28
+ scrollbar-color: rgba(255, 255, 255, 0.15) transparent;
29
+ }
30
+ .dark ::-webkit-scrollbar-thumb {
31
+ background: rgba(255, 255, 255, 0.15);
32
+ }
33
+
34
+ /* 文本渲染 */
35
+ html {
36
+ -webkit-font-smoothing: antialiased;
37
+ -moz-osx-font-smoothing: grayscale;
38
+ text-rendering: optimizeLegibility;
39
+ }
40
+
41
+ /* 选中色 */
42
+ ::selection {
43
+ background: rgba(59, 130, 246, 0.2);
44
+ }
45
+
46
+ /* 触摸优化 */
47
+ body {
48
+ -webkit-tap-highlight-color: transparent;
49
+ }
50
+ }
51
+
52
+ /* 链接和图片禁止拖拽 + 长按菜单 + 点击高亮 */
53
+ a,
54
+ img {
55
+ -webkit-user-drag: none;
56
+ -webkit-tap-highlight-color: transparent;
57
+ -webkit-touch-callout: none;
58
+ }
59
+
60
+ /* input number 去掉上下箭头 */
61
+ input[type='number']::-webkit-inner-spin-button,
62
+ input[type='number']::-webkit-outer-spin-button {
63
+ -webkit-appearance: none;
64
+ margin: 0;
65
+ }
66
+
67
+ /* textarea 禁止拖拽调整大小 */
68
+ textarea {
69
+ resize: none;
70
+ }
71
+
72
+ /* details/summary 去掉默认三角 */
73
+ summary {
74
+ list-style: none;
75
+ }
76
+ summary::marker {
77
+ display: none;
78
+ content: '';
79
+ }
80
+
81
+ /* focus 焦点环 */
82
+ a:focus-visible {
83
+ outline: 2px solid var(--color-primary, #3b82f6);
84
+ outline-offset: 2px;
85
+ border-radius: 2px;
86
+ }
87
+ `
88
+
89
+ const vitePlusDeps = (isUno) => ({
90
+ dependencies: isUno ? { '@unocss/reset': 'latest' } : undefined,
91
+ devDependencies: isUno ? { unocss: 'latest' } : undefined,
92
+ overrides: {
93
+ vite: 'npm:@voidzero-dev/vite-plus-core@latest',
94
+ vitest: 'npm:@voidzero-dev/vite-plus-test@latest',
95
+ },
96
+ })
97
+
10
98
  const FRAMEWORK_CONFIG = {
11
99
  next: {
12
100
  deps: (isUno) => ({
13
101
  devDependencies: isUno ? { unocss: 'latest' } : { tailwindcss: 'latest' },
14
102
  }),
15
103
  },
16
- react: {
17
- deps: (isUno) => ({
18
- dependencies: isUno ? { '@unocss/reset': 'latest' } : {},
19
- devDependencies: {
20
- 'vite-plus': 'latest',
21
- '@vitejs/plugin-react': 'latest',
22
- '@rolldown/plugin-babel': 'latest',
23
- '@babel/core': 'latest',
24
- 'babel-plugin-react-compiler': 'latest',
25
- '@types/babel__core': 'latest',
26
- typescript: 'latest',
27
-
28
- ...(isUno
29
- ? { unocss: 'latest' }
30
- : { tailwindcss: 'latest', '@tailwindcss/vite': 'latest' }),
31
- },
32
-
33
- overrides: {
34
- vite: 'npm:@voidzero-dev/vite-plus-core@latest',
35
- vitest: 'npm:@voidzero-dev/vite-plus-test@latest',
36
- },
37
- }),
38
- },
39
- vue: {
40
- deps: (isUno) => FRAMEWORK_CONFIG.react.deps(isUno),
41
- },
104
+ react: { deps: vitePlusDeps },
105
+ vue: { deps: vitePlusDeps },
42
106
  }
43
107
 
44
108
  const CSS_STRATEGIES = {
@@ -119,8 +183,7 @@ export default defineConfig({
119
183
  pluginCode: 'tailwindcss(), ',
120
184
  entryImport: "import './style.css'\n",
121
185
  async setup(ctx) {
122
- // 模板自带 style.css(含 @import 'tailwindcss' + base UX),不覆盖
123
- // 仅确保文件存在
186
+ // style.css 已由 applyProjectTransform 写入,此处仅兜底
124
187
  const stylePath = path.join(ctx.targetDir, 'src/style.css')
125
188
  if (!fs.existsSync(stylePath)) {
126
189
  await fs.ensureDir(path.dirname(stylePath))
@@ -183,6 +246,10 @@ async function applyProjectTransform(ctx) {
183
246
  const extraConfig = config.deps(isUno)
184
247
  pkg.dependencies = { ...pkg.dependencies, ...extraConfig.dependencies }
185
248
  pkg.devDependencies = { ...pkg.devDependencies, ...extraConfig.devDependencies }
249
+ if (isUno) {
250
+ delete pkg.devDependencies?.['tailwindcss']
251
+ delete pkg.devDependencies?.['@tailwindcss/vite']
252
+ }
186
253
  if (extraConfig.overrides) pkg.overrides = { ...pkg.overrides, ...extraConfig.overrides }
187
254
  try {
188
255
  const version = execSync('pnpm --version', { encoding: 'utf-8', timeout: 5000 }).trim()
@@ -192,6 +259,13 @@ async function applyProjectTransform(ctx) {
192
259
 
193
260
  await resolveLatestVersions(pkgPath)
194
261
 
262
+ const indexPath = path.join(targetDir, 'index.html')
263
+ if (fs.existsSync(indexPath)) {
264
+ let indexContent = await fs.readFile(indexPath, 'utf-8')
265
+ indexContent = indexContent.replace(/<title>.*?<\/title>/, `<title>${ctx.name}</title>`)
266
+ await fs.writeFile(indexPath, indexContent)
267
+ }
268
+
195
269
  if (isNext) return
196
270
 
197
271
  const strategy = CSS_STRATEGIES[css]
@@ -209,6 +283,10 @@ async function applyProjectTransform(ctx) {
209
283
  let mainContent = await fs.readFile(paths.main, 'utf-8')
210
284
  await fs.writeFile(paths.main, strategy.entryImport + mainContent)
211
285
 
286
+ const stylePath = path.join(targetDir, 'src/style.css')
287
+ await fs.ensureDir(path.dirname(stylePath))
288
+ await fs.writeFile(stylePath, `@import 'tailwindcss';\n\n${BASE_STYLE}\n`)
289
+
212
290
  await strategy.setup(ctx)
213
291
  }
214
292
 
package/index.js CHANGED
@@ -7,12 +7,24 @@ import path from 'node:path'
7
7
  import { execSync } from 'node:child_process'
8
8
  import { __dirname, applyProjectTransform, cleanupTemplate, installDependencies } from './config.js'
9
9
 
10
+ function parseArgs() {
11
+ const args = process.argv.slice(2)
12
+ const flags = {}
13
+ const positional = []
14
+ for (const arg of args) {
15
+ if (arg === '--default') flags.default = true
16
+ else if (!arg.startsWith('-')) positional.push(arg)
17
+ }
18
+ return { flags, positional }
19
+ }
20
+
10
21
  function randomName() {
11
22
  const hash = Math.random().toString(36).slice(2, 6)
12
23
  return `vite-app-${hash}`
13
24
  }
14
25
 
15
26
  async function main() {
27
+ const { flags, positional } = parseArgs()
16
28
  process.stdout.write('\u001b[3J\u001b[2J\u001b[1J')
17
29
  console.clear()
18
30
  const logo = `
@@ -23,18 +35,22 @@ async function main() {
23
35
  console.log(logo)
24
36
  p.intro(`${pc.bgCyan(pc.black(' LVE-CLI '))}`)
25
37
 
38
+ const defaultName = positional[0] || randomName()
39
+
26
40
  const project = await p.group(
27
41
  {
28
42
  path: () =>
29
- p.text({
30
- message: '项目名称',
31
- placeholder: 'your-project-name',
32
- defaultValue: randomName(),
33
- validate: (value) => {
34
- if (!value || value.length === 0) return
35
- if (value.match(/[<>:"|?*]/)) return '路径包含非法字符'
36
- },
37
- }),
43
+ flags.default
44
+ ? defaultName
45
+ : p.text({
46
+ message: '项目名称',
47
+ placeholder: 'your-project-name',
48
+ defaultValue: randomName(),
49
+ validate: (value) => {
50
+ if (!value || value.length === 0) return
51
+ if (value.match(/[<>:"|?*]/)) return '路径包含非法字符'
52
+ },
53
+ }),
38
54
 
39
55
  shouldOverwrite: ({ results }) => {
40
56
  const targetDir = path.resolve(process.cwd(), results.path)
@@ -44,25 +60,29 @@ async function main() {
44
60
  },
45
61
 
46
62
  framework: () =>
47
- p.select({
48
- message: '选择框架',
49
- options: [
50
- { value: 'react', label: 'React 19', hint: '' },
51
- { value: 'vue', label: 'Vue 3', hint: '' },
52
- { value: 'next', label: 'Next.js 16', hint: '' },
53
- ],
54
- }),
55
-
56
- cssEngine: ({ results }) =>
57
- results.framework === 'next'
58
- ? p.note('Next.js 已内置 Tailwind,无需选择')
63
+ flags.default
64
+ ? 'react'
59
65
  : p.select({
60
- message: '选择 CSS',
66
+ message: '选择框架',
61
67
  options: [
62
- { value: 'tailwind', label: 'Tailwind v4' },
63
- { value: 'unocss', label: 'UnoCSS' },
68
+ { value: 'react', label: 'React 19', hint: '' },
69
+ { value: 'vue', label: 'Vue 3', hint: '' },
70
+ { value: 'next', label: 'Next.js 16', hint: '' },
64
71
  ],
65
72
  }),
73
+
74
+ cssEngine: ({ results }) =>
75
+ results.framework === 'next'
76
+ ? p.note('Next.js 已内置 Tailwind,无需选择')
77
+ : flags.default
78
+ ? 'tailwind'
79
+ : p.select({
80
+ message: '选择 CSS',
81
+ options: [
82
+ { value: 'tailwind', label: 'Tailwind v4' },
83
+ { value: 'unocss', label: 'UnoCSS' },
84
+ ],
85
+ }),
66
86
  },
67
87
  {
68
88
  onCancel: () => {
@@ -76,7 +96,6 @@ async function main() {
76
96
  name: project.path,
77
97
  framework: project.framework,
78
98
  css: project.cssEngine,
79
- shouldInstall: true,
80
99
  targetDir: path.resolve(process.cwd(), project.path),
81
100
  templateDir: path.resolve(__dirname, `template-${project.framework}`),
82
101
  isNext: project.framework === 'next',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-lve",
3
- "version": "0.4.27",
3
+ "version": "0.4.29",
4
4
  "bin": {
5
5
  "create-lve": "index.js"
6
6
  },
@@ -18,7 +18,10 @@
18
18
  "zustand": "latest"
19
19
  },
20
20
  "devDependencies": {
21
+ "@babel/core": "latest",
22
+ "@rolldown/plugin-babel": "latest",
21
23
  "@tailwindcss/vite": "latest",
24
+ "@types/babel__core": "latest",
22
25
  "@types/node": "latest",
23
26
  "@types/react": "latest",
24
27
  "@types/react-dom": "latest",
@@ -10,10 +10,12 @@
10
10
  },
11
11
 
12
12
  /* Linting */
13
+ "strict": true,
13
14
  "noUnusedLocals": true,
14
15
  "noUnusedParameters": true,
15
16
  "erasableSyntaxOnly": true,
16
- "noFallthroughCasesInSwitch": true
17
+ "noFallthroughCasesInSwitch": true,
18
+ "noUncheckedSideEffectImports": true
17
19
  },
18
20
  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
19
21
  }
@@ -15,10 +15,12 @@
15
15
  "noEmit": true,
16
16
 
17
17
  /* Linting */
18
+ "strict": true,
18
19
  "noUnusedLocals": true,
19
20
  "noUnusedParameters": true,
20
21
  "erasableSyntaxOnly": true,
21
- "noFallthroughCasesInSwitch": true
22
+ "noFallthroughCasesInSwitch": true,
23
+ "noUncheckedSideEffectImports": true
22
24
  },
23
25
  "include": [
24
26
  "vite.config.ts"
@@ -23,8 +23,7 @@ export default defineConfig({
23
23
  output: {
24
24
  manualChunks(id) {
25
25
  if (!id.includes('node_modules')) return
26
- if (id.includes('vue') && !id.includes('vue-router'))
27
- return 'vendor-vue'
26
+ if (id.includes('vue') && !id.includes('vue-router')) return 'vendor-vue'
28
27
  if (id.includes('vue-router')) return 'vendor-router'
29
28
  if (id.includes('pinia')) return 'vendor-pinia'
30
29
  if (id.includes('@vueuse')) return 'vendor-vueuse'
@@ -1,81 +0,0 @@
1
- @import 'tailwindcss';
2
-
3
- /* Scrollbar */
4
- @layer base {
5
- * {
6
- scrollbar-width: thin;
7
- scrollbar-color: rgba(0, 0, 0, 0.2) transparent;
8
- }
9
-
10
- ::-webkit-scrollbar {
11
- width: 6px;
12
- height: 6px;
13
- }
14
- ::-webkit-scrollbar-thumb {
15
- background: rgba(0, 0, 0, 0.2);
16
- border-radius: 3px;
17
- }
18
- ::-webkit-scrollbar-track {
19
- background: transparent;
20
- }
21
-
22
- .dark * {
23
- scrollbar-color: rgba(255, 255, 255, 0.15) transparent;
24
- }
25
- .dark ::-webkit-scrollbar-thumb {
26
- background: rgba(255, 255, 255, 0.15);
27
- }
28
- }
29
-
30
- /* 链接和图片禁止拖拽 + 长按菜单 + 点击高亮 */
31
- a,
32
- img {
33
- -webkit-user-drag: none;
34
- -webkit-tap-highlight-color: transparent;
35
- -webkit-touch-callout: none;
36
- }
37
-
38
- /* input number 去掉上下箭头 */
39
- input[type='number']::-webkit-inner-spin-button,
40
- input[type='number']::-webkit-outer-spin-button {
41
- -webkit-appearance: none;
42
- margin: 0;
43
- }
44
-
45
- /* textarea 禁止拖拽调整大小 */
46
- textarea {
47
- resize: none;
48
- }
49
-
50
- /* details/summary 去掉默认三角 */
51
- summary {
52
- list-style: none;
53
- }
54
- summary::marker {
55
- display: none;
56
- content: '';
57
- }
58
-
59
- /* focus 焦点环 */
60
- a:focus-visible {
61
- outline: 2px solid var(--color-primary, #3b82f6);
62
- outline-offset: 2px;
63
- border-radius: 2px;
64
- }
65
-
66
- /* 文本渲染 */
67
- html {
68
- -webkit-font-smoothing: antialiased;
69
- -moz-osx-font-smoothing: grayscale;
70
- text-rendering: optimizeLegibility;
71
- }
72
-
73
- /* 选中色 */
74
- ::selection {
75
- background: rgba(59, 130, 246, 0.2);
76
- }
77
-
78
- /* 触摸优化 */
79
- body {
80
- -webkit-tap-highlight-color: transparent;
81
- }
@@ -1,79 +0,0 @@
1
- @import 'tailwindcss';
2
-
3
- @layer base {
4
- /* Scrollbar */
5
- * {
6
- scrollbar-width: thin;
7
- scrollbar-color: rgba(0, 0, 0, 0.2) transparent;
8
- }
9
- ::-webkit-scrollbar {
10
- width: 6px;
11
- height: 6px;
12
- }
13
- ::-webkit-scrollbar-thumb {
14
- background: rgba(0, 0, 0, 0.2);
15
- border-radius: 3px;
16
- }
17
- ::-webkit-scrollbar-track {
18
- background: transparent;
19
- }
20
- .dark * {
21
- scrollbar-color: rgba(255, 255, 255, 0.15) transparent;
22
- }
23
- .dark ::-webkit-scrollbar-thumb {
24
- background: rgba(255, 255, 255, 0.15);
25
- }
26
-
27
- /* 文本渲染 */
28
- html {
29
- -webkit-font-smoothing: antialiased;
30
- -moz-osx-font-smoothing: grayscale;
31
- text-rendering: optimizeLegibility;
32
- }
33
-
34
- /* 选中色 */
35
- ::selection {
36
- background: rgba(59, 130, 246, 0.2);
37
- }
38
-
39
- /* 触摸优化 */
40
- body {
41
- -webkit-tap-highlight-color: transparent;
42
- }
43
- }
44
-
45
- /* 链接和图片禁止拖拽 + 长按菜单 + 点击高亮 */
46
- a,
47
- img {
48
- -webkit-user-drag: none;
49
- -webkit-tap-highlight-color: transparent;
50
- -webkit-touch-callout: none;
51
- }
52
-
53
- /* input number 去掉上下箭头 */
54
- input[type='number']::-webkit-inner-spin-button,
55
- input[type='number']::-webkit-outer-spin-button {
56
- -webkit-appearance: none;
57
- margin: 0;
58
- }
59
-
60
- /* textarea 禁止拖拽调整大小 */
61
- textarea {
62
- resize: none;
63
- }
64
-
65
- /* details/summary 去掉默认三角 */
66
- summary {
67
- list-style: none;
68
- }
69
- summary::marker {
70
- display: none;
71
- content: '';
72
- }
73
-
74
- /* focus 焦点环 */
75
- a:focus-visible {
76
- outline: 2px solid var(--color-primary, #3b82f6);
77
- outline-offset: 2px;
78
- border-radius: 2px;
79
- }