@ifc-lite/codegen 1.15.4 → 1.15.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +18 -15
  2. package/package.json +16 -6
  3. package/.turbo/turbo-build.log +0 -4
  4. package/CHANGELOG.md +0 -165
  5. package/INTEGRATION.md +0 -360
  6. package/generated/ifc4/entities.ts +0 -7400
  7. package/generated/ifc4/enums.ts +0 -2467
  8. package/generated/ifc4/index.ts +0 -17
  9. package/generated/ifc4/schema-registry.ts +0 -63186
  10. package/generated/ifc4/selects.ts +0 -315
  11. package/generated/ifc4/serializers.ts +0 -51
  12. package/generated/ifc4/test-compile.ts +0 -49
  13. package/generated/ifc4/type-ids.ts +0 -1682
  14. package/generated/ifc4/types.ts +0 -401
  15. package/generated/ifc4x3/entities.ts +0 -8300
  16. package/generated/ifc4x3/enums.ts +0 -3096
  17. package/generated/ifc4x3/index.ts +0 -17
  18. package/generated/ifc4x3/schema-registry.ts +0 -71489
  19. package/generated/ifc4x3/selects.ts +0 -312
  20. package/generated/ifc4x3/serializers.ts +0 -51
  21. package/generated/ifc4x3/test-compile.ts +0 -49
  22. package/generated/ifc4x3/type-ids.ts +0 -1882
  23. package/generated/ifc4x3/types.ts +0 -407
  24. package/src/cli.ts +0 -72
  25. package/src/crc32.ts +0 -77
  26. package/src/express-parser.ts +0 -457
  27. package/src/generator.ts +0 -268
  28. package/src/index.ts +0 -57
  29. package/src/rust-generator.ts +0 -714
  30. package/src/serialization-generator.ts +0 -74
  31. package/src/type-ids-generator.ts +0 -166
  32. package/src/typescript-generator.ts +0 -631
  33. package/test/express-parser.test.ts +0 -363
  34. package/test/integration.test.ts +0 -246
  35. package/test/serialization-generator.test.ts +0 -65
  36. package/test/typescript-generator.test.ts +0 -348
  37. package/tsconfig.json +0 -21
  38. package/vitest.config.ts +0 -18
