File size: 5,126 Bytes
55d9355
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// 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}`);