raizcode-ofc 1.10.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,299 +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 com suporte a Temas de Ícones
30
97
  const pkgExtensao = {
31
- name: "raizcode-extension",
32
- displayName: "Raizcode",
33
- version: "1.9.0",
34
- publisher: "riquefla",
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
- },
43
- {
44
- id: "raizcode-estrutura",
45
- aliases: ["Raizcode Estrutura", "rcx"],
46
- extensions: [".rcx"]
47
- },
48
- {
49
- id: "raizcode-estilo",
50
- aliases: ["Raizcode Estilo", "rcc"],
51
- extensions: [".rcc"]
52
- }
53
- ],
54
- grammars: [
55
- {
56
- language: "raizcode",
57
- scopeName: "source.rc",
58
- path: "./syntaxes/raizcode.tmLanguage.json"
59
- }
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'] }
60
110
  ],
61
- // Ativa o suporte oficial aos seus ícones no VS Code
62
- iconThemes: [
63
- {
64
- id: "raizcode-icons",
65
- label: "Raizcode Icons",
66
- path: "./icons/raizcode-icon-theme.json"
67
- }
68
- ]
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
+ : []
69
115
  }
70
116
  };
71
117
 
72
- fs.writeFileSync(
73
- path.join(extDir, 'package.json'),
74
- JSON.stringify(pkgExtensao, null, 2)
75
- );
118
+ fs.writeFileSync(path.join(extDir, 'package.json'), JSON.stringify(pkgExtensao, null, 2));
76
119
 
77
- console.log("✅ Extensão Raizcode instalada! Reinicie o VS Code.");
78
- console.log("💡 Lembre-se de selecionar 'Raizcode Icons' em: Arquivo > Preferências > Tema de Ícone de Arquivo");
79
- } catch (e) {
80
- console.error("❌ Erro no setup:", e.message);
120
+ console.log('✅ Extensão Raizcode instalada.');
121
+ console.log(`📁 Local: ${extDir}`);
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;
81
130
  }
82
- process.exit();
83
131
  }
84
132
 
85
- // ─────────────────────────────────────────
86
- // UTILITÁRIOS
87
- // ─────────────────────────────────────────
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
+ }
88
147
 
89
- // Abre o navegador de forma correta em qualquer sistema
90
148
  function abrirNavegador(url) {
91
149
  const plataforma = os.platform();
92
- if (plataforma === 'win32') exec(`start ${url}`);
93
- else if (plataforma === 'darwin') exec(`open ${url}`);
94
- else if (process.env.TERM === 'xterm-termux' || fs.existsSync('/data/data/com.termux')) exec(`termux-open ${url}`);
95
- 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);
194
+ }
195
+
196
+ function criarErro(mensagem, arquivo, numeroLinha, codigoLinha = '') {
197
+ throw new ErroRaizcode(mensagem, arquivo, numeroLinha, codigoLinha);
96
198
  }
97
199
 
98
- // Reporta erros com número de linha para o usuário
99
- function erroDeCompilacao(mensagem, numeroLinha) {
100
- console.error(`\n❌ Erro na linha ${numeroLinha}: ${mensagem}`);
101
- console.error(` Verifique seu código .rc e tente novamente.\n`);
102
- process.exit(1);
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;');
103
207
  }
104
208
 