@@ -1,631 +0,0 @@
1
- /* This Source Code Form is subject to the terms of the Mozilla Public
2
- * License, v. 2.0. If a copy of the MPL was not distributed with this
3
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
-
5
- /**
6
- * TypeScript Code Generator
7
- *
8
- * Generates TypeScript interfaces, types, and schema registry from parsed EXPRESS schemas.
9
- */
10
-
11
- import type {
12
- ExpressSchema,
13
- EntityDefinition,
14
- AttributeDefinition,
15
- EnumDefinition,
16
- SelectDefinition,
17
- TypeDefinition,
18
- } from './express-parser.js';
19
- import { getAllAttributes, getInheritanceChain } from './express-parser.js';
20
-
21
- export interface GeneratedCode {
22
- entities: string; // Entity interfaces
23
- types: string; // Type aliases
24
- enums: string; // Enum definitions
25
- selects: string; // Union types
26
- schemaRegistry: string; // Runtime schema metadata
27
- }
28
-
29
- /**
30
- * Generate all TypeScript code from EXPRESS schema
31
- */
32
- export function generateTypeScript(schema: ExpressSchema): GeneratedCode {
33
- return {
34
- entities: generateEntityInterfaces(schema),
35
- types: generateTypeAliases(schema),
36
- enums: generateEnums(schema),
37
- selects: generateSelectTypes(schema),
38
- schemaRegistry: generateSchemaRegistry(schema),
39
- };
40
- }
41
-
42
- /**
43
- * Generate entity interfaces
44
- */
45
- function generateEntityInterfaces(schema: ExpressSchema): string {
46
- // Collect type, enum, and select names - filtering types that are also enums/selects
47
- const enumNamesSet = new Set(schema.enums.map(e => e.name));
48
- const selectNamesSet = new Set(schema.selects.map(s => s.name));
49
-
50
- // Types to import from types.ts (exclude those that are enums or selects)
51
- const typeNames = schema.types
52
- .filter(t => !enumNamesSet.has(t.name) && !selectNamesSet.has(t.name))
53
- .map(t => t.name);
54
- const enumNames = schema.enums.map(e => e.name);
55
- const selectNames = schema.selects.map(s => s.name);
56
-
57
- let code = `/**
58
- * IFC Entity Interfaces
59
- * Generated from EXPRESS schema: ${schema.name}
60
- *
61
- * DO NOT EDIT - This file is auto-generated
62
- */
63
-
64
- `;
65
-
66
- // Add imports for types, enums, and selects
67
- if (typeNames.length > 0) {
68
- code += `import type {\n ${typeNames.join(',\n ')},\n} from './types.js';\n\n`;
69
- }
70
-
71
- if (enumNames.length > 0) {
72
- code += `import type {\n ${enumNames.join(',\n ')},\n} from './enums.js';\n\n`;
73
- }
74
-
75
- if (selectNames.length > 0) {
76
- code += `import type {\n ${selectNames.join(',\n ')},\n} from './selects.js';\n\n`;
77
- }
78
-
79
- // Sort entities by dependency order (parents before children)
80
- const sortedEntities = topologicalSort(schema.entities);
81
-
82
- for (const entity of sortedEntities) {
83
- code += generateEntityInterface(entity, schema);
84
- code += '\n\n';
85
- }
86
-
87
- return code;
88
- }
89
-
90
- /**
91
- * Generate a single entity interface
92
- */
93
- function generateEntityInterface(
94
- entity: EntityDefinition,
95
- schema: ExpressSchema
96
- ): string {
97
- let code = '';
98
-
99
- // Add JSDoc comment
100
- code += `/**\n * ${entity.name}\n`;
101
- if (entity.isAbstract) {
102
- code += ` * @abstract\n`;
103
- }
104
- if (entity.supertype) {
105
- code += ` * @extends ${entity.supertype}\n`;
106
- }
107
- code += ` */\n`;
108
-
109
- // Generate interface
110
- code += `export interface ${entity.name}`;
111
-
112
- // Add extends clause
113
- if (entity.supertype) {
114
- code += ` extends ${entity.supertype}`;
115
- }
116
-
117
- code += ` {\n`;
118
-
119
- // Add attributes (only this level, not inherited)
120
- for (const attr of entity.attributes) {
121
- code += generateAttribute(attr);
122
- }
123
-
124
- code += `}`;
125
-
126
- return code;
127
- }
128
-
129
- /**
130
- * Generate an attribute declaration
131
- */
132
- function generateAttribute(attr: AttributeDefinition): string {
133
- let code = ` ${attr.name}`;
134
-
135
- // Add optional marker
136
- if (attr.optional) {
137
- code += '?';
138
- }
139
-
140
- code += ': ';
141
-
142
- // Map EXPRESS type to TypeScript type
143
- let tsType = mapExpressTypeToTypeScript(attr.type);
144
-
145
- // Wrap in array if needed
146
- // Note: attr.type may already contain [] for nested collections from the parser
147
- if (attr.isArray || attr.isList || attr.isSet) {
148
- // Parenthesize union element types so `boolean | null` becomes
149
- // `(boolean | null)[]` rather than the precedence-misparsed `boolean | null[]`.
150
- tsType = /\|/.test(tsType) ? `(${tsType})[]` : `${tsType}[]`;
151
- }
152
-
153
- code += tsType;
154
- code += ';\n';
155
-
156
- return code;
157
- }
158
-
159
- /**
160
- * Map EXPRESS types to TypeScript types
161
- */
162
- function mapExpressTypeToTypeScript(expressType: string): string {
163
- // Handle basic EXPRESS types
164
- const typeMap: Record<string, string> = {
165
- REAL: 'number',
166
- INTEGER: 'number',
167
- NUMBER: 'number',
168
- BOOLEAN: 'boolean',
169
- LOGICAL: 'boolean | null',
170
- STRING: 'string',
171
- BINARY: 'string',
172
- };
173
-
174
- // Handle STRING(N) and STRING(N) FIXED patterns
175
- if (/^STRING\s*\(/i.test(expressType)) {
176
- return 'string';
177
- }
178
-
179
- // Handle BINARY(N) patterns
180
- if (/^BINARY\s*\(/i.test(expressType)) {
181
- return 'string';
182
- }
183
-
184
- // Handle LIST [N:?] OF X, SET [N:?] OF X, ARRAY [N:M] OF X patterns
185
- const collectionMatch = expressType.match(/^(LIST|SET|ARRAY)\s*\[.*?\]\s*OF\s+(.+)$/i);
186
- if (collectionMatch) {
187
- const innerType = mapExpressTypeToTypeScript(collectionMatch[2].trim());
188
- // Parenthesize union element types to preserve `(union)[]` precedence.
189
- return /\|/.test(innerType) ? `(${innerType})[]` : `${innerType}[]`;
190
- }
191
-
192
- // Handle ENUMERATION OF (...) - these should be handled by enum generation
193
- if (/^ENUMERATION\s+OF/i.test(expressType)) {
194
- return 'string'; // Fallback to string for inline enumerations
195
- }
196
-
197
- // Check if it's a measure type (ends with Measure)
198
- if (expressType.endsWith('Measure')) {
199
- return 'number';
200
- }
201
-
202
- // Check if it's a simple type in our map
203
- const upperType = expressType.toUpperCase();
204
- if (typeMap[upperType]) {
205
- return typeMap[upperType];
206
- }
207
-
208
- // Check for IFC types
209
- if (expressType.startsWith('Ifc')) {
210
- return expressType;
211
- }
212
-
213
- // Default: use as-is (likely a custom type or entity reference)
214
- return expressType;
215
- }
216
-
217
- /**
218
- * Generate type aliases
219
- */
220
- function generateTypeAliases(schema: ExpressSchema): string {
221
- // Collect entity names and type names
222
- const entityNames = new Set(schema.entities.map(e => e.name));
223
- const enumNames = new Set(schema.enums.map(e => e.name));
224
- const selectNames = new Set(schema.selects.map(s => s.name));
225
-
226
- // Filter out types that are also defined as enums or selects (they have separate files)
227
- const typesToGenerate = schema.types.filter(
228
- t => !enumNames.has(t.name) && !selectNames.has(t.name)
229
- );
230
-
231
- // Track referenced entities
232
- const referencedEntities = new Set<string>();
233
-
234
- for (const type of typesToGenerate) {
235
- const mapped = mapExpressTypeToTypeScript(type.underlyingType);
236
- // Check if the underlying type or array element type is an entity
237
- const baseType = mapped.replace(/\[\]$/, '');
238
- if (entityNames.has(baseType)) {
239
- referencedEntities.add(baseType);
240
- }
241
- }
242
-
243
- let code = `/**
244
- * IFC Type Aliases
245
- * Generated from EXPRESS schema: ${schema.name}
246
- *
247
- * DO NOT EDIT - This file is auto-generated
248
- */
249
-
250
- `;
251
-
252
- // Add imports for referenced entities (circular but allowed with import type)
253
- if (referencedEntities.size > 0) {
254
- const sortedEntities = Array.from(referencedEntities).sort();
255
- code += `import type {\n ${sortedEntities.join(',\n ')},\n} from './entities.js';\n\n`;
256
- }
257
-
258
- for (const type of typesToGenerate) {
259
- code += `/** ${type.name} */\n`;
260
- code += `export type ${type.name} = ${mapExpressTypeToTypeScript(type.underlyingType)};\n\n`;
261
- }
262
-
263
- return code;
264
- }
265
-
266
- /**
267
- * Generate enum definitions
268
- */
269
- function generateEnums(schema: ExpressSchema): string {
270
- let code = `/**
271
- * IFC Enumerations
272
- * Generated from EXPRESS schema: ${schema.name}
273
- *
274
- * DO NOT EDIT - This file is auto-generated
275
- */
276
-
277
- `;
278
-
279
- for (const enumDef of schema.enums) {
280
- code += generateEnum(enumDef);
281
- code += '\n\n';
282
- }
283
-
284
- return code;
285
- }
286
-
287
- /**
288
- * Generate a single enum
289
- */
290
- function generateEnum(enumDef: EnumDefinition): string {
291
- let code = `/** ${enumDef.name} */\n`;
292
- code += `export enum ${enumDef.name} {\n`;
293
-
294
- for (const value of enumDef.values) {
295
- // Convert to PascalCase for enum member
296
- const memberName = value.toUpperCase();
297
- code += ` ${memberName} = '${value}',\n`;
298
- }
299
-
300
- code += `}`;
301
-
302
- return code;
303
- }
304
-
305
- /**
306
- * Generate SELECT type unions
307
- */
308
- function generateSelectTypes(schema: ExpressSchema): string {
309
- // Collect referenced types from selects
310
- const entityNames = new Set(schema.entities.map(e => e.name));
311
- const enumNames = new Set(schema.enums.map(e => e.name));
312
- const selectNames = new Set(schema.selects.map(s => s.name));
313
- // Types in types.ts (filtered: not enums, not selects)
314
- const typeNames = new Set(
315
- schema.types
316
- .filter(t => !enumNames.has(t.name) && !selectNames.has(t.name))
317
- .map(t => t.name)
318
- );
319
-
320
- // Track which names are referenced in selects
321
- const referencedEntities = new Set<string>();
322
- const referencedTypes = new Set<string>();
323
- const referencedEnums = new Set<string>();
324
-
325
- for (const select of schema.selects) {
326
- for (const typeName of select.types) {
327
- const mapped = mapExpressTypeToTypeScript(typeName);
328
- if (entityNames.has(mapped)) {
329
- referencedEntities.add(mapped);
330
- } else if (typeNames.has(mapped)) {
331
- referencedTypes.add(mapped);
332
- } else if (enumNames.has(mapped)) {
333
- referencedEnums.add(mapped);
334
- }
335
- // Selects referencing other selects are fine - they're in the same file
336
- }
337
- }
338
-
339
- let code = `/**
340
- * IFC SELECT Types (Unions)
341
- * Generated from EXPRESS schema: ${schema.name}
342
- *
343
- * DO NOT EDIT - This file is auto-generated
344
- */
345
-
346
- `;
347
-
348
- // Add imports for entities (circular but allowed with import type)
349
- if (referencedEntities.size > 0) {
350
- const sortedEntities = Array.from(referencedEntities).sort();
351
- code += `import type {\n ${sortedEntities.join(',\n ')},\n} from './entities.js';\n\n`;
352
- }
353
-
354
- // Add imports for types
355
- if (referencedTypes.size > 0) {
356
- const sortedTypes = Array.from(referencedTypes).sort();
357
- code += `import type {\n ${sortedTypes.join(',\n ')},\n} from './types.js';\n\n`;
358
- }
359
-
360
- // Add imports for enums
361
- if (referencedEnums.size > 0) {
362
- const sortedEnums = Array.from(referencedEnums).sort();
363
- code += `import type {\n ${sortedEnums.join(',\n ')},\n} from './enums.js';\n\n`;
364
- }
365
-
366
- for (const select of schema.selects) {
367
- code += `/** ${select.name} */\n`;
368
- code += `export type ${select.name} = `;
369
-
370
- // Join types with |
371
- const tsTypes = select.types.map(t => mapExpressTypeToTypeScript(t));
372
- code += tsTypes.join(' | ');
373
-
374
- code += ';\n\n';
375
- }
376
-
377
- return code;
378
- }
379
-
380
- /**
381
- * Generate schema registry with runtime metadata
382
- */
383
- function generateSchemaRegistry(schema: ExpressSchema): string {
384
- let code = `/**
385
- * IFC Schema Registry
386
- * Generated from EXPRESS schema: ${schema.name}
387
- *
388
- * Runtime metadata for IFC entities, types, and relationships.
389
- *
390
- * DO NOT EDIT - This file is auto-generated
391
- */
392
-
393
- export interface EntityMetadata {
394
- name: string;
395
- isAbstract: boolean;
396
- parent?: string;
397
- attributes: AttributeMetadata[];
398
- allAttributes?: AttributeMetadata[]; // Including inherited
399
- inheritanceChain?: string[]; // From root to entity
400
- }
401
-
402
- export interface AttributeMetadata {
403
- name: string;
404
- type: string;
405
- optional: boolean;
406
- isArray: boolean;
407
- isList: boolean;
408
- isSet: boolean;
409
- arrayBounds?: [number, number];
410
- }
411
-
412
- export interface SchemaRegistry {
413
- name: string;
414
- entities: Record<string, EntityMetadata>;
415
- types: Record<string, string>; // name -> underlying type
416
- enums: Record<string, string[]>; // name -> values
417
- selects: Record<string, string[]>; // name -> types
418
- }
419
-
420
- export const SCHEMA_REGISTRY: SchemaRegistry = {
421
- name: '${schema.name}',
422
-
423
- entities: {
424
- `;
425
-
426
- // Generate entity metadata
427
- for (const entity of schema.entities) {
428
- code += generateEntityMetadata(entity, schema);
429
- }
430
-
431
- code += ` },
432
-
433
- types: {
434
- `;
435
-
436
- // Generate type metadata (escape single quotes in underlying types)
437
- for (const type of schema.types) {
438
- const escapedType = type.underlyingType.replace(/'/g, "\\'").replace(/\n/g, ' ');
439
- code += ` ${type.name}: '${escapedType}',\n`;
440
- }
441
-
442
- code += ` },
443
-
444
- enums: {
445
- `;
446
-
447
- // Generate enum metadata (escape single quotes in values)
448
- for (const enumDef of schema.enums) {
449
- const escapedValues = enumDef.values.map(v => `'${v.replace(/'/g, "\\'")}'`);
450
- code += ` ${enumDef.name}: [${escapedValues.join(', ')}],\n`;
451
- }
452
-
453
- code += ` },
454
-
455
- selects: {
456
- `;
457
-
458
- // Generate select metadata (escape single quotes in types)
459
- for (const select of schema.selects) {
460
- const escapedTypes = select.types.map(t => `'${t.replace(/'/g, "\\'")}'`);
461
- code += ` ${select.name}: [${escapedTypes.join(', ')}],\n`;
462
- }
463
-
464
- code += ` },
465
- };
466
-
467
- /**
468
- * Get entity metadata by name (case-insensitive)
469
- */
470
- export function getEntityMetadata(typeName: string): EntityMetadata | undefined {
471
- // Normalize to IfcXxx format
472
- const normalized = normalizeTypeName(typeName);
473
- return SCHEMA_REGISTRY.entities[normalized];
474
- }
475
-
476
- /**
477
- * Get all attributes for an entity (including inherited)
478
- */
479
- export function getAllAttributesForEntity(typeName: string): AttributeMetadata[] {
480
- const metadata = getEntityMetadata(typeName);
481
- return metadata?.allAttributes || [];
482
- }
483
-
484
- /**
485
- * Get inheritance chain for an entity
486
- */
487
- export function getInheritanceChainForEntity(typeName: string): string[] {
488
- const metadata = getEntityMetadata(typeName);
489
- return metadata?.inheritanceChain || [];
490
- }
491
-
492
- /**
493
- * Check if a type is a known entity
494
- */
495
- export function isKnownEntity(typeName: string): boolean {
496
- const normalized = normalizeTypeName(typeName);
497
- return normalized in SCHEMA_REGISTRY.entities;
498
- }
499
-
500
- /**
501
- * Normalize type name to IfcXxx format
502
- */
503
- function normalizeTypeName(name: string): string {
504
- // Already in IfcPascalCase — return as-is
505
- if (name.startsWith('Ifc') && name.length > 3 && name[3] >= 'A' && name[3] <= 'Z') {
506
- return name;
507
- }
508
- // Convert UPPERCASE STEP names: IFCWALL -> IfcWall, IFCWALLTYPE -> IfcWallType
509
- const upper = name.toUpperCase();
510
- if (upper.startsWith('IFC')) {
511
- const rest = upper.substring(3);
512
- // Lookup by matching uppercase keys in the registry
513
- for (const key of Object.keys(SCHEMA_REGISTRY.entities)) {
514
- if (key.toUpperCase() === 'IFC' + rest) return key;
515
- }
516
- // Fallback: best-effort single-word conversion
517
- return 'Ifc' + rest.charAt(0) + rest.substring(1).toLowerCase();
518
- }
519
- return name;
520
- }
521
- `;
522
-
523
- return code;
524
- }
525
-
526
- /**
527
- * Generate metadata for a single entity
528
- */
529
- function generateEntityMetadata(
530
- entity: EntityDefinition,
531
- schema: ExpressSchema
532
- ): string {
533
- let code = ` ${entity.name}: {\n`;
534
- code += ` name: '${entity.name}',\n`;
535
- code += ` isAbstract: ${entity.isAbstract},\n`;
536
-
537
- if (entity.supertype) {
538
- code += ` parent: '${entity.supertype}',\n`;
539
- }
540
-
541
- code += ` attributes: [\n`;
542
- for (const attr of entity.attributes) {
543
- code += ` {\n`;
544
- code += ` name: '${attr.name}',\n`;
545
- code += ` type: '${attr.type}',\n`;
546
- code += ` optional: ${attr.optional},\n`;
547
- code += ` isArray: ${attr.isArray},\n`;
548
- code += ` isList: ${attr.isList},\n`;
549
- code += ` isSet: ${attr.isSet},\n`;
550
- if (attr.arrayBounds) {
551
- code += ` arrayBounds: [${attr.arrayBounds[0]}, ${attr.arrayBounds[1]}],\n`;
552
- }
553
- code += ` },\n`;
554
- }
555
- code += ` ],\n`;
556
-
557
- // Add all attributes (including inherited)
558
- const allAttrs = getAllAttributes(entity, schema);
559
- code += ` allAttributes: [\n`;
560
- for (const attr of allAttrs) {
561
- code += ` {\n`;
562
- code += ` name: '${attr.name}',\n`;
563
- code += ` type: '${attr.type}',\n`;
564
- code += ` optional: ${attr.optional},\n`;
565
- code += ` isArray: ${attr.isArray},\n`;
566
- code += ` isList: ${attr.isList},\n`;
567
- code += ` isSet: ${attr.isSet},\n`;
568
- if (attr.arrayBounds) {
569
- code += ` arrayBounds: [${attr.arrayBounds[0]}, ${attr.arrayBounds[1]}],\n`;
570
- }
571
- code += ` },\n`;
572
- }
573
- code += ` ],\n`;
574
-
575
- // Add inheritance chain
576
- const chain = getInheritanceChain(entity, schema);
577
- code += ` inheritanceChain: [${chain.map(c => `'${c}'`).join(', ')}],\n`;
578
-
579
- code += ` },\n`;
580
-
581
- return code;
582
- }
583
-
584
- /**
585
- * Topological sort of entities by dependency order
586
- * Ensures parent entities are generated before children
587
- */
588
- function topologicalSort(entities: EntityDefinition[]): EntityDefinition[] {
589
- const sorted: EntityDefinition[] = [];
590
- const visited = new Set<string>();
591
-
592
- function visit(entity: EntityDefinition) {
593
- if (visited.has(entity.name)) {
594
- return;
595
- }
596
-
597
- visited.add(entity.name);
598
-
599
- // Visit parent first
600
- if (entity.supertype) {
601
- const parent = entities.find(e => e.name === entity.supertype);
602
- if (parent) {
603
- visit(parent);
604
- }
605
- }
606
-
607
- sorted.push(entity);
608
- }
609
-
610
- for (const entity of entities) {
611
- visit(entity);
612
- }
613
-
614
- return sorted;
615
- }
616
-
617
- /**
618
- * Write generated code to files
619
- */
620
- export function writeGeneratedFiles(
621
- code: GeneratedCode,
622
- outputDir: string
623
- ): { entities: string; types: string; enums: string; selects: string; schema: string } {
624
- return {
625
- entities: `${outputDir}/entities.ts`,
626
- types: `${outputDir}/types.ts`,
627
- enums: `${outputDir}/enums.ts`,
628
- selects: `${outputDir}/selects.ts`,
629
- schema: `${outputDir}/schema-registry.ts`,
630
- };
631
- }