create-lve 0.1.2 → 0.1.5

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/index.js CHANGED
@@ -10,20 +10,36 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
10
 
11
11
  async function main() {
12
12
  console.clear();
13
-
14
- p.intro(`${pc.bgCyan(pc.black(' LVE-CLI '))}`);
15
13
 
16
- // 1. 交互收集信息
14
+ p.intro(
15
+ `${pc.bgCyan(pc.black(' LVE-CLI '))} ` +
16
+ pc.gray('The Ultra-Fast React Stack (') +
17
+ pc.blue('Vite') + pc.gray(' + ') +
18
+ pc.yellow('Oxc') + pc.gray(' + ') +
19
+ pc.cyan('Tailwind') + pc.gray(')')
20
+ );
21
+
17
22
  const project = await p.group(
18
23
  {
19
- path: () =>
24
+ path: () =>
20
25
  p.text({
21
- message: '项目名称(或路径)?',
22
- placeholder: './lve-app',
26
+ message: '你的项目叫什么名字?',
27
+ placeholder: 'react-app',
28
+ defaultValue: 'react-app',
23
29
  validate: (value) => {
24
- if (value.length === 0) return '路径不能为空';
30
+ if (!value || value.length === 0) return;
31
+ if (value.match(/[<>:"|?*]/)) return '路径包含非法字符';
25
32
  }
26
33
  }),
34
+ shouldOverwrite: ({ results }) => {
35
+ const targetDir = path.resolve(process.cwd(), results.path);
36
+ if (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0) {
37
+ return p.confirm({
38
+ message: `⚠️ 目录 ${pc.yellow(results.path)} 已存在且不为空,是否清空?`,
39
+ initialValue: false,
40
+ });
41
+ }
42
+ },
27
43
  },
28
44
  {
29
45
  onCancel: () => {
@@ -33,29 +49,38 @@ async function main() {
33
49
  }
34
50
  );
35
51
 
52
+ if (project.shouldOverwrite === false) {
53
+ p.cancel('操作终止:请更换目录名后再试');
54
+ process.exit(0);
55
+ }
56
+
36
57
  const targetDir = path.resolve(process.cwd(), project.path);
37
58
  const templateDir = path.resolve(__dirname, 'template');
38
59
 
39
60
  const s = p.spinner();
40
- s.start('🚀 正在初始化项目模板...');
61
+ s.start('🚀 正在搬运模板...');
41
62
 
42
63
  try {
43
- if (!fs.existsSync(targetDir)) {
64
+ if (project.shouldOverwrite) {
65
+ await fs.emptyDir(targetDir);
66
+ } else {
44
67
  await fs.ensureDir(targetDir);
45
68
  }
46
69
 
47
70
  await fs.copy(templateDir, targetDir);
48
71
 
49
- const renameList = [
50
- ['_package.json', 'package.json'],
51
- ['_gitignore', '.gitignore']
52
- ];
72
+ const renameMap = {
73
+ '_package.json': 'package.json',
74
+ '_gitignore': '.gitignore',
75
+ '_oxfmtrc.json': '.oxfmtrc.json',
76
+ '_oxlintrc.json': '.oxlintrc.json',
77
+ '_vscode': '.vscode',
78
+ };
53
79
 
54
- for (const [oldName, newName] of renameList) {
55
- const oldPath = path.join(targetDir, oldName);
56
- const newPath = path.join(targetDir, newName);
80
+ for (const [oldFile, newFile] of Object.entries(renameMap)) {
81
+ const oldPath = path.join(targetDir, oldFile);
57
82
  if (fs.existsSync(oldPath)) {
58
- await fs.move(oldPath, newPath, { overwrite: true });
83
+ await fs.move(oldPath, path.join(targetDir, newFile), { overwrite: true });
59
84
  }
60
85
  }
61
86
 
@@ -66,24 +91,26 @@ async function main() {
66
91
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
67
92
  }
68
93
 
69
- const lockFile = path.join(targetDir, 'pnpm-lock.yaml');
70
- if (fs.existsSync(lockFile)) {
71
- await fs.remove(lockFile);
72
- }
94
+ const toRemove = ['pnpm-lock.yaml', 'node_modules', 'dist'];
95
+ await Promise.all(toRemove.map(file => fs.remove(path.join(targetDir, file))));
73
96
 
74
- s.stop('项目初始化成功!');
97
+ s.stop(pc.green('项目准备就绪!'));
98
+
99
+ const relativePath = path.relative(process.cwd(), targetDir);
100
+ const cdCmd = relativePath === '' ? '' : `cd ${relativePath}\n`;
75
101
 
76
- const cdPath = path.relative(process.cwd(), targetDir);
77
-
78
102
  p.note(
79
- pc.cyan(`cd ${cdPath}\npnpm install\npnpm dev`),
80
- '快速开始'
103
+ pc.cyan(`${cdCmd}pnpm install\npnpm dev`),
104
+ '快速开始指南'
81
105
  );
82
106
 
83
- p.outro(`✨ 祝你开发愉快!如有问题请反馈。`);
107
+ p.outro(
108
+ `${pc.magenta('✨ Happy Coding!')}\n` +
109
+ `${pc.gray('已为你配置 OXC 规则:享受秒级校验与格式化。')}`
110
+ );
84
111
 
85
112
  } catch (err) {
86
- s.stop('初始化失败');
113
+ s.stop('失败');
87
114
  console.error(pc.red(err));
88
115
  process.exit(1);
89
116
  }
package/package.json CHANGED
@@ -1,14 +1,13 @@
1
1
  {
2
2
  "name": "create-lve",
3
- "version": "0.1.2",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "create-lve": "index.js"
7
7
  },
8
8
  "files": [
9
9
  "index.js",
10
- "template",
11
- "dist"
10
+ "template"
12
11
  ],
13
12
  "dependencies": {
14
13
  "@clack/prompts": "^1.1.0",
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "./node_modules/oxfmt/configuration_schema.json",
3
+ "semi": false,
4
+ "singleQuote": true,
5
+ "bracketSpacing": true,
6
+ "tabWidth": 2,
7
+ "useTabs": false,
8
+ "ignorePatterns": ["node_modules/**", "dist/**", "public/**", "*.min.js"]
9
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "$schema": "./node_modules/oxlint/configuration_schema.json",
3
+ "categories": {
4
+ "correctness": "warn",
5
+ "perf": "warn",
6
+ "suspicious": "warn",
7
+ "pedantic": "allow"
8
+ },
9
+ "rules": {
10
+ "eslint/no-unused-vars": "error",
11
+ "react/jsx-no-target-blank": "error",
12
+ "react-hooks/rules-of-hooks": "error"
13
+ },
14
+ "ignorePatterns": ["dist/**", "coverage/**", "vendor/**", "test/snapshots/**"]
15
+ }
@@ -7,9 +7,10 @@
7
7
  "dev": "vite",
8
8
  "build": "tsc -b && vite build",
9
9
  "build-only": "vite build",
10
- "lint": "eslint .",
11
10
  "preview": "vite preview",
12
- "format": "oxfmt src/",
11
+ "lint": "oxlint",
12
+ "lint:fix": "oxlint --fix",
13
+ "format": "oxfmt .",
13
14
  "format:check": "oxfmt --check"
14
15
  },
15
16
  "dependencies": {
@@ -23,10 +24,9 @@
23
24
  "@types/react": "^19.2.7",
24
25
  "@types/react-dom": "^19.2.3",
25
26
  "@vitejs/plugin-react": "^5.1.1",
26
- "globals": "^16.5.0",
27
27
  "oxfmt": "^0.36.0",
28
28
  "oxlint": "^1.52.0",
29
29
  "typescript": "~5.9.3",
30
30
  "vite": "^8.0.0-beta.18"
31
31
  }
32
- }
32
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "editor.formatOnSave": true,
3
+ "editor.defaultFormatter": "oxc.oxc-vscode",
4
+ "[json]": {
5
+ "editor.defaultFormatter": "oxc.oxc-vscode"
6
+ },
7
+ "[jsonc]": {
8
+ "editor.defaultFormatter": "oxc.oxc-vscode"
9
+ }
10
+ }
@@ -1,5 +1,21 @@
1
+ import { useState } from 'react'
2
+
1
3
  function App() {
2
- return <div className="text-5xl font-semibold text-center">You did it!</div>
4
+ const [count, setCount] = useState(0)
5
+
6
+ console.log(`%cApp Comp re-render`, 'background-color:blue;color:white;padding:20px;')
7
+ return (
8
+ <div className="min-h-screen flex flex-col gap-6 justify-center items-center">
9
+ <p className="text-6xl">{count}</p>
10
+ <button
11
+ className="px-6 py-2 text-white bg-zinc-900 hover:bg-zinc-900/60 rounded-3xl"
12
+ disabled={count < 0}
13
+ onClick={() => setCount((pre) => pre + 1)}
14
+ >
15
+ increment
16
+ </button>
17
+ </div>
18
+ )
3
19
  }
4
20
 
5
21
  export default App
@@ -1,10 +1,10 @@
1
- import { StrictMode } from "react";
2
- import { createRoot } from "react-dom/client";
3
- import "./index.css";
4
- import App from "./App.tsx";
1
+ import { StrictMode } from 'react'
2
+ import { createRoot } from 'react-dom/client'
3
+ import './index.css'
4
+ import App from './App.tsx'
5
5
 
6
- createRoot(document.getElementById("root")!).render(
6
+ createRoot(document.getElementById('root')!).render(
7
7
  <StrictMode>
8
8
  <App />
9
9
  </StrictMode>,
10
- );
10
+ )
@@ -1,8 +1,8 @@
1
- import { defineConfig } from "vite";
2
- import react from "@vitejs/plugin-react";
3
- import tailwindcss from "@tailwindcss/vite";
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+ import tailwindcss from '@tailwindcss/vite'
4
4
 
5
5
  // https://vite.dev/config/
6
6
  export default defineConfig({
7
7
  plugins: [react(), tailwindcss()],
8
- });
8
+ })
@@ -1,5 +0,0 @@
1
- {
2
- "$schema": "./node_modules/oxfmt/configuration_schema.json",
3
- "semi": false,
4
- "singleQuote": true
5
- }