diff --git a/package.json5 b/package.json5 index 5ede49d..12d0a32 100644 --- a/package.json5 +++ b/package.json5 @@ -4,7 +4,8 @@ description: 'Another prototype for a math core', scripts: { build: 'tsc && cp etc/package.json build/', - go: 'pnpm build && pnpm start', + reflect: 'node tools/reflectTypes.mjs', + go: 'pnpm build && pnpm reflect && pnpm start', start: 'node --experimental-loader tsc-module-loader build', test: 'echo Error no test specified && exit 1', }, diff --git a/src/core/Dispatcher.ts b/src/core/Dispatcher.ts index 69b90ee..5b4bd1d 100644 --- a/src/core/Dispatcher.ts +++ b/src/core/Dispatcher.ts @@ -135,7 +135,7 @@ interface ImplementationBuilder< Specs & {[K in NewKeys]: DepCheck} > - ship(): Implementations + ship(): Implementations & { reflectedType5: string } } // And a function that actually provides the builder interface: @@ -189,7 +189,7 @@ function impBuilder< }, ship() { return (sofar as - Implementations) + Implementations & { reflectedType5: string }) } } } diff --git a/tools/reflectTypes.mjs b/tools/reflectTypes.mjs new file mode 100644 index 0000000..4ffa0ad --- /dev/null +++ b/tools/reflectTypes.mjs @@ -0,0 +1,58 @@ +import { readFileSync, writeFileSync, readdirSync } from 'node:fs' +import { fileURLToPath } from 'node:url' +import { dirname, join, relative } from 'node:path' + +const __filename = fileURLToPath(import.meta.url) +const __dirname = dirname(__filename) + +const buildDir = join(__dirname, '..', 'build') +const files = (await readdirSync(buildDir, { recursive: true })) + .filter(file => file.endsWith('.js')) + .map(file => join(buildDir, file)) + +for (const file of files) { + reflectType(file, { debug: true }) +} + +function reflectType(srcFile, options = { debug: false }) { + log(`Reflecting file "${relative(__dirname, srcFile)}"`) + + const defFile = srcFile.replace(/.js$/, '.d.ts') + const src = String(readFileSync(srcFile)) + const defs = String(readFileSync(defFile)) + + if (src.includes('.reflectedType5')) { + log(' Source file already contains reflected types and will be ignored') + return + } + + const matches = Array.from(src.matchAll(/[^\n](export +)?const +([\w$]+) += +implementations\b/g)) + + if (matches.length === 0) { + log(` No implementations found`) + return + } + + for (const match of matches) { + const name = match[2] + log(` Found an implementations definition: "${name}"`) + + const def = new RegExp(`export declare const ${name}: {(?:(?!\n}).)+\n}`, 's').exec(defs) + + if (def) { + log(` Injected type definitions for "${name}"`) + + const srcReflected = `${src}\n${name}.reflectedType5 = \`${def}\`\n` + + writeFileSync(srcFile, srcReflected) + } else { + log(` No type definitions found for "${name}"`) + } + } + + function log(...args) { + if (options.debug) { + console.log(...args) + } + } +}