Files
compras/scripts/sgf-admin-ui-build.mjs
T
2026-11-02 20:27:11 +02:00

49 lines
4.4 KiB
JavaScript

// Build in a private source copy; never writes the installed SGF or dependencies.
import fs from 'node:fs';
import path from 'node:path';
import crypto from 'node:crypto';
import {spawnSync} from 'node:child_process';
const host=fs.realpathSync(process.argv[2]||'');
if(fs.existsSync(path.join(host,'public/hot')))throw Error('Stop the source frontend watcher before freezing a candidate.');
const work=path.join('C:/laragon/data/sgf-tempo/run','candidate-ui-'+crypto.randomBytes(6).toString('hex'));
fs.mkdirSync(work,{recursive:true});
const replace=(text,old,next)=>{if(text.split(old).length!==2)throw Error('Frontend baseline changed: '+old.slice(0,70));return text.replace(old,next);};
fs.cpSync(path.join(host,'resources'),path.join(work,'resources'),{recursive:true});
for(const file of ['package.json','vite.config.js'])fs.copyFileSync(path.join(host,file),path.join(work,file));
fs.mkdirSync(path.join(work,'public/build'),{recursive:true});
fs.symlinkSync(path.join(host,'node_modules'),path.join(work,'node_modules'),'junction');
let app=fs.readFileSync(path.join(work,'resources/js/app.jsx'),'utf8');
app=replace(app,"const options = (unit === null ? ['company.view', 'company.edit', 'unit.create', 'access.manage'] : ['unit.view', 'unit.edit']).filter(p => can(p, unit));", "const options = (list?.catalogue?.[unit === null ? 'company' : 'unit'] ?? []).filter(p => can(p, unit));");
app=replace(app,'{labels[p]}','{list?.catalogue_labels?.[p] ?? labels[p] ?? p}');
app=replace(app,'{labels[g.permission]}','{list?.catalogue_labels?.[g.permission] ?? labels[g.permission] ?? g.permission}');
fs.writeFileSync(path.join(work,'resources/js/app.jsx'),app);
let home=fs.readFileSync(path.join(work,'resources/js/PurchasingHome.jsx'),'utf8');
home=replace(home,"const payables=unit!==null&&can('supplier_payables.view',unit);", "const payables=unit!==null&&can('supplier_payables.view',unit);\n const requisitions=unit!==null&&can('purchasing_requisitions.view',unit);");
home=replace(home,'<div className="actions">','<div className="actions">{requisitions&&<a href="/purchasing/internal-requisitions">Requisições internas</a>}{budget&&<a href="/purchasing/internal-requisitions/budget-setup">Preparação de centros e dotações</a>}');
home=replace(home,'!budget&&!payables&&','!budget&&!payables&&!requisitions&&');
fs.writeFileSync(path.join(work,'resources/js/PurchasingHome.jsx'),home);
let config=fs.readFileSync(path.join(work,'vite.config.js'),'utf8');
config=replace(config,'defineConfig({',`defineConfig({\n cacheDir: ${JSON.stringify(path.join(work,'vite-cache'))},`);
fs.writeFileSync(path.join(work,'vite.config.js'),config);
const result=spawnSync(process.execPath,[path.join(host,'node_modules/vite/bin/vite.js'),'build','--config',path.join(work,'vite.config.js'),'--configLoader','native'],{cwd:work,encoding:'utf8',windowsHide:true,maxBuffer:4*1024*1024,env:{...process.env,NODE_ENV:'production'}});
fs.writeFileSync(path.join(work,'build.log'),(result.stdout||'')+(result.stderr||''));
if(result.status!==0)throw Error('Candidate frontend build failed; private log '+path.join(work,'build.log'));
const original=JSON.parse(fs.readFileSync(path.join(work,'public/build/manifest.json'),'utf8'));
const assets={};
for(const chunk of Object.values(original))for(const file of [chunk.file,...(chunk.css||[]),...(chunk.assets||[])]){
if(!/^assets\/[A-Za-z0-9_-]+\.(js|css)$/.test(file))throw Error('Unsupported frontend asset '+file);
assets['integrations/sgf/frontend/'+file]=path.join(work,'public/build',file);
}
const manifest=JSON.parse(JSON.stringify(original));
for(const chunk of Object.values(manifest)){
chunk.file='modules/internal-requisitions/'+chunk.file;
if(chunk.css)chunk.css=chunk.css.map(p=>'modules/internal-requisitions/'+p);
if(chunk.assets)chunk.assets=chunk.assets.map(p=>'modules/internal-requisitions/'+p);
}
const manifestFile=path.join(work,'candidate-manifest.json');fs.writeFileSync(manifestFile,JSON.stringify(manifest,null,2)+'\n');
const overlays={'resources/js/app.jsx':path.join(work,'resources/js/app.jsx'),'resources/js/PurchasingHome.jsx':path.join(work,'resources/js/PurchasingHome.jsx'),'public/build/manifest.json':manifestFile};
const metadata={work,overlays,assets,dependency_install:false,source_host_modified:false};
const metadataFile=path.join(work,'candidate-ui.json');fs.writeFileSync(metadataFile,JSON.stringify(metadata,null,2));
console.log(JSON.stringify({metadata:metadataFile}));