// 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,'
','
{requisitions&&Requisições internas}{budget&&Preparação de centros e dotações}'); 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}));