#!/usr/bin/env node /* eslint-disable no-console */ const fs = require('fs'); const path = require('path'); const vm = require('vm'); require('dotenv').config(); const { closeDatabase, openDatabase } = require('../lib/sqlite'); const { ensurePlantSchema, rebuildPlantsCatalog } = require('../lib/plants'); let ts; try { ts = require('typescript'); } catch (error) { console.error('The rebuild script needs the "typescript" package in node_modules.'); console.error('Install dependencies in the repository root before running this script.'); process.exit(1); } const ROOT_DIR = path.resolve(__dirname, '..', '..'); const BATCH_1_PATH = path.join(ROOT_DIR, 'constants', 'lexiconBatch1.ts'); const BATCH_2_PATH = path.join(ROOT_DIR, 'constants', 'lexiconBatch2.ts'); const resolveTsFilePath = (fromFile, specifier) => { if (!specifier.startsWith('.')) return null; const fromDirectory = path.dirname(fromFile); const absoluteBase = path.resolve(fromDirectory, specifier); const candidates = [ absoluteBase, `${absoluteBase}.ts`, `${absoluteBase}.tsx`, path.join(absoluteBase, 'index.ts'), ]; for (const candidate of candidates) { if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) { return candidate; } } return null; }; const loadTsModule = (absolutePath, cache = new Map()) => { if (cache.has(absolutePath)) return cache.get(absolutePath); const source = fs.readFileSync(absolutePath, 'utf8'); const transpiled = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020, esModuleInterop: true, jsx: ts.JsxEmit.ReactJSX, }, fileName: absolutePath, reportDiagnostics: false, }).outputText; const module = { exports: {} }; cache.set(absolutePath, module.exports); const localRequire = (specifier) => { const resolvedTsPath = resolveTsFilePath(absolutePath, specifier); if (resolvedTsPath) { return loadTsModule(resolvedTsPath, cache); } return require(specifier); }; const sandbox = { module, exports: module.exports, require: localRequire, __dirname: path.dirname(absolutePath), __filename: absolutePath, console, process, Buffer, setTimeout, clearTimeout, }; vm.runInNewContext(transpiled, sandbox, { filename: absolutePath }); cache.set(absolutePath, module.exports); return module.exports; }; const loadBatchEntries = () => { const batch1Module = loadTsModule(BATCH_1_PATH); const batch2Module = loadTsModule(BATCH_2_PATH); const batch1Entries = batch1Module.LEXICON_BATCH_1_ENTRIES; const batch2Entries = batch2Module.LEXICON_BATCH_2_ENTRIES; if (!Array.isArray(batch1Entries) || !Array.isArray(batch2Entries)) { throw new Error('Could not load lexicon batch entries from TypeScript constants.'); } return [...batch1Entries, ...batch2Entries]; }; const main = async () => { const entries = loadBatchEntries(); const db = await openDatabase(); try { await ensurePlantSchema(db); const summary = await rebuildPlantsCatalog(db, entries, { source: 'local_batch_script', preserveExistingIds: true, enforceUniqueImages: true, }); console.log('Rebuild finished successfully.'); console.log(JSON.stringify(summary, null, 2)); } finally { await closeDatabase(db); } }; main().catch((error) => { console.error('Failed to rebuild plants from local batches.'); console.error(error instanceof Error ? error.stack || error.message : String(error)); process.exit(1); });