105
- // ─────────────────────────────────────────
106
- // TRADUTORES DE ESTRUTURA (RCX) E ESTILO (RCC)
107
- // ─────────────────────────────────────────
209
+ function escaparAtributo(valor) {
210
+ return escaparHTML(valor).replace(/`/g, '&#96;');
211
+ }
108
212
 
109
- function traduzirEstrutura(conteudo) {
110
- if (!conteudo) return "";
213
+ function substituirForaDeStrings(texto, transformador) {
214
+ let resultado = '';
215
+ let trecho = '';
216
+ let aspas = null;
217
+ let escapado = false;
111
218
 
112
- return conteudo.split('\n').map((linha, idx) => {
113
- let t = linha.trim();
114
- if (!t || t.startsWith('//')) return "";
219
+ const descarregar = () => {
220
+ if (!trecho) return;
221
+ resultado += aspas ? trecho : transformador(trecho);
222
+ trecho = '';
223
+ };
115
224
 
116
- // Captura string entre aspas de forma segura
117
- const pegarTexto = (cmd) => {
118
- const match = t.match(new RegExp(`^${cmd}\\s+"(.*)"`));
119
- return match ? match[1] : t.replace(cmd + ' ', '').replace(/"/g, '');
120
- };
225
+ for (const caractere of texto) {
226
+ trecho += caractere;
227
+
228
+ if (escapado) {
229
+ escapado = false;
230
+ continue;
231
+ }
232
+
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
+ }
121
245
 
122
- if (t.startsWith('titulo ')) return `<h1>${pegarTexto('titulo')}</h1>`;
123
- if (t.startsWith('subtitulo ')) return `<h2>${pegarTexto('subtitulo')}</h2>`;
124
- if (t.startsWith('texto ')) return `<p>${pegarTexto('texto')}</p>`;
125
- if (t.startsWith('link ')) {
126
- // link "Clique aqui" url "https://..."
127
- const m = t.match(/^link\s+"(.+?)"\s+url\s+"(.+?)"/);
128
- if (m) return `<a href="${m[2]}" target="_blank">${m[1]}</a>`;
129
- }
130
- if (t.startsWith('imagem ')) return `<img src="${pegarTexto('imagem')}" alt="imagem" style="max-width:100%;">`;
131
- if (t.startsWith('botao ')) {
132
- const m = t.match(/^botao\s+"(.+?)"\s+id\s+"(.+?)"/);
133
- if (m) return `<button id="${m[2]}">${m[1]}</button>`;
134
- return `<button>${pegarTexto('botao')}</button>`;
135
- }
136
- if (t.startsWith('entrada ')) {
137
- const m = t.match(/^entrada\s+id\s+"(.+?)"\s+dica\s+"(.+?)"/);
138
- if (m) return `<input id="${m[1]}" placeholder="${m[2]}">`;
139
- return `<input placeholder="${pegarTexto('entrada')}">`;
140
- }
141
- if (t.startsWith('caixa ')) return `<div class="${pegarTexto('caixa')}">`;
142
- if (t === 'fim') return `</div>`;
143
-
144
- return ``;
145
- }).join('\n');
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}`;
422
+ }
423
+
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;
146
430
  }
147
431
 
148
- function traduzirEstilo(conteudo) {
149
- if (!conteudo) return "";
150
- return conteudo
151
- .replace(/fundo:/g, 'background:')
152
- .replace(/cor-texto:/g, 'color:')
153
- .replace(/tamanho-texto:/g, 'font-size:')
154
- .replace(/margem:/g, 'margin:')
155
- .replace(/borda:/g, 'border:')
156
- .replace(/arredondado:/g, 'border-radius:')
157
- .replace(/verde-neon/g, '#00ff88')
158
- .replace(/amarelo-neon/g, '#ffff00')
159
- .replace(/azul-neon/g, '#00cfff')
160
- .replace(/vermelho-neon/g, '#ff4444')
161
- .replace(/ao-passar-mouse:/g, '&:hover')
162
- .replace(/estilo\s+/g, '.')
163
- .replace(/\bfim\b/g, '}');
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 = [];
438
+
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
+ }
461
+
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');
164
487
  }
165
488
 
166
- // ─────────────────────────────────────────
167
- // TRADUTOR PRINCIPAL (RC → JS)
168
- // ─────────────────────────────────────────
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
+ }
169
529
 
