@perma-tools/cli 0.0.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.
- package/README.md +159 -0
- package/dist/actions.d.ts +12 -0
- package/dist/actions.js +157 -0
- package/dist/cli-parser.d.ts +21 -0
- package/dist/cli-parser.js +141 -0
- package/dist/configs/axios.config.d.ts +2 -0
- package/dist/configs/axios.config.js +12 -0
- package/dist/configs/biome.config.d.ts +2 -0
- package/dist/configs/biome.config.js +12 -0
- package/dist/configs/cookies-next.config.d.ts +2 -0
- package/dist/configs/cookies-next.config.js +6 -0
- package/dist/configs/date-fns.config.d.ts +2 -0
- package/dist/configs/date-fns.config.js +6 -0
- package/dist/configs/eslint-prettier.config.d.ts +2 -0
- package/dist/configs/eslint-prettier.config.js +35 -0
- package/dist/configs/framer-motion.config.d.ts +2 -0
- package/dist/configs/framer-motion.config.js +6 -0
- package/dist/configs/ky.config.d.ts +2 -0
- package/dist/configs/ky.config.js +12 -0
- package/dist/configs/lucide-react.config.d.ts +2 -0
- package/dist/configs/lucide-react.config.js +6 -0
- package/dist/configs/nuqs.config.d.ts +2 -0
- package/dist/configs/nuqs.config.js +12 -0
- package/dist/configs/react-hook-form.config.d.ts +2 -0
- package/dist/configs/react-hook-form.config.js +6 -0
- package/dist/configs/recharts.config.d.ts +2 -0
- package/dist/configs/recharts.config.js +6 -0
- package/dist/configs/tanstack-query.config.d.ts +2 -0
- package/dist/configs/tanstack-query.config.js +19 -0
- package/dist/configs/zod.config.d.ts +2 -0
- package/dist/configs/zod.config.js +6 -0
- package/dist/dependency-versions.d.ts +13 -0
- package/dist/dependency-versions.js +50 -0
- package/dist/git-setup.d.ts +23 -0
- package/dist/git-setup.js +275 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +537 -0
- package/dist/project-generator.d.ts +15 -0
- package/dist/project-generator.js +311 -0
- package/dist/snippet-manager.d.ts +11 -0
- package/dist/snippet-manager.js +76 -0
- package/dist/snippets/axios/index.ts +6 -0
- package/dist/snippets/biome/biome.json +93 -0
- package/dist/snippets/eslint-prettier/eslintignore +6 -0
- package/dist/snippets/eslint-prettier/eslintrc.js +3 -0
- package/dist/snippets/eslint-prettier/prettierignore +9 -0
- package/dist/snippets/eslint-prettier/prettierrc.js +9 -0
- package/dist/snippets/ky/index.ts +5 -0
- package/dist/snippets/react-query/index.tsx +53 -0
- package/dist/snippets/vscode/extensions.json +3 -0
- package/dist/snippets/vscode/settings.json +19 -0
- package/dist/templates/frontend/next15/base/.env.example +3 -0
- package/dist/templates/frontend/next15/base/README.md +50 -0
- package/dist/templates/frontend/next15/base/next.config.ts +7 -0
- package/dist/templates/frontend/next15/base/package.json +23 -0
- package/dist/templates/frontend/next15/base/postcss.config.mjs +8 -0
- package/dist/templates/frontend/next15/base/public/public.md +12 -0
- package/dist/templates/frontend/next15/base/src/@types/@types.md +7 -0
- package/dist/templates/frontend/next15/base/src/@types/types.d.ts +1 -0
- package/dist/templates/frontend/next15/base/src/app/(application)/page.tsx +50 -0
- package/dist/templates/frontend/next15/base/src/app/favicon.ico +0 -0
- package/dist/templates/frontend/next15/base/src/app/layout.tsx +23 -0
- package/dist/templates/frontend/next15/base/src/app/providers.tsx +5 -0
- package/dist/templates/frontend/next15/base/src/components/components.md +9 -0
- package/dist/templates/frontend/next15/base/src/constants/constants.md +10 -0
- package/dist/templates/frontend/next15/base/src/hooks/hooks.md +11 -0
- package/dist/templates/frontend/next15/base/src/lib/lib.md +9 -0
- package/dist/templates/frontend/next15/base/src/services/services.md +14 -0
- package/dist/templates/frontend/next15/base/src/styles/globals.css +1 -0
- package/dist/templates/frontend/next15/base/src/types/types.md +15 -0
- package/dist/templates/frontend/next15/base/src/utils/utils.md +18 -0
- package/dist/templates/frontend/next15/base/tsconfig.json +26 -0
- package/dist/types.d.ts +60 -0
- package/dist/types.js +1 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# @perma-tools/cli
|
|
2
|
+
|
|
3
|
+
š CLI moderna e interativa para geração rĆ”pida de projetos frontend e backend com configuraƧƵes personalizadas.
|
|
4
|
+
|
|
5
|
+
## š¦ Instalação
|
|
6
|
+
|
|
7
|
+
### Uso com npx (Recomendado - sem instalação)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx @perma-tools/cli@latest
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Instalação Global
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g @perma-tools/cli
|
|
17
|
+
# ou
|
|
18
|
+
pnpm add -g @perma-tools/cli
|
|
19
|
+
# ou
|
|
20
|
+
yarn global add @perma-tools/cli
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## š Uso RĆ”pido
|
|
24
|
+
|
|
25
|
+
### Modo Interativo
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx @perma-tools/cli@latest
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
A CLI irƔ guiƔ-lo atravƩs de perguntas interativas para configurar seu projeto.
|
|
32
|
+
|
|
33
|
+
### Modo CLI com Flags
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx @perma-tools/cli@latest \
|
|
37
|
+
--name meu-app \
|
|
38
|
+
--type frontend \
|
|
39
|
+
--framework nextjs-15 \
|
|
40
|
+
--http-client ky \
|
|
41
|
+
--linter biome \
|
|
42
|
+
--libs tanstack-query,zod,react-hook-form \
|
|
43
|
+
--with-shadcn \
|
|
44
|
+
--shadcn-color slate \
|
|
45
|
+
--install
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## ⨠CaracterĆsticas
|
|
49
|
+
|
|
50
|
+
- ā
Suporte completo para **Frontend** (NextJS 15) e **Backend** (em desenvolvimento)
|
|
51
|
+
- ā
Escolha entre **ky**, **axios** ou **fetch** nativo como cliente HTTP
|
|
52
|
+
- ā
Configuração automÔtica de **Biome** ou **ESLint + Prettier**
|
|
53
|
+
- ā
Instalação e configuração de bibliotecas populares com **versƵes especĆficas testadas**
|
|
54
|
+
- ā
Integração completa com **shadcn/ui**
|
|
55
|
+
- ā
Sistema de **providers** com injeção automÔtica robusta
|
|
56
|
+
- ā
**Criação automÔtica de repositório no GitHub** com GitHub CLI
|
|
57
|
+
- ā
**Deploy automƔtico na Vercel** para projetos frontend
|
|
58
|
+
- ā
Instalação automÔtica de dependências
|
|
59
|
+
- ā
Validação completa de nomes de projetos (seguindo regras do npm)
|
|
60
|
+
- ā
Verificação de projetos existentes para evitar sobrescritas
|
|
61
|
+
- ā
Compatibilidade total com Windows, Linux e macOS
|
|
62
|
+
|
|
63
|
+
## š Git & Deploy AutomĆ”tico
|
|
64
|
+
|
|
65
|
+
A CLI oferece configuração automĆ”tica de repositório e deploy, adaptando-se automaticamente Ć s ferramentas disponĆveis no seu sistema:
|
|
66
|
+
|
|
67
|
+
### PrƩ-requisitos
|
|
68
|
+
|
|
69
|
+
- **GitHub CLI** (`gh`): [Instalar e autenticar](https://cli.github.com/)
|
|
70
|
+
- **Obrigatório** para criação de repositórios
|
|
71
|
+
- Se não estiver instalado, a opção de Git setup serÔ ocultada
|
|
72
|
+
- **Vercel CLI** (opcional): `npm i -g vercel && vercel login`
|
|
73
|
+
- **NecessƔrio** apenas para deploy automƔtico
|
|
74
|
+
- Se não estiver instalado, apenas a opção "criar repo" serÔ mostrada
|
|
75
|
+
|
|
76
|
+
### Detecção AutomÔtica de Ferramentas
|
|
77
|
+
|
|
78
|
+
A CLI detecta automaticamente quais ferramentas estĆ£o instaladas e **ajusta as opƧƵes disponĆveis**:
|
|
79
|
+
|
|
80
|
+
| GitHub CLI | Vercel CLI | OpƧƵes Mostradas |
|
|
81
|
+
|------------|------------|------------------|
|
|
82
|
+
| ā
Instalado | ā
Instalado | Repo + Deploy, Repo only, Nenhum |
|
|
83
|
+
| ā
Instalado | ā NĆ£o instalado | Repo only, Nenhum |
|
|
84
|
+
| ā NĆ£o instalado | ā
/ā Qualquer | Nenhum (pula essa etapa) |
|
|
85
|
+
|
|
86
|
+
**Comportamento inteligente:**
|
|
87
|
+
- Se você **não tem GitHub CLI**, a CLI pula completamente a configuração de Git
|
|
88
|
+
- Se você **só tem GitHub CLI**, mostra apenas a opção de criar repositório
|
|
89
|
+
- Se você **tem ambas** e é um projeto **frontend**, mostra todas as opções
|
|
90
|
+
|
|
91
|
+
### OpƧƵes DisponĆveis
|
|
92
|
+
|
|
93
|
+
1. **Criar repo e deploy na Vercel**: Cria repositório no GitHub e configura deploy automÔtico (frontend apenas, requer ambas CLIs)
|
|
94
|
+
2. **Apenas criar repo**: Cria repositório no GitHub sem deploy (requer apenas GitHub CLI)
|
|
95
|
+
3. **Não configurar agora**: Pula a configuração (pode fazer manualmente depois)
|
|
96
|
+
|
|
97
|
+
### Criação em Organizações
|
|
98
|
+
|
|
99
|
+
A CLI detecta automaticamente suas organizações do GitHub e permite escolher onde criar o repositório:
|
|
100
|
+
|
|
101
|
+
- **Perfil Pessoal**: `github.com/seu-usuario/repo`
|
|
102
|
+
- **Organização**: `github.com/sua-org/repo`
|
|
103
|
+
|
|
104
|
+
Se você tiver acesso a múltiplas organizações, poderÔ selecionÔ-las interativamente.
|
|
105
|
+
|
|
106
|
+
### Como Funciona
|
|
107
|
+
|
|
108
|
+
Após criar o projeto, a CLI:
|
|
109
|
+
- š§ Inicializa o repositório Git local
|
|
110
|
+
- š Pergunta onde criar (perfil pessoal ou organização)
|
|
111
|
+
- š¢ Lista suas organizaƧƵes do GitHub (se aplicĆ”vel)
|
|
112
|
+
- š¦ Cria repositório no GitHub (pĆŗblico ou privado)
|
|
113
|
+
- š Faz push do código inicial
|
|
114
|
+
- ⳠAguarda o GitHub processar o repositório (3 segundos)
|
|
115
|
+
- ā” Configura deploy na Vercel (se selecionado)
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# A CLI faz tudo isso automaticamente:
|
|
119
|
+
git init
|
|
120
|
+
git add .
|
|
121
|
+
git commit -m "chore: initial commit from @perma-tools/cli"
|
|
122
|
+
gh repo create my-org/my-repo --public --source . --push # Com org
|
|
123
|
+
# ou
|
|
124
|
+
gh repo create my-repo --public --source . --push # Perfil pessoal
|
|
125
|
+
vercel --prod --yes # Se deploy estiver habilitado
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## šļø Estrutura do Projeto Gerado
|
|
129
|
+
|
|
130
|
+
### Providers e Client Components
|
|
131
|
+
|
|
132
|
+
Quando vocĆŖ seleciona bibliotecas que usam Context API (TanStack Query, nuqs, etc.), a CLI:
|
|
133
|
+
|
|
134
|
+
1. Cria um arquivo `src/app/providers.tsx` com a diretiva `'use client'`
|
|
135
|
+
2. Injeta automaticamente os providers necessƔrios
|
|
136
|
+
3. MantƩm a estrutura aninhada corretamente
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
'use client';
|
|
140
|
+
|
|
141
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
142
|
+
return (
|
|
143
|
+
<>
|
|
144
|
+
<QueryClientProvider>
|
|
145
|
+
<NuqsAdapter>
|
|
146
|
+
{children}
|
|
147
|
+
</NuqsAdapter>
|
|
148
|
+
</QueryClientProvider>
|
|
149
|
+
</>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Por que 'use client'?**
|
|
155
|
+
Providers que usam hooks do React (useState, useContext, etc.) precisam ser Client Components no Next.js 15.
|
|
156
|
+
|
|
157
|
+
## š LicenƧa
|
|
158
|
+
|
|
159
|
+
MIT
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SnippetAction } from "./types.js";
|
|
2
|
+
export declare class ActionExecutor {
|
|
3
|
+
private projectPath;
|
|
4
|
+
private rootPath;
|
|
5
|
+
private packageManager;
|
|
6
|
+
constructor(projectPath: string, rootPath?: string, packageManager?: string);
|
|
7
|
+
execute(action: SnippetAction): Promise<void>;
|
|
8
|
+
private executeCopyFile;
|
|
9
|
+
private executeInjectProvider;
|
|
10
|
+
private executeInstallDeps;
|
|
11
|
+
private executeRunCommand;
|
|
12
|
+
}
|
package/dist/actions.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { copyFile, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
export class ActionExecutor {
|
|
5
|
+
projectPath;
|
|
6
|
+
rootPath;
|
|
7
|
+
packageManager;
|
|
8
|
+
constructor(projectPath, rootPath = process.cwd(), packageManager = "pnpm") {
|
|
9
|
+
this.projectPath = projectPath;
|
|
10
|
+
this.rootPath = rootPath;
|
|
11
|
+
this.packageManager = packageManager;
|
|
12
|
+
}
|
|
13
|
+
async execute(action) {
|
|
14
|
+
switch (action.type) {
|
|
15
|
+
case "copy-file":
|
|
16
|
+
await this.executeCopyFile(action);
|
|
17
|
+
break;
|
|
18
|
+
case "inject-provider":
|
|
19
|
+
await this.executeInjectProvider(action);
|
|
20
|
+
break;
|
|
21
|
+
case "modify-file":
|
|
22
|
+
// TODO: implementar
|
|
23
|
+
break;
|
|
24
|
+
case "install-deps":
|
|
25
|
+
await this.executeInstallDeps(action);
|
|
26
|
+
break;
|
|
27
|
+
case "run-command":
|
|
28
|
+
await this.executeRunCommand(action);
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async executeCopyFile(action) {
|
|
33
|
+
const sourcePath = join(this.rootPath, action.source);
|
|
34
|
+
const destPath = join(this.projectPath, action.destination);
|
|
35
|
+
// Criar diretório de destino se não existir
|
|
36
|
+
await mkdir(dirname(destPath), { recursive: true });
|
|
37
|
+
// Copiar arquivo
|
|
38
|
+
await copyFile(sourcePath, destPath);
|
|
39
|
+
console.log(`ā
Arquivo copiado: ${action.destination}`);
|
|
40
|
+
}
|
|
41
|
+
async executeInjectProvider(action) {
|
|
42
|
+
const providersPath = join(this.projectPath, "src/app/providers.tsx");
|
|
43
|
+
try {
|
|
44
|
+
let content = await readFile(providersPath, "utf-8");
|
|
45
|
+
// 1. Adicionar import no topo do arquivo
|
|
46
|
+
if (!content.includes(action.providerImport)) {
|
|
47
|
+
// Encontrar a posição após o último import ou após 'use client'
|
|
48
|
+
const lines = content.split("\n");
|
|
49
|
+
let lastImportIndex = -1;
|
|
50
|
+
let useClientIndex = -1;
|
|
51
|
+
for (let i = 0; i < lines.length; i++) {
|
|
52
|
+
const line = lines[i].trim();
|
|
53
|
+
// Detectar 'use client'
|
|
54
|
+
if (line === "'use client';" || line === '"use client";') {
|
|
55
|
+
useClientIndex = i;
|
|
56
|
+
}
|
|
57
|
+
// Detectar imports
|
|
58
|
+
if (line.startsWith("import ") || line.startsWith("import{")) {
|
|
59
|
+
lastImportIndex = i;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (lastImportIndex >= 0) {
|
|
63
|
+
// Inserir após o último import
|
|
64
|
+
lines.splice(lastImportIndex + 1, 0, action.providerImport);
|
|
65
|
+
}
|
|
66
|
+
else if (useClientIndex >= 0) {
|
|
67
|
+
// Se não hÔ imports mas tem 'use client', inserir após linha vazia
|
|
68
|
+
let insertIndex = useClientIndex + 1;
|
|
69
|
+
// Pular linhas vazias após 'use client'
|
|
70
|
+
while (insertIndex < lines.length &&
|
|
71
|
+
lines[insertIndex].trim() === "") {
|
|
72
|
+
insertIndex++;
|
|
73
|
+
}
|
|
74
|
+
lines.splice(insertIndex, 0, action.providerImport);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
// Inserir no inĆcio se nĆ£o hĆ” nem imports nem 'use client'
|
|
78
|
+
lines.unshift(action.providerImport);
|
|
79
|
+
}
|
|
80
|
+
content = lines.join("\n");
|
|
81
|
+
}
|
|
82
|
+
// 2. Envolver o children com o provider
|
|
83
|
+
// Verificar se jĆ” tem este provider
|
|
84
|
+
if (content.includes(`<${action.providerWrapper}`)) {
|
|
85
|
+
console.log(`ā ļø Provider ${action.providerWrapper} jĆ” existe, pulando...`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
// Estratégia simples: encontrar {children} com sua indentação e wrappear
|
|
89
|
+
if (!content.includes("{children}")) {
|
|
90
|
+
throw new Error("NĆ£o foi possĆvel encontrar '{children}' no arquivo");
|
|
91
|
+
}
|
|
92
|
+
// Captura a linha inteira com {children}, incluindo indentação
|
|
93
|
+
const childrenLineRegex = /^(\s*)(\{children\})$/m;
|
|
94
|
+
const match = content.match(childrenLineRegex);
|
|
95
|
+
if (!match) {
|
|
96
|
+
throw new Error("NĆ£o foi possĆvel encontrar {children} em sua própria linha");
|
|
97
|
+
}
|
|
98
|
+
const indent = match[1]; // indentação atual de {children}
|
|
99
|
+
// Substituir {children} com provider ao redor, mantendo indentação consistente
|
|
100
|
+
content = content.replace(childrenLineRegex, `${indent}<${action.providerWrapper}>\n${indent} {children}\n${indent}</${action.providerWrapper}>`);
|
|
101
|
+
await writeFile(providersPath, content, "utf-8");
|
|
102
|
+
console.log(`ā
Provider injetado: ${action.providerWrapper}`);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
console.error(`ā Erro ao injetar provider ${action.providerWrapper}:`, error);
|
|
106
|
+
console.error(`š” VocĆŖ pode precisar adicionar manualmente o provider ${action.providerWrapper}`);
|
|
107
|
+
// NĆ£o fazer throw - continuar mesmo se falhar
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async executeInstallDeps(action) {
|
|
111
|
+
console.log(`\nš¦ Instalando dependĆŖncias com ${this.packageManager}...`);
|
|
112
|
+
return new Promise((resolve, reject) => {
|
|
113
|
+
const command = this.packageManager;
|
|
114
|
+
const args = ["install"];
|
|
115
|
+
const child = spawn(command, args, {
|
|
116
|
+
cwd: this.projectPath,
|
|
117
|
+
stdio: "inherit",
|
|
118
|
+
shell: true,
|
|
119
|
+
});
|
|
120
|
+
child.on("close", (code) => {
|
|
121
|
+
if (code === 0) {
|
|
122
|
+
console.log(`ā
DependĆŖncias instaladas com sucesso!`);
|
|
123
|
+
resolve();
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
reject(new Error(`Erro ao instalar dependências (código ${code})`));
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
child.on("error", (error) => {
|
|
130
|
+
reject(error);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
async executeRunCommand(action) {
|
|
135
|
+
console.log(`\nāļø Executando: ${action.command} ${action.args?.join(" ") || ""}...`);
|
|
136
|
+
return new Promise((resolve, reject) => {
|
|
137
|
+
const args = action.args || [];
|
|
138
|
+
const child = spawn(action.command, args, {
|
|
139
|
+
cwd: this.projectPath,
|
|
140
|
+
stdio: "inherit",
|
|
141
|
+
shell: true,
|
|
142
|
+
});
|
|
143
|
+
child.on("close", (code) => {
|
|
144
|
+
if (code === 0) {
|
|
145
|
+
console.log(`ā
Comando executado com sucesso!`);
|
|
146
|
+
resolve();
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
reject(new Error(`Erro ao executar comando (código ${code})`));
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
child.on("error", (error) => {
|
|
153
|
+
reject(error);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ProjectConfig } from "./types.js";
|
|
2
|
+
export interface CLIOptions {
|
|
3
|
+
name?: string;
|
|
4
|
+
packageManager?: "pnpm" | "yarn" | "npm";
|
|
5
|
+
type?: "frontend" | "backend";
|
|
6
|
+
framework?: "nextjs-15" | "vitejs-6";
|
|
7
|
+
httpClient?: "ky" | "axios" | "fetch";
|
|
8
|
+
linter?: "biome" | "eslint-prettier";
|
|
9
|
+
libs?: string;
|
|
10
|
+
install?: boolean;
|
|
11
|
+
withShadcn?: boolean;
|
|
12
|
+
shadcnColor?: "neutral" | "slate" | "gray" | "zinc" | "stone";
|
|
13
|
+
gitSetup?: "repo-and-deploy" | "repo-only" | "none";
|
|
14
|
+
repoVisibility?: "public" | "private";
|
|
15
|
+
repoName?: string;
|
|
16
|
+
repoOrg?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function parseArguments(): CLIOptions;
|
|
19
|
+
export declare function hasArguments(): boolean;
|
|
20
|
+
export declare function validateOptions(options: CLIOptions): string[];
|
|
21
|
+
export declare function optionsToConfig(options: CLIOptions): Partial<ProjectConfig>;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
export function parseArguments() {
|
|
4
|
+
const program = new Command();
|
|
5
|
+
program
|
|
6
|
+
.name("perma-tools")
|
|
7
|
+
.description("CLI para geração de projetos customizados")
|
|
8
|
+
.version("0.0.1");
|
|
9
|
+
program.option("-n, --name <name>", "Nome do projeto");
|
|
10
|
+
program.option("--package-manager <manager>", "Package manager (pnpm, yarn, npm)");
|
|
11
|
+
program.option("--type <type>", "Tipo de aplicação (frontend, backend)");
|
|
12
|
+
program.option("--framework <framework>", "Framework (nextjs-15, vitejs-6)");
|
|
13
|
+
program.option("--http-client <client>", "Cliente HTTP (ky, axios, fetch)");
|
|
14
|
+
program.option("--linter <linter>", "Linter (biome, eslint-prettier)");
|
|
15
|
+
program.option("--libs <libs>", "Bibliotecas adicionais separadas por vĆrgula");
|
|
16
|
+
program.option("--install", "Instalar dependĆŖncias automaticamente");
|
|
17
|
+
program.option("--with-shadcn", "Configurar shadcn/ui com componentes selecionƔveis");
|
|
18
|
+
program.option("--shadcn-color <color>", "Cor base do shadcn/ui (neutral, slate, gray, zinc, stone)");
|
|
19
|
+
program.option("--git-setup <setup>", "Setup do Git (repo-and-deploy, repo-only, none)");
|
|
20
|
+
program.option("--repo-visibility <visibility>", "Visibilidade do repositório (public, private)");
|
|
21
|
+
program.option("--repo-name <name>", "Nome do repositório no GitHub");
|
|
22
|
+
program.option("--repo-org <org>", "Organização do GitHub para criar o repositório");
|
|
23
|
+
program.parse(process.argv);
|
|
24
|
+
const options = program.opts();
|
|
25
|
+
return options;
|
|
26
|
+
}
|
|
27
|
+
export function hasArguments() {
|
|
28
|
+
// Verifica se hƔ argumentos alƩm do comando base
|
|
29
|
+
return process.argv.length > 2;
|
|
30
|
+
}
|
|
31
|
+
export function validateOptions(options) {
|
|
32
|
+
const errors = [];
|
|
33
|
+
if (options.packageManager &&
|
|
34
|
+
!["pnpm", "yarn", "npm"].includes(options.packageManager)) {
|
|
35
|
+
errors.push("Package manager deve ser: pnpm, yarn ou npm");
|
|
36
|
+
}
|
|
37
|
+
if (options.type && !["frontend", "backend"].includes(options.type)) {
|
|
38
|
+
errors.push("Tipo deve ser: frontend ou backend");
|
|
39
|
+
}
|
|
40
|
+
if (options.framework &&
|
|
41
|
+
!["nextjs-15", "vitejs-6"].includes(options.framework)) {
|
|
42
|
+
errors.push("Framework deve ser: nextjs-15 ou vitejs-6");
|
|
43
|
+
}
|
|
44
|
+
// Validar se ViteJS foi selecionado (ainda nĆ£o disponĆvel)
|
|
45
|
+
if (options.framework === "vitejs-6") {
|
|
46
|
+
errors.push("ā ViteJS ainda nĆ£o estĆ” disponĆvel. Use nextjs-15 ou aguarde a próxima versĆ£o.");
|
|
47
|
+
}
|
|
48
|
+
if (options.httpClient &&
|
|
49
|
+
!["ky", "axios", "fetch"].includes(options.httpClient)) {
|
|
50
|
+
errors.push("HTTP Client deve ser: ky, axios ou fetch");
|
|
51
|
+
}
|
|
52
|
+
if (options.linter &&
|
|
53
|
+
!["biome", "eslint-prettier"].includes(options.linter)) {
|
|
54
|
+
errors.push("Linter deve ser: biome ou eslint-prettier");
|
|
55
|
+
}
|
|
56
|
+
if (options.shadcnColor &&
|
|
57
|
+
!["neutral", "slate", "gray", "zinc", "stone"].includes(options.shadcnColor)) {
|
|
58
|
+
errors.push("Cor do shadcn deve ser: neutral, slate, gray, zinc ou stone");
|
|
59
|
+
}
|
|
60
|
+
if (options.gitSetup &&
|
|
61
|
+
!["repo-and-deploy", "repo-only", "none"].includes(options.gitSetup)) {
|
|
62
|
+
errors.push("Git setup deve ser: repo-and-deploy, repo-only ou none");
|
|
63
|
+
}
|
|
64
|
+
if (options.repoVisibility &&
|
|
65
|
+
!["public", "private"].includes(options.repoVisibility)) {
|
|
66
|
+
errors.push("Visibilidade do repositório deve ser: public ou private");
|
|
67
|
+
}
|
|
68
|
+
// Validar conflito entre framework e tipo de aplicação
|
|
69
|
+
if (options.framework && options.type) {
|
|
70
|
+
const frontendFrameworks = ["nextjs-15", "vitejs-6"];
|
|
71
|
+
const backendFrameworks = ["nestjs", "honojs", "express", "fastify"];
|
|
72
|
+
const isFrontendFramework = frontendFrameworks.includes(options.framework);
|
|
73
|
+
const isBackendFramework = backendFrameworks.includes(options.framework);
|
|
74
|
+
if (isFrontendFramework && options.type === "backend") {
|
|
75
|
+
errors.push(`ā Conflito: ${options.framework} Ć© um framework de FRONTEND, mas vocĆŖ especificou tipo BACKEND`);
|
|
76
|
+
}
|
|
77
|
+
if (isBackendFramework && options.type === "frontend") {
|
|
78
|
+
errors.push(`ā Conflito: ${options.framework} Ć© um framework de BACKEND, mas vocĆŖ especificou tipo FRONTEND`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return errors;
|
|
82
|
+
}
|
|
83
|
+
export function optionsToConfig(options) {
|
|
84
|
+
const config = {};
|
|
85
|
+
if (options.name) {
|
|
86
|
+
config.projectName = options.name;
|
|
87
|
+
config.projectPath = join(process.cwd(), options.name);
|
|
88
|
+
}
|
|
89
|
+
if (options.packageManager) {
|
|
90
|
+
config.packageManager = options.packageManager;
|
|
91
|
+
}
|
|
92
|
+
// Inferir tipo de aplicação baseado no framework se não foi explicitamente passado
|
|
93
|
+
if (options.framework && !options.type) {
|
|
94
|
+
const frontendFrameworks = ["nextjs-15", "vitejs-6"];
|
|
95
|
+
const backendFrameworks = ["nestjs", "honojs", "express", "fastify"]; // preparado para futuros backends
|
|
96
|
+
if (frontendFrameworks.includes(options.framework)) {
|
|
97
|
+
config.applicationType = "frontend";
|
|
98
|
+
}
|
|
99
|
+
else if (backendFrameworks.includes(options.framework)) {
|
|
100
|
+
config.applicationType = "backend";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Se o tipo foi explicitamente passado, usa ele (tem prioridade sobre inferĆŖncia)
|
|
104
|
+
if (options.type) {
|
|
105
|
+
config.applicationType = options.type;
|
|
106
|
+
}
|
|
107
|
+
if (options.framework) {
|
|
108
|
+
config.framework = options.framework;
|
|
109
|
+
}
|
|
110
|
+
if (options.httpClient) {
|
|
111
|
+
config.httpClient = options.httpClient;
|
|
112
|
+
}
|
|
113
|
+
if (options.linter) {
|
|
114
|
+
config.linter = options.linter;
|
|
115
|
+
}
|
|
116
|
+
if (options.libs) {
|
|
117
|
+
config.additionalLibs = options.libs.split(",").map((lib) => lib.trim());
|
|
118
|
+
}
|
|
119
|
+
if (options.install !== undefined) {
|
|
120
|
+
config.shouldInstallDeps = options.install;
|
|
121
|
+
}
|
|
122
|
+
if (options.withShadcn !== undefined) {
|
|
123
|
+
config.withShadcn = options.withShadcn;
|
|
124
|
+
}
|
|
125
|
+
if (options.shadcnColor) {
|
|
126
|
+
config.shadcnColor = options.shadcnColor;
|
|
127
|
+
}
|
|
128
|
+
if (options.gitSetup) {
|
|
129
|
+
config.gitSetup = options.gitSetup;
|
|
130
|
+
}
|
|
131
|
+
if (options.repoVisibility) {
|
|
132
|
+
config.repoVisibility = options.repoVisibility;
|
|
133
|
+
}
|
|
134
|
+
if (options.repoName) {
|
|
135
|
+
config.repoName = options.repoName;
|
|
136
|
+
}
|
|
137
|
+
if (options.repoOrg) {
|
|
138
|
+
config.repoOrg = options.repoOrg;
|
|
139
|
+
}
|
|
140
|
+
return config;
|
|
141
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export const eslintPrettierConfig = {
|
|
2
|
+
name: "eslint-prettier",
|
|
3
|
+
devDependencies: [
|
|
4
|
+
"eslint",
|
|
5
|
+
"eslint-config-next",
|
|
6
|
+
"prettier",
|
|
7
|
+
"prettier-plugin-tailwindcss",
|
|
8
|
+
],
|
|
9
|
+
actions: [
|
|
10
|
+
{
|
|
11
|
+
type: "copy-file",
|
|
12
|
+
source: "snippets/eslint-prettier/eslintrc.js",
|
|
13
|
+
destination: ".eslintrc.js",
|
|
14
|
+
priority: 1,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
type: "copy-file",
|
|
18
|
+
source: "snippets/eslint-prettier/eslintignore",
|
|
19
|
+
destination: ".eslintignore",
|
|
20
|
+
priority: 1,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
type: "copy-file",
|
|
24
|
+
source: "snippets/eslint-prettier/prettierrc.js",
|
|
25
|
+
destination: ".prettierrc.js",
|
|
26
|
+
priority: 1,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
type: "copy-file",
|
|
30
|
+
source: "snippets/eslint-prettier/prettierignore",
|
|
31
|
+
destination: ".prettierignore",
|
|
32
|
+
priority: 1,
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
};
|