From 22e3dbbfc7b1524a5e3fccb250691f5baa2d4a35 Mon Sep 17 00:00:00 2001 From: GSGerritsen Date: Thu, 31 Jul 2025 16:25:34 -0700 Subject: [PATCH 1/2] fix: handle external type references in TypeScript generation - Add tryLookupNode function to safely check for node existence - Update getJsType to generate placeholder types for external structs/enums/interfaces - Update getConcreteListType to handle missing struct nodes - Update generateNestedImports to skip missing import nodes This allows TypeScript generation to complete for files that import types from other compilation units, fixing the 'Failed to look up node id' errors. --- src/compiler/generators/imports.ts | 8 +++-- src/compiler/node-util.ts | 54 ++++++++++++++++++++++++------ 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/src/compiler/generators/imports.ts b/src/compiler/generators/imports.ts index 92df77f..4af5375 100644 --- a/src/compiler/generators/imports.ts +++ b/src/compiler/generators/imports.ts @@ -1,6 +1,6 @@ import type { CodeGeneratorFileContext } from "."; import { TS_FILE_ID } from "../constants"; -import { getFullClassName, hasNode, lookupNode } from "../node-util"; +import { getFullClassName, hasNode, lookupNode, tryLookupNode } from "../node-util"; import * as util from "../util"; import type * as schema from "../../capnp/schema"; @@ -60,7 +60,11 @@ export function generateNestedImports(ctx: CodeGeneratorFileContext): void { } } - const importNode = lookupNode(ctx, imp); + const importNode = tryLookupNode(ctx, imp); + if (!importNode) { + // Skip imports for nodes that aren't available in the current context + continue; + } const imports = getImportNodes(ctx, importNode) .flatMap((node) => { diff --git a/src/compiler/node-util.ts b/src/compiler/node-util.ts index 9832607..7c77d5c 100644 --- a/src/compiler/node-util.ts +++ b/src/compiler/node-util.ts @@ -29,13 +29,15 @@ export function getConcreteListType( if (elementTypeWhich === schema.Type.LIST) { return `$.PointerList(${getConcreteListType(ctx, elementType)})`; } else if (elementTypeWhich === schema.Type.STRUCT) { - const structNode = lookupNode(ctx, elementType.struct.typeId); - - if ( - structNode.struct.preferredListEncoding !== - schema.ElementSize.INLINE_COMPOSITE - ) { - throw new Error(E.GEN_FIELD_NON_INLINE_STRUCT_LIST); + const structNode = tryLookupNode(ctx, elementType.struct.typeId); + + if (structNode) { + if ( + structNode.struct.preferredListEncoding !== + schema.ElementSize.INLINE_COMPOSITE + ) { + throw new Error(E.GEN_FIELD_NON_INLINE_STRUCT_LIST); + } } return `$.CompositeList(${getJsType(ctx, elementType, false)})`; @@ -85,7 +87,12 @@ export function getJsType( } case schema.Type.ENUM: { - return getFullClassName(lookupNode(ctx, type.enum.typeId)); + const node = tryLookupNode(ctx, type.enum.typeId); + if (!node) { + // External enum type - generate a placeholder type name + return `Enum_${type.enum.typeId.toString(16)}`; + } + return getFullClassName(node); } case schema.Type.FLOAT32: @@ -105,7 +112,12 @@ export function getJsType( } case schema.Type.INTERFACE: { - return getFullClassName(lookupNode(ctx, type.interface.typeId)); + const node = tryLookupNode(ctx, type.interface.typeId); + if (!node) { + // External interface type - generate a placeholder type name + return `Interface_${type.interface.typeId.toString(16)}`; + } + return getFullClassName(node); } case schema.Type.LIST: { @@ -113,8 +125,13 @@ export function getJsType( } case schema.Type.STRUCT: { - const c = getFullClassName(lookupNode(ctx, type.struct.typeId)); - + const node = tryLookupNode(ctx, type.struct.typeId); + if (!node) { + // External struct type - generate a placeholder type name + const c = `Struct_${type.struct.typeId.toString(16)}`; + return constructor ? `$.StructCtor<${c}>` : c; + } + const c = getFullClassName(node); return constructor ? `$.StructCtor<${c}>` : c; } @@ -196,6 +213,21 @@ export function lookupNode( return node; } +/** + * Attempts to look up a Node in the schema by its ID without throwing. + * + * @param ctx - The file context containing all nodes from the schema + * @param lookup - Either a Node ID as a bigint, or an object containing an ID field + * @returns The found Node from the schema, or undefined if not found + */ +export function tryLookupNode( + ctx: CodeGeneratorFileContext, + lookup: { readonly id: bigint } | bigint, +): schema.Node | undefined { + const id = typeof lookup === "bigint" ? lookup : lookup.id; + return ctx.nodes.find((n) => n.id === id); +} + /** * Looks up source information for a Node in the schema by its ID. * From b4410533189f4e4ea886740f1c778a18d5de7688 Mon Sep 17 00:00:00 2001 From: GSGerritsen Date: Thu, 31 Jul 2025 16:37:43 -0700 Subject: [PATCH 2/2] fix: handle external type references in TypeScript generation Add tryLookupNode function to safely handle missing external type references without throwing errors. Generate placeholder type names for external types that aren't available in the current compilation context. --- src/compiler/generators/imports.ts | 7 ++++++- src/compiler/node-util.ts | 11 +++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/compiler/generators/imports.ts b/src/compiler/generators/imports.ts index 4af5375..db7c94b 100644 --- a/src/compiler/generators/imports.ts +++ b/src/compiler/generators/imports.ts @@ -1,6 +1,11 @@ import type { CodeGeneratorFileContext } from "."; import { TS_FILE_ID } from "../constants"; -import { getFullClassName, hasNode, lookupNode, tryLookupNode } from "../node-util"; +import { + getFullClassName, + hasNode, + lookupNode, + tryLookupNode, +} from "../node-util"; import * as util from "../util"; import type * as schema from "../../capnp/schema"; diff --git a/src/compiler/node-util.ts b/src/compiler/node-util.ts index 7c77d5c..57a5e21 100644 --- a/src/compiler/node-util.ts +++ b/src/compiler/node-util.ts @@ -31,13 +31,12 @@ export function getConcreteListType( } else if (elementTypeWhich === schema.Type.STRUCT) { const structNode = tryLookupNode(ctx, elementType.struct.typeId); - if (structNode) { - if ( - structNode.struct.preferredListEncoding !== + if ( + structNode && + structNode.struct.preferredListEncoding !== schema.ElementSize.INLINE_COMPOSITE - ) { - throw new Error(E.GEN_FIELD_NON_INLINE_STRUCT_LIST); - } + ) { + throw new Error(E.GEN_FIELD_NON_INLINE_STRUCT_LIST); } return `$.CompositeList(${getJsType(ctx, elementType, false)})`;