feat: Try two different kinds of type reflection #5

Merged
glen merged 3 commits from feat/reflect-types into main 2024-10-21 17:00:59 +00:00
2 changed files with 25 additions and 28 deletions
Showing only changes of commit b9675c6d59 - Show all commits

View File

@ -102,6 +102,10 @@ type Implementations<
Specs extends Specifications<Signatures, NeedKeys, NeedList> Specs extends Specifications<Signatures, NeedKeys, NeedList>
> = {[K in NeedKeys]: ImpType<Signatures, K, Specs[K]>} > = {[K in NeedKeys]: ImpType<Signatures, K, Specs[K]>}
export interface ReflectedTypeInfo {
reflectedType5: string
}
// The builder interface that lets us assemble narrowly-typed Implementations: // The builder interface that lets us assemble narrowly-typed Implementations:
interface ImplementationBuilder< interface ImplementationBuilder<
Signatures extends GenSigs, Signatures extends GenSigs,
@ -135,7 +139,7 @@ interface ImplementationBuilder<
Specs & {[K in NewKeys]: DepCheck<RD, Signatures>} Specs & {[K in NewKeys]: DepCheck<RD, Signatures>}
> >
ship(): Implementations<Signatures, NeedKeys, NeedList, Specs> & { reflectedType5: string } ship(info?: ReflectedTypeInfo): Implementations<Signatures, NeedKeys, NeedList, Specs> & ReflectedTypeInfo
} }
// And a function that actually provides the builder interface: // And a function that actually provides the builder interface:
@ -187,9 +191,8 @@ function impBuilder<
Specs & {[K in NewKeys]: DepCheck<RD, Signatures, DepKeys>} Specs & {[K in NewKeys]: DepCheck<RD, Signatures, DepKeys>}
> >
}, },
ship() { ship(info?: ReflectedTypeInfo) {
return (sofar as return { ...sofar, ...info }
Implementations<Signatures, NeedKeys, NeedList, Specs> & { reflectedType5: string })
} }
} }
} }

View File

@ -11,44 +11,38 @@ const files = (await readdirSync(buildDir, { recursive: true }))
.map(file => join(buildDir, file)) .map(file => join(buildDir, file))
for (const file of files) { for (const file of files) {
reflectType(file, { debug: true }) reflectType5(file, { debug: true })
} }
function reflectType(srcFile, options = { debug: false }) { function reflectType5(srcFile, options = { debug: false }) {
log(`Reflecting file "${relative(__dirname, srcFile)}"`) log(`Reflecting file "${relative(__dirname, srcFile)}"`)
const defFile = srcFile.replace(/.js$/, '.d.ts') const defFile = srcFile.replace(/.js$/, '.d.ts')
const src = String(readFileSync(srcFile)) const src = String(readFileSync(srcFile))
const defs = String(readFileSync(defFile)) const defs = String(readFileSync(defFile))
if (src.includes('.reflectedType5')) { const typeDefMatches = defs.matchAll(/: ({(?:(?!\n}).)+\n}) & (?:(?!ReflectedTypeInfo).)+ReflectedTypeInfo/gs)
log(' Source file already contains reflected types and will be ignored') if (!typeDefMatches) {
log('No ReflectedTypeInfo found.')
return return
} }
const matches = Array.from(src.matchAll(/[^\n](export +)?const +([\w$]+) += +implementations\b/g)) const typeDefs = Array.from(typeDefMatches).map(def => def[1])
log(` ${typeDefs.length} ReflectedTypeInfo found`)
if (matches.length === 0) { let index = 0
log(` No implementations found`) const srcReflected = src.replaceAll(/(\s*)\.ship\(\)/g, () => {
return const def = typeDefs[index]
index++
return `.ship({ reflectedType5: \`${def}\` })`
})
log(` ReflectedTypeInfo injected in ${index} occurrences of .ship()`)
if (index !== typeDefs.length) {
log(' WARNING: not all ReflectedTypeInfo occurrences could be injected')
} }
for (const match of matches) { writeFileSync(srcFile, srcReflected)
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) { function log(...args) {
if (options.debug) { if (options.debug) {