-
Notifications
You must be signed in to change notification settings - Fork 1
codegen
Layer 1 — Depends on express-parser. Generates TypeScript code from an EXPRESS AST. Used internally to generate the @ifc-factory/schema package.
pnpm add @ifc-factory/codegenGenerate TypeScript from an EXPRESS schema:
npx tsx packages/codegen/src/cli.ts <schema.exp> <output-dir>Via pnpm workspace:
pnpm build:schema
# Equivalent to: tsx packages/codegen/src/cli.ts schemas/IFC4X3_ADD2.exp packages/schema/src/generated/From the IFC4X3 ADD2 EXPRESS schema, the codegen produces:
| Output | Count | Directory | Description |
|---|---|---|---|
| Entity interfaces | 876 | entities/ |
One TypeScript interface per IFC entity |
| Enumerations | 243 | enums/ |
String enums matching STEP .VALUES.
|
| Type aliases | 132 | types/ |
Primitive type wrappers (IfcLabel = string) |
| Select types | 61 | selects/ |
Union types for EXPRESS SELECTs |
| Schema metadata | 1 | metadata/ |
Runtime attribute info for STEP parsing |
| Entity registry | 1 | metadata/ |
UPPERCASE → PascalCase name mapping |
| Barrel index | 1 | . |
Re-exports everything |
packages/schema/src/generated/
├── entities/
│ ├── IfcActionRequest.ts
│ ├── IfcActor.ts
│ ├── IfcActorRole.ts
│ ├── ...
│ └── IfcZone.ts (876 files)
├── enums/
│ ├── IfcActionRequestTypeEnum.ts
│ ├── ...
│ └── IfcWorkScheduleTypeEnum.ts (243 files)
├── types/
│ ├── IfcAbsorbedDoseMeasure.ts
│ ├── ...
│ └── IfcVolumetricFlowRateMeasure.ts (132 files)
├── selects/
│ ├── IfcActorSelect.ts
│ ├── ...
│ └── IfcValue.ts (61 files)
├── metadata/
│ ├── schema-metadata.ts (~12,000 lines)
│ └── entity-registry.ts (~900 lines)
└── index.ts (barrel export)
The codegen consists of specialized emitters, each responsible for one category:
Converts EXPRESS ENUMERATION to TypeScript string enums:
TYPE IfcWallTypeEnum = ENUMERATION OF
(MOVABLE, PARAPET, PARTITIONING, SOLIDWALL, STANDARD, USERDEFINED, NOTDEFINED);
END_TYPE;
→
export enum IfcWallTypeEnum {
MOVABLE = 'MOVABLE',
PARAPET = 'PARAPET',
PARTITIONING = 'PARTITIONING',
SOLIDWALL = 'SOLIDWALL',
STANDARD = 'STANDARD',
USERDEFINED = 'USERDEFINED',
NOTDEFINED = 'NOTDEFINED',
}Converts EXPRESS TYPE aliases to TypeScript types:
TYPE IfcLabel = STRING(255);
END_TYPE;
TYPE IfcLengthMeasure = REAL;
END_TYPE;
→
export type IfcLabel = string;
export type IfcLengthMeasure = number;Converts EXPRESS SELECT to TypeScript union types:
TYPE IfcValue = SELECT
(IfcMeasureValue, IfcSimpleValue, IfcDerivedMeasureValue);
END_TYPE;
→
export type IfcValue = IfcMeasureValue | IfcSimpleValue | IfcDerivedMeasureValue;Converts EXPRESS ENTITY to TypeScript interfaces:
ENTITY IfcWall
SUBTYPE OF (IfcBuildingElement);
PredefinedType : OPTIONAL IfcWallTypeEnum;
END_ENTITY;
→
import type { IfcBuildingElement } from './IfcBuildingElement.js';
export interface IfcWall extends IfcBuildingElement {
PredefinedType: string | null;
}Design decisions:
- Root entities (like
IfcRoot) getreadonly type: string - Child entities inherit
typewithout redeclaring it -
OPTIONALattributes becomeT | null - Entity references become
number(expressID) -
import typeused for cross-references (no runtime cycles)
Generates runtime schema metadata needed for STEP parsing:
export const SCHEMA_METADATA: Record<string, EntityMetadata> = {
IfcWall: {
parent: 'IfcBuildingElement',
abstract: false,
allAttributes: [
{ name: 'GlobalId', type: 'string', optional: false },
{ name: 'OwnerHistory', type: 'ref', optional: true },
{ name: 'Name', type: 'string', optional: true },
{ name: 'Description', type: 'string', optional: true },
{ name: 'ObjectType', type: 'string', optional: true },
{ name: 'ObjectPlacement', type: 'ref', optional: true },
{ name: 'Representation', type: 'ref', optional: true },
{ name: 'Tag', type: 'string', optional: true },
{ name: 'PredefinedType', type: 'enum', optional: true },
],
},
};allAttributes includes all inherited attributes in STEP order — essential for correct positional parsing.
Generates the UPPERCASE → PascalCase mapping:
export const ENTITY_REGISTRY: Record<string, string> = {
'IFCWALL': 'IfcWall',
'IFCDOOR': 'IfcDoor',
// ...
};Generates barrel export files. Uses export type for interfaces to avoid runtime overhead:
export type { IfcWall } from './entities/IfcWall.js';
export { IfcWallTypeEnum } from './enums/IfcWallTypeEnum.js';
export { SCHEMA_METADATA } from './metadata/schema-metadata.js';| EXPRESS Type | TypeScript Type | Notes |
|---|---|---|
INTEGER |
number |
|
REAL |
number |
|
NUMBER |
number |
|
BOOLEAN |
boolean |
|
LOGICAL |
boolean | null |
Three-valued: TRUE, FALSE, UNKNOWN |
STRING |
string |
Width/fixed constraints ignored |
STRING(n) |
string |
|
BINARY |
Uint8Array |
|
SET OF T |
T[] |
Bounds ignored in TypeScript |
LIST OF T |
T[] |
|
BAG OF T |
T[] |
|
ARRAY OF T |
T[] |
|
OPTIONAL T |
T | null |
|
ENUMERATION |
enum (string values) |
|
SELECT |
union type | |
| Entity reference | number |
expressID |
import { generateFromSchema } from '@ifc-factory/codegen';
import { parseExpress } from '@ifc-factory/express-parser';
import { readFileSync } from 'node:fs';
const source = readFileSync('schema.exp', 'utf-8');
const schema = parseExpress(source);
await generateFromSchema(schema, './output/');- express-parser — Provides the EXPRESS AST input
- schema — The generated output
- Architecture — Design decisions for generated code
Ifc-Factory
Getting Started
Concepts
Packages
API & Reference
Development