rook-cli 1.0.1 → 1.0.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rook-cli",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "CLI para instalar componentes Shopify de um repositório centralizado",
5
5
  "repository": {
6
6
  "type": "git",
package/src/app.js CHANGED
@@ -60,7 +60,7 @@ export class App {
60
60
  this.programa
61
61
  .name(CLI_NAME)
62
62
  .description('CLI para instalar componentes Shopify de um repositório centralizado')
63
- .version('1.0.0');
63
+ .version('1.0.1');
64
64
 
65
65
  // Comando: rook add
66
66
  this.programa
@@ -59,18 +59,75 @@ export class AddCommand {
59
59
  }
60
60
 
61
61
  /**
62
- * Fluxo de instalação de um kit completo.
62
+ * Fluxo de instalação de um kit completo (nova arquitetura de composição).
63
+ *
64
+ * Segue o padrão descrito no PRD (Seção 5.3):
65
+ * 1. Lista kits disponíveis (com dados do kit.json)
66
+ * 2. Usuário seleciona o kit
67
+ * 3. Instala cada componente referenciado no manifesto
68
+ * 4. Instala os arquivos locais exclusivos do kit
69
+ *
63
70
  * @private
64
71
  */
65
72
  async _instalarKit() {
66
- // Lista kits disponíveis
73
+ // 1. Lista kits disponíveis (já enriquecidos com dados do kit.json)
67
74
  const kits = await this.githubService.listarKits();
75
+
76
+ if (kits.length === 0) {
77
+ this.logger.aviso('Nenhum kit disponível no repositório.');
78
+ return;
79
+ }
80
+
81
+ // 2. Usuário seleciona o kit
68
82
  const kitSelecionado = await this.promptUI.selecionarKit(kits);
69
83
 
70
84
  this.logger.destaque(`\n📥 Instalando kit: ${kitSelecionado.nome}\n`);
71
85
 
72
- // Baixa e distribui
73
- await this._baixarEDistribuir(kitSelecionado.caminho, kitSelecionado.nome);
86
+ if (kitSelecionado.descricao) {
87
+ this.logger.sutil(kitSelecionado.descricao);
88
+ console.log('');
89
+ }
90
+
91
+ let totalComponentesInstalados = 0;
92
+ let totalArquivosCopiados = 0;
93
+
94
+ // 3. Instala cada componente referenciado no manifesto (kit.json)
95
+ if (kitSelecionado.componentes.length > 0) {
96
+ this.logger.destaque(`🧩 Instalando ${kitSelecionado.componentes.length} componente(s) do manifesto...\n`);
97
+
98
+ for (const nomeComponente of kitSelecionado.componentes) {
99
+ const caminhoComponente = `components/${nomeComponente}`;
100
+
101
+ try {
102
+ this.logger.info(`[${totalComponentesInstalados + 1}/${kitSelecionado.componentes.length}] ${nomeComponente}`);
103
+ const copiados = await this._baixarEDistribuir(caminhoComponente, nomeComponente);
104
+ totalArquivosCopiados += copiados;
105
+ totalComponentesInstalados++;
106
+ } catch (erro) {
107
+ this.logger.erro(`Falha ao instalar componente "${nomeComponente}": ${erro.message}`);
108
+ this.logger.aviso('Continuando com os próximos componentes...');
109
+ }
110
+ }
111
+ }
112
+
113
+ // 4. Instala os arquivos locais exclusivos do kit (ex: layout/theme.liquid, config/)
114
+ this.logger.destaque(`\n📁 Instalando arquivos base do kit "${kitSelecionado.slug}"...\n`);
115
+
116
+ try {
117
+ const copiadosKit = await this._baixarEDistribuir(kitSelecionado.caminho, `kit:${kitSelecionado.slug}`);
118
+ totalArquivosCopiados += copiadosKit;
119
+ } catch (erro) {
120
+ this.logger.erro(`Falha ao instalar arquivos base do kit: ${erro.message}`);
121
+ }
122
+
123
+ // 5. Resumo final
124
+ console.log('');
125
+ this.logger.destaque('═══════════════════════════════════════════');
126
+ this.logger.sucesso(`🎉 Kit "${kitSelecionado.nome}" instalado com sucesso!`);
127
+ this.logger.sutil(` Componentes: ${totalComponentesInstalados}/${kitSelecionado.componentes.length}`);
128
+ this.logger.sutil(` Arquivos copiados: ${totalArquivosCopiados}`);
129
+ this.logger.destaque('═══════════════════════════════════════════');
130
+ console.log('');
74
131
  }
75
132
 
76
133
  /**
@@ -84,10 +141,16 @@ export class AddCommand {
84
141
 
85
142
  this.logger.destaque(`\n📥 Instalando ${selecionados.length} componente(s)...\n`);
86
143
 
144
+ let totalArquivosCopiados = 0;
145
+
87
146
  // Baixa e distribui cada componente
88
147
  for (const componente of selecionados) {
89
- await this._baixarEDistribuir(componente.caminho, componente.nome);
148
+ const copiados = await this._baixarEDistribuir(componente.caminho, componente.nome);
149
+ totalArquivosCopiados += copiados;
90
150
  }
151
+
152
+ console.log('');
153
+ this.logger.sucesso(`🎉 ${selecionados.length} componente(s) instalado(s)! (${totalArquivosCopiados} arquivo(s) copiados)\n`);
91
154
  }
92
155
 
93
156
  /**
@@ -95,6 +158,7 @@ export class AddCommand {
95
158
  *
96
159
  * @param {string} caminhoRemoto - Caminho no repositório (ex: "components/whatsapp-btn")
97
160
  * @param {string} nomePacote - Nome do pacote para exibição nos logs
161
+ * @returns {Promise<number>} Número de arquivos copiados
98
162
  * @private
99
163
  */
100
164
  async _baixarEDistribuir(caminhoRemoto, nomePacote) {
@@ -104,14 +168,16 @@ export class AddCommand {
104
168
  try {
105
169
  await fs.ensureDir(pastaTmp);
106
170
 
107
- // Download via tiged
171
+ // Download via tiged ou GitHub API
108
172
  await this.downloadService.baixar(caminhoRemoto, pastaTmp);
109
173
 
110
174
  // Distribui nas pastas Shopify locais
111
175
  const diretorioAtual = process.cwd();
112
176
  const totalCopiados = await this.fileMapper.distribuir(pastaTmp, diretorioAtual);
113
177
 
114
- this.logger.sucesso(`\n🎉 "${nomePacote}" instalado! (${totalCopiados} arquivo(s) copiados)\n`);
178
+ this.logger.sucesso(` ✔ "${nomePacote}" ${totalCopiados} arquivo(s)`);
179
+
180
+ return totalCopiados;
115
181
 
116
182
  } finally {
117
183
  // Limpa pasta temporária
@@ -65,19 +65,66 @@ export class GitHubService {
65
65
 
66
66
  /**
67
67
  * Lista os kits disponíveis no repositório.
68
- * @returns {Promise<Array<{nome: string, caminho: string}>>} Lista de kits
68
+ * Busca o kit.json de cada kit para obter nome e descrição.
69
+ *
70
+ * @returns {Promise<Array<{nome: string, slug: string, descricao: string, componentes: string[], caminho: string}>>} Lista de kits
69
71
  */
70
72
  async listarKits() {
71
73
  this.logger.info('Buscando kits disponíveis...');
72
74
 
73
75
  const itens = await this.listarDiretorio(REMOTE_PATHS.KITS);
76
+ const pastasKit = itens.filter(item => item.type === 'dir');
74
77
 
75
- return itens
76
- .filter(item => item.type === 'dir')
77
- .map(item => ({
78
- nome: item.name,
79
- caminho: `${REMOTE_PATHS.KITS}/${item.name}`,
80
- }));
78
+ const kits = [];
79
+
80
+ for (const pasta of pastasKit) {
81
+ try {
82
+ const manifesto = await this.buscarKitManifesto(pasta.name);
83
+ kits.push({
84
+ nome: manifesto.name || pasta.name,
85
+ slug: pasta.name,
86
+ descricao: manifesto.description || '',
87
+ componentes: manifesto.components || [],
88
+ caminho: `${REMOTE_PATHS.KITS}/${pasta.name}`,
89
+ });
90
+ } catch {
91
+ // Se o kit não tem kit.json válido, lista com dados básicos
92
+ this.logger.aviso(`Kit "${pasta.name}" sem kit.json válido. Ignorando.`);
93
+ }
94
+ }
95
+
96
+ return kits;
97
+ }
98
+
99
+ /**
100
+ * Busca e parseia o manifesto (kit.json) de um kit específico.
101
+ *
102
+ * @param {string} nomeKit - Nome/slug da pasta do kit (ex: "starter-base")
103
+ * @returns {Promise<{name: string, version: string, description: string, components: string[]}>} Manifesto do kit
104
+ */
105
+ async buscarKitManifesto(nomeKit) {
106
+ const caminho = `${REMOTE_PATHS.KITS}/${nomeKit}/kit.json`;
107
+ const url = `${GITHUB_API_BASE}/${caminho}`;
108
+ const headers = await this.tokenManager.obterHeaders();
109
+
110
+ try {
111
+ const resposta = await fetch(url, { headers });
112
+
113
+ if (!resposta.ok) {
114
+ throw new Error(`kit.json não encontrado para "${nomeKit}" (HTTP ${resposta.status})`);
115
+ }
116
+
117
+ const dados = await resposta.json();
118
+
119
+ // A API do GitHub retorna o conteúdo em base64
120
+ const conteudo = Buffer.from(dados.content, 'base64').toString('utf-8');
121
+ const manifesto = JSON.parse(conteudo);
122
+
123
+ return manifesto;
124
+ } catch (erro) {
125
+ this.logger.erro(`Falha ao ler kit.json de "${nomeKit}": ${erro.message}`);
126
+ throw erro;
127
+ }
81
128
  }
82
129
 
83
130
  /**
@@ -39,24 +39,26 @@ export class PromptUI {
39
39
 
40
40
  /**
41
41
  * Exibe a lista de kits disponíveis para seleção.
42
+ * Mostra nome e descrição vindos do kit.json (manifesto).
42
43
  *
43
- * @param {Array<{nome: string, caminho: string}>} kits - Kits disponíveis
44
- * @returns {Promise<{nome: string, caminho: string}>} Kit selecionado
44
+ * @param {Array<{nome: string, slug: string, descricao: string, componentes: string[], caminho: string}>} kits - Kits disponíveis
45
+ * @returns {Promise<{nome: string, slug: string, descricao: string, componentes: string[], caminho: string}>} Kit selecionado
45
46
  */
46
47
  async selecionarKit(kits) {
47
48
  if (kits.length === 0) {
48
49
  throw new Error('Nenhum kit disponível no repositório.');
49
50
  }
50
51
 
51
- const caminho = await select({
52
+ const slug = await select({
52
53
  message: '🎯 Selecione o kit para instalar:',
53
54
  choices: kits.map(kit => ({
54
- name: `📁 ${kit.nome}`,
55
- value: kit.caminho,
55
+ name: `📁 ${kit.nome} (${kit.componentes.length} componentes)`,
56
+ value: kit.slug,
57
+ description: kit.descricao || undefined,
56
58
  })),
57
59
  });
58
60
 
59
- return kits.find(k => k.caminho === caminho);
61
+ return kits.find(k => k.slug === slug);
60
62
  }
61
63
 
62
64
  /**
@@ -48,10 +48,10 @@ export class Logger {
48
48
  */
49
49
  banner() {
50
50
  console.log('');
51
- console.log(pc.bold(pc.magenta(' ╔══════════════════════════════╗')));
52
- console.log(pc.bold(pc.magenta(' ║ 🚀 ROOK CLI v1.0.0 ║')));
53
- console.log(pc.bold(pc.magenta(' ║ Shopify Component Tool ║')));
54
- console.log(pc.bold(pc.magenta(' ╚══════════════════════════════╝')));
51
+ console.log(pc.bold(pc.white(' ╔══════════════════════════════╗')));
52
+ console.log(pc.bold(pc.white(' ║ ♟️ ROOK CLI v1.0.3 ║')));
53
+ console.log(pc.bold(pc.white(' ║ Shopify Component Tool ║')));
54
+ console.log(pc.bold(pc.white(' ╚══════════════════════════════╝')));
55
55
  console.log('');
56
56
  }
57
57