import { resolve } from 'node:path'; import type { Extension, NodeIO } from '@gltf-transform/core'; import { ALL_EXTENSIONS } from '@gltf-transform/extensions'; import draco3d from 'draco3dgltf'; import { MeshoptDecoder, MeshoptEncoder } from 'meshoptimizer'; import type { program } from './program.js'; import type { Session } from './session.js'; interface Config { extensions: (typeof Extension)[]; dependencies: Record; onProgramReady?: (params: { program: typeof program; io: NodeIO; Session: typeof Session }) => Promise; } export type CustomConfig = Partial; type ConfigModule = { default: CustomConfig }; let customConfigPromise: Promise | null = null; export async function defineConfig( configProvider: CustomConfig | (() => Promise), ): Promise { if (typeof configProvider === 'function') { configProvider = await configProvider(); } return configProvider; } export function createDefaultConfig(): Promise { return Promise.all([ draco3d.createDecoderModule(), draco3d.createEncoderModule(), MeshoptDecoder.ready, MeshoptEncoder.ready, ]).then(([decoder, encoder, _]) => { return { extensions: ALL_EXTENSIONS, dependencies: { 'draco3d.decoder': decoder, 'draco3d.encoder': encoder, 'meshopt.decoder': MeshoptDecoder, 'meshopt.encoder': MeshoptEncoder, }, onProgramReady: undefined, }; }); } export function loadConfig(path: string): void { path = resolve(process.cwd(), path); path = `file:${path}`; // Required on Windows. customConfigPromise = import(path).then(validateConfig) as Promise; } export function validateConfig(config: CustomConfig): CustomConfig { for (const extension of config.extensions || []) { if (!extension.EXTENSION_NAME) { throw new Error('Invalid extension in config.extensions.'); } } return config; } export async function getConfig(): Promise { const config = await createDefaultConfig(); if (customConfigPromise) { const { default: customConfig } = await customConfigPromise; Object.assign(config, customConfig); } return config; }