diff --git a/src/commands/docs.js b/src/commands/docs.js index 98769a8..ce3fe48 100644 --- a/src/commands/docs.js +++ b/src/commands/docs.js @@ -2,29 +2,94 @@ var path = require('path'); var fs = require('fs'); +var http = require('http'); var { exec } = require('child_process'); var { getPlatformDir } = require('../utils/platform'); var DOCS = { - desktop: { label: 'Desktop (Java)', subpath: path.join('docs', 'java', 'index.html') }, - mobile: { label: 'Mobile (React Native)', subpath: path.join('docs', 'react-native', 'index.html') }, + desktop: { label: 'Desktop (Java)', subpath: 'docs/java/index.html' }, + mobile: { label: 'Mobile (React Native)', subpath: 'docs/react-native/index.html' }, }; -function openUrl(filePath) { - var cmd; - if (process.platform === 'win32') cmd = 'start "" "' + filePath + '"'; - else if (process.platform === 'darwin') cmd = 'open "' + filePath + '"'; - else cmd = 'xdg-open "' + filePath + '"'; +var MIME = { + '.html': 'text/html; charset=utf-8', + '.css': 'text/css', + '.js': 'application/javascript', + '.json': 'application/json', + '.png': 'image/png', + '.jpg': 'image/jpeg', + '.gif': 'image/gif', + '.svg': 'image/svg+xml', + '.ico': 'image/x-icon', + '.woff': 'font/woff', + '.woff2':'font/woff2', + '.ttf': 'font/ttf', +}; +function getMime(filePath) { + return MIME[path.extname(filePath).toLowerCase()] || 'application/octet-stream'; +} + +function openBrowser(url) { + var cmd; + if (process.platform === 'win32') cmd = 'start "" "' + url + '"'; + else if (process.platform === 'darwin') cmd = 'open "' + url + '"'; + else cmd = 'xdg-open "' + url + '"'; exec(cmd, function (err) { if (err) { console.error('Erro ao abrir o navegador: ' + err.message); - console.error('Abra manualmente: ' + filePath); - process.exit(1); + console.error('Acesse manualmente: ' + url); } }); } +function serve(platformDir, entryUrl, selectorHtml) { + var server = http.createServer(function (req, res) { + var urlPath = req.url.split('?')[0]; + + if (urlPath === '/' || urlPath === '/index.html') { + res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); + res.end(selectorHtml); + return; + } + + // Normalize URL path separators for Windows + var relative = urlPath.replace(/\//g, path.sep).replace(/^\\/, ''); + var filePath = path.join(platformDir, relative); + + // Prevent path traversal + if (!filePath.startsWith(platformDir + path.sep) && filePath !== platformDir) { + res.writeHead(403); + res.end(); + return; + } + + // Serve directory index + try { + if (fs.statSync(filePath).isDirectory()) { + filePath = path.join(filePath, 'index.html'); + } + } catch (_) {} + + fs.readFile(filePath, function (err, data) { + if (err) { res.writeHead(404); res.end(); return; } + res.writeHead(200, { 'Content-Type': getMime(filePath) }); + res.end(data); + }); + }); + + server.listen(0, '127.0.0.1', function () { + var port = server.address().port; + var url = 'http://127.0.0.1:' + port + entryUrl; + console.log('Servidor local em ' + url); + console.log('Pressione Ctrl+C para encerrar.'); + openBrowser(url); + }); + + process.on('SIGINT', function () { server.close(function () { process.exit(0); }); }); + process.on('SIGTERM', function () { server.close(function () { process.exit(0); }); }); +} + function buildSelectionPage() { return ` @@ -105,16 +170,8 @@ function buildSelectionPage() { box-shadow: 0 6px 20px rgba(247, 130, 89, 0.25); } - .btn-primary { - background: #F78259; - color: #fff; - } - - .btn-secondary { - background: #fff3ef; - color: #c05a30; - border: 2px solid #f7c2ad; - } + .btn-primary { background: #F78259; color: #fff; } + .btn-secondary { background: #fff3ef; color: #c05a30; border: 2px solid #f7c2ad; } .btn-icon { width: 38px; @@ -127,10 +184,7 @@ function buildSelectionPage() { flex-shrink: 0; } - .btn-secondary .btn-icon { - background: rgba(247, 130, 89, 0.12); - } - + .btn-secondary .btn-icon { background: rgba(247, 130, 89, 0.12); } .btn-icon svg { width: 20px; height: 20px; } .btn-primary .btn-icon svg { fill: #fff; } .btn-secondary .btn-icon svg { fill: #F78259; } @@ -144,11 +198,7 @@ function buildSelectionPage() { margin-top: 2px; } - footer { - margin-top: 32px; - font-size: 0.78rem; - color: #bbb; - } + footer { margin-top: 32px; font-size: 0.78rem; color: #bbb; } @@ -163,7 +213,7 @@ function buildSelectionPage() {

Selecione abaixo qual documentação você deseja consultar.

- + @@ -175,7 +225,7 @@ function buildSelectionPage() { - + @@ -218,34 +268,25 @@ function register(program) { console.error('Erro: documentação não encontrada em:'); console.error(' ' + docPath); console.error(''); - console.error('Execute "vitruvio update-base" para baixar os arquivos de plataforma.'); + console.error('Execute "vitruvio update base" para baixar os arquivos de plataforma.'); process.exit(1); } console.log('Abrindo documentação ' + DOCS[key].label + '...'); - openUrl(docPath); + serve(platformDir, '/' + DOCS[key].subpath, buildSelectionPage()); return; } - // No argument — generate and open selection page var javaPath = path.join(platformDir, DOCS.desktop.subpath); var reactNativePath = path.join(platformDir, DOCS.mobile.subpath); - var missingDocs = []; - if (!fs.existsSync(javaPath)) missingDocs.push('desktop (Java)'); - if (!fs.existsSync(reactNativePath)) missingDocs.push('mobile (React Native)'); - - if (missingDocs.length === 2) { + if (!fs.existsSync(javaPath) && !fs.existsSync(reactNativePath)) { console.error('Erro: nenhuma documentação encontrada em: ' + platformDir); - console.error('Execute "vitruvio update-base" para baixar os arquivos de plataforma.'); + console.error('Execute "vitruvio update base" para baixar os arquivos de plataforma.'); process.exit(1); } - var html = buildSelectionPage(); - var tmpFile = path.join(platformDir, 'docs-selector.html'); - fs.writeFileSync(tmpFile, html, 'utf8'); - console.log('Abrindo seletor de documentação...'); - openUrl(tmpFile); + serve(platformDir, '/', buildSelectionPage()); }); } diff --git a/src/commands/update-ide.js b/src/commands/update-ide.js index 53e1ee4..bdfd957 100644 --- a/src/commands/update-ide.js +++ b/src/commands/update-ide.js @@ -18,7 +18,7 @@ var HTTPS_URL = 'https://git.davinti.com.br/davinTI/ide-config.git'; * Linux base: ~/. Windows base: %APPDATA%\ */ function resolveSnippetsDir(ideEntry) { - var platform = process.platform === 'win32' ? 'win32' : 'linux'; + var platform = process.platform === 'win32' ? 'win32' : process.platform === 'darwin' ? 'darwin' : 'linux'; var rel = ideEntry.snippetsDir[platform]; if (!rel) throw new Error('snippetsDir not defined for platform ' + platform); if (platform === 'win32') { @@ -91,7 +91,7 @@ function promptIde(ideKeys, manifest) { // ── Installers ──────────────────────────────────────────────────────────────── function installVscodeFork(ideEntry, repoRoot) { - var platform = process.platform === 'win32' ? 'win32' : 'linux'; + var platform = process.platform === 'win32' ? 'win32' : process.platform === 'darwin' ? 'darwin' : 'linux'; var bin = ideEntry.bin[platform]; // Extension