From 894b619af48cbecf6453d177c37c336f7cb94353 Mon Sep 17 00:00:00 2001 From: "Alexander J. Vincent" Date: Thu, 11 Jan 2024 23:11:18 -0800 Subject: [PATCH] #36, getTypeAugmentedStructure, part 1. --- stage_1_bootstrap/build/BuildClassesDriver.ts | 18 ++++ .../bootstrap/getTypeAugmentedStructure.ts | 82 ++++++++++++++ .../bootstrap/types/conversions.d.ts | 6 ++ .../bootstrap/getTypeAugmentedStructure.ts | 82 ++++++++++++++ .../source/bootstrap/types/conversions.d.ts | 6 ++ stage_2_fullset/snapshot/source/exports.ts | 4 + .../bootstrap/getTypeAugmentedStructure.ts | 101 ++++++++++++++++++ 7 files changed, 299 insertions(+) create mode 100644 stage_1_integration/bootstrap/getTypeAugmentedStructure.ts create mode 100644 stage_2_fullset/snapshot/source/bootstrap/getTypeAugmentedStructure.ts create mode 100644 stage_2_fullset/spec-snapshot/source/bootstrap/getTypeAugmentedStructure.ts diff --git a/stage_1_bootstrap/build/BuildClassesDriver.ts b/stage_1_bootstrap/build/BuildClassesDriver.ts index 3263d63b..55d8ec21 100644 --- a/stage_1_bootstrap/build/BuildClassesDriver.ts +++ b/stage_1_bootstrap/build/BuildClassesDriver.ts @@ -90,6 +90,24 @@ async function defineExistingExports( isType: true }); + dictionaries.publicExports.addExports({ + absolutePathToModule: path.join(distDir, "source/bootstrap/getTypeAugmentedStructure.ts"), + exportNames: [ + "getTypeAugmentedStructure", + ], + isDefaultExport: true, + isType: false + }); + + dictionaries.publicExports.addExports({ + absolutePathToModule: path.join(distDir, "source/bootstrap/getTypeAugmentedStructure.ts"), + exportNames: [ + "TypeNodeToTypeStructureConsole" + ], + isDefaultExport: false, + isType: true + }); + dictionaries.publicExports.addExports({ absolutePathToModule: path.join(distDir, "source/toolbox/ClassMembersMap.ts"), exportNames: ["ClassMembersMap"], diff --git a/stage_1_integration/bootstrap/getTypeAugmentedStructure.ts b/stage_1_integration/bootstrap/getTypeAugmentedStructure.ts new file mode 100644 index 00000000..1346404f --- /dev/null +++ b/stage_1_integration/bootstrap/getTypeAugmentedStructure.ts @@ -0,0 +1,82 @@ +// #region preamble +import assert from "node:assert/strict"; + +import { + Node +} from "ts-morph"; + +import type { + StructureImpls, +} from "../snapshot/source/exports.js"; + +import { + structureImplToNodeMap +} from "./structureToNodeMap.js"; + +import buildTypesForStructures from "./buildTypesForStructures.js"; + +import convertTypeNode from "./convertTypeNode.js"; + +import type { + BuildTypesForStructureFailures, + NodeWithStructures, + RootStructureWithConvertFailures, + TypeNodeToTypeStructureConsole, +} from "./types/conversions.js"; +// #endregion preamble + +export type { + TypeNodeToTypeStructureConsole +}; + +/** + * Get a structure for a node, with type structures installed throughout its descendants. + * @param rootNode - The node to start from. + * @param userConsole - a callback for conversion failures. + * @param assertNoFailures - if true, assert there are no conversion failures. + * @returns the root structure, the root node, and any failures during recursion. + */ +export default function getTypeAugmentedStructure( + rootNode: NodeWithStructures, + userConsole: TypeNodeToTypeStructureConsole, + assertNoFailures: boolean, +): RootStructureWithConvertFailures +{ + const map: ReadonlyMap = structureImplToNodeMap(rootNode); + assert(map.size > 0, "we should have some structures"); + + let rootStructure: StructureImpls | undefined; + for (const [structure, node] of map.entries()) { + if (node === rootNode) { + rootStructure = structure; + break; + } + } + assert(rootStructure, "we should have a root structure"); + + const subFailures: BuildTypesForStructureFailures[] = []; + + const failures = buildTypesForStructures( + map, + userConsole, + (nodeWithStructure: NodeWithStructures): StructureImpls => { + const subStructureResults: RootStructureWithConvertFailures = getTypeAugmentedStructure( + nodeWithStructure, userConsole, false + ); + subFailures.push(...subStructureResults.failures); + return subStructureResults.rootStructure; + }, + convertTypeNode + ); + failures.push(...subFailures); + + if (assertNoFailures) { + assert(failures.length === 0, "caller required no failures, but we did fail"); + } + + return { + rootStructure, + rootNode, + failures, + } +} diff --git a/stage_1_integration/bootstrap/types/conversions.d.ts b/stage_1_integration/bootstrap/types/conversions.d.ts index f0566d76..27919d53 100644 --- a/stage_1_integration/bootstrap/types/conversions.d.ts +++ b/stage_1_integration/bootstrap/types/conversions.d.ts @@ -43,3 +43,9 @@ export type TypeNodeToTypeStructure = ( _console: TypeNodeToTypeStructureConsole, subStructureResolver: SubstructureResolver, ) => stringTypeStructuresOrNull + +export interface RootStructureWithConvertFailures { + rootStructure: StructureImpls; + rootNode: NodeWithStructures; + failures: readonly BuildTypesForStructureFailures[]; +} diff --git a/stage_2_fullset/snapshot/source/bootstrap/getTypeAugmentedStructure.ts b/stage_2_fullset/snapshot/source/bootstrap/getTypeAugmentedStructure.ts new file mode 100644 index 00000000..413379f2 --- /dev/null +++ b/stage_2_fullset/snapshot/source/bootstrap/getTypeAugmentedStructure.ts @@ -0,0 +1,82 @@ +// #region preamble +import assert from "node:assert/strict"; + +import { + Node +} from "ts-morph"; + +import type { + StructureImpls, +} from "../exports.js"; + +import { + structureImplToNodeMap +} from "./structureToNodeMap.js"; + +import buildTypesForStructures from "./buildTypesForStructures.js"; + +import convertTypeNode from "./convertTypeNode.js"; + +import type { + BuildTypesForStructureFailures, + NodeWithStructures, + RootStructureWithConvertFailures, + TypeNodeToTypeStructureConsole, +} from "./types/conversions.js"; +// #endregion preamble + +export type { + TypeNodeToTypeStructureConsole +}; + +/** + * Get a structure for a node, with type structures installed throughout its descendants. + * @param rootNode - The node to start from. + * @param userConsole - a callback for conversion failures. + * @param assertNoFailures - if true, assert there are no conversion failures. + * @returns the root structure, the root node, and any failures during recursion. + */ +export default function getTypeAugmentedStructure( + rootNode: NodeWithStructures, + userConsole: TypeNodeToTypeStructureConsole, + assertNoFailures: boolean, +): RootStructureWithConvertFailures +{ + const map: ReadonlyMap = structureImplToNodeMap(rootNode); + assert(map.size > 0, "we should have some structures"); + + let rootStructure: StructureImpls | undefined; + for (const [structure, node] of map.entries()) { + if (node === rootNode) { + rootStructure = structure; + break; + } + } + assert(rootStructure, "we should have a root structure"); + + const subFailures: BuildTypesForStructureFailures[] = []; + + const failures = buildTypesForStructures( + map, + userConsole, + (nodeWithStructure: NodeWithStructures): StructureImpls => { + const subStructureResults: RootStructureWithConvertFailures = getTypeAugmentedStructure( + nodeWithStructure, userConsole, false + ); + subFailures.push(...subStructureResults.failures); + return subStructureResults.rootStructure; + }, + convertTypeNode + ); + failures.push(...subFailures); + + if (assertNoFailures) { + assert(failures.length === 0, "caller required no failures, but we did fail"); + } + + return { + rootStructure, + rootNode, + failures, + } +} diff --git a/stage_2_fullset/snapshot/source/bootstrap/types/conversions.d.ts b/stage_2_fullset/snapshot/source/bootstrap/types/conversions.d.ts index 60432201..6056b20e 100644 --- a/stage_2_fullset/snapshot/source/bootstrap/types/conversions.d.ts +++ b/stage_2_fullset/snapshot/source/bootstrap/types/conversions.d.ts @@ -43,3 +43,9 @@ export type TypeNodeToTypeStructure = ( _console: TypeNodeToTypeStructureConsole, subStructureResolver: SubstructureResolver, ) => stringTypeStructuresOrNull + +export interface RootStructureWithConvertFailures { + rootStructure: StructureImpls; + rootNode: NodeWithStructures; + failures: readonly BuildTypesForStructureFailures[]; +} diff --git a/stage_2_fullset/snapshot/source/exports.ts b/stage_2_fullset/snapshot/source/exports.ts index ddda368b..341df89c 100644 --- a/stage_2_fullset/snapshot/source/exports.ts +++ b/stage_2_fullset/snapshot/source/exports.ts @@ -3,6 +3,10 @@ export { type KindedTypeStructure, TypeStructureKind, } from "./base/TypeStructureKind.js"; +export { + default as getTypeAugmentedStructure, + type TypeNodeToTypeStructureConsole, +} from "./bootstrap/getTypeAugmentedStructure.js"; export { default as CallSignatureDeclarationImpl } from "./structures/CallSignatureDeclarationImpl.js"; export { default as ClassDeclarationImpl } from "./structures/ClassDeclarationImpl.js"; export { default as ClassStaticBlockDeclarationImpl } from "./structures/ClassStaticBlockDeclarationImpl.js"; diff --git a/stage_2_fullset/spec-snapshot/source/bootstrap/getTypeAugmentedStructure.ts b/stage_2_fullset/spec-snapshot/source/bootstrap/getTypeAugmentedStructure.ts new file mode 100644 index 00000000..e36998ca --- /dev/null +++ b/stage_2_fullset/spec-snapshot/source/bootstrap/getTypeAugmentedStructure.ts @@ -0,0 +1,101 @@ +import { + TypeNode, +} from "ts-morph"; + +import type { + ModuleSourceDirectory, +} from "#utilities/source/AsyncSpecModules.js"; + +import getTS_SourceFile from "#utilities/source/getTS_SourceFile.js"; + +import { + ClassDeclarationImpl, + /* + FunctionTypeStructureImpl, + */ + getTypeAugmentedStructure, + MethodDeclarationImpl, + TypeArgumentedTypeStructureImpl, + TypeNodeToTypeStructureConsole, + TypeParameterDeclarationImpl, +} from "#stage_two/snapshot/source/exports.js"; + +it("getTypeAugmentedStructure gets structures having type structures for types", () => { + const stageDir: ModuleSourceDirectory = { + isAbsolutePath: true, + pathToDirectory: "#stage_two", + }; + + const sourceFile = getTS_SourceFile(stageDir, "fixtures/stage_utilities/DefaultMap.ts"); + const DefaultWeakMapClass = sourceFile.getClassOrThrow("DefaultWeakMap"); + + function typeStructureConsole( + message: string, + failingTypeNode: TypeNode + ): void { + void(message); + void(failingTypeNode); + } + typeStructureConsole satisfies TypeNodeToTypeStructureConsole; + + const { + rootNode, + rootStructure, + failures + } = getTypeAugmentedStructure(DefaultWeakMapClass, typeStructureConsole, false); + expect(rootNode).toBe(DefaultWeakMapClass); + + expect(rootStructure).toBeInstanceOf(ClassDeclarationImpl); + if (!(rootStructure instanceof ClassDeclarationImpl)) + return; + + expect(rootStructure.typeParameters.length).toBe(2); + if (rootStructure.typeParameters.length === 2) { + const [firstParam, secondParam] = rootStructure.typeParameters; + expect(firstParam).toBeInstanceOf(TypeParameterDeclarationImpl); + if (firstParam instanceof TypeParameterDeclarationImpl) { + expect(firstParam.name).toBe("K"); + expect(firstParam.constraintStructure).toBe("object"); + } + + expect(secondParam).toBeInstanceOf(TypeParameterDeclarationImpl); + if (secondParam instanceof TypeParameterDeclarationImpl) { + expect(secondParam.name).toBe("V"); + expect(secondParam.constraintStructure).toBe(undefined); + expect(secondParam.constraint).toBe(undefined); + } + } + + const { extendsStructure } = rootStructure; + + expect(extendsStructure).toBeInstanceOf(TypeArgumentedTypeStructureImpl); + if (extendsStructure instanceof TypeArgumentedTypeStructureImpl) { + expect(extendsStructure.objectType).toBe("WeakMap"); + expect(extendsStructure.childTypes[0]).toBe("K"); + expect(extendsStructure.childTypes[1]).toBe("V"); + } + + expect(rootStructure.methods.length).toBe(1); + + const getDefaultMethod = rootStructure.methods[0]; + expect(getDefaultMethod).toBeInstanceOf(MethodDeclarationImpl); + if (getDefaultMethod instanceof MethodDeclarationImpl) { + expect(getDefaultMethod.typeParameters.length).toBe(0); + expect(getDefaultMethod.parameters.length).toBe(2); + /* + if (getDefaultMethod.parameters.length === 2) { + const [key, builder] = getDefaultMethod.parameters; + expect(key.typeStructure).toBe("K"); + + expect(builder.typeStructure).toBeInstanceOf(FunctionTypeStructureImpl) + if (builder.typeStructure instanceof FunctionTypeStructureImpl) { + expect(builder.typeStructure.typeParameters.length).toBe(0) + expect(builder.typeStructure.parameters.length).toBe(0); + expect(builder.typeStructure.returnType).toBe("V"); + } + } + */ + } + + expect(failures.length).withContext("failure count").toBe(0); +}); \ No newline at end of file