feat(compat): added mac OS compat

This commit is contained in:
jb
2026-04-24 16:37:24 -03:00
parent 8a0a99b2cf
commit a151e96ead
2 changed files with 87 additions and 46 deletions
+85 -44
View File
@@ -2,29 +2,94 @@
var path = require('path'); var path = require('path');
var fs = require('fs'); var fs = require('fs');
var http = require('http');
var { exec } = require('child_process'); var { exec } = require('child_process');
var { getPlatformDir } = require('../utils/platform'); var { getPlatformDir } = require('../utils/platform');
var DOCS = { var DOCS = {
desktop: { label: 'Desktop (Java)', subpath: path.join('docs', 'java', 'index.html') }, desktop: { label: 'Desktop (Java)', subpath: 'docs/java/index.html' },
mobile: { label: 'Mobile (React Native)', subpath: path.join('docs', 'react-native', 'index.html') }, mobile: { label: 'Mobile (React Native)', subpath: 'docs/react-native/index.html' },
}; };
function openUrl(filePath) { var MIME = {
var cmd; '.html': 'text/html; charset=utf-8',
if (process.platform === 'win32') cmd = 'start "" "' + filePath + '"'; '.css': 'text/css',
else if (process.platform === 'darwin') cmd = 'open "' + filePath + '"'; '.js': 'application/javascript',
else cmd = 'xdg-open "' + filePath + '"'; '.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) { exec(cmd, function (err) {
if (err) { if (err) {
console.error('Erro ao abrir o navegador: ' + err.message); console.error('Erro ao abrir o navegador: ' + err.message);
console.error('Abra manualmente: ' + filePath); console.error('Acesse manualmente: ' + url);
process.exit(1);
} }
}); });
} }
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() { function buildSelectionPage() {
return `<!DOCTYPE html> return `<!DOCTYPE html>
<html lang="pt-BR"> <html lang="pt-BR">
@@ -105,16 +170,8 @@ function buildSelectionPage() {
box-shadow: 0 6px 20px rgba(247, 130, 89, 0.25); box-shadow: 0 6px 20px rgba(247, 130, 89, 0.25);
} }
.btn-primary { .btn-primary { background: #F78259; color: #fff; }
background: #F78259; .btn-secondary { background: #fff3ef; color: #c05a30; border: 2px solid #f7c2ad; }
color: #fff;
}
.btn-secondary {
background: #fff3ef;
color: #c05a30;
border: 2px solid #f7c2ad;
}
.btn-icon { .btn-icon {
width: 38px; width: 38px;
@@ -127,10 +184,7 @@ function buildSelectionPage() {
flex-shrink: 0; flex-shrink: 0;
} }
.btn-secondary .btn-icon { .btn-secondary .btn-icon { background: rgba(247, 130, 89, 0.12); }
background: rgba(247, 130, 89, 0.12);
}
.btn-icon svg { width: 20px; height: 20px; } .btn-icon svg { width: 20px; height: 20px; }
.btn-primary .btn-icon svg { fill: #fff; } .btn-primary .btn-icon svg { fill: #fff; }
.btn-secondary .btn-icon svg { fill: #F78259; } .btn-secondary .btn-icon svg { fill: #F78259; }
@@ -144,11 +198,7 @@ function buildSelectionPage() {
margin-top: 2px; margin-top: 2px;
} }
footer { footer { margin-top: 32px; font-size: 0.78rem; color: #bbb; }
margin-top: 32px;
font-size: 0.78rem;
color: #bbb;
}
</style> </style>
</head> </head>
<body> <body>
@@ -163,7 +213,7 @@ function buildSelectionPage() {
<p class="subtitle">Selecione abaixo qual documentação você deseja consultar.</p> <p class="subtitle">Selecione abaixo qual documentação você deseja consultar.</p>
<div class="buttons"> <div class="buttons">
<a class="btn btn-primary" href="docs/java/index.html"> <a class="btn btn-primary" href="/docs/java/index.html">
<span class="btn-icon"> <span class="btn-icon">
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M20 3H4a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1zm-1 16H5V5h14v14zM7 12h2v5H7zm4-3h2v8h-2zm4-3h2v11h-2z"/> <path d="M20 3H4a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1zm-1 16H5V5h14v14zM7 12h2v5H7zm4-3h2v8h-2zm4-3h2v11h-2z"/>
@@ -175,7 +225,7 @@ function buildSelectionPage() {
</span> </span>
</a> </a>
<a class="btn btn-secondary" href="docs/react-native/index.html"> <a class="btn btn-secondary" href="/docs/react-native/index.html">
<span class="btn-icon"> <span class="btn-icon">
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M17 2H7a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2zm0 15H7V5h10v12zm-4 3h-2v-1h2v1z"/> <path d="M17 2H7a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2zm0 15H7V5h10v12zm-4 3h-2v-1h2v1z"/>
@@ -218,34 +268,25 @@ function register(program) {
console.error('Erro: documentação não encontrada em:'); console.error('Erro: documentação não encontrada em:');
console.error(' ' + docPath); console.error(' ' + docPath);
console.error(''); 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); process.exit(1);
} }
console.log('Abrindo documentação ' + DOCS[key].label + '...'); console.log('Abrindo documentação ' + DOCS[key].label + '...');
openUrl(docPath); serve(platformDir, '/' + DOCS[key].subpath, buildSelectionPage());
return; return;
} }
// No argument — generate and open selection page
var javaPath = path.join(platformDir, DOCS.desktop.subpath); var javaPath = path.join(platformDir, DOCS.desktop.subpath);
var reactNativePath = path.join(platformDir, DOCS.mobile.subpath); var reactNativePath = path.join(platformDir, DOCS.mobile.subpath);
var missingDocs = []; if (!fs.existsSync(javaPath) && !fs.existsSync(reactNativePath)) {
if (!fs.existsSync(javaPath)) missingDocs.push('desktop (Java)');
if (!fs.existsSync(reactNativePath)) missingDocs.push('mobile (React Native)');
if (missingDocs.length === 2) {
console.error('Erro: nenhuma documentação encontrada em: ' + platformDir); 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); 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...'); console.log('Abrindo seletor de documentação...');
openUrl(tmpFile); serve(platformDir, '/', buildSelectionPage());
}); });
} }
+2 -2
View File
@@ -18,7 +18,7 @@ var HTTPS_URL = 'https://git.davinti.com.br/davinTI/ide-config.git';
* Linux base: ~/. Windows base: %APPDATA%\ * Linux base: ~/. Windows base: %APPDATA%\
*/ */
function resolveSnippetsDir(ideEntry) { 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]; var rel = ideEntry.snippetsDir[platform];
if (!rel) throw new Error('snippetsDir not defined for platform ' + platform); if (!rel) throw new Error('snippetsDir not defined for platform ' + platform);
if (platform === 'win32') { if (platform === 'win32') {
@@ -91,7 +91,7 @@ function promptIde(ideKeys, manifest) {
// ── Installers ──────────────────────────────────────────────────────────────── // ── Installers ────────────────────────────────────────────────────────────────
function installVscodeFork(ideEntry, repoRoot) { 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]; var bin = ideEntry.bin[platform];
// Extension // Extension