rook-cli 1.3.2 → 1.3.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/package.json +3 -2
- package/rook-framework/PRD-INSTALL-COMMAND.md +379 -0
- package/rook-framework/PRD.md +1214 -0
- package/rook-framework/README.md +143 -0
- package/rook-framework/assets/rk-accordion.js +99 -0
- package/rook-framework/assets/rk-alert-dialog.js +132 -0
- package/rook-framework/assets/rk-bottom-app-bar.js +88 -0
- package/rook-framework/assets/rk-carousel.js +145 -0
- package/rook-framework/assets/rk-collapsible.js +151 -0
- package/rook-framework/assets/rk-dialog.js +161 -0
- package/rook-framework/assets/rk-drawer.js +214 -0
- package/rook-framework/assets/rk-framework-core.css +2554 -0
- package/rook-framework/assets/rk-framework-tokens.css +101 -0
- package/rook-framework/assets/rk-modal.js +91 -0
- package/rook-framework/assets/rk-popover.js +264 -0
- package/rook-framework/assets/rk-progress.js +81 -0
- package/rook-framework/assets/rk-quantity.js +91 -0
- package/rook-framework/assets/rk-scroll-area.js +286 -0
- package/rook-framework/assets/rk-sheet.js +157 -0
- package/rook-framework/assets/rk-tabs.js +179 -0
- package/rook-framework/assets/rk-toggle.js +153 -0
- package/rook-framework/blocks/rk-accordion.liquid +97 -0
- package/rook-framework/blocks/rk-badge.liquid +103 -0
- package/rook-framework/blocks/rk-button.liquid +166 -0
- package/rook-framework/blocks/rk-divider.liquid +100 -0
- package/rook-framework/blocks/rk-form-field.liquid +120 -0
- package/rook-framework/blocks/rk-icon.liquid +134 -0
- package/rook-framework/blocks/rk-image.liquid +198 -0
- package/rook-framework/blocks/rk-installments.liquid +99 -0
- package/rook-framework/blocks/rk-pix-discount.liquid +99 -0
- package/rook-framework/blocks/rk-price.liquid +128 -0
- package/rook-framework/blocks/rk-quantity.liquid +108 -0
- package/rook-framework/blocks/rk-quick-add.liquid +137 -0
- package/rook-framework/blocks/rk-skeleton.liquid +104 -0
- package/rook-framework/blocks/rk-typography.liquid +183 -0
- package/rook-framework/config/rk-color-scheme-group.json +138 -0
- package/rook-framework/config/rk-settings_schema.json +259 -0
- package/rook-framework/snippets/rk-accordion.liquid +31 -0
- package/rook-framework/snippets/rk-alert-dialog.liquid +83 -0
- package/rook-framework/snippets/rk-aspect-ratio.liquid +23 -0
- package/rook-framework/snippets/rk-badge.liquid +17 -0
- package/rook-framework/snippets/rk-bottom-app-bar.liquid +51 -0
- package/rook-framework/snippets/rk-button.liquid +49 -0
- package/rook-framework/snippets/rk-card.liquid +64 -0
- package/rook-framework/snippets/rk-carousel.liquid +74 -0
- package/rook-framework/snippets/rk-checkbox.liquid +34 -0
- package/rook-framework/snippets/rk-collapsible.liquid +52 -0
- package/rook-framework/snippets/rk-color-schemes-standalone.liquid +61 -0
- package/rook-framework/snippets/rk-color-schemes.liquid +43 -0
- package/rook-framework/snippets/rk-dialog.liquid +85 -0
- package/rook-framework/snippets/rk-divider.liquid +25 -0
- package/rook-framework/snippets/rk-drawer.liquid +81 -0
- package/rook-framework/snippets/rk-external-assets copy.liquid +33 -0
- package/rook-framework/snippets/rk-external-assets.liquid +68 -0
- package/rook-framework/snippets/rk-form-field.liquid +83 -0
- package/rook-framework/snippets/rk-gap-style.liquid +32 -0
- package/rook-framework/snippets/rk-icon.liquid +28 -0
- package/rook-framework/snippets/rk-image.liquid +60 -0
- package/rook-framework/snippets/rk-input.liquid +35 -0
- package/rook-framework/snippets/rk-installments.liquid +54 -0
- package/rook-framework/snippets/rk-item.liquid +69 -0
- package/rook-framework/snippets/rk-layout-style.liquid +37 -0
- package/rook-framework/snippets/rk-modal.liquid +31 -0
- package/rook-framework/snippets/rk-pix-discount.liquid +34 -0
- package/rook-framework/snippets/rk-popover.liquid +77 -0
- package/rook-framework/snippets/rk-price.liquid +48 -0
- package/rook-framework/snippets/rk-progress.liquid +38 -0
- package/rook-framework/snippets/rk-quantity.liquid +56 -0
- package/rook-framework/snippets/rk-quick-add.liquid +67 -0
- package/rook-framework/snippets/rk-scripts.liquid +17 -0
- package/rook-framework/snippets/rk-scroll-area.liquid +60 -0
- package/rook-framework/snippets/rk-sheet.liquid +86 -0
- package/rook-framework/snippets/rk-size-style.liquid +48 -0
- package/rook-framework/snippets/rk-skeleton.liquid +25 -0
- package/rook-framework/snippets/rk-spacing-padding.liquid +18 -0
- package/rook-framework/snippets/rk-spacing-style.liquid +54 -0
- package/rook-framework/snippets/rk-spinner.liquid +43 -0
- package/rook-framework/snippets/rk-swatch.liquid +33 -0
- package/rook-framework/snippets/rk-table.liquid +44 -0
- package/rook-framework/snippets/rk-tabs.liquid +52 -0
- package/rook-framework/snippets/rk-textarea.liquid +42 -0
- package/rook-framework/snippets/rk-toggle-group.liquid +27 -0
- package/rook-framework/snippets/rk-toggle.liquid +58 -0
- package/rook-framework/snippets/rk-typography.liquid +27 -0
- package/rook-framework/snippets/rk-variables.liquid +76 -0
- package/src/app.js +24 -0
- package/src/commands/InstallCommand.js +133 -0
- package/src/mcp/server.js +111 -1
- package/src/services/FrameworkInstaller.js +485 -0
- package/src/templates/block.liquid.txt +0 -15
- package/src/ui/PromptUI.js +15 -1
- package/src/utils/logger.js +1 -1
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FrameworkInstaller — Serviço de instalação do Rook UI Core Framework.
|
|
3
|
+
*
|
|
4
|
+
* Encapsula toda a lógica de instalação do framework em um tema Shopify:
|
|
5
|
+
* 1. Copiar assets (CSS + JS)
|
|
6
|
+
* 2. Copiar snippets (Liquid)
|
|
7
|
+
* 3. Copiar blocks (Liquid)
|
|
8
|
+
* 4. Injetar settings no settings_schema.json
|
|
9
|
+
* 5. Patch do layout/theme.liquid
|
|
10
|
+
*
|
|
11
|
+
* Detecta automaticamente se o tema já possui um color_scheme_group:
|
|
12
|
+
* - SIM → usa rk-color-schemes.liquid (ponte para os schemes do tema)
|
|
13
|
+
* - NÃO → injeta rk-color-scheme-group.json + usa rk-color-schemes-standalone.liquid
|
|
14
|
+
*
|
|
15
|
+
* Princípio: Responsabilidade Única (SRP) — só instala o framework
|
|
16
|
+
* Princípio: Inversão de Dependência (DIP) — dependências injetadas
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import path from 'path';
|
|
20
|
+
import fs from 'fs-extra';
|
|
21
|
+
import { fileURLToPath } from 'url';
|
|
22
|
+
|
|
23
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
24
|
+
const FRAMEWORK_DIR = path.resolve(__dirname, '../../rook-framework');
|
|
25
|
+
|
|
26
|
+
/** Bloco Liquid injetado no <head> do theme.liquid */
|
|
27
|
+
const HEAD_INJECTION = `
|
|
28
|
+
{%- comment -%} Rook UI Core Framework {%- endcomment -%}
|
|
29
|
+
{%- render 'rk-color-schemes' -%}
|
|
30
|
+
{%- render 'rk-variables' -%}
|
|
31
|
+
{{ 'rk-framework-tokens.css' | asset_url | stylesheet_tag }}
|
|
32
|
+
{{ 'rk-framework-core.css' | asset_url | stylesheet_tag }}
|
|
33
|
+
{%- render 'rk-external-assets', location: 'head' -%}`;
|
|
34
|
+
|
|
35
|
+
/** Bloco Liquid injetado antes do </body> do theme.liquid */
|
|
36
|
+
const BODY_INJECTION = `
|
|
37
|
+
{%- comment -%} Rook UI Core Framework — Scripts {%- endcomment -%}
|
|
38
|
+
{%- render 'rk-external-assets', location: 'body' -%}
|
|
39
|
+
{%- render 'rk-scripts' -%}`;
|
|
40
|
+
|
|
41
|
+
/** Marcador para detectar instalação prévia */
|
|
42
|
+
const RK_MARKER = 'rk-variables';
|
|
43
|
+
|
|
44
|
+
/** Prefixo das seções no settings_schema */
|
|
45
|
+
const RK_SECTION_PREFIX = 'Rook UI';
|
|
46
|
+
|
|
47
|
+
export class FrameworkInstaller {
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {import('../utils/logger.js').Logger} logger
|
|
51
|
+
*/
|
|
52
|
+
constructor(logger) {
|
|
53
|
+
this.logger = logger;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Executa a instalação completa do framework.
|
|
58
|
+
*
|
|
59
|
+
* @param {string} themePath - Caminho raiz do tema Shopify
|
|
60
|
+
* @param {Object} [options={}]
|
|
61
|
+
* @param {boolean} [options.force=false] - Sobrescrever sem perguntar
|
|
62
|
+
* @param {boolean} [options.skipLayout=false] - Não modificar theme.liquid
|
|
63
|
+
* @param {boolean} [options.skipSettings=false] - Não modificar settings_schema.json
|
|
64
|
+
* @returns {Promise<{assets: number, snippets: number, blocks: number, settingsSections: number, layoutPatched: boolean, colorSchemeMode: string}>}
|
|
65
|
+
*/
|
|
66
|
+
async install(themePath, options = {}) {
|
|
67
|
+
const resultado = {
|
|
68
|
+
assets: 0,
|
|
69
|
+
snippets: 0,
|
|
70
|
+
blocks: 0,
|
|
71
|
+
settingsSections: 0,
|
|
72
|
+
layoutPatched: false,
|
|
73
|
+
colorSchemeMode: 'unknown',
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Detectar color_scheme_group do tema antes de copiar snippets
|
|
77
|
+
const themeHasColorSchemes = await this._themeHasColorSchemeGroup(themePath);
|
|
78
|
+
resultado.colorSchemeMode = themeHasColorSchemes ? 'bridge' : 'standalone';
|
|
79
|
+
|
|
80
|
+
if (themeHasColorSchemes) {
|
|
81
|
+
this.logger.info(' Tema possui color_scheme_group → modo ponte (reutiliza esquemas do tema)');
|
|
82
|
+
} else {
|
|
83
|
+
this.logger.info(' Tema sem color_scheme_group → modo standalone (esquemas próprios Rook UI)');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Etapa 1: Assets
|
|
87
|
+
this.logger.destaque('\n [1/5] Copiando assets...');
|
|
88
|
+
resultado.assets = await this._copyDir(
|
|
89
|
+
path.join(FRAMEWORK_DIR, 'assets'),
|
|
90
|
+
path.join(themePath, 'assets'),
|
|
91
|
+
options.force
|
|
92
|
+
);
|
|
93
|
+
this.logger.sucesso(` ${resultado.assets} arquivo(s) copiados para assets/`);
|
|
94
|
+
|
|
95
|
+
// Etapa 2: Snippets (com resolução do color-schemes correto)
|
|
96
|
+
this.logger.destaque('\n [2/5] Copiando snippets...');
|
|
97
|
+
resultado.snippets = await this._copySnippets(themePath, themeHasColorSchemes, options.force);
|
|
98
|
+
this.logger.sucesso(` ${resultado.snippets} arquivo(s) copiados para snippets/`);
|
|
99
|
+
|
|
100
|
+
// Etapa 3: Blocks
|
|
101
|
+
this.logger.destaque('\n [3/5] Copiando blocks...');
|
|
102
|
+
resultado.blocks = await this._copyDir(
|
|
103
|
+
path.join(FRAMEWORK_DIR, 'blocks'),
|
|
104
|
+
path.join(themePath, 'blocks'),
|
|
105
|
+
options.force
|
|
106
|
+
);
|
|
107
|
+
this.logger.sucesso(` ${resultado.blocks} arquivo(s) copiados para blocks/`);
|
|
108
|
+
|
|
109
|
+
// Etapa 4: Settings (com injeção condicional do color_scheme_group)
|
|
110
|
+
if (!options.skipSettings) {
|
|
111
|
+
this.logger.destaque('\n [4/5] Injetando settings no settings_schema.json...');
|
|
112
|
+
resultado.settingsSections = await this._injectSettings(themePath, themeHasColorSchemes);
|
|
113
|
+
this.logger.sucesso(` ${resultado.settingsSections} seção(ões) Rook UI injetadas`);
|
|
114
|
+
} else {
|
|
115
|
+
this.logger.sutil('[4/5] Pulando settings (--skip-settings)');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Etapa 5: Layout
|
|
119
|
+
if (!options.skipLayout) {
|
|
120
|
+
this.logger.destaque('\n [5/5] Configurando layout/theme.liquid...');
|
|
121
|
+
resultado.layoutPatched = await this._patchThemeLayout(themePath);
|
|
122
|
+
if (resultado.layoutPatched) {
|
|
123
|
+
this.logger.sucesso(' theme.liquid atualizado com sucesso');
|
|
124
|
+
} else {
|
|
125
|
+
this.logger.sutil('theme.liquid já contém o Rook UI, pulando');
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
this.logger.sutil('[5/5] Pulando layout (--skip-layout)');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return resultado;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Valida que o diretório é um tema Shopify.
|
|
136
|
+
*
|
|
137
|
+
* @param {string} themePath
|
|
138
|
+
* @returns {Promise<boolean>}
|
|
139
|
+
*/
|
|
140
|
+
async validate(themePath) {
|
|
141
|
+
const indicators = [
|
|
142
|
+
'config/settings_schema.json',
|
|
143
|
+
'layout/theme.liquid',
|
|
144
|
+
'snippets',
|
|
145
|
+
'sections',
|
|
146
|
+
'templates',
|
|
147
|
+
];
|
|
148
|
+
|
|
149
|
+
let found = 0;
|
|
150
|
+
for (const indicator of indicators) {
|
|
151
|
+
if (await fs.pathExists(path.join(themePath, indicator))) {
|
|
152
|
+
found++;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return found >= 3;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Detecta se o Rook UI já está instalado.
|
|
161
|
+
*
|
|
162
|
+
* @param {string} themePath
|
|
163
|
+
* @returns {Promise<boolean>}
|
|
164
|
+
*/
|
|
165
|
+
async isAlreadyInstalled(themePath) {
|
|
166
|
+
const checks = [
|
|
167
|
+
path.join(themePath, 'snippets', 'rk-variables.liquid'),
|
|
168
|
+
path.join(themePath, 'assets', 'rk-framework-core.css'),
|
|
169
|
+
];
|
|
170
|
+
|
|
171
|
+
for (const check of checks) {
|
|
172
|
+
if (await fs.pathExists(check)) {
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Verificar settings_schema.json
|
|
178
|
+
try {
|
|
179
|
+
const settingsPath = path.join(themePath, 'config', 'settings_schema.json');
|
|
180
|
+
if (await fs.pathExists(settingsPath)) {
|
|
181
|
+
const schema = JSON.parse(await fs.readFile(settingsPath, 'utf8'));
|
|
182
|
+
return schema.some(s => s.name && s.name.startsWith(RK_SECTION_PREFIX));
|
|
183
|
+
}
|
|
184
|
+
} catch {
|
|
185
|
+
// Ignora erro de leitura
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Detecta informações do tema (nome e versão).
|
|
193
|
+
*
|
|
194
|
+
* @param {string} themePath
|
|
195
|
+
* @returns {Promise<{name: string, version: string}|null>}
|
|
196
|
+
*/
|
|
197
|
+
async detectThemeInfo(themePath) {
|
|
198
|
+
try {
|
|
199
|
+
const settingsPath = path.join(themePath, 'config', 'settings_schema.json');
|
|
200
|
+
const schema = JSON.parse(await fs.readFile(settingsPath, 'utf8'));
|
|
201
|
+
const themeInfo = schema.find(s => s.name === 'theme_info');
|
|
202
|
+
|
|
203
|
+
if (themeInfo) {
|
|
204
|
+
return {
|
|
205
|
+
name: themeInfo.theme_name || 'Desconhecido',
|
|
206
|
+
version: themeInfo.theme_version || '?',
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
} catch {
|
|
210
|
+
// Ignora
|
|
211
|
+
}
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Verifica se o tema já possui um color_scheme_group no settings_schema.json.
|
|
217
|
+
*
|
|
218
|
+
* @param {string} themePath
|
|
219
|
+
* @returns {Promise<boolean>}
|
|
220
|
+
* @private
|
|
221
|
+
*/
|
|
222
|
+
async _themeHasColorSchemeGroup(themePath) {
|
|
223
|
+
try {
|
|
224
|
+
const settingsPath = path.join(themePath, 'config', 'settings_schema.json');
|
|
225
|
+
if (!await fs.pathExists(settingsPath)) return false;
|
|
226
|
+
|
|
227
|
+
const schema = JSON.parse(await fs.readFile(settingsPath, 'utf8'));
|
|
228
|
+
|
|
229
|
+
for (const section of schema) {
|
|
230
|
+
if (!section.settings) continue;
|
|
231
|
+
for (const setting of section.settings) {
|
|
232
|
+
if (setting.type === 'color_scheme_group') {
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
} catch {
|
|
238
|
+
// Ignora
|
|
239
|
+
}
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Copia snippets com resolução inteligente do rk-color-schemes.
|
|
245
|
+
*
|
|
246
|
+
* - Tema COM color_scheme_group → copia rk-color-schemes.liquid (ponte)
|
|
247
|
+
* - Tema SEM color_scheme_group → copia rk-color-schemes-standalone.liquid como rk-color-schemes.liquid
|
|
248
|
+
*
|
|
249
|
+
* Exclui o arquivo alternativo que não foi usado.
|
|
250
|
+
*
|
|
251
|
+
* @param {string} themePath
|
|
252
|
+
* @param {boolean} themeHasColorSchemes
|
|
253
|
+
* @param {boolean} force
|
|
254
|
+
* @returns {Promise<number>}
|
|
255
|
+
* @private
|
|
256
|
+
*/
|
|
257
|
+
async _copySnippets(themePath, themeHasColorSchemes, force) {
|
|
258
|
+
const srcDir = path.join(FRAMEWORK_DIR, 'snippets');
|
|
259
|
+
const destDir = path.join(themePath, 'snippets');
|
|
260
|
+
|
|
261
|
+
// Arquivos a ignorar na cópia genérica
|
|
262
|
+
const skipFiles = new Set([
|
|
263
|
+
'rk-color-schemes.liquid',
|
|
264
|
+
'rk-color-schemes-standalone.liquid',
|
|
265
|
+
]);
|
|
266
|
+
|
|
267
|
+
// Copiar todos os snippets exceto os dois de color-schemes
|
|
268
|
+
const count = await this._copyDir(srcDir, destDir, force, (filename) => {
|
|
269
|
+
if (filename.includes(' copy')) return false;
|
|
270
|
+
if (skipFiles.has(filename)) return false;
|
|
271
|
+
return true;
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
// Copiar a versão correta como rk-color-schemes.liquid
|
|
275
|
+
const sourceFile = themeHasColorSchemes
|
|
276
|
+
? 'rk-color-schemes.liquid' // Ponte para schemes do tema
|
|
277
|
+
: 'rk-color-schemes-standalone.liquid'; // Schemes próprios do Rook UI
|
|
278
|
+
|
|
279
|
+
const srcPath = path.join(srcDir, sourceFile);
|
|
280
|
+
const destPath = path.join(destDir, 'rk-color-schemes.liquid');
|
|
281
|
+
|
|
282
|
+
if (await fs.pathExists(srcPath)) {
|
|
283
|
+
await fs.copy(srcPath, destPath, { overwrite: true });
|
|
284
|
+
this.logger.sutil(`→ snippets/rk-color-schemes.liquid (${themeHasColorSchemes ? 'ponte' : 'standalone'})`);
|
|
285
|
+
return count + 1;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return count;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Copia todos os arquivos de um diretório para outro.
|
|
293
|
+
*
|
|
294
|
+
* @param {string} srcDir - Diretório de origem
|
|
295
|
+
* @param {string} destDir - Diretório de destino
|
|
296
|
+
* @param {boolean} force - Sobrescrever sem perguntar
|
|
297
|
+
* @param {Function} [filter] - Função de filtro (recebe filename, retorna boolean)
|
|
298
|
+
* @returns {Promise<number>} Quantidade de arquivos copiados
|
|
299
|
+
* @private
|
|
300
|
+
*/
|
|
301
|
+
async _copyDir(srcDir, destDir, force = false, filter = null) {
|
|
302
|
+
if (!await fs.pathExists(srcDir)) {
|
|
303
|
+
return 0;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
await fs.ensureDir(destDir);
|
|
307
|
+
|
|
308
|
+
const files = await fs.readdir(srcDir);
|
|
309
|
+
let count = 0;
|
|
310
|
+
|
|
311
|
+
for (const file of files) {
|
|
312
|
+
if (filter && !filter(file)) {
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const srcFile = path.join(srcDir, file);
|
|
317
|
+
const destFile = path.join(destDir, file);
|
|
318
|
+
const stat = await fs.stat(srcFile);
|
|
319
|
+
|
|
320
|
+
if (!stat.isFile()) {
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Se o arquivo já existe e não é force, pular
|
|
325
|
+
if (!force && await fs.pathExists(destFile)) {
|
|
326
|
+
// Em modo não-force, sobrescreve mesmo assim para garantir atualização
|
|
327
|
+
// O ConflictResolver é usado no nível do comando, não aqui
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
await fs.copy(srcFile, destFile, { overwrite: true });
|
|
331
|
+
this.logger.sutil(`→ ${path.basename(destDir)}/${file}`);
|
|
332
|
+
count++;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return count;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Injeta as seções do Rook UI no settings_schema.json do tema.
|
|
340
|
+
*
|
|
341
|
+
* Se o tema NÃO possui color_scheme_group, injeta também o
|
|
342
|
+
* rk-color-scheme-group.json com o grupo de cores próprio do Rook UI.
|
|
343
|
+
*
|
|
344
|
+
* @param {string} themePath
|
|
345
|
+
* @param {boolean} themeHasColorSchemes
|
|
346
|
+
* @returns {Promise<number>} Quantidade de seções injetadas
|
|
347
|
+
* @private
|
|
348
|
+
*/
|
|
349
|
+
async _injectSettings(themePath, themeHasColorSchemes) {
|
|
350
|
+
const settingsPath = path.join(themePath, 'config', 'settings_schema.json');
|
|
351
|
+
const rkSettingsPath = path.join(FRAMEWORK_DIR, 'config', 'rk-settings_schema.json');
|
|
352
|
+
const rkColorGroupPath = path.join(FRAMEWORK_DIR, 'config', 'rk-color-scheme-group.json');
|
|
353
|
+
|
|
354
|
+
if (!await fs.pathExists(settingsPath)) {
|
|
355
|
+
this.logger.aviso('settings_schema.json não encontrado, pulando injeção de settings');
|
|
356
|
+
return 0;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Backup
|
|
360
|
+
const backupPath = settingsPath + '.rk-backup';
|
|
361
|
+
await fs.copy(settingsPath, backupPath, { overwrite: true });
|
|
362
|
+
this.logger.sutil(`Backup criado: settings_schema.json.rk-backup`);
|
|
363
|
+
|
|
364
|
+
try {
|
|
365
|
+
// Ler arquivos
|
|
366
|
+
let schema = JSON.parse(await fs.readFile(settingsPath, 'utf8'));
|
|
367
|
+
const rkSections = JSON.parse(await fs.readFile(rkSettingsPath, 'utf8'));
|
|
368
|
+
|
|
369
|
+
// Remover seções Rook UI existentes (para atualização limpa)
|
|
370
|
+
schema = schema.filter(s => !s.name || !s.name.startsWith(RK_SECTION_PREFIX));
|
|
371
|
+
|
|
372
|
+
// Se o tema NÃO tem color_scheme_group, injetar o do Rook UI
|
|
373
|
+
if (!themeHasColorSchemes && await fs.pathExists(rkColorGroupPath)) {
|
|
374
|
+
const colorGroup = JSON.parse(await fs.readFile(rkColorGroupPath, 'utf8'));
|
|
375
|
+
schema.push(...colorGroup);
|
|
376
|
+
this.logger.sutil('→ Injetado color_scheme_group próprio do Rook UI');
|
|
377
|
+
|
|
378
|
+
for (const section of colorGroup) {
|
|
379
|
+
this.logger.sutil(`→ ${section.name}`);
|
|
380
|
+
}
|
|
381
|
+
} else if (themeHasColorSchemes) {
|
|
382
|
+
this.logger.sutil('→ Tema já possui color_scheme_group, reaproveitando');
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Inserir seções padrão no final
|
|
386
|
+
schema.push(...rkSections);
|
|
387
|
+
|
|
388
|
+
// Salvar
|
|
389
|
+
await fs.writeFile(settingsPath, JSON.stringify(schema, null, 2), 'utf8');
|
|
390
|
+
|
|
391
|
+
// Validar JSON resultante
|
|
392
|
+
JSON.parse(await fs.readFile(settingsPath, 'utf8'));
|
|
393
|
+
|
|
394
|
+
// Log das seções inseridas
|
|
395
|
+
for (const section of rkSections) {
|
|
396
|
+
const settingsCount = section.settings?.filter(s => s.id).length || 0;
|
|
397
|
+
this.logger.sutil(`→ ${section.name} (${settingsCount} settings)`);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const totalSections = rkSections.length + (!themeHasColorSchemes ? 1 : 0);
|
|
401
|
+
return totalSections;
|
|
402
|
+
} catch (erro) {
|
|
403
|
+
// Restaurar backup em caso de erro
|
|
404
|
+
this.logger.erro(`Erro ao injetar settings: ${erro.message}`);
|
|
405
|
+
this.logger.info('Restaurando backup...');
|
|
406
|
+
await fs.copy(backupPath, settingsPath, { overwrite: true });
|
|
407
|
+
throw erro;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Modifica o layout/theme.liquid para carregar o framework.
|
|
413
|
+
*
|
|
414
|
+
* @param {string} themePath
|
|
415
|
+
* @returns {Promise<boolean>} true se modificou, false se já existia
|
|
416
|
+
* @private
|
|
417
|
+
*/
|
|
418
|
+
async _patchThemeLayout(themePath) {
|
|
419
|
+
const layoutPath = path.join(themePath, 'layout', 'theme.liquid');
|
|
420
|
+
|
|
421
|
+
if (!await fs.pathExists(layoutPath)) {
|
|
422
|
+
this.logger.aviso('layout/theme.liquid não encontrado, pulando patch');
|
|
423
|
+
return false;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Backup
|
|
427
|
+
const backupPath = layoutPath + '.rk-backup';
|
|
428
|
+
await fs.copy(layoutPath, backupPath, { overwrite: true });
|
|
429
|
+
this.logger.sutil(`Backup criado: theme.liquid.rk-backup`);
|
|
430
|
+
|
|
431
|
+
let content = await fs.readFile(layoutPath, 'utf8');
|
|
432
|
+
|
|
433
|
+
// Detectar instalação prévia
|
|
434
|
+
if (content.includes(RK_MARKER)) {
|
|
435
|
+
// Remover blocos antigos para reinstalar limpo
|
|
436
|
+
content = this._removeExistingRkBlocks(content);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Injetar no <head> — antes de {{ content_for_header }}
|
|
440
|
+
const headAnchor = '{{ content_for_header }}';
|
|
441
|
+
const headFallback = '</head>';
|
|
442
|
+
|
|
443
|
+
if (content.includes(headAnchor)) {
|
|
444
|
+
content = content.replace(headAnchor, HEAD_INJECTION + '\n\n ' + headAnchor);
|
|
445
|
+
this.logger.sutil('→ Injetado no <head>: rk-color-schemes, rk-variables, tokens, core CSS');
|
|
446
|
+
} else if (content.includes(headFallback)) {
|
|
447
|
+
content = content.replace(headFallback, HEAD_INJECTION + '\n ' + headFallback);
|
|
448
|
+
this.logger.sutil('→ Injetado antes de </head> (fallback)');
|
|
449
|
+
} else {
|
|
450
|
+
this.logger.aviso('Não foi possível encontrar ponto de injeção no <head>');
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Injetar antes do </body>
|
|
454
|
+
const bodyAnchor = '</body>';
|
|
455
|
+
|
|
456
|
+
if (content.includes(bodyAnchor)) {
|
|
457
|
+
content = content.replace(bodyAnchor, BODY_INJECTION + '\n ' + bodyAnchor);
|
|
458
|
+
this.logger.sutil('→ Injetado antes do </body>: rk-scripts');
|
|
459
|
+
} else {
|
|
460
|
+
this.logger.aviso('Não foi possível encontrar </body> no theme.liquid');
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
await fs.writeFile(layoutPath, content, 'utf8');
|
|
464
|
+
return true;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Remove blocos Rook UI existentes do theme.liquid para reinstalação limpa.
|
|
469
|
+
*
|
|
470
|
+
* @param {string} content - Conteúdo do theme.liquid
|
|
471
|
+
* @returns {string} Conteúdo limpo
|
|
472
|
+
* @private
|
|
473
|
+
*/
|
|
474
|
+
_removeExistingRkBlocks(content) {
|
|
475
|
+
// Remover bloco do <head> (detecta tanto versões com quanto sem rk-color-schemes)
|
|
476
|
+
const headBlockRegex = /\n?\s*\{%-?\s*comment\s*-?%\}\s*Rook UI Core Framework\s*\{%-?\s*endcomment\s*-?%\}[\s\S]*?\{%-?\s*render\s+'rk-external-assets'[^%]*%\}\s*\n?/g;
|
|
477
|
+
content = content.replace(headBlockRegex, '\n');
|
|
478
|
+
|
|
479
|
+
// Remover bloco do </body>
|
|
480
|
+
const bodyBlockRegex = /\n?\s*\{%-?\s*comment\s*-?%\}\s*Rook UI Core Framework — Scripts\s*\{%-?\s*endcomment\s*-?%\}[\s\S]*?\{%-?\s*render\s+'rk-scripts'\s*-?%\}\s*\n?/g;
|
|
481
|
+
content = content.replace(bodyBlockRegex, '\n');
|
|
482
|
+
|
|
483
|
+
return content;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
@@ -58,18 +58,3 @@
|
|
|
58
58
|
}
|
|
59
59
|
{% endschema %}
|
|
60
60
|
|
|
61
|
-
{% javascript %}
|
|
62
|
-
class {{PascalName}}Block extends HTMLElement {
|
|
63
|
-
connectedCallback() {
|
|
64
|
-
this.addEventListener('click', this.handleClick.bind(this));
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
handleClick() {
|
|
68
|
-
console.log('Block clicked:', this.dataset.blockId);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (!customElements.get('{{kebabName}}-block')) {
|
|
73
|
-
customElements.define('{{kebabName}}-block', {{PascalName}}Block);
|
|
74
|
-
}
|
|
75
|
-
{% endjavascript %}
|
package/src/ui/PromptUI.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Princípio: Aberto/Fechado (OCP) — fácil adicionar novos menus
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { select, checkbox, input } from '@inquirer/prompts';
|
|
11
|
+
import { select, checkbox, input, confirm } from '@inquirer/prompts';
|
|
12
12
|
|
|
13
13
|
export class PromptUI {
|
|
14
14
|
|
|
@@ -120,6 +120,20 @@ export class PromptUI {
|
|
|
120
120
|
* @param {Array<{nome: string, caminho: string}>} componentes - Componentes disponíveis
|
|
121
121
|
* @returns {Promise<Array<{nome: string, caminho: string}>>} Componentes selecionados
|
|
122
122
|
*/
|
|
123
|
+
/**
|
|
124
|
+
* Exibe uma pergunta de confirmação (sim/não).
|
|
125
|
+
*
|
|
126
|
+
* @param {string} mensagem - Pergunta a ser exibida
|
|
127
|
+
* @param {boolean} [padrao=true] - Valor padrão (true = Sim)
|
|
128
|
+
* @returns {Promise<boolean>}
|
|
129
|
+
*/
|
|
130
|
+
async confirmar(mensagem, padrao = true) {
|
|
131
|
+
return confirm({
|
|
132
|
+
message: mensagem,
|
|
133
|
+
default: padrao,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
123
137
|
async selecionarComponentes(componentes) {
|
|
124
138
|
if (componentes.length === 0) {
|
|
125
139
|
throw new Error('Nenhum componente disponível no repositório.');
|
package/src/utils/logger.js
CHANGED
|
@@ -49,7 +49,7 @@ export class Logger {
|
|
|
49
49
|
banner() {
|
|
50
50
|
console.log('');
|
|
51
51
|
console.log(pc.bold(pc.white(' ╔══════════════════════════════╗')));
|
|
52
|
-
console.log(pc.bold(pc.white(' ║ ♟️ ROOK CLI v1.3.
|
|
52
|
+
console.log(pc.bold(pc.white(' ║ ♟️ ROOK CLI v1.3.5 ║')));
|
|
53
53
|
console.log(pc.bold(pc.white(' ║ Shopify Component Tool ║')));
|
|
54
54
|
console.log(pc.bold(pc.white(' ╚══════════════════════════════╝')));
|
|
55
55
|
console.log('');
|