96 lines
2.7 KiB
TypeScript
96 lines
2.7 KiB
TypeScript
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import { execSync } from 'child_process';
|
|
|
|
import type { ProductData } from './types';
|
|
import { normalizeValue } from './utils';
|
|
|
|
type ExcelRow = Record<string, unknown>;
|
|
export type ExcelMatch = { rows: ExcelRow[]; units: Record<string, string> };
|
|
|
|
const EXCEL_SOURCE_FILES = [
|
|
path.join(process.cwd(), 'data/source/high-voltage.xlsx'),
|
|
path.join(process.cwd(), 'data/source/medium-voltage-KM.xlsx'),
|
|
path.join(process.cwd(), 'data/source/low-voltage-KM.xlsx'),
|
|
path.join(process.cwd(), 'data/source/solar-cables.xlsx'),
|
|
];
|
|
|
|
let EXCEL_INDEX: Map<string, ExcelMatch> | null = null;
|
|
|
|
export function normalizeExcelKey(value: string): string {
|
|
return String(value || '')
|
|
.toUpperCase()
|
|
.replace(/-\d+$/g, '')
|
|
.replace(/[^A-Z0-9]+/g, '');
|
|
}
|
|
|
|
function loadExcelRows(filePath: string): ExcelRow[] {
|
|
const out = execSync(`npx -y xlsx-cli -j "${filePath}"`, {
|
|
encoding: 'utf8',
|
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
});
|
|
const trimmed = out.trim();
|
|
const jsonStart = trimmed.indexOf('[');
|
|
if (jsonStart < 0) return [];
|
|
const jsonText = trimmed.slice(jsonStart);
|
|
try {
|
|
return JSON.parse(jsonText) as ExcelRow[];
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
export function getExcelIndex(): Map<string, ExcelMatch> {
|
|
if (EXCEL_INDEX) return EXCEL_INDEX;
|
|
const idx = new Map<string, ExcelMatch>();
|
|
|
|
for (const file of EXCEL_SOURCE_FILES) {
|
|
if (!fs.existsSync(file)) continue;
|
|
const rows = loadExcelRows(file);
|
|
|
|
const unitsRow = rows.find(r => r && r['Part Number'] === 'Units') || null;
|
|
const units: Record<string, string> = {};
|
|
if (unitsRow) {
|
|
for (const [k, v] of Object.entries(unitsRow)) {
|
|
if (k === 'Part Number') continue;
|
|
const unit = normalizeValue(String(v ?? ''));
|
|
if (unit) units[k] = unit;
|
|
}
|
|
}
|
|
|
|
for (const r of rows) {
|
|
const pn = r?.['Part Number'];
|
|
if (!pn || pn === 'Units') continue;
|
|
const key = normalizeExcelKey(String(pn));
|
|
if (!key) continue;
|
|
const cur = idx.get(key);
|
|
if (!cur) {
|
|
idx.set(key, { rows: [r], units });
|
|
} else {
|
|
cur.rows.push(r);
|
|
if (Object.keys(cur.units).length < Object.keys(units).length) cur.units = units;
|
|
}
|
|
}
|
|
}
|
|
|
|
EXCEL_INDEX = idx;
|
|
return idx;
|
|
}
|
|
|
|
export function findExcelForProduct(product: ProductData): ExcelMatch | null {
|
|
const idx = getExcelIndex();
|
|
const candidates = [
|
|
product.name,
|
|
product.slug ? product.slug.replace(/-\d+$/g, '') : '',
|
|
product.sku,
|
|
product.translationKey,
|
|
].filter(Boolean) as string[];
|
|
|
|
for (const c of candidates) {
|
|
const key = normalizeExcelKey(c);
|
|
const match = idx.get(key);
|
|
if (match && match.rows.length) return match;
|
|
}
|
|
return null;
|
|
}
|