raizcode-ofc 1.9.0 → 1.10.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/compilador.js CHANGED
@@ -1,294 +1,727 @@
1
1
  #!/usr/bin/env node
2
+ 'use strict';
2
3
 
3
4
  const fs = require('fs');
4
5
  const path = require('path');
5
6
  const os = require('os');
6
7
  const http = require('http');
7
- const { execSync, exec } = require('child_process');
8
+ const Module = require('module');
9
+ const { spawn } = require('child_process');
8
10
 
11
+ const VERSAO_INTERNA = '1.10.1';
9
12
  const args = process.argv.slice(2);
13
+ const flags = new Set(args.filter((arg) => arg.startsWith('--')));
14
+ const posicionais = args.filter((arg) => !arg.startsWith('--'));
10
15
  const arquivoOuCmd = args[0];
11
16
 
12
- // ─────────────────────────────────────────
13
- // SETUP DO VSCODE
14
- // ─────────────────────────────────────────
15
- if (arquivoOuCmd === '--setup') {
16
- const extDir = path.join(os.homedir(), '.vscode', 'extensions', 'raizcode-extension');
17
+ class ErroRaizcode extends Error {
18
+ constructor(mensagem, arquivo, numeroLinha, codigoLinha = '') {
19
+ super(mensagem);
20
+ this.name = 'ErroRaizcode';
21
+ this.arquivo = arquivo;
22
+ this.numeroLinha = numeroLinha;
23
+ this.codigoLinha = codigoLinha;
24
+ }
25
+ }
26
+
27
+ function obterVersao() {
28
+ return VERSAO_INTERNA;
29
+ }
30
+
31
+ function estaNoTermux() {
32
+ return Boolean(
33
+ process.env.TERMUX_VERSION ||
34
+ process.env.PREFIX?.includes('com.termux') ||
35
+ fs.existsSync('/data/data/com.termux')
36
+ );
37
+ }
38
+
39
+ function copiarRecursivo(origem, destino) {
40
+ if (typeof fs.cpSync === 'function') {
41
+ fs.cpSync(origem, destino, { recursive: true });
42
+ return;
43
+ }
44
+
45
+ const status = fs.statSync(origem);
46
+ if (status.isDirectory()) {
47
+ fs.mkdirSync(destino, { recursive: true });
48
+ for (const item of fs.readdirSync(origem)) {
49
+ copiarRecursivo(path.join(origem, item), path.join(destino, item));
50
+ }
51
+ return;
52
+ }
53
+
54
+ fs.copyFileSync(origem, destino);
55
+ }
56
+
57
+ function encontrarPastaExtensoes() {
58
+ const home = os.homedir();
59
+ const candidatos = [
60
+ process.env.VSCODE_EXTENSIONS,
61
+ path.join(home, '.local', 'share', 'code-server', 'extensions'),
62
+ path.join(home, '.vscode-server', 'extensions'),
63
+ path.join(home, '.vscode', 'extensions')
64
+ ].filter(Boolean);
65
+
66
+ const existente = candidatos.find((pasta) => fs.existsSync(path.dirname(pasta)) || fs.existsSync(pasta));
67
+ return existente || path.join(home, '.vscode', 'extensions');
68
+ }
69
+
70
+ function instalarExtensaoVSCode() {
71
+ const extDir = path.join(encontrarPastaExtensoes(), `riquefla.raizcode-${obterVersao()}`);
72
+
17
73
  try {
18
- if (!fs.existsSync(extDir)) fs.mkdirSync(extDir, { recursive: true });
19
-
20
- // Copia arquivos necessários
21
- ['syntaxes', 'icons'].forEach(pasta => {
22
- const src = path.join(__dirname, pasta);
23
- const dest = path.join(extDir, pasta);
24
- if (fs.existsSync(src)) {
25
- fs.cpSync(src, dest, { recursive: true });
26
- }
27
- });
74
+ fs.mkdirSync(extDir, { recursive: true });
75
+
76
+ for (const pasta of ['syntaxes', 'icons']) {
77
+ const origem = path.join(__dirname, pasta);
78
+ const destino = path.join(extDir, pasta);
79
+ if (fs.existsSync(origem)) copiarRecursivo(origem, destino);
80
+ }
81
+
82
+ const gramaticaRC = './syntaxes/raizcode.tmLanguage.json';
83
+ const gramaticaRCX = './syntaxes/raizcode-estrutura.tmLanguage.json';
84
+ const gramaticaRCC = './syntaxes/raizcode-estilo.tmLanguage.json';
85
+
86
+ const grammars = [];
87
+ if (fs.existsSync(path.join(extDir, gramaticaRC))) {
88
+ grammars.push({ language: 'raizcode', scopeName: 'source.rc', path: gramaticaRC });
89
+ }
90
+ if (fs.existsSync(path.join(extDir, gramaticaRCX))) {
91
+ grammars.push({ language: 'raizcode-estrutura', scopeName: 'source.rcx', path: gramaticaRCX });
92
+ }
93
+ if (fs.existsSync(path.join(extDir, gramaticaRCC))) {
94
+ grammars.push({ language: 'raizcode-estilo', scopeName: 'source.rcc', path: gramaticaRCC });
95
+ }
28
96
 
29
- // Gera o package.json da extensão VSCode corretamente
30
97
  const pkgExtensao = {
31
- name: "raizcode-extension",
32
- displayName: "Raizcode",
33
- version: "1.8.0",
34
- publisher: "raizcode",
35
- engines: { vscode: "^1.60.0" },
98
+ name: 'raizcode-extension',
99
+ displayName: 'Raizcode',
100
+ description: 'Suporte aos arquivos .rc, .rcx e .rcc',
101
+ version: obterVersao(),
102
+ publisher: 'riquefla',
103
+ engines: { vscode: '^1.60.0' },
104
+ categories: ['Programming Languages'],
36
105
  contributes: {
37
106
  languages: [
38
- {
39
- id: "raizcode",
40
- aliases: ["Raizcode", "rc"],
41
- extensions: [".rc"],
42
- icon: { dark: "./icons/rc.png", light: "./icons/rc.png" }
43
- },
44
- {
45
- id: "raizcode-estrutura",
46
- aliases: ["Raizcode Estrutura", "rcx"],
47
- extensions: [".rcx"],
48
- icon: { dark: "./icons/rcx.png", light: "./icons/rcx.png" }
49
- },
50
- {
51
- id: "raizcode-estilo",
52
- aliases: ["Raizcode Estilo", "rcc"],
53
- extensions: [".rcc"],
54
- icon: { dark: "./icons/rcc.png", light: "./icons/rcc.png" }
55
- }
107
+ { id: 'raizcode', aliases: ['Raizcode', 'rc'], extensions: ['.rc'] },
108
+ { id: 'raizcode-estrutura', aliases: ['Raizcode Estrutura', 'rcx'], extensions: ['.rcx'] },
109
+ { id: 'raizcode-estilo', aliases: ['Raizcode Estilo', 'rcc'], extensions: ['.rcc'] }
56
110
  ],
57
- grammars: [
58
- {
59
- language: "raizcode",
60
- scopeName: "source.rc",
61
- path: "./syntaxes/raizcode.tmLanguage.json"
62
- }
63
- ]
111
+ grammars,
112
+ iconThemes: fs.existsSync(path.join(extDir, 'icons', 'raizcode-icon-theme.json'))
113
+ ? [{ id: 'raizcode-icons', label: 'Raizcode Icons', path: './icons/raizcode-icon-theme.json' }]
114
+ : []
64
115
  }
65
116
  };
66
117
 
67
- fs.writeFileSync(
68
- path.join(extDir, 'package.json'),
69
- JSON.stringify(pkgExtensao, null, 2)
70
- );
118
+ fs.writeFileSync(path.join(extDir, 'package.json'), JSON.stringify(pkgExtensao, null, 2));
71
119
 
72
- console.log("✅ Extensão Raizcode instalada! Reinicie o VS Code.");
120
+ console.log('✅ Extensão Raizcode instalada.');
73
121
  console.log(`📁 Local: ${extDir}`);
74
- } catch (e) {
75
- console.error("❌ Erro no setup:", e.message);
122
+ if (estaNoTermux()) {
123
+ console.log('💡 No Termux, use code-server ou um editor compatível com extensões do VS Code.');
124
+ } else {
125
+ console.log('💡 Reinicie o VS Code e selecione o tema "Raizcode Icons", caso ele esteja disponível.');
126
+ }
127
+ } catch (erro) {
128
+ console.error(`❌ Não foi possível instalar a extensão: ${erro.message}`);
129
+ process.exitCode = 1;
76
130
  }
77
- process.exit();
78
131
  }
79
132
 
80
- // ─────────────────────────────────────────
81
- // UTILITÁRIOS
82
- // ─────────────────────────────────────────
133
+ function mostrarAjuda() {
134
+ console.log(`🌱 Raizcode v${obterVersao()} — A linguagem brasileira\n`);
135
+ console.log('Uso:');
136
+ console.log(' raizcode arquivo.rc');
137
+ console.log(' raizcode arquivo.rc 3000');
138
+ console.log(' raizcode arquivo.rc --no-open');
139
+ console.log(' raizcode --setup');
140
+ console.log(' raizcode --version');
141
+ console.log(' raizcode --help');
142
+ console.log('\nOpções:');
143
+ console.log(' --no-open Não abre o navegador automaticamente.');
144
+ console.log(' --permitir-js Permite JavaScript puro sem o prefixo "js".');
145
+ console.log(' --estrito Recusa comandos desconhecidos.');
146
+ }
83
147
 
84
- // Abre o navegador de forma correta em qualquer sistema
85
148
  function abrirNavegador(url) {
86
149
  const plataforma = os.platform();
87
- if (plataforma === 'win32') exec(`start ${url}`);
88
- else if (plataforma === 'darwin') exec(`open ${url}`);
89
- else if (process.env.TERM === 'xterm-termux' || fs.existsSync('/data/data/com.termux')) exec(`termux-open ${url}`);
90
- else exec(`xdg-open ${url}`);
150
+ let comandos;
151
+
152
+ if (plataforma === 'win32') {
153
+ comandos = [['cmd', ['/c', 'start', '', url]]];
154
+ } else if (plataforma === 'darwin') {
155
+ comandos = [['open', [url]]];
156
+ } else if (estaNoTermux()) {
157
+ comandos = [
158
+ ['termux-open-url', [url]],
159
+ ['termux-open', [url]]
160
+ ];
161
+ } else {
162
+ comandos = [['xdg-open', [url]]];
163
+ }
164
+
165
+ const tentar = (indice) => {
166
+ if (indice >= comandos.length) {
167
+ console.log(`ℹ️ Abra manualmente no navegador: ${url}`);
168
+ return;
169
+ }
170
+
171
+ const [comando, argumentos] = comandos[indice];
172
+ let iniciou = false;
173
+
174
+ try {
175
+ const processo = spawn(comando, argumentos, {
176
+ detached: true,
177
+ stdio: 'ignore'
178
+ });
179
+
180
+ processo.once('spawn', () => {
181
+ iniciou = true;
182
+ processo.unref();
183
+ });
184
+
185
+ processo.once('error', () => {
186
+ if (!iniciou) tentar(indice + 1);
187
+ });
188
+ } catch (_) {
189
+ tentar(indice + 1);
190
+ }
191
+ };
192
+
193
+ tentar(0);
91
194
  }
92
195
 
93
- // Reporta erros com número de linha para o usuário
94
- function erroDeCompilacao(mensagem, numeroLinha) {
95
- console.error(`\n❌ Erro na linha ${numeroLinha}: ${mensagem}`);
96
- console.error(` Verifique seu código .rc e tente novamente.\n`);
97
- process.exit(1);
196
+ function criarErro(mensagem, arquivo, numeroLinha, codigoLinha = '') {
197
+ throw new ErroRaizcode(mensagem, arquivo, numeroLinha, codigoLinha);
98
198
  }
99
199
 
100
- // ─────────────────────────────────────────
101
- // TRADUTORES DE ESTRUTURA (RCX) E ESTILO (RCC)
102
- // ─────────────────────────────────────────
200
+ function escaparHTML(valor) {
201
+ return String(valor)
202
+ .replace(/&/g, '&')
203
+ .replace(/</g, '&lt;')
204
+ .replace(/>/g, '&gt;')
205
+ .replace(/"/g, '&quot;')
206
+ .replace(/'/g, '&#39;');
207
+ }
103
208
 
104
- function traduzirEstrutura(conteudo) {
105
- if (!conteudo) return "";
209
+ function escaparAtributo(valor) {
210
+ return escaparHTML(valor).replace(/`/g, '&#96;');
211
+ }
106
212
 
107
- return conteudo.split('\n').map((linha, idx) => {
108
- let t = linha.trim();
109
- if (!t || t.startsWith('//')) return "";
213
+ function substituirForaDeStrings(texto, transformador) {
214
+ let resultado = '';
215
+ let trecho = '';
216
+ let aspas = null;
217
+ let escapado = false;
110
218
 
111
- // Captura string entre aspas de forma segura
112
- const pegarTexto = (cmd) => {
113
- const match = t.match(new RegExp(`^${cmd}\\s+"(.*)"`));
114
- return match ? match[1] : t.replace(cmd + ' ', '').replace(/"/g, '');
115
- };
219
+ const descarregar = () => {
220
+ if (!trecho) return;
221
+ resultado += aspas ? trecho : transformador(trecho);
222
+ trecho = '';
223
+ };
224
+
225
+ for (const caractere of texto) {
226
+ trecho += caractere;
227
+
228
+ if (escapado) {
229
+ escapado = false;
230
+ continue;
231
+ }
116
232
 
117
- if (t.startsWith('titulo ')) return `<h1>${pegarTexto('titulo')}</h1>`;
118
- if (t.startsWith('subtitulo ')) return `<h2>${pegarTexto('subtitulo')}</h2>`;
119
- if (t.startsWith('texto ')) return `<p>${pegarTexto('texto')}</p>`;
120
- if (t.startsWith('link ')) {
121
- // link "Clique aqui" url "https://..."
122
- const m = t.match(/^link\s+"(.+?)"\s+url\s+"(.+?)"/);
123
- if (m) return `<a href="${m[2]}" target="_blank">${m[1]}</a>`;
124
- }
125
- if (t.startsWith('imagem ')) return `<img src="${pegarTexto('imagem')}" alt="imagem" style="max-width:100%;">`;
126
- if (t.startsWith('botao ')) {
127
- const m = t.match(/^botao\s+"(.+?)"\s+id\s+"(.+?)"/);
128
- if (m) return `<button id="${m[2]}">${m[1]}</button>`;
129
- return `<button>${pegarTexto('botao')}</button>`;
130
- }
131
- if (t.startsWith('entrada ')) {
132
- const m = t.match(/^entrada\s+id\s+"(.+?)"\s+dica\s+"(.+?)"/);
133
- if (m) return `<input id="${m[1]}" placeholder="${m[2]}">`;
134
- return `<input placeholder="${pegarTexto('entrada')}">`;
135
- }
136
- if (t.startsWith('caixa ')) return `<div class="${pegarTexto('caixa')}">`;
137
- if (t === 'fim') return `</div>`;
138
-
139
- return `<!-- linha ${idx + 1} não reconhecida: ${t} -->`;
140
- }).join('\n');
233
+ if (caractere === '\\') {
234
+ escapado = true;
235
+ continue;
236
+ }
237
+
238
+ if (aspas) {
239
+ if (caractere === aspas) {
240
+ descarregar();
241
+ aspas = null;
242
+ }
243
+ continue;
244
+ }
245
+
246
+ if (caractere === '"' || caractere === "'" || caractere === '`') {
247
+ trecho = trecho.slice(0, -1);
248
+ descarregar();
249
+ aspas = caractere;
250
+ trecho = caractere;
251
+ }
252
+ }
253
+
254
+ descarregar();
255
+ return resultado;
256
+ }
257
+
258
+ function traduzirExpressao(expressao) {
259
+ return substituirForaDeStrings(expressao, (trecho) => trecho
260
+ .replace(/\bverdadeiro\b/g, 'true')
261
+ .replace(/\bfalso\b/g, 'false')
262
+ .replace(/\bnulo\b/g, 'null')
263
+ .replace(/\bnao\b/g, '!')
264
+ .replace(/\be\b/g, '&&')
265
+ .replace(/\bou\b/g, '||'));
266
+ }
267
+
268
+ function extrairTextoEntreAspas(linha, comando, arquivo, numeroLinha) {
269
+ const padrao = new RegExp(`^${comando}\\s+"([\\s\\S]*)"$`);
270
+ const resultado = linha.match(padrao);
271
+ if (!resultado) {
272
+ criarErro(`Sintaxe inválida. Use: ${comando} "texto"`, arquivo, numeroLinha, linha);
273
+ }
274
+ return resultado[1];
275
+ }
276
+
277
+ function traduzirEstrutura(conteudo, arquivo) {
278
+ if (!conteudo) return '';
279
+
280
+ const linhas = conteudo.replace(/^\uFEFF/, '').split(/\r\n|\n|\r/);
281
+ const pilha = [];
282
+ const saida = [];
283
+
284
+ linhas.forEach((linhaOriginal, indice) => {
285
+ const numeroLinha = indice + 1;
286
+ const linha = linhaOriginal.trim();
287
+
288
+ if (!linha || linha.startsWith('//')) {
289
+ saida.push('');
290
+ return;
291
+ }
292
+
293
+ if (linha.startsWith('titulo ')) {
294
+ saida.push(`<h1>${escaparHTML(extrairTextoEntreAspas(linha, 'titulo', arquivo, numeroLinha))}</h1>`);
295
+ return;
296
+ }
297
+
298
+ if (linha.startsWith('subtitulo ')) {
299
+ saida.push(`<h2>${escaparHTML(extrairTextoEntreAspas(linha, 'subtitulo', arquivo, numeroLinha))}</h2>`);
300
+ return;
301
+ }
302
+
303
+ if (linha.startsWith('texto ')) {
304
+ saida.push(`<p>${escaparHTML(extrairTextoEntreAspas(linha, 'texto', arquivo, numeroLinha))}</p>`);
305
+ return;
306
+ }
307
+
308
+ if (linha.startsWith('link ')) {
309
+ const resultado = linha.match(/^link\s+"(.+?)"\s+url\s+"(.+?)"$/);
310
+ if (!resultado) criarErro('Use: link "Texto" url "https://exemplo.com"', arquivo, numeroLinha, linhaOriginal);
311
+ saida.push(`<a href="${escaparAtributo(resultado[2])}" target="_blank" rel="noopener noreferrer">${escaparHTML(resultado[1])}</a>`);
312
+ return;
313
+ }
314
+
315
+ if (linha.startsWith('imagem ')) {
316
+ const endereco = extrairTextoEntreAspas(linha, 'imagem', arquivo, numeroLinha);
317
+ saida.push(`<img src="${escaparAtributo(endereco)}" alt="imagem" style="max-width:100%;">`);
318
+ return;
319
+ }
320
+
321
+ if (linha.startsWith('botao ')) {
322
+ const comId = linha.match(/^botao\s+"(.+?)"\s+id\s+"([A-Za-z][\w:-]*)"$/);
323
+ const semId = linha.match(/^botao\s+"(.+?)"$/);
324
+ if (comId) {
325
+ saida.push(`<button id="${escaparAtributo(comId[2])}">${escaparHTML(comId[1])}</button>`);
326
+ return;
327
+ }
328
+ if (semId) {
329
+ saida.push(`<button>${escaparHTML(semId[1])}</button>`);
330
+ return;
331
+ }
332
+ criarErro('Use: botao "Texto" id "meuBotao"', arquivo, numeroLinha, linhaOriginal);
333
+ }
334
+
335
+ if (linha.startsWith('entrada ')) {
336
+ const resultado = linha.match(/^entrada\s+id\s+"([A-Za-z][\w:-]*)"\s+dica\s+"(.+?)"$/);
337
+ if (!resultado) criarErro('Use: entrada id "nome" dica "Digite seu nome"', arquivo, numeroLinha, linhaOriginal);
338
+ saida.push(`<input id="${escaparAtributo(resultado[1])}" placeholder="${escaparAtributo(resultado[2])}">`);
339
+ return;
340
+ }
341
+
342
+ if (linha.startsWith('caixa ')) {
343
+ const classe = extrairTextoEntreAspas(linha, 'caixa', arquivo, numeroLinha);
344
+ pilha.push({ tipo: 'caixa', linha: numeroLinha });
345
+ saida.push(`<div class="${escaparAtributo(classe)}">`);
346
+ return;
347
+ }
348
+
349
+ if (linha === 'fim') {
350
+ if (pilha.length === 0) criarErro('Existe um "fim" sem uma caixa aberta.', arquivo, numeroLinha, linhaOriginal);
351
+ pilha.pop();
352
+ saida.push('</div>');
353
+ return;
354
+ }
355
+
356
+ criarErro(`Comando de estrutura desconhecido: "${linha}"`, arquivo, numeroLinha, linhaOriginal);
357
+ });
358
+
359
+ if (pilha.length > 0) {
360
+ const aberto = pilha[pilha.length - 1];
361
+ criarErro(`A caixa aberta na linha ${aberto.linha} não foi fechada com "fim".`, arquivo, linhas.length, linhas[linhas.length - 1] || '');
362
+ }
363
+
364
+ return saida.join('\n');
365
+ }
366
+
367
+ const PROPRIEDADES_RCC = {
368
+ 'fundo': 'background',
369
+ 'cor-texto': 'color',
370
+ 'tamanho-texto': 'font-size',
371
+ 'familia-fonte': 'font-family',
372
+ 'peso-fonte': 'font-weight',
373
+ 'margem': 'margin',
374
+ 'margem-interna': 'padding',
375
+ 'borda': 'border',
376
+ 'arredondado': 'border-radius',
377
+ 'largura': 'width',
378
+ 'altura': 'height',
379
+ 'largura-maxima': 'max-width',
380
+ 'altura-minima': 'min-height',
381
+ 'exibir': 'display',
382
+ 'alinhar-itens': 'align-items',
383
+ 'justificar': 'justify-content',
384
+ 'direcao': 'flex-direction',
385
+ 'espaco': 'gap',
386
+ 'sombra': 'box-shadow',
387
+ 'cursor': 'cursor',
388
+ 'opacidade': 'opacity',
389
+ 'transicao': 'transition',
390
+ 'posicao': 'position',
391
+ 'topo': 'top',
392
+ 'direita': 'right',
393
+ 'baixo': 'bottom',
394
+ 'esquerda': 'left'
395
+ };
396
+
397
+ const VALORES_RCC = {
398
+ 'preto': '#000000',
399
+ 'branco': '#ffffff',
400
+ 'cinza': '#808080',
401
+ 'vermelho': '#ff0000',
402
+ 'verde': '#00a650',
403
+ 'azul': '#0066ff',
404
+ 'transparente': 'transparent',
405
+ 'verde-neon': '#00ff88',
406
+ 'amarelo-neon': '#ffff00',
407
+ 'azul-neon': '#00cfff',
408
+ 'vermelho-neon': '#ff4444'
409
+ };
410
+
411
+ function traduzirSeletor(texto, arquivo, numeroLinha, linhaOriginal) {
412
+ const seletor = texto.trim();
413
+ if (!seletor) criarErro('Informe um seletor depois de "estilo".', arquivo, numeroLinha, linhaOriginal);
414
+
415
+ if (seletor === 'corpo') return 'body';
416
+ if (seletor === 'raiz') return '#raiz-app';
417
+ if (seletor.startsWith('classe ')) return `.${seletor.slice(7).trim()}`;
418
+ if (seletor.startsWith('id ')) return `#${seletor.slice(3).trim()}`;
419
+ if (seletor.startsWith('elemento ')) return seletor.slice(9).trim();
420
+ if (/^[.#[:*]/.test(seletor)) return seletor;
421
+ return `.${seletor}`;
141
422
  }
142
423
 
143
- function traduzirEstilo(conteudo) {
144
- if (!conteudo) return "";
145
- return conteudo
146
- .replace(/fundo:/g, 'background:')
147
- .replace(/cor-texto:/g, 'color:')
148
- .replace(/tamanho-texto:/g, 'font-size:')
149
- .replace(/margem:/g, 'margin:')
150
- .replace(/borda:/g, 'border:')
151
- .replace(/arredondado:/g, 'border-radius:')
152
- .replace(/verde-neon/g, '#00ff88')
153
- .replace(/amarelo-neon/g, '#ffff00')
154
- .replace(/azul-neon/g, '#00cfff')
155
- .replace(/vermelho-neon/g, '#ff4444')
156
- .replace(/ao-passar-mouse:/g, '&:hover')
157
- .replace(/estilo\s+/g, '.')
158
- .replace(/\bfim\b/g, '}');
424
+ function traduzirValorCSS(valor) {
425
+ let resultado = valor.trim();
426
+ for (const [portugues, css] of Object.entries(VALORES_RCC)) {
427
+ resultado = resultado.replace(new RegExp(`\\b${portugues}\\b`, 'g'), css);
428
+ }
429
+ return resultado;
159
430
  }
160
431
 
161
- // ─────────────────────────────────────────
162
- // TRADUTOR PRINCIPAL (RC JS)
163
- // ─────────────────────────────────────────
432
+ function traduzirEstilo(conteudo, arquivo) {
433
+ if (!conteudo) return '';
434
+
435
+ const linhas = conteudo.replace(/^\uFEFF/, '').split(/\r\n|\n|\r/);
436
+ const pilha = [];
437
+ const saida = [];
164
438
 
165
- function traduzir(linha, numeroLinha, contexto) {
166
- let t = linha.trim();
167
- if (!t || t.startsWith("//")) return linha; // preserva indentação
439
+ linhas.forEach((linhaOriginal, indice) => {
440
+ const numeroLinha = indice + 1;
441
+ const linha = linhaOriginal.trim();
442
+
443
+ if (!linha || linha.startsWith('//')) {
444
+ saida.push('');
445
+ return;
446
+ }
447
+
448
+ if (linha.startsWith('estilo ')) {
449
+ const seletor = traduzirSeletor(linha.slice(7), arquivo, numeroLinha, linhaOriginal);
450
+ pilha.push({ seletor, linha: numeroLinha });
451
+ saida.push(`${seletor} {`);
452
+ return;
453
+ }
454
+
455
+ if (linha.startsWith('ao-passar-mouse ')) {
456
+ const seletor = traduzirSeletor(linha.slice('ao-passar-mouse '.length), arquivo, numeroLinha, linhaOriginal);
457
+ pilha.push({ seletor: `${seletor}:hover`, linha: numeroLinha });
458
+ saida.push(`${seletor}:hover {`);
459
+ return;
460
+ }
168
461
 
169
- // Título da página
170
- if (t.startsWith('pagina ')) {
462
+ if (linha === 'fim') {
463
+ if (pilha.length === 0) criarErro('Existe um "fim" sem um estilo aberto.', arquivo, numeroLinha, linhaOriginal);
464
+ pilha.pop();
465
+ saida.push('}');
466
+ return;
467
+ }
468
+
469
+ if (pilha.length === 0) {
470
+ criarErro('Propriedade encontrada fora de um bloco "estilo".', arquivo, numeroLinha, linhaOriginal);
471
+ }
472
+
473
+ const propriedade = linha.match(/^([\w-]+)\s*:\s*(.+?);?$/);
474
+ if (!propriedade) criarErro('Use o formato: propriedade: valor', arquivo, numeroLinha, linhaOriginal);
475
+
476
+ const nome = PROPRIEDADES_RCC[propriedade[1]] || propriedade[1];
477
+ const valor = traduzirValorCSS(propriedade[2].replace(/;$/, ''));
478
+ saida.push(` ${nome}: ${valor};`);
479
+ });
480
+
481
+ if (pilha.length > 0) {
482
+ const aberto = pilha[pilha.length - 1];
483
+ criarErro(`O estilo aberto na linha ${aberto.linha} não foi fechado com "fim".`, arquivo, linhas.length, linhas[linhas.length - 1] || '');
484
+ }
485
+
486
+ return saida.join('\n');
487
+ }
488
+
489
+ function validarBlocos(linhas, arquivo) {
490
+ const pilha = [];
491
+
492
+ linhas.forEach((linhaOriginal, indice) => {
493
+ const numeroLinha = indice + 1;
494
+ const linha = linhaOriginal.trim();
495
+ if (!linha || linha.startsWith('//')) return;
496
+
497
+ let tipo = null;
498
+ if (linha.startsWith('se ')) tipo = 'se';
499
+ else if (linha.startsWith('enquanto ')) tipo = 'enquanto';
500
+ else if (linha.startsWith('funcao ')) tipo = 'funcao';
501
+ else if (linha.startsWith('repetir ')) tipo = 'repetir';
502
+ else if (linha.startsWith('para cada ')) tipo = 'para';
503
+ else if (linha.startsWith('ao_clicar ')) tipo = 'evento';
504
+
505
+ if (tipo) {
506
+ pilha.push({ tipo, linha: numeroLinha });
507
+ return;
508
+ }
509
+
510
+ if (linha === 'senao' || linha.startsWith('senaose ')) {
511
+ const atual = pilha[pilha.length - 1];
512
+ if (!atual || atual.tipo !== 'se') {
513
+ criarErro(`"${linha.split(' ')[0]}" só pode ser usado dentro de um bloco "se".`, arquivo, numeroLinha, linhaOriginal);
514
+ }
515
+ return;
516
+ }
517
+
518
+ if (linha === 'fim') {
519
+ if (pilha.length === 0) criarErro('Existe um "fim" sem bloco aberto.', arquivo, numeroLinha, linhaOriginal);
520
+ pilha.pop();
521
+ }
522
+ });
523
+
524
+ if (pilha.length > 0) {
525
+ const aberto = pilha[pilha.length - 1];
526
+ criarErro(`O bloco "${aberto.tipo}" aberto na linha ${aberto.linha} não foi fechado com "fim".`, arquivo, linhas.length, linhas[linhas.length - 1] || '');
527
+ }
528
+ }
529
+
530
+ function traduzirLinha(linhaOriginal, numeroLinha, contexto) {
531
+ const linha = linhaOriginal.trim();
532
+ if (!linha || linha.startsWith('//')) return linhaOriginal;
533
+
534
+ if (linha.startsWith('pagina ')) {
171
535
  contexto.ehSite = true;
172
- const titulo = t.replace(/^pagina\s+/, '');
536
+ const valor = linha.slice(7).trim();
537
+ const titulo = /^(["'`]).*\1$/.test(valor) ? traduzirExpressao(valor) : JSON.stringify(valor);
173
538
  return `document.title = ${titulo};`;
174
539
  }
175
540
 
176
- // UI: mostrar elemento na tela pelo id
177
- if (t.startsWith('mostrar_tela ')) {
178
- const m = t.match(/^mostrar_tela\s+"(.+?)"\s+em\s+"(.+?)"/);
179
- if (m) return `document.getElementById("${m[2]}").innerText = "${m[1]}";`;
541
+ if (linha.startsWith('mostrar_tela ')) {
542
+ contexto.ehSite = true;
543
+ const resultado = linha.match(/^mostrar_tela\s+(.+)\s+em\s+"([A-Za-z][\w:-]*)"$/);
544
+ if (!resultado) criarErro('Use: mostrar_tela "Texto" em "elementoId"', contexto.arquivo, numeroLinha, linhaOriginal);
545
+ const valor = traduzirExpressao(resultado[1]);
546
+ const id = resultado[2];
547
+ return `(()=>{const __el=document.getElementById(${JSON.stringify(id)});if(!__el)throw new Error(${JSON.stringify(`Elemento #${id} não encontrado.`)});__el.innerText=String(${valor});})();`;
548
+ }
549
+
550
+ if (linha.startsWith('ao_clicar ')) {
551
+ contexto.ehSite = true;
552
+ const resultado = linha.match(/^ao_clicar\s+"([A-Za-z][\w:-]*)"$/);
553
+ if (!resultado) criarErro('Use: ao_clicar "elementoId"', contexto.arquivo, numeroLinha, linhaOriginal);
554
+ const id = resultado[1];
555
+ contexto.pilhaTraducao.push({ tipo: 'evento', linha: numeroLinha });
556
+ return `(()=>{const __el=document.getElementById(${JSON.stringify(id)});if(!__el)throw new Error(${JSON.stringify(`Elemento #${id} não encontrado.`)});__el.addEventListener("click",function(){`;
180
557
  }
181
558
 
182
- // Ao clicar: evento de clique
183
- if (t.startsWith('ao_clicar ')) {
184
- const m = t.match(/^ao_clicar\s+"(.+?)"/);
185
- if (m) return `document.getElementById("${m[1]}").addEventListener("click", function() {`;
559
+ if (linha.startsWith('alerta ')) {
560
+ const valor = traduzirExpressao(linha.slice(7));
561
+ return `(typeof alert==="function"?alert(${valor}):console.log(${valor}));`;
186
562
  }
187
563
 
188
- // Comandos básicos
189
- if (t.startsWith('alerta ')) return `alert(${t.replace(/^alerta\s+/, '')});`;
190
- if (t.startsWith('mudar_fundo ')) return `document.body.style.background = ${t.replace(/^mudar_fundo\s+/, '')};`;
191
- if (t.startsWith('mostrar ')) return `console.log(${t.replace(/^mostrar\s+/, '')});`;
564
+ if (linha.startsWith('mudar_fundo ')) {
565
+ contexto.ehSite = true;
566
+ const valor = traduzirExpressao(linha.slice('mudar_fundo '.length));
567
+ return `document.body.style.background=${valor};`;
568
+ }
192
569
 
193
- // Variável com parsing correto de strings com espaços
194
- if (t.startsWith('variavel ')) {
195
- const m = t.match(/^variavel\s+(\w+)\s+(.*)/);
196
- if (!m) erroDeCompilacao(`Sintaxe incorreta: "${t}" — Use: variavel nome "valor"`, numeroLinha);
197
- return `let ${m[1]} = ${m[2]};`;
570
+ if (linha.startsWith('mostrar ')) {
571
+ return `console.log(${traduzirExpressao(linha.slice(8))});`;
198
572
  }
199
573
 
200
- if (t.startsWith('constante ')) {
201
- const m = t.match(/^constante\s+(\w+)\s+(.*)/);
202
- if (!m) erroDeCompilacao(`Sintaxe incorreta: "${t}" — Use: constante nome "valor"`, numeroLinha);
203
- return `const ${m[1]} = ${m[2]};`;
574
+ if (linha.startsWith('variavel ')) {
575
+ const resultado = linha.match(/^variavel\s+([A-Za-z_$][\w$]*)\s*(?:=\s*)?(.+)$/);
576
+ if (!resultado) criarErro('Use: variavel nome = "valor"', contexto.arquivo, numeroLinha, linhaOriginal);
577
+ return `let ${resultado[1]} = ${traduzirExpressao(resultado[2])};`;
204
578
  }
205
579
 
206
- // Estruturas de controle
207
- if (t.startsWith('se ')) return `if (${t.replace(/^se\s+/, '').replace(/\bend\b/, '')}) {`;
208
- if (t === 'senao') return `} else {`;
209
- if (t.startsWith('senaose ')) return `} else if (${t.replace(/^senaose\s+/, '')}) {`;
210
- if (t === 'fim') return `}`;
580
+ if (linha.startsWith('constante ')) {
581
+ const resultado = linha.match(/^constante\s+([A-Za-z_$][\w$]*)\s*(?:=\s*)?(.+)$/);
582
+ if (!resultado) criarErro('Use: constante nome = "valor"', contexto.arquivo, numeroLinha, linhaOriginal);
583
+ return `const ${resultado[1]} = ${traduzirExpressao(resultado[2])};`;
584
+ }
211
585
 
212
- // Loops
213
- if (t.startsWith('repetir ')) {
214
- const m = t.match(/^repetir\s+(\d+)\s+vezes/);
215
- if (m) return `for (let _i = 0; _i < ${m[1]}; _i++) {`;
586
+ if (linha.startsWith('lista ')) {
587
+ const resultado = linha.match(/^lista\s+([A-Za-z_$][\w$]*)\s*(?:=\s*)?(\[.*\])$/);
588
+ if (!resultado) criarErro('Use: lista nomes = ["Ana", "Rique"]', contexto.arquivo, numeroLinha, linhaOriginal);
589
+ return `let ${resultado[1]} = ${traduzirExpressao(resultado[2])};`;
216
590
  }
217
- if (t.startsWith('enquanto ')) return `while (${t.replace(/^enquanto\s+/, '')}) {`;
218
591
 
219
- // Funções
220
- if (t.startsWith('funcao ')) {
221
- const m = t.match(/^funcao\s+(\w+)\s*\((.*)\)/);
222
- if (m) return `function ${m[1]}(${m[2]}) {`;
592
+ if (linha.startsWith('definir ')) {
593
+ const resultado = linha.match(/^definir\s+([A-Za-z_$][\w$.[\]]*)\s*=\s*(.+)$/);
594
+ if (!resultado) criarErro('Use: definir nome = "novo valor"', contexto.arquivo, numeroLinha, linhaOriginal);
595
+ return `${resultado[1]} = ${traduzirExpressao(resultado[2])};`;
223
596
  }
224
- if (t.startsWith('retornar ')) return `return ${t.replace(/^retornar\s+/, '')};`;
225
597
 
226
- return t; // passa JS puro direto
227
- }
598
+ if (linha.startsWith('senaose ')) {
599
+ return `} else if (${traduzirExpressao(linha.slice(8))}) {`;
600
+ }
228
601
 
229
- // ─────────────────────────────────────────
230
- // VALIDAÇÃO DE BLOCOS
231
- // ─────────────────────────────────────────
232
- function validarBlocos(linhas) {
233
- let pilha = 0;
234
- linhas.forEach((linha, idx) => {
235
- const t = linha.trim();
236
- if (t.startsWith('se ') || t.startsWith('enquanto ') || t.startsWith('funcao ') || t.startsWith('repetir ') || t.startsWith('ao_clicar ')) pilha++;
237
- if (t === 'fim') pilha--;
238
- if (pilha < 0) erroDeCompilacao(`"fim" sobrando sem um bloco aberto.`, idx + 1);
239
- });
240
- if (pilha > 0) erroDeCompilacao(`Faltam ${pilha} "fim" para fechar bloco(s) abertos.`, linhas.length);
241
- }
602
+ if (linha === 'senao') return '} else {';
242
603
 
243
- // ─────────────────────────────────────────
244
- // PONTO DE ENTRADA
245
- // ─────────────────────────────────────────
246
- if (!arquivoOuCmd) {
247
- console.log("🌱 Raizcode v1.8 — A linguagem brasileira");
248
- console.log(" Uso: raizcode <arquivo.rc>");
249
- console.log(" Setup VSCode: raizcode --setup");
250
- process.exit();
251
- }
604
+ if (linha.startsWith('se ')) {
605
+ contexto.pilhaTraducao.push({ tipo: 'se', linha: numeroLinha });
606
+ return `if (${traduzirExpressao(linha.slice(3))}) {`;
607
+ }
252
608
 
253
- const caminho = path.resolve(arquivoOuCmd);
254
- const nomeBase = caminho.substring(0, caminho.lastIndexOf('.'));
609
+ if (linha.startsWith('repetir ')) {
610
+ const resultado = linha.match(/^repetir\s+(.+)\s+vezes$/);
611
+ if (!resultado) criarErro('Use: repetir 5 vezes', contexto.arquivo, numeroLinha, linhaOriginal);
612
+ contexto.pilhaTraducao.push({ tipo: 'repetir', linha: numeroLinha });
613
+ const indice = `__raiz_i_${numeroLinha}`;
614
+ return `for (let ${indice}=0;${indice}<(${traduzirExpressao(resultado[1])});${indice}++) {`;
615
+ }
255
616
 
256
- if (!fs.existsSync(caminho)) {
257
- console.error(`❌ Arquivo não encontrado: ${arquivoOuCmd}`);
258
- process.exit(1);
259
- }
617
+ if (linha.startsWith('enquanto ')) {
618
+ contexto.pilhaTraducao.push({ tipo: 'enquanto', linha: numeroLinha });
619
+ return `while (${traduzirExpressao(linha.slice(9))}) {`;
620
+ }
260
621
 
261
- try {
262
- const arqRcx = nomeBase + '.rcx';
263
- const arqRcc = nomeBase + '.rcc';
622
+ if (linha.startsWith('para cada ')) {
623
+ const resultado = linha.match(/^para\s+cada\s+([A-Za-z_$][\w$]*)\s+em\s+(.+)$/);
624
+ if (!resultado) criarErro('Use: para cada item em lista', contexto.arquivo, numeroLinha, linhaOriginal);
625
+ contexto.pilhaTraducao.push({ tipo: 'para', linha: numeroLinha });
626
+ return `for (const ${resultado[1]} of ${traduzirExpressao(resultado[2])}) {`;
627
+ }
264
628
 
265
- let htmlExtra = "";
266
- let cssExtra = "";
267
- const contexto = { ehSite: false };
629
+ if (linha.startsWith('funcao ')) {
630
+ const resultado = linha.match(/^funcao\s+([A-Za-z_$][\w$]*)\s*\((.*)\)$/);
631
+ if (!resultado) criarErro('Use: funcao nome(parametro)', contexto.arquivo, numeroLinha, linhaOriginal);
632
+ contexto.pilhaTraducao.push({ tipo: 'funcao', linha: numeroLinha });
633
+ return `function ${resultado[1]}(${resultado[2]}) {`;
634
+ }
268
635
 
269
- if (fs.existsSync(arqRcx)) {
270
- htmlExtra = traduzirEstrutura(fs.readFileSync(arqRcx, 'utf-8'));
271
- contexto.ehSite = true;
636
+ if (linha.startsWith('retornar ')) {
637
+ return `return ${traduzirExpressao(linha.slice(9))};`;
272
638
  }
273
- if (fs.existsSync(arqRcc)) {
274
- cssExtra = traduzirEstilo(fs.readFileSync(arqRcc, 'utf-8'));
275
- contexto.ehSite = true;
639
+
640
+ if (linha === 'parar') return 'break;';
641
+ if (linha === 'continuar') return 'continue;';
642
+
643
+ if (linha === 'fim') {
644
+ const bloco = contexto.pilhaTraducao.pop();
645
+ if (!bloco) criarErro('Existe um "fim" sem bloco aberto.', contexto.arquivo, numeroLinha, linhaOriginal);
646
+ if (bloco.tipo === 'evento') return '});})();';
647
+ return '}';
648
+ }
649
+
650
+ if (linha.startsWith('js ')) return linha.slice(3);
651
+
652
+ if (contexto.estrito && !contexto.permitirJs) {
653
+ criarErro(`Comando desconhecido: "${linha}"`, contexto.arquivo, numeroLinha, linhaOriginal);
276
654
  }
277
655
 
278
- const codigo = fs.readFileSync(caminho, 'utf-8');
279
- const linhas = codigo.split('\n');
656
+ return linhaOriginal;
657
+ }
658
+
659
+ function compilarRC(codigo, arquivo, opcoes = {}) {
660
+ const linhas = codigo.replace(/^\uFEFF/, '').split(/\r\n|\n|\r/);
661
+ validarBlocos(linhas, arquivo);
662
+
663
+ const contexto = {
664
+ arquivo,
665
+ ehSite: false,
666
+ pilhaTraducao: [],
667
+ permitirJs: Boolean(opcoes.permitirJs),
668
+ estrito: Boolean(opcoes.estrito)
669
+ };
670
+
671
+ const javascript = linhas
672
+ .map((linha, indice) => traduzirLinha(linha, indice + 1, contexto))
673
+ .join('\n');
674
+
675
+ return { javascript, ehSite: contexto.ehSite };
676
+ }
677
+
678
+ function executarComoModulo(javascript, arquivo) {
679
+ const moduloRaizcode = new Module(arquivo, module);
680
+ moduloRaizcode.filename = arquivo;
681
+ moduloRaizcode.paths = Module._nodeModulePaths(path.dirname(arquivo));
682
+ moduloRaizcode._compile(javascript, arquivo);
683
+ }
684
+
685
+ function obterLinhaDoErro(erro, arquivo) {
686
+ if (!erro || !erro.stack) return null;
687
+ const arquivoEscapado = arquivo.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
688
+ const resultado = erro.stack.match(new RegExp(`${arquivoEscapado}:(\\d+):(\\d+)`));
689
+ if (!resultado) return null;
690
+ return { linha: Number(resultado[1]), coluna: Number(resultado[2]) };
691
+ }
692
+
693
+ function mostrarErro(erro, arquivo, codigoFonte = '') {
694
+ if (erro instanceof ErroRaizcode) {
695
+ console.error(`\n❌ Erro Raizcode em ${path.basename(erro.arquivo)}:${erro.numeroLinha}`);
696
+ console.error(` ${erro.message}`);
697
+ if (erro.codigoLinha) {
698
+ console.error(`\n ${erro.numeroLinha} | ${erro.codigoLinha}`);
699
+ }
700
+ console.error('');
701
+ return;
702
+ }
280
703
 
281
- // Valida antes de compilar
282
- validarBlocos(linhas);
704
+ const local = obterLinhaDoErro(erro, arquivo);
705
+ const linhas = codigoFonte.split(/\r\n|\n|\r/);
283
706
 
284
- const js = linhas.map((linha, idx) => traduzir(linha, idx + 1, contexto)).join('\n');
707
+ if (local) {
708
+ console.error(`\n❌ Erro ao executar ${path.basename(arquivo)}:${local.linha}:${local.coluna}`);
709
+ console.error(` ${erro.name || 'Erro'}: ${erro.message}`);
710
+ if (linhas[local.linha - 1] !== undefined) {
711
+ console.error(`\n ${local.linha} | ${linhas[local.linha - 1]}`);
712
+ console.error(` ${' '.repeat(Math.max(0, local.coluna - 1))}^`);
713
+ }
714
+ console.error('');
715
+ return;
716
+ }
285
717
 
286
- const saidaJS = nomeBase + '.js';
287
- const saidaHTML = nomeBase + '.html';
718
+ console.error(`\n❌ ${erro.name || 'Erro'}: ${erro.message}\n`);
719
+ if (flags.has('--debug') && erro.stack) console.error(erro.stack);
720
+ }
288
721
 
289
- if (contexto.ehSite) {
290
- const porta = args[1] ? parseInt(args[1]) : 3000;
291
- const estruturaHTML = `<!DOCTYPE html>
722
+ function montarHTML(javascript, htmlExtra, cssExtra, nomeArquivo) {
723
+ const origemSegura = String(nomeArquivo).replace(/[\r\n]/g, '');
724
+ return `<!DOCTYPE html>
292
725
  <html lang="pt-br">
293
726
  <head>
294
727
  <meta charset="UTF-8">
@@ -299,7 +732,7 @@ try {
299
732
  body {
300
733
  background: #0d0d0d;
301
734
  color: #f0f0f0;
302
- font-family: sans-serif;
735
+ font-family: Arial, sans-serif;
303
736
  display: flex;
304
737
  justify-content: center;
305
738
  align-items: center;
@@ -313,13 +746,12 @@ try {
313
746
  padding: 40px;
314
747
  border-radius: 20px;
315
748
  background: #1a1a1a;
316
- box-shadow: 0 0 30px rgba(0,255,136,0.2);
317
749
  max-width: 800px;
318
750
  width: 100%;
319
751
  }
320
- h1 { color: #00ff88; text-shadow: 0 0 10px #00ff88; }
752
+ h1 { color: #00ff88; }
321
753
  h2 { color: #00cfff; }
322
- a { color: #00ff88; }
754
+ a { color: #00ff88; }
323
755
  button {
324
756
  background: #00ff88;
325
757
  color: #000;
@@ -330,7 +762,6 @@ try {
330
762
  cursor: pointer;
331
763
  margin: 8px;
332
764
  font-size: 1rem;
333
- transition: opacity 0.2s;
334
765
  }
335
766
  button:hover { opacity: 0.8; }
336
767
  input {
@@ -342,52 +773,138 @@ try {
342
773
  margin: 8px;
343
774
  font-size: 1rem;
344
775
  }
345
- ${cssExtra}
776
+ ${cssExtra}
346
777
  </style>
347
778
  </head>
348
779
  <body>
349
- <div id="raiz-app">${htmlExtra}</div>
780
+ <div id="raiz-app">
781
+ ${htmlExtra}
782
+ </div>
350
783
  <script>
351
- ${js}
784
+ window.addEventListener('error', function(event) {
785
+ console.error('Erro Raizcode:', event.message, 'linha', event.lineno);
786
+ });
787
+ ${javascript}
788
+ //# sourceURL=${origemSegura}
352
789
  </script>
353
790
  </body>
354
791
  </html>`;
792
+ }
355
793
 
356
- // Salva HTML para inspeção (opcional)
357
- fs.writeFileSync(saidaHTML, estruturaHTML);
794
+ function iniciarServidor(html, porta, abrirAutomaticamente) {
795
+ const servidor = http.createServer((requisicao, resposta) => {
796
+ if (requisicao.url === '/favicon.ico') {
797
+ resposta.writeHead(204);
798
+ resposta.end();
799
+ return;
800
+ }
358
801
 
359
- const server = http.createServer((req, res) => {
360
- res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
361
- res.end(estruturaHTML);
802
+ resposta.writeHead(200, {
803
+ 'Content-Type': 'text/html; charset=utf-8',
804
+ 'Cache-Control': 'no-store'
362
805
  });
806
+ resposta.end(html);
807
+ });
363
808
 
364
- server.listen(porta, () => {
365
- console.log(`\n🌱 Raizcode Servidor ativo!`);
366
- console.log(`🌐 Acesse: http://localhost:${porta}`);
367
- console.log(`⏹ Pressione Ctrl+C para encerrar.\n`);
368
- abrirNavegador(`http://localhost:${porta}`);
369
- });
809
+ servidor.on('error', (erro) => {
810
+ if (erro.code === 'EADDRINUSE') {
811
+ console.error(`\n❌ A porta ${porta} já está sendo usada.`);
812
+ console.error(` Tente: raizcode seu-arquivo.rc ${porta + 1}\n`);
813
+ } else {
814
+ console.error(`\n❌ Erro no servidor: ${erro.message}\n`);
815
+ }
816
+ process.exitCode = 1;
817
+ });
818
+
819
+ servidor.listen(porta, '127.0.0.1', () => {
820
+ const url = `http://127.0.0.1:${porta}`;
821
+ console.log('\n🌱 Raizcode — servidor ativo!');
822
+ console.log(`🌐 Acesse: ${url}`);
823
+ console.log('⏹ Pressione Ctrl+C para encerrar.\n');
824
+ if (abrirAutomaticamente) abrirNavegador(url);
825
+ });
370
826
 
371
- // Mantém servidor vivo até Ctrl+C
372
- process.on('SIGINT', () => {
373
- console.log('\n👋 Servidor encerrado.');
374
- if (fs.existsSync(saidaHTML)) fs.unlinkSync(saidaHTML);
375
- process.exit();
827
+ process.once('SIGINT', () => {
828
+ console.log('\n👋 Servidor encerrado.');
829
+ servidor.close(() => process.exit(0));
830
+ });
831
+ }
832
+
833
+ function executarProjeto() {
834
+ if (!arquivoOuCmd || flags.has('--help') || arquivoOuCmd === '-h') {
835
+ mostrarAjuda();
836
+ return;
837
+ }
838
+
839
+ if (flags.has('--version') || arquivoOuCmd === '-v') {
840
+ console.log(obterVersao());
841
+ return;
842
+ }
843
+
844
+ if (arquivoOuCmd === '--setup') {
845
+ instalarExtensaoVSCode();
846
+ return;
847
+ }
848
+
849
+ const arquivoEntrada = posicionais[0];
850
+ if (!arquivoEntrada) {
851
+ mostrarAjuda();
852
+ return;
853
+ }
854
+
855
+ const caminho = path.resolve(arquivoEntrada);
856
+ if (!fs.existsSync(caminho)) {
857
+ console.error(`❌ Arquivo não encontrado: ${arquivoEntrada}`);
858
+ process.exitCode = 1;
859
+ return;
860
+ }
861
+
862
+ if (path.extname(caminho).toLowerCase() !== '.rc') {
863
+ console.error('❌ O arquivo principal precisa ter a extensão .rc');
864
+ process.exitCode = 1;
865
+ return;
866
+ }
867
+
868
+ const nomeBase = caminho.slice(0, -path.extname(caminho).length);
869
+ const arqRcx = `${nomeBase}.rcx`;
870
+ const arqRcc = `${nomeBase}.rcc`;
871
+ const saidaHTML = `${nomeBase}.html`;
872
+ let codigoFonte = '';
873
+
874
+ try {
875
+ const htmlExtra = fs.existsSync(arqRcx)
876
+ ? traduzirEstrutura(fs.readFileSync(arqRcx, 'utf8'), arqRcx)
877
+ : '';
878
+ const cssExtra = fs.existsSync(arqRcc)
879
+ ? traduzirEstilo(fs.readFileSync(arqRcc, 'utf8'), arqRcc)
880
+ : '';
881
+
882
+ codigoFonte = fs.readFileSync(caminho, 'utf8');
883
+ const compilado = compilarRC(codigoFonte, caminho, {
884
+ permitirJs: flags.has('--permitir-js'),
885
+ estrito: flags.has('--estrito')
376
886
  });
377
887
 
378
- } else {
379
- // Modo script: executa e apaga
380
- fs.writeFileSync(saidaJS, js);
381
- try {
382
- execSync(`node "${saidaJS}"`, { stdio: 'inherit' });
383
- } finally {
384
- if (fs.existsSync(saidaJS)) fs.unlinkSync(saidaJS);
888
+ const ehSite = compilado.ehSite || Boolean(htmlExtra) || Boolean(cssExtra);
889
+ if (!ehSite) {
890
+ executarComoModulo(compilado.javascript, caminho);
891
+ return;
892
+ }
893
+
894
+ const portaInformada = posicionais[1] || '3000';
895
+ const porta = Number(portaInformada);
896
+ if (!Number.isInteger(porta) || porta < 1 || porta > 65535) {
897
+ criarErro(`Porta inválida: ${portaInformada}`, caminho, 1, codigoFonte.split(/\r\n|\n|\r/)[0] || '');
385
898
  }
386
- }
387
899
 
388
- } catch (err) {
389
- if (!err.already_reported) {
390
- console.error(`\n❌ Erro inesperado: ${err.message}\n`);
900
+ const html = montarHTML(compilado.javascript, htmlExtra, cssExtra, path.basename(caminho));
901
+ fs.writeFileSync(saidaHTML, html, 'utf8');
902
+ console.log(`✅ Página gerada: ${saidaHTML}`);
903
+ iniciarServidor(html, porta, !flags.has('--no-open'));
904
+ } catch (erro) {
905
+ mostrarErro(erro, caminho, codigoFonte);
906
+ process.exitCode = 1;
391
907
  }
392
- process.exit(1);
393
- }
908
+ }
909
+
910
+ executarProjeto();
@@ -0,0 +1,17 @@
1
+ {
2
+ "iconDefinitions": {
3
+ "_rc": { "iconPath": "./rc.png" },
4
+ "_rcx": { "iconPath": "./rcx.png" },
5
+ "_rcc": { "iconPath": "./rcc.png" }
6
+ },
7
+ "fileExtensions": {
8
+ "rc": "_rc",
9
+ "rcx": "_rcx",
10
+ "rcc": "_rcc"
11
+ },
12
+ "fileNames": {
13
+ "index.rc": "_rc",
14
+ "index.rcx": "_rcx",
15
+ "index.rcc": "_rcc"
16
+ }
17
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "raizcode-ofc",
3
- "version": "1.9.0",
3
+ "version": "1.10.1",
4
4
  "description": "Linguagem de programação brasileira — escreva código em português.",
5
5
  "main": "compilador.js",
6
6
  "bin": {
package/exemplos/teste.js DELETED
@@ -1,6 +0,0 @@
1
- // Gerado automaticamente pelo Raizcode
2
- console.log("Iniciando sistema Raizcode...");
3
- let criador = "Riquefla";
4
- if (10 > 5) {
5
- console.log("O criador é " + criador);
6
- }