@ifc-lite/codegen 1.14.2 → 1.15.0

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ifc-lite/codegen",
3
- "version": "1.14.2",
3
+ "version": "1.15.0",
4
4
  "description": "TypeScript code generator from IFC EXPRESS schemas",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -161,11 +161,12 @@ function parseEntity(name: string, body: string): EntityDefinition {
161
161
  let attributesSection = body;
162
162
 
163
163
  // Find where attributes section ends
164
+ // Match only section-level keywords at line start (not inside attribute types, e.g. "OF UNIQUE")
164
165
  const sectionMatches = [
165
- body.match(/\bWHERE\b/),
166
- body.match(/\bDERIVE\b/),
167
- body.match(/\bINVERSE\b/),
168
- body.match(/\bUNIQUE\b/),
166
+ body.match(/^\s*WHERE\b/m),
167
+ body.match(/^\s*DERIVE\b/m),
168
+ body.match(/^\s*INVERSE\b/m),
169
+ body.match(/^\s*UNIQUE\b/m),
169
170
  ].filter(m => m !== null) as RegExpMatchArray[];
170
171
 
171
172
  if (sectionMatches.length > 0) {
@@ -398,13 +399,15 @@ export function getAllAttributes(
398
399
  entity: EntityDefinition,
399
400
  schema: ExpressSchema
400
401
  ): AttributeDefinition[] {
401
- const attributes: AttributeDefinition[] = [];
402
+ // Collect attributes by walking up the inheritance chain,
403
+ // then reverse to get STEP order (parent-first / root → leaf)
404
+ const levels: AttributeDefinition[][] = [];
402
405
 
403
- // Walk up the inheritance chain
404
406
  let current: EntityDefinition | undefined = entity;
405
407
  while (current) {
406
- // Add attributes from this level
407
- attributes.push(...current.attributes);
408
+ if (current.attributes.length > 0) {
409
+ levels.push(current.attributes);
410
+ }
408
411
 
409
412
  // Move to parent
410
413
  if (current.supertype) {
@@ -414,6 +417,12 @@ export function getAllAttributes(
414
417
  }
415
418
  }
416
419
 
420
+ // Reverse: root attributes first (STEP positional order)
421
+ const attributes: AttributeDefinition[] = [];
422
+ for (let i = levels.length - 1; i >= 0; i--) {
423
+ attributes.push(...levels[i]);
424
+ }
425
+
417
426
  return attributes;
418
427
  }
419
428
 
@@ -498,10 +498,20 @@ export function isKnownEntity(typeName: string): boolean {
498
498
  * Normalize type name to IfcXxx format
499
499
  */
500
500
  function normalizeTypeName(name: string): string {
501
- // Convert IFCWALL -> IfcWall
502
- if (name.toUpperCase().startsWith('IFC')) {
503
- return 'Ifc' + name.substring(3).charAt(0).toUpperCase() +
504
- name.substring(4).toLowerCase();
501
+ // Already in IfcPascalCase — return as-is
502
+ if (name.startsWith('Ifc') && name.length > 3 && name[3] >= 'A' && name[3] <= 'Z') {
503
+ return name;
504
+ }
505
+ // Convert UPPERCASE STEP names: IFCWALL -> IfcWall, IFCWALLTYPE -> IfcWallType
506
+ const upper = name.toUpperCase();
507
+ if (upper.startsWith('IFC')) {
508
+ const rest = upper.substring(3);
509
+ // Lookup by matching uppercase keys in the registry
510
+ for (const key of Object.keys(SCHEMA_REGISTRY.entities)) {
511
+ if (key.toUpperCase() === 'IFC' + rest) return key;
512
+ }
513
+ // Fallback: best-effort single-word conversion
514
+ return 'Ifc' + rest.charAt(0) + rest.substring(1).toLowerCase();
505
515
  }
506
516
  return name;
507
517
  }
@@ -293,10 +293,10 @@ describe('EXPRESS Parser', () => {
293
293
 
294
294
  expect(allAttrs).toHaveLength(4);
295
295
  expect(allAttrs.map(a => a.name)).toEqual([
296
- 'Tag', // IfcProduct
297
- 'ObjectType', // IfcObject
298
296
  'GlobalId', // IfcRoot
299
297
  'Name', // IfcRoot
298
+ 'ObjectType', // IfcObject
299
+ 'Tag', // IfcProduct
300
300
  ]);
301
301
  });
302
302