170
- function traduzir(linha, numeroLinha, contexto) {
171
- let t = linha.trim();
172
- if (!t || t.startsWith("//")) return linha; // preserva indentação
530
+ function traduzirLinha(linhaOriginal, numeroLinha, contexto) {
531
+ const linha = linhaOriginal.trim();
532
+ if (!linha || linha.startsWith('//')) return linhaOriginal;
173
533
 
174
- // Título da página
175
- if (t.startsWith('pagina ')) {
534
+ if (linha.startsWith('pagina ')) {
176
535
  contexto.ehSite = true;
177
- const titulo = t.replace(/^pagina\s+/, '');
536
+ const valor = linha.slice(7).trim();
537
+ const titulo = /^(["'`]).*\1$/.test(valor) ? traduzirExpressao(valor) : JSON.stringify(valor);
178
538
  return `document.title = ${titulo};`;
179
539
  }
180
540
 
181
- // UI: mostrar elemento na tela pelo id
182
- if (t.startsWith('mostrar_tela ')) {
183
- const m = t.match(/^mostrar_tela\s+"(.+?)"\s+em\s+"(.+?)"/);
184
- 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});})();`;
185
548
  }
186
549
 
187
- // Ao clicar: evento de clique
188
- if (t.startsWith('ao_clicar ')) {
189
- const m = t.match(/^ao_clicar\s+"(.+?)"/);
190
- if (m) return `document.getElementById("${m[1]}").addEventListener("click", function() {`;
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(){`;
191
557
  }
192
558
 
193
- // Comandos básicos
194
- if (t.startsWith('alerta ')) return `alert(${t.replace(/^alerta\s+/, '')});`;
195
- if (t.startsWith('mudar_fundo ')) return `document.body.style.background = ${t.replace(/^mudar_fundo\s+/, '')};`;
196
- if (t.startsWith('mostrar ')) return `console.log(${t.replace(/^mostrar\s+/, '')});`;
559
+ if (linha.startsWith('alerta ')) {
560
+ const valor = traduzirExpressao(linha.slice(7));
561
+ return `(typeof alert==="function"?alert(${valor}):console.log(${valor}));`;
562
+ }
197
563
 
198
- // Variável com parsing correto de strings com espaços
199
- if (t.startsWith('variavel ')) {
200
- const m = t.match(/^variavel\s+(\w+)\s+(.*)/);
201
- if (!m) erroDeCompilacao(`Sintaxe incorreta: "${t}" — Use: variavel nome "valor"`, numeroLinha);
202
- return `let ${m[1]} = ${m[2]};`;
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};`;
203
568
  }
204
569
 
205
- if (t.startsWith('constante ')) {
206
- const m = t.match(/^constante\s+(\w+)\s+(.*)/);
207
- if (!m) erroDeCompilacao(`Sintaxe incorreta: "${t}" — Use: constante nome "valor"`, numeroLinha);
208
- return `const ${m[1]} = ${m[2]};`;
570
+ if (linha.startsWith('mostrar ')) {
571
+ return `console.log(${traduzirExpressao(linha.slice(8))});`;
209
572
  }
210
573
 
211
- // Estruturas de controle
212
- if (t.startsWith('se ')) return `if (${t.replace(/^se\s+/, '').replace(/\bend\b/, '')}) {`;
213
- if (t === 'senao') return `} else {`;
214
- if (t.startsWith('senaose ')) return `} else if (${t.replace(/^senaose\s+/, '')}) {`;
215
- if (t === 'fim') return `}`;
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])};`;
578
+ }
216
579
 
217
- // Loops
218
- if (t.startsWith('repetir ')) {
219
- const m = t.match(/^repetir\s+(\d+)\s+vezes/);
220
- if (m) return `for (let _i = 0; _i < ${m[1]}; _i++) {`;
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])};`;
221
584
  }
222
- if (t.startsWith('enquanto ')) return `while (${t.replace(/^enquanto\s+/, '')}) {`;
223
585
 
224
- // Funções
225
- if (t.startsWith('funcao ')) {
226
- const m = t.match(/^funcao\s+(\w+)\s*\((.*)\)/);
227
- if (m) return `function ${m[1]}(${m[2]}) {`;
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])};`;
228
590
  }
229
- if (t.startsWith('retornar ')) return `return ${t.replace(/^retornar\s+/, '')};`;
230
591
 
231
- return t; // passa JS puro direto
232
- }
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])};`;
596
+ }
233
597
 
234
- // ─────────────────────────────────────────
235
- // VALIDAÇÃO DE BLOCOS
236
- // ─────────────────────────────────────────
237
- function validarBlocos(linhas) {
238
- let pilha = 0;
239
- linhas.forEach((linha, idx) => {
240
- const t = linha.trim();
241
- if (t.startsWith('se ') || t.startsWith('enquanto ') || t.startsWith('funcao ') || t.startsWith('repetir ') || t.startsWith('ao_clicar ')) pilha++;
242
- if (t === 'fim') pilha--;
243
- if (pilha < 0) erroDeCompilacao(`"fim" sobrando sem um bloco aberto.`, idx + 1);
244
- });
245
- if (pilha > 0) erroDeCompilacao(`Faltam ${pilha} "fim" para fechar bloco(s) abertos.`, linhas.length);
246
- }
598
+ if (linha.startsWith('senaose ')) {
599
+ return `} else if (${traduzirExpressao(linha.slice(8))}) {`;
600
+ }
247
601
 
248
- // ─────────────────────────────────────────
249
- // PONTO DE ENTRADA
250
- // ─────────────────────────────────────────
251
- if (!arquivoOuCmd) {
252
- console.log("🌱 Raizcode v1.9 — A linguagem brasileira");
253
- console.log(" Uso: raizcode <arquivo.rc>");
254
- console.log(" Setup VSCode: raizcode --setup");
255
- process.exit();
256
- }
602
+ if (linha === 'senao') return '} else {';
257
603
 
258
- const caminho = path.resolve(arquivoOuCmd);
259
- const nomeBase = caminho.substring(0, caminho.lastIndexOf('.'));
604
+ if (linha.startsWith('se ')) {
605
+ contexto.pilhaTraducao.push({ tipo: 'se', linha: numeroLinha });
606
+ return `if (${traduzirExpressao(linha.slice(3))}) {`;
607
+ }
260
608
 
261
- if (!fs.existsSync(caminho)) {
262
- console.error(`❌ Arquivo não encontrado: ${arquivoOuCmd}`);
263
- process.exit(1);
264
- }
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
+ }
265
616
 
266
- try {
267
- const arqRcx = nomeBase + '.rcx';
268
- const arqRcc = nomeBase + '.rcc';
617
+ if (linha.startsWith('enquanto ')) {
618
+ contexto.pilhaTraducao.push({ tipo: 'enquanto', linha: numeroLinha });
619
+ return `while (${traduzirExpressao(linha.slice(9))}) {`;
620
+ }
269
621
 
270
- let htmlExtra = "";
271
- let cssExtra = "";
272
- const contexto = { ehSite: false };
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
+ }
273
628
 
274
- if (fs.existsSync(arqRcx)) {
275
- htmlExtra = traduzirEstrutura(fs.readFileSync(arqRcx, 'utf-8'));
276
- contexto.ehSite = true;
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]}) {`;
277
634
  }
278
- if (fs.existsSync(arqRcc)) {
279
- cssExtra = traduzirEstilo(fs.readFileSync(arqRcc, 'utf-8'));
280
- contexto.ehSite = true;
635
+
636
+ if (linha.startsWith('retornar ')) {
637
+ return `return ${traduzirExpressao(linha.slice(9))};`;
281
638
  }
282
639
 
283
- const codigo = fs.readFileSync(caminho, 'utf-8');
284
- const linhas = codigo.split('\n');
640
+ if (linha === 'parar') return 'break;';
641
+ if (linha === 'continuar') return 'continue;';
285
642
 
286
- // Valida antes de compilar
287
- validarBlocos(linhas);
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);
654
+ }
655
+
656
+ return linhaOriginal;
657
+ }
288
658
 
