tumemo 1.0.1 → 1.1.0
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/README.md +11 -11
- package/index.js +131 -73
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
# Tumemo CLI
|
|
1
|
+
# 🚀 Tumemo CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
O jeito mais rápido de iniciar seus projetos React com TypeScript, Axios e as melhores ferramentas de estilo.
|
|
4
4
|
|
|
5
5
|
## 🛠️ O que ele faz?
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
- **
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
- **
|
|
12
|
-
- **Organização Sênior:** Cria pastas `components`, `pages`, `hooks` e `lib`.
|
|
6
|
+
- Gera um projeto **Vite (React + TS)** limpo.
|
|
7
|
+
- Remove arquivos desnecessários (`App.css`, `index.css`, ativos de exemplo).
|
|
8
|
+
- Instala automaticamente: **Axios**, **React Router Dom**, **Tailwind Merge** e **Clsx**.
|
|
9
|
+
- Configura Aliases de caminho (`@/` aponta para `src/`).
|
|
10
|
+
- Já entrega as pastas `src/components`, `src/pages` e `src/shared` criadas.
|
|
11
|
+
- **Inicia o servidor automaticamente** após o setup.
|
|
13
12
|
|
|
14
13
|
## 🚀 Como usar
|
|
15
14
|
|
|
16
|
-
|
|
15
|
+
Acesse a pasta onde deseja criar o projeto e rode:
|
|
16
|
+
|
|
17
17
|
```bash
|
|
18
|
-
npx tumemo
|
|
18
|
+
npx tumemo@latest
|
package/index.js
CHANGED
|
@@ -6,7 +6,6 @@ import { execa } from 'execa';
|
|
|
6
6
|
import fs from 'fs-extra';
|
|
7
7
|
import path from 'path';
|
|
8
8
|
|
|
9
|
-
// Força o Chalk a usar cores mesmo em sub-processos
|
|
10
9
|
process.env.FORCE_COLOR = "1";
|
|
11
10
|
|
|
12
11
|
const log = {
|
|
@@ -18,97 +17,171 @@ const log = {
|
|
|
18
17
|
|
|
19
18
|
async function runCommand(command, args, options = {}) {
|
|
20
19
|
try {
|
|
21
|
-
// Adicionado env para forçar cores nos comandos do NPM/Vite
|
|
22
20
|
await execa(command, args, {
|
|
23
21
|
stdio: 'inherit',
|
|
24
|
-
|
|
22
|
+
shell: true,
|
|
23
|
+
env: { ...process.env, FORCE_COLOR: "1" },
|
|
25
24
|
...options
|
|
26
25
|
});
|
|
27
26
|
} catch (error) {
|
|
28
|
-
log.error(`Erro
|
|
27
|
+
log.error(`Erro: ${error.message}`);
|
|
29
28
|
process.exit(1);
|
|
30
29
|
}
|
|
31
30
|
}
|
|
32
31
|
|
|
33
32
|
program
|
|
34
33
|
.name('Tumemo')
|
|
35
|
-
.
|
|
36
|
-
.
|
|
37
|
-
.option('-
|
|
38
|
-
.option('-s, --shadcn', 'Instalar Tailwind + Shadcn UI')
|
|
34
|
+
.version('1.1.9')
|
|
35
|
+
.option('-t, --tailwind', 'Instalar Tailwind v4')
|
|
36
|
+
.option('-s, --shadcn', 'Instalar Shadcn')
|
|
39
37
|
.action(async (options) => {
|
|
40
38
|
const root = process.cwd();
|
|
39
|
+
const projectName = path.basename(root);
|
|
41
40
|
|
|
42
41
|
try {
|
|
43
42
|
log.step(`Iniciando Setup Tumemo em: ${root}`);
|
|
44
43
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
await runCommand('npm', ['create', 'vite@latest', '.', '--yes', '--', '--template', 'react-ts']);
|
|
44
|
+
log.info('Baixando template React + TS...');
|
|
45
|
+
await runCommand('npx', ['--yes', 'degit', 'vitejs/vite/packages/create-vite/template-react-ts', '.']);
|
|
48
46
|
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
if (fs.existsSync(path.join(root, '_gitignore'))) {
|
|
48
|
+
fs.renameSync(path.join(root, '_gitignore'), path.join(root, '.gitignore'));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const pkgPath = path.join(root, 'package.json');
|
|
52
|
+
const pkg = await fs.readJson(pkgPath);
|
|
53
|
+
pkg.name = projectName;
|
|
54
|
+
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
55
|
+
|
|
56
|
+
log.info('Instalando dependências...');
|
|
51
57
|
await runCommand('npm', ['install']);
|
|
52
|
-
|
|
53
|
-
log.info('Instalando bibliotecas auxiliares (Axios, Router, etc)...');
|
|
58
|
+
await runCommand('npm', ['install', '-D', '@types/node', '@vitejs/plugin-react-swc']);
|
|
54
59
|
await runCommand('npm', ['install', 'axios', 'react-router-dom', 'tailwind-merge', 'clsx']);
|
|
55
|
-
await runCommand('npm', ['install', '-D', '@types/node']);
|
|
56
60
|
|
|
57
|
-
// 3. Limpeza Radical
|
|
58
61
|
log.info('Limpando boilerplate...');
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
await fs.remove(fullPath);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
62
|
+
['src/assets', 'src/App.css', 'src/index.css', 'public/vite.svg'].forEach(item => {
|
|
63
|
+
const fullPath = path.resolve(root, item);
|
|
64
|
+
if (fs.existsSync(fullPath)) fs.removeSync(fullPath);
|
|
65
|
+
});
|
|
66
66
|
|
|
67
67
|
const useTailwind = options.tailwind || options.shadcn;
|
|
68
68
|
|
|
69
|
-
// 4. Configuração Tailwind
|
|
70
69
|
if (useTailwind) {
|
|
71
|
-
log.info('Configurando
|
|
72
|
-
await runCommand('npm', ['install', '
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const tailwindContent = `@tailwind base;\n@tailwind components;\n@tailwind utilities;`;
|
|
76
|
-
await fs.ensureDir(path.join(root, 'src/shared'));
|
|
77
|
-
await fs.writeFile(path.join(root, 'src/shared/tailwind.css'), tailwindContent);
|
|
70
|
+
log.info('Configurando Tailwind v4...');
|
|
71
|
+
await runCommand('npm', ['install', 'tailwindcss', '@tailwindcss/vite']);
|
|
72
|
+
fs.ensureDirSync(path.join(root, 'src/shared'));
|
|
73
|
+
fs.writeFileSync(path.join(root, 'src/shared/tailwind.css'), `@import "tailwindcss";`);
|
|
78
74
|
}
|
|
79
75
|
|
|
80
|
-
//
|
|
81
|
-
if (options.shadcn) {
|
|
82
|
-
log.info('Configurando Shadcn UI...');
|
|
83
|
-
await runCommand('npm', ['install', 'lucide-react', 'class-variance-authority']);
|
|
84
|
-
await runCommand('npx', ['shadcn-ui@latest', 'init', '-y']);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// 6. Config de Paths (@/)
|
|
76
|
+
// --- 1. SEU VITE CONFIG ---
|
|
88
77
|
const viteConfig = `
|
|
78
|
+
import { defineConfig } from 'vite'
|
|
79
|
+
import react from '@vitejs/plugin-react-swc'
|
|
89
80
|
import path from "path"
|
|
90
|
-
import
|
|
91
|
-
import { defineConfig } from "vite"
|
|
81
|
+
import tailwindcss from "@tailwindcss/vite"
|
|
92
82
|
|
|
93
83
|
export default defineConfig({
|
|
94
|
-
plugins: [react()],
|
|
84
|
+
plugins: [react(), tailwindcss()],
|
|
95
85
|
resolve: {
|
|
96
86
|
alias: {
|
|
97
87
|
"@": path.resolve(__dirname, "./src"),
|
|
98
88
|
},
|
|
99
89
|
},
|
|
100
90
|
})`;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
91
|
+
fs.writeFileSync(path.join(root, 'vite.config.ts'), viteConfig.trim());
|
|
92
|
+
|
|
93
|
+
// --- 2. SEU TSCONFIG APP ---
|
|
94
|
+
const tsConfigApp = {
|
|
95
|
+
"compilerOptions": {
|
|
96
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
97
|
+
"target": "ES2022",
|
|
98
|
+
"useDefineForClassFields": true,
|
|
99
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
100
|
+
"module": "ESNext",
|
|
101
|
+
"types": ["vite/client", "node"],
|
|
102
|
+
"skipLibCheck": true,
|
|
103
|
+
"moduleResolution": "bundler",
|
|
104
|
+
"allowImportingTsExtensions": true,
|
|
105
|
+
"verbatimModuleSyntax": true,
|
|
106
|
+
"moduleDetection": "force",
|
|
107
|
+
"noEmit": true,
|
|
108
|
+
"jsx": "react-jsx",
|
|
109
|
+
"strict": true,
|
|
110
|
+
"noUnusedLocals": true,
|
|
111
|
+
"noUnusedParameters": true,
|
|
112
|
+
"erasableSyntaxOnly": true,
|
|
113
|
+
"noFallthroughCasesInSwitch": true,
|
|
114
|
+
"noUncheckedSideEffectImports": true,
|
|
115
|
+
"baseUrl": ".",
|
|
116
|
+
"paths": { "@/*": ["./src/*"] }
|
|
117
|
+
},
|
|
118
|
+
"include": ["src"]
|
|
119
|
+
};
|
|
120
|
+
fs.writeJsonSync(path.join(root, 'tsconfig.app.json'), tsConfigApp, { spaces: 2 });
|
|
121
|
+
|
|
122
|
+
// --- 3. SEU TSCONFIG RAIZ ---
|
|
123
|
+
const tsConfig = {
|
|
124
|
+
"files": [],
|
|
125
|
+
"references": [
|
|
126
|
+
{ "path": "./tsconfig.app.json" },
|
|
127
|
+
{ "path": "./tsconfig.node.json" }
|
|
128
|
+
],
|
|
129
|
+
"compilerOptions": {
|
|
130
|
+
"baseUrl": ".",
|
|
131
|
+
"paths": { "@/*": ["./src/*"] }
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
fs.writeJsonSync(path.join(root, 'tsconfig.json'), tsConfig, { spaces: 2 });
|
|
135
|
+
|
|
136
|
+
if (options.shadcn) {
|
|
137
|
+
log.info('Configurando Shadcn UI');
|
|
138
|
+
await runCommand('npx', ['shadcn@latest', 'init']);
|
|
109
139
|
}
|
|
110
140
|
|
|
111
|
-
//
|
|
141
|
+
// --- 4. APP.TSX COM VISUAL CAPRICHADO ---
|
|
142
|
+
let techStack = "React + TS";
|
|
143
|
+
if (options.shadcn) techStack += " + Shadcn UI";
|
|
144
|
+
else if (useTailwind) techStack += " + Tailwind v4";
|
|
145
|
+
|
|
146
|
+
const appContent = `
|
|
147
|
+
export default function App() {
|
|
148
|
+
return (
|
|
149
|
+
<div style={{
|
|
150
|
+
display: 'flex',
|
|
151
|
+
flexDirection: 'column',
|
|
152
|
+
alignItems: 'center',
|
|
153
|
+
justifyContent: 'center',
|
|
154
|
+
minHeight: '100vh',
|
|
155
|
+
width: '100%',
|
|
156
|
+
fontFamily: 'system-ui, sans-serif',
|
|
157
|
+
backgroundColor: '#0f172a',
|
|
158
|
+
color: '#f8fafc',
|
|
159
|
+
textAlign: 'center',
|
|
160
|
+
margin: 0,
|
|
161
|
+
padding: 0
|
|
162
|
+
}}>
|
|
163
|
+
<h1 style={{ fontSize: '3.5rem', marginBottom: '0.5rem', letterSpacing: '-0.025em' }}>Tumemo CLI</h1>
|
|
164
|
+
<p style={{ fontSize: '1.25rem', color: '#94a3b8' }}>
|
|
165
|
+
Projeto: <strong style={{ color: '#38bdf8' }}>${projectName}</strong>
|
|
166
|
+
</p>
|
|
167
|
+
<div style={{
|
|
168
|
+
marginTop: '2.5rem',
|
|
169
|
+
padding: '1.5rem 3rem',
|
|
170
|
+
borderRadius: '12px',
|
|
171
|
+
backgroundColor: '#1e293b',
|
|
172
|
+
border: '1px solid #334155',
|
|
173
|
+
boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1)'
|
|
174
|
+
}}>
|
|
175
|
+
<p style={{ margin: 0 }}>Stack: <span style={{ color: '#60a5fa', fontWeight: 'bold' }}>${techStack}</span></p>
|
|
176
|
+
</div>
|
|
177
|
+
<footer style={{ marginTop: '3rem', fontSize: '0.875rem', color: '#475569' }}>
|
|
178
|
+
Edite o <code>src/App.tsx</code> para começar.
|
|
179
|
+
</footer>
|
|
180
|
+
</div>
|
|
181
|
+
)
|
|
182
|
+
}`;
|
|
183
|
+
fs.writeFileSync(path.join(root, 'src/App.tsx'), appContent.trim());
|
|
184
|
+
|
|
112
185
|
const mainContent = `
|
|
113
186
|
import React from 'react'
|
|
114
187
|
import ReactDOM from 'react-dom/client'
|
|
@@ -118,32 +191,17 @@ ${useTailwind ? "import '@/shared/tailwind.css'" : ""}
|
|
|
118
191
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
119
192
|
<React.StrictMode>
|
|
120
193
|
<App />
|
|
121
|
-
</React.StrictMode
|
|
194
|
+
</React.StrictMode>
|
|
122
195
|
)`;
|
|
123
|
-
|
|
196
|
+
fs.writeFileSync(path.join(root, 'src/main.tsx'), mainContent.trim());
|
|
124
197
|
|
|
125
|
-
|
|
126
|
-
const appContent = `
|
|
127
|
-
export default function App() {
|
|
128
|
-
return (
|
|
129
|
-
<div style={{ padding: '2rem', fontFamily: 'sans-serif' }}>
|
|
130
|
-
<h1>Setup Tumemo</h1>
|
|
131
|
-
<p>Projeto pronto. React + TS + Axios configurados.</p>
|
|
132
|
-
</div>
|
|
133
|
-
)
|
|
134
|
-
}
|
|
135
|
-
`;
|
|
136
|
-
await fs.writeFile(path.join(root, 'src/App.tsx'), appContent.trim());
|
|
198
|
+
['src/components', 'src/pages'].forEach(dir => fs.ensureDirSync(path.join(root, dir)));
|
|
137
199
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
for (const dir of dirs) {
|
|
141
|
-
await fs.ensureDir(path.join(root, dir));
|
|
142
|
-
}
|
|
200
|
+
log.success(`\nTumemo v1.1.0 finalizado!`);
|
|
201
|
+
await runCommand('npm', ['run', 'dev']);
|
|
143
202
|
|
|
144
|
-
log.success('Setup Tumemo finalizado! Bom trabalho.');
|
|
145
203
|
} catch (err) {
|
|
146
|
-
log.error(`
|
|
204
|
+
log.error(`Falha: ${err.message}`);
|
|
147
205
|
}
|
|
148
206
|
});
|
|
149
207
|
|