73 lines
2.3 KiB
JavaScript
73 lines
2.3 KiB
JavaScript
|
|
const fs = require('fs');
|
||
|
|
const path = require('path');
|
||
|
|
|
||
|
|
// Helpers de detecção de formato/cabeçalho compartilhados entre viabilidadeService e csvService.
|
||
|
|
// Antes estavam duplicados nos dois arquivos. Todos normalizam internamente (normalizeHeader
|
||
|
|
// é idempotente), então funcionam tanto com cabeçalhos crus quanto já normalizados.
|
||
|
|
|
||
|
|
function normalizeHeader(value) {
|
||
|
|
return String(value || '')
|
||
|
|
.trim()
|
||
|
|
.toLowerCase()
|
||
|
|
.normalize('NFD')
|
||
|
|
.replace(/[̀-ͯ]/g, '')
|
||
|
|
.replace(/[_-]+/g, ' ')
|
||
|
|
.replace(/\s+/g, ' ');
|
||
|
|
}
|
||
|
|
|
||
|
|
function isExcelFile(filePath) {
|
||
|
|
const ext = path.extname(filePath).toLowerCase();
|
||
|
|
if (['.xls', '.xlsx'].includes(ext)) return true;
|
||
|
|
|
||
|
|
const fileStart = fs.readFileSync(filePath).subarray(0, 512);
|
||
|
|
const signature = fileStart.subarray(0, 8);
|
||
|
|
const isZipBasedXlsx = signature[0] === 0x50 && signature[1] === 0x4b;
|
||
|
|
const isOleBasedXls = signature[0] === 0xd0
|
||
|
|
&& signature[1] === 0xcf
|
||
|
|
&& signature[2] === 0x11
|
||
|
|
&& signature[3] === 0xe0
|
||
|
|
&& signature[4] === 0xa1
|
||
|
|
&& signature[5] === 0xb1
|
||
|
|
&& signature[6] === 0x1a
|
||
|
|
&& signature[7] === 0xe1;
|
||
|
|
|
||
|
|
const startText = fileStart.toString('latin1').trimStart().toLowerCase();
|
||
|
|
const isHtmlExcel = startText.startsWith('<html')
|
||
|
|
|| startText.startsWith('<!doctype html')
|
||
|
|
|| startText.includes('<table');
|
||
|
|
|
||
|
|
return isZipBasedXlsx || isOleBasedXls || isHtmlExcel;
|
||
|
|
}
|
||
|
|
|
||
|
|
function detectDelimiter(line) {
|
||
|
|
const delimiters = [';', '\t', ','];
|
||
|
|
return delimiters
|
||
|
|
.map(delimiter => ({ delimiter, count: line.split(delimiter).length }))
|
||
|
|
.sort((a, b) => b.count - a.count)[0].delimiter;
|
||
|
|
}
|
||
|
|
|
||
|
|
function hasCepHeader(headers) {
|
||
|
|
return headers.map(normalizeHeader).some(header => /\bcep\b/.test(header) || header === 'codigo postal');
|
||
|
|
}
|
||
|
|
|
||
|
|
function hasAddressOrNumberHeader(headers) {
|
||
|
|
return headers.map(normalizeHeader).some(header => ['numero', 'num', 'nº', 'n°'].includes(header)
|
||
|
|
|| header.includes('endereco')
|
||
|
|
|| header.includes('logradouro'));
|
||
|
|
}
|
||
|
|
|
||
|
|
function hasGeoHeaders(headers) {
|
||
|
|
const norm = headers.map(normalizeHeader);
|
||
|
|
return norm.some(h => /\blat(itude)?\b/.test(h))
|
||
|
|
&& norm.some(h => /\blo?n[g]?(itude)?\b/.test(h));
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = {
|
||
|
|
normalizeHeader,
|
||
|
|
isExcelFile,
|
||
|
|
detectDelimiter,
|
||
|
|
hasCepHeader,
|
||
|
|
hasAddressOrNumberHeader,
|
||
|
|
hasGeoHeaders
|
||
|
|
};
|