289
- const js = linhas.map((linha, idx) => traduzir(linha, idx + 1, contexto)).join('\n');
659
+ function compilarRC(codigo, arquivo, opcoes = {}) {
660
+ const linhas = codigo.replace(/^\uFEFF/, '').split(/\r\n|\n|\r/);
661
+ validarBlocos(linhas, arquivo);
290
662
 
291
- const saidaJS = nomeBase + '.js';
292
- const saidaHTML = nomeBase + '.html';
663
+ const contexto = {
664
+ arquivo,
665
+ ehSite: false,
666
+ pilhaTraducao: [],
667
+ permitirJs: Boolean(opcoes.permitirJs),
668
+ estrito: Boolean(opcoes.estrito)
669
+ };
293
670
 
294
- if (contexto.ehSite) {
295
- const porta = args[1] ? parseInt(args[1]) : 3000;
296
- const estruturaHTML = `<!DOCTYPE html>
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
+ }
703
+
704
+ const local = obterLinhaDoErro(erro, arquivo);
705
+ const linhas = codigoFonte.split(/\r\n|\n|\r/);
706
+
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
+ }
717
+
718
+ console.error(`\n❌ ${erro.name || 'Erro'}: ${erro.message}\n`);
719
+ if (flags.has('--debug') && erro.stack) console.error(erro.stack);
720
+ }
721
+
722
+ function montarHTML(javascript, htmlExtra, cssExtra, nomeArquivo) {
723
+ const origemSegura = String(nomeArquivo).replace(/[\r\n]/g, '');
724
+ return `<!DOCTYPE html>
297
725
  <html lang="pt-br">
298
726
  <head>
299
727
  <meta charset="UTF-8">
@@ -304,7 +732,7 @@ try {
304
732
  body {
305
733
  background: #0d0d0d;
306
734
  color: #f0f0f0;
307
- font-family: sans-serif;
735
+ font-family: Arial, sans-serif;
308
736
  display: flex;
309
737
  justify-content: center;
310
738
  align-items: center;
@@ -318,13 +746,12 @@ try {
318
746
  padding: 40px;
319
747
  border-radius: 20px;
320
748
  background: #1a1a1a;
321
- box-shadow: 0 0 30px rgba(0,255,136,0.2);
322
749
  max-width: 800px;
323
750
  width: 100%;
324
751
  }
325
- h1 { color: #00ff88; text-shadow: 0 0 10px #00ff88; }
752
+ h1 { color: #00ff88; }
326
753
  h2 { color: #00cfff; }
327
- a { color: #00ff88; }
754
+ a { color: #00ff88; }
328
755
  button {
329
756
  background: #00ff88;
330
757
  color: #000;
@@ -335,7 +762,6 @@ try {
335
762
  cursor: pointer;
336
763
  margin: 8px;
337
764
  font-size: 1rem;
338
- transition: opacity 0.2s;
339
765
  }
340
766
  button:hover { opacity: 0.8; }
341
767
  input {
@@ -347,52 +773,138 @@ try {
347
773
  margin: 8px;
348
774
  font-size: 1rem;
349
775
  }
350
- ${cssExtra}
776
+ ${cssExtra}
351
777
  </style>
352
778
  </head>
353
779
  <body>
354
- <div id="raiz-app">${htmlExtra}</div>
780
+ <div id="raiz-app">
781
+ ${htmlExtra}
782
+ </div>
355
783
  <script>
356
- ${js}
784
+ window.addEventListener('error', function(event) {
785
+ console.error('Erro Raizcode:', event.message, 'linha', event.lineno);
786
+ });
787
+ ${javascript}
788
+ //# sourceURL=${origemSegura}
357
789
  </script>
358
790
  </body>
359
791
  </html>`;
792
+ }
360
793
 
361
- // Salva HTML para inspeção (opcional)
362
- 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
+ }
363
801
 
364
- const server = http.createServer((req, res) => {
365
- res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
366
- res.end(estruturaHTML);
802
+ resposta.writeHead(200, {
803
+ 'Content-Type': 'text/html; charset=utf-8',
804
+ 'Cache-Control': 'no-store'
367
805
  });
806
+ resposta.end(html);
807
+ });
368
808
 
369
- server.listen(porta, () => {
370
- console.log(`\n🌱 Raizcode Servidor ativo!`);
371
- console.log(`🌐 Acesse: http://localhost:${porta}`);
372
- console.log(`⏹ Pressione Ctrl+C para encerrar.\n`);
373
- abrirNavegador(`http://localhost:${porta}`);
374
- });
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
+ });
375
826
 
376
- // Mantém servidor vivo até Ctrl+C
377
- process.on('SIGINT', () => {
378
- console.log('\n👋 Servidor encerrado.');
379
- if (fs.existsSync(saidaHTML)) fs.unlinkSync(saidaHTML);
380
- 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')
381
886
  });
382
887
 
383
- } else {
384
- // Modo script: executa e apaga
385
- fs.writeFileSync(saidaJS, js);
386
- try {
387
- execSync(`node "${saidaJS}"`, { stdio: 'inherit' });
388
- } finally {
389
- 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] || '');
390
898
  }
391
- }
392
899
 
393
- } catch (err) {
394
- if (!err.already_reported) {
395
- 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;
396
907
  }
397
- process.exit(1);
398
- }
908
+ }
909
+
910
+ executarProjeto();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "raizcode-ofc",
3
- "version": "1.10.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
- }