#!/usr/bin/env node // Exporteert de standaardmodellen van de configurator (HouseModel.MODELS) als house-design-JSON, // precies zoals Bestand › Opslaan in de configurator ze wegschrijft. // Gebruik: node tools/export-models.js [pad-naar-configurator] [uitvoermap] // Standaard: ../configurator → demo/modellen/ 'use strict'; const fs = require('fs'); const path = require('path'); const vm = require('vm'); const root = path.dirname(__dirname); const SRC = path.resolve(process.argv[2] || path.join(root, '..', 'configurator')); const OUT = path.resolve(process.argv[3] || path.join(root, 'demo', 'modellen')); // De rekenmodules in de volgorde van light.html; alleen wat nodig is om een model op te bouwen. const SCRIPTS = [ 'js/vendor/three.min.js', 'js/core/geometry.js', 'js/core/view-layers.js', 'js/wall/kozijn-model.js', 'js/core/ui-kit.js', 'js/wall/wall-model.js', 'js/floor/floor-model.js', 'js/roof/roof-model.js', 'js/roof/post-model.js', 'js/lighting/lighting-model.js', 'js/elektra/elektra-model.js', 'js/sandwich/sandwich-model.js', 'js/wall/wall.js', 'js/floor/floor.js', 'js/unit/unit-model.js', 'js/room/room-model.js', 'js/interior/fixture-model.js', 'js/house/chimney-model.js', 'js/house/house-model.js' ]; // Minimale browseromgeving: de modules raken het DOM alleen bij het tekenen, niet bij het opbouwen. const noop = () => {}; const el = () => new Proxy(function () {}, { get: (t, k) => (k === Symbol.toPrimitive ? () => '' : el()), apply: () => el(), construct: () => el() }); const ctx = { console, Math, JSON, Date, setTimeout, clearTimeout, URL, Blob: function () {}, Image: function () { return el(); } }; ctx.window = ctx; ctx.self = ctx; ctx.globalThis = ctx; ctx.document = { createElement: () => el(), getElementById: () => null, querySelector: () => null, querySelectorAll: () => [], addEventListener: noop, head: el(), body: el(), currentScript: null }; ctx.navigator = { userAgent: 'node', platform: 'node', maxTouchPoints: 0 }; ctx.location = { href: 'http://localhost/', search: '' }; ctx.fetch = () => Promise.reject(new Error('geen fetch')); ctx.addEventListener = noop; vm.createContext(ctx); for (const s of SCRIPTS) { const file = path.join(SRC, s); // const/let op het hoogste niveau blijven in het script; zet de modulenamen expliciet op het globale object. let code = fs.readFileSync(file, 'utf8'); const names = [...code.matchAll(/^(?:const|let|var|class)\s+([A-Z][A-Za-z0-9_]*)\s*=?/gm)].map(m => m[1]); code += '\n' + names.map(n => `try { globalThis.${n} = ${n}; } catch (e) {}`).join('\n'); vm.runInContext(code, ctx, { filename: file }); } const version = (fs.readFileSync(path.join(SRC, 'js/app.js'), 'utf8').match(/APP_VERSION\s*=\s*['"]([^'"]+)/) || [])[1] || ''; fs.mkdirSync(OUT, { recursive: true }); const index = []; for (const [key, def] of Object.entries(ctx.HouseModel.MODELS)) { const state = ctx.HouseModel.normalize(ctx.HouseModel.clone(def.create())); state.project = Object.assign({}, state.project, { naam: state.project.naam || def.label }); const design = { type: 'house-design', version: 1, exportedAt: new Date().toISOString(), model: key, configurator: version, ...state }; fs.writeFileSync(path.join(OUT, `${key}.json`), JSON.stringify(design)); index.push({ key, label: def.label }); console.log(`${key}.json ${def.label} (${state.units.length} units)`); } fs.writeFileSync(path.join(OUT, 'index.json'), JSON.stringify({ configurator: version, models: index }, null, 2) + '\n');