// Extrai os conteúdos compartilhados da MESA sem copiar definições manualmente. import fs from 'node:fs'; import path from 'node:path'; import crypto from 'node:crypto'; import { createRequire } from 'node:module'; import { fileURLToPath } from 'node:url'; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const mesa = path.resolve(process.argv[2] || path.join(root, '..', 'mesa-frame')); const require = createRequire(path.join(mesa, 'frontend/package.json')); const { parse } = require('@babel/parser'); const sources = {}; function ast(relative) { const text = fs.readFileSync(path.join(mesa, relative), 'utf8'); sources[relative] = crypto.createHash('sha256').update(text).digest('hex'); return parse(text, { sourceType: 'module', plugins: ['jsx'] }); } function walk(node, fn) { if (!node || typeof node !== 'object') return; fn(node); for (const value of Object.values(node)) { if (Array.isArray(value)) value.forEach((child) => walk(child, fn)); else if (value && typeof value === 'object') walk(value, fn); } } function literal(node) { if (['StringLiteral', 'NumericLiteral', 'BooleanLiteral'].includes(node?.type)) return node.value; if (node?.type === 'NullLiteral') return null; if (node?.type === 'ArrayExpression') return node.elements.map(literal); if (node?.type === 'ObjectExpression') return Object.fromEntries(node.properties.map((prop) => [prop.key.name || prop.key.value, literal(prop.value)])); if (node?.type === 'UnaryExpression' && node.operator === '-') return -literal(node.argument); throw new Error(`Conteúdo não literal: ${node?.type}`); } function variable(tree, name) { let result; walk(tree, (node) => { if (node.type === 'VariableDeclarator' && node.id.name === name) result = node.init; }); if (!result) throw new Error(`Variável ausente na MESA: ${name}`); return result; } const refs = ast('frontend/src/mqc/data/referenceData.js'); const notes = ast('frontend/src/mqc/wordExportOptions.js'); const norms = ast('frontend/src/components/NormativeReference.jsx'); const reducers = ast('frontend/src/components/RedutoresAvaliacaoModal.jsx'); const dep = ast('frontend/src/mqc/calculations.js'); const models = ast('frontend/src/modelFoundation.js'); const global = ast('frontend/src/globalFoundation.js'); const exports = ast('frontend/src/mqc/exporters.js'); const redutoresTermos = {}; walk(variable(reducers, 'memoriaCompleta'), (node) => { if (node.type !== 'ObjectExpression') return; const props = Object.fromEntries(node.properties.filter((p) => p.type === 'ObjectProperty').map((p) => [p.key.name || p.key.value, p.value])); if (props.nome?.type === 'StringLiteral' && props.descricao?.type === 'StringLiteral') redutoresTermos[props.nome.value] = props.descricao.value; }); const functions = {}; for (const name of ['calcularDepreciacao', 'calcularKd', 'calcularEnquadramento']) { walk(dep, (node) => { if (node.type === 'FunctionDeclaration' && node.id.name === name) functions[name] = fs.readFileSync(path.join(mesa, 'frontend/src/mqc/calculations.js'), 'utf8').slice(node.start, node.end); }); } const annotations = {}; walk(exports, (node) => { if (node.type === 'FunctionDeclaration' && ['wordCubTypeExplanation', 'wordMixExplanation', 'wordLifeAdjustmentExplanation'].includes(node.id.name)) annotations[node.id.name] = fs.readFileSync(path.join(mesa, 'frontend/src/mqc/exporters.js'), 'utf8').slice(node.start, node.end); }); const areaNotes = []; walk(notes, (node) => { if (node.type === 'FunctionDeclaration' && node.id.name === 'wordAreaHeaderNote') { walk(node, (child) => { if (child.type === 'StringLiteral' && child.value.includes(': área')) areaNotes.push(child.value); }); } }); // As fórmulas e critérios passam a ter a mesma origem do conteúdo da interface. const catalog = { versao: 1, fontes: sources, notas: Object.fromEntries(literal(variable(notes, 'WORD_HEADER_NOTES'))), notas_area: areaNotes, referencias: Object.fromEntries(['bdiAdicionais', 'bdiTiposConstrucao', 'estadosConservacao', 'vidaUtilTipos', 'valorResidualReferencias', 'areaEquivalenteCoeficientes', 'siglasCub'].map((name) => [name, literal(variable(refs, name))])), normas: literal(variable(norms, 'REFERENCES')), redutores_termos: redutoresTermos, funcoes_mqc: functions, anotacoes_mqc: annotations, criterios_fc: literal(variable(global, 'FC_FOUNDATION_OPTIONS')), descricoes_modelo: literal(variable(models, 'MODEL_FOUNDATION_MANUAL_DESCRIPTIONS')), requisitos_modelo: literal(variable(models, 'MODEL_FOUNDATION_REQUIREMENTS')), }; const encoded = JSON.stringify(catalog); catalog.identificador = crypto.createHash('sha256').update(encoded).digest('hex').slice(0, 16); fs.writeFileSync(path.join(root, 'config/mesa_documentacao.json'), JSON.stringify(catalog, null, 2) + '\n'); // O mesmo pacote acompanha as novas exportações EVO para leitura histórica. fs.writeFileSync(path.join(mesa, 'frontend/src/evolutivo/documentacaoTecnica.json'), JSON.stringify(catalog, null, 2) + '\n'); console.log(`Conteúdo técnico sincronizado da MESA: ${catalog.identificador}`);