@ifc-lite/codegen 1.15.3 → 1.15.4

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.
@@ -1,4 +1,4 @@
1
1
 
2
- > @ifc-lite/codegen@1.15.3 build /home/runner/work/ifc-lite/ifc-lite/packages/codegen
2
+ > @ifc-lite/codegen@1.15.4 build /home/runner/work/ifc-lite/ifc-lite/packages/codegen
3
3
  > pnpm exec tsc
4
4
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,53 @@
1
1
  # @ifc-lite/codegen
2
2
 
3
+ ## 1.15.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [#1151](https://github.com/LTplus-AG/ifc-lite/pull/1151) [`bfd9004`](https://github.com/LTplus-AG/ifc-lite/commit/bfd9004daa17f481a7b33b5c3c11f620e6cd894d) Thanks [@louistrue](https://github.com/louistrue)! - De-duplicate the STEP serializer into a single source of truth. The
8
+ schema-agnostic STEP serialization logic (`serializeValue`, `generateHeader`,
9
+ `parseStepValue`, `ref`/`enumVal`/`isEntityRef`/`isEnumValue`, and the
10
+ registry-injected `toStepLineWithRegistry` / `generateStepFileWithRegistry`)
11
+ previously existed as four hand-synced copies — the codegen template plus three
12
+ generated `serializers.ts` files — which had already silently drifted (the
13
+ runtime copy carried a `?? []` hardening the template lacked). It now lives once
14
+ in `@ifc-lite/data`; the per-schema bundles (parser runtime + codegen outputs)
15
+ are thin re-exports that only bind their own `SCHEMA_REGISTRY` to the
16
+ registry-coupled helpers, so the copies can never diverge again. A codegen test
17
+ asserts the generated bundle stays a thin re-export rather than re-inlining
18
+ logic.
19
+
20
+ Also fixes the broken `generate:ifc4` script (it pointed at a non-existent
21
+ `schemas/IFC4.exp`; the real file is `schemas/IFC4_ADD2_TC1.exp`). No public
22
+ behaviour change: `@ifc-lite/parser` re-exports the same serializer symbols as
23
+ before; `@ifc-lite/data` gains the shared primitives; `@ifc-lite/codegen` now
24
+ declares `@ifc-lite/data` as a dependency since the generated bundle imports it.
25
+
26
+ - [#1143](https://github.com/LTplus-AG/ifc-lite/pull/1143) [`248f2c0`](https://github.com/LTplus-AG/ifc-lite/commit/248f2c09a4d61fa27dfeaba5511a2a641d4cd278) Thanks [@louistrue](https://github.com/louistrue)! - Preserve source IFC HEADER fields on round-trip export. Re-exporting an
27
+ imported file previously regenerated a fresh ifc-lite header, silently dropping
28
+ the source `FILE_DESCRIPTION` items (any `ViewDefinition [...]` label and vendor
29
+ identifier / coordinate-reference strings) and flattening the exact
30
+ `FILE_SCHEMA` token (e.g. `IFC4X3_ADD2` → `IFC4X3`, which some toolchains
31
+ reject).
32
+
33
+ The parser now captures the verbatim HEADER onto a new
34
+ `IfcDataStore.sourceHeader` (`IfcSourceHeader`, exported from `@ifc-lite/data`;
35
+ parser also exports `parseSourceHeader`), threaded through the worker transport.
36
+ `StepExporter` reproduces the source `FILE_DESCRIPTION` items and the exact
37
+ `FILE_SCHEMA` token when not converting schemas, falling back to parsing the
38
+ source bytes for cache-restored stores. Provenance stays honest:
39
+ `preprocessor_version` is set to `ifc-lite` while the source authoring tool is
40
+ kept as `originating_system`, and when mutations exist exactly one
41
+ `Re-exported by ifc-lite, N modification(s)` item is appended without removing
42
+ the source items. `generateHeader` now accepts description/author/organization
43
+ arrays plus a free-form schema token and STEP-escapes all fields; it also emits
44
+ a properly parenthesised `FILE_DESCRIPTION` list (the prior single-string form
45
+ was malformed STEP). Created-from-scratch (`IfcCreator`) and federated/merged
46
+ exports are unaffected — they keep their own provenance headers by design.
47
+
48
+ - Updated dependencies [[`bfd9004`](https://github.com/LTplus-AG/ifc-lite/commit/bfd9004daa17f481a7b33b5c3c11f620e6cd894d), [`248f2c0`](https://github.com/LTplus-AG/ifc-lite/commit/248f2c09a4d61fa27dfeaba5511a2a641d4cd278), [`ddae2b0`](https://github.com/LTplus-AG/ifc-lite/commit/ddae2b0024f071d00f9e6e4b77e0be3965412ec3)]:
49
+ - @ifc-lite/data@2.1.0
50
+
3
51
  ## 1.15.3
4
52
 
5
53
  ### Patch Changes
package/INTEGRATION.md CHANGED
@@ -44,6 +44,12 @@ npm run generate:ifc4x3
44
44
 
45
45
  Generated files: `packages/codegen/generated/ifc4/` or `generated/ifc4x3/`
46
46
 
47
+ > **Runtime dependency:** the generated `serializers.ts` is a thin re-export of
48
+ > the schema-agnostic STEP serializer in `@ifc-lite/data` (it only binds the
49
+ > bundle's `SCHEMA_REGISTRY`). Any project that consumes a generated bundle must
50
+ > therefore have `@ifc-lite/data` installed. This keeps the serialization logic
51
+ > in one place so the per-schema copies can never drift.
52
+
47
53
  ### Step 2: Copy Generated Files to Parser
48
54
 
49
55
  Option A: **Direct Copy** (recommended for testing)
@@ -1,11 +1,16 @@
1
1
  /**
2
2
  * Serialization Generator
3
3
  *
4
- * Generates TypeScript code for serializing IFC entities to STEP format.
4
+ * Emits the per-schema STEP serializer bundle file. The serialization LOGIC is
5
+ * schema-agnostic and lives once in `@ifc-lite/data`; this generator emits only
6
+ * a thin binding that re-exports those primitives and binds the bundle's
7
+ * `SCHEMA_REGISTRY` to the registry-coupled helpers. Keeping the logic in a
8
+ * single place means the generated copies can never drift from the runtime.
5
9
  */
6
10
  import type { ExpressSchema } from './express-parser.js';
7
11
  /**
8
- * Generate serialization support code
12
+ * Generate serialization support code — a thin re-export of the shared
13
+ * `@ifc-lite/data` STEP serializer with this schema's registry bound in.
9
14
  */
10
15
  export declare function generateSerializers(schema: ExpressSchema): string;
11
16
  //# sourceMappingURL=serialization-generator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"serialization-generator.d.ts","sourceRoot":"","sources":["../src/serialization-generator.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAyC,MAAM,qBAAqB,CAAC;AAGhG;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAsUjE"}
1
+ {"version":3,"file":"serialization-generator.d.ts","sourceRoot":"","sources":["../src/serialization-generator.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAqDjE"}
@@ -2,10 +2,11 @@
2
2
  * License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
4
  /**
5
- * Generate serialization support code
5
+ * Generate serialization support code — a thin re-export of the shared
6
+ * `@ifc-lite/data` STEP serializer with this schema's registry bound in.
6
7
  */
7
8
  export function generateSerializers(schema) {
8
- let code = `/* This Source Code Form is subject to the terms of the Mozilla Public
9
+ return `/* This Source Code Form is subject to the terms of the Mozilla Public
9
10
  * License, v. 2.0. If a copy of the MPL was not distributed with this
10
11
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
11
12
 
@@ -13,321 +14,49 @@ export function generateSerializers(schema) {
13
14
  * IFC Serialization Support
14
15
  * Generated from EXPRESS schema: ${schema.name}
15
16
  *
16
- * Utilities for serializing IFC entities to STEP format.
17
+ * Thin binding over the shared, schema-agnostic STEP serializer in
18
+ * \`@ifc-lite/data\`. All serialization logic lives there (single source of
19
+ * truth); only this bundle's \`SCHEMA_REGISTRY\` is bound to the registry-coupled
20
+ * helpers below.
17
21
  *
18
22
  * DO NOT EDIT - This file is auto-generated
19
23
  */
20
24
 
21
25
  import { SCHEMA_REGISTRY } from './schema-registry.js';
22
-
23
- /**
24
- * STEP value types
25
- */
26
- export type StepValue =
27
- | string
28
- | number
29
- | boolean
30
- | null
31
- | undefined
32
- | StepValue[]
33
- | EntityRef
34
- | EnumValue;
35
-
36
- /**
37
- * Entity reference (#123)
38
- */
39
- export interface EntityRef {
40
- ref: number;
41
- }
42
-
43
- /**
44
- * Enum value (.VALUE.)
45
- */
46
- export interface EnumValue {
47
- enum: string;
48
- }
49
-
50
- /**
51
- * Check if value is an entity reference
52
- */
53
- export function isEntityRef(value: unknown): value is EntityRef {
54
- return typeof value === 'object' && value !== null && 'ref' in value;
55
- }
56
-
57
- /**
58
- * Check if value is an enum value
59
- */
60
- export function isEnumValue(value: unknown): value is EnumValue {
61
- return typeof value === 'object' && value !== null && 'enum' in value;
62
- }
63
-
64
- /**
65
- * Create an entity reference
66
- */
67
- export function ref(id: number): EntityRef {
68
- return { ref: id };
69
- }
70
-
71
- /**
72
- * Create an enum value
73
- */
74
- export function enumVal(value: string): EnumValue {
75
- return { enum: value };
76
- }
77
-
78
- /**
79
- * Base interface for serializable entities
80
- */
81
- export interface StepEntity {
82
- /** Express ID (#123) */
83
- expressId: number;
84
- /** IFC type name */
85
- type: string;
86
- /** Attribute values */
87
- [key: string]: unknown;
88
- }
89
-
90
- /**
91
- * Serialize a single value to STEP format
92
- */
93
- export function serializeValue(value: StepValue): string {
94
- // Null/undefined -> $
95
- if (value === null || value === undefined) {
96
- return '$';
97
- }
98
-
99
- // Derived value -> *
100
- if (value === '*') {
101
- return '*';
102
- }
103
-
104
- // Boolean
105
- if (typeof value === 'boolean') {
106
- return value ? '.T.' : '.F.';
107
- }
108
-
109
- // Number
110
- if (typeof value === 'number') {
111
- if (!Number.isFinite(value)) {
112
- return '$';
113
- }
114
- // Use exponential notation for large/small numbers
115
- if (Math.abs(value) > 1e10 || (Math.abs(value) < 1e-10 && value !== 0)) {
116
- return value.toExponential(10).toUpperCase().replace('E+', 'E');
117
- }
118
- // Otherwise use fixed notation
119
- const str = value.toString();
120
- // Ensure there's a decimal point for REAL values
121
- return str.includes('.') ? str : str + '.';
122
- }
123
-
124
- // String
125
- if (typeof value === 'string') {
126
- return "'" + escapeStepString(value) + "'";
127
- }
128
-
129
- // Entity reference
130
- if (isEntityRef(value)) {
131
- return '#' + value.ref;
132
- }
133
-
134
- // Enum value
135
- if (isEnumValue(value)) {
136
- return '.' + value.enum + '.';
137
- }
138
-
139
- // Array/List
140
- if (Array.isArray(value)) {
141
- if (value.length === 0) {
142
- return '()';
143
- }
144
- return '(' + value.map(v => serializeValue(v as StepValue)).join(',') + ')';
145
- }
146
-
147
- // Object (shouldn't happen for valid STEP values)
148
- return '$';
149
- }
150
-
151
- /**
152
- * Escape a string for STEP format
153
- */
154
- function escapeStepString(str: string): string {
155
- return str
156
- .replace(/\\\\/g, '\\\\\\\\') // Backslash
157
- .replace(/'/g, "''"); // Single quote
158
- }
159
-
160
- /**
161
- * Serialize an entity to a STEP line
26
+ import {
27
+ generateHeader,
28
+ generateStepFileWithRegistry,
29
+ toStepLineWithRegistry,
30
+ type StepEntity,
31
+ } from '@ifc-lite/data';
32
+
33
+ export {
34
+ isEntityRef,
35
+ isEnumValue,
36
+ ref,
37
+ enumVal,
38
+ serializeValue,
39
+ generateHeader,
40
+ parseStepValue,
41
+ } from '@ifc-lite/data';
42
+ export type { StepValue, EntityRef, EnumValue, StepEntity } from '@ifc-lite/data';
43
+
44
+ /**
45
+ * Serialize an entity to a STEP line (bound to this bundle's schema).
162
46
  */
163
47
  export function toStepLine(entity: StepEntity): string {
164
- const schema = SCHEMA_REGISTRY.entities[entity.type];
165
- if (!schema) {
166
- throw new Error(\`Unknown entity type: \${entity.type}\`);
167
- }
168
-
169
- // Get all attributes in order
170
- const values: string[] = [];
171
- for (const attr of schema.allAttributes) {
172
- const value = entity[attr.name];
173
- values.push(serializeValue(value as StepValue));
174
- }
175
-
176
- return \`#\${entity.expressId}=\${entity.type.toUpperCase()}(\${values.join(',')});\`;
48
+ return toStepLineWithRegistry(SCHEMA_REGISTRY, entity);
177
49
  }
178
50
 
179
51
  /**
180
- * Generate STEP file header
181
- */
182
- export function generateHeader(options: {
183
- description?: string;
184
- author?: string;
185
- organization?: string;
186
- application?: string;
187
- schema: 'IFC2X3' | 'IFC4' | 'IFC4X3' | 'IFC5';
188
- filename?: string;
189
- }): string {
190
- const now = new Date().toISOString().replace(/[-:]/g, '').split('.')[0];
191
- const desc = options.description || 'ViewDefinition [CoordinationView]';
192
- const author = options.author || '';
193
- const org = options.organization || '';
194
- const app = options.application || 'ifc-lite';
195
- const filename = options.filename || 'output.ifc';
196
-
197
- return \`ISO-10303-21;
198
- HEADER;
199
- FILE_DESCRIPTION('\${desc}','2;1');
200
- FILE_NAME('\${filename}','\${now}',('\${author}'),('\${org}'),'\${app}','\${app}','');
201
- FILE_SCHEMA(('\${options.schema}'));
202
- ENDSEC;
203
- \`;
204
- }
205
-
206
- /**
207
- * Generate complete STEP file content
52
+ * Generate complete STEP file content (bound to this bundle's schema).
208
53
  */
209
54
  export function generateStepFile(
210
55
  entities: StepEntity[],
211
56
  options: Parameters<typeof generateHeader>[0]
212
57
  ): string {
213
- const header = generateHeader(options);
214
-
215
- // Sort entities by ID for deterministic output
216
- const sorted = [...entities].sort((a, b) => a.expressId - b.expressId);
217
-
218
- const data = sorted.map(e => toStepLine(e)).join('\\n');
219
-
220
- return \`\${header}DATA;
221
- \${data}
222
- ENDSEC;
223
- END-ISO-10303-21;
224
- \`;
225
- }
226
-
227
- /**
228
- * Parse a STEP value from string
229
- */
230
- export function parseStepValue(str: string): StepValue {
231
- str = str.trim();
232
-
233
- // Null
234
- if (str === '$') {
235
- return null;
236
- }
237
-
238
- // Derived
239
- if (str === '*') {
240
- return '*' as unknown as StepValue;
241
- }
242
-
243
- // Boolean
244
- if (str === '.T.') {
245
- return true;
246
- }
247
- if (str === '.F.') {
248
- return false;
249
- }
250
- if (str === '.U.') {
251
- return null; // Unknown/indeterminate
252
- }
253
-
254
- // Entity reference
255
- if (str.startsWith('#')) {
256
- return { ref: parseInt(str.substring(1), 10) };
257
- }
258
-
259
- // Enum
260
- if (str.startsWith('.') && str.endsWith('.')) {
261
- return { enum: str.substring(1, str.length - 1) };
262
- }
263
-
264
- // String
265
- if (str.startsWith("'") && str.endsWith("'")) {
266
- return unescapeStepString(str.substring(1, str.length - 1));
267
- }
268
-
269
- // List
270
- if (str.startsWith('(') && str.endsWith(')')) {
271
- return parseStepList(str);
272
- }
273
-
274
- // Number
275
- const num = parseFloat(str);
276
- if (!isNaN(num)) {
277
- return num;
278
- }
279
-
280
- // Unknown
281
- return str;
282
- }
283
-
284
- /**
285
- * Parse a STEP list
286
- */
287
- function parseStepList(str: string): StepValue[] {
288
- // Remove outer parentheses
289
- const inner = str.substring(1, str.length - 1).trim();
290
- if (inner === '') {
291
- return [];
292
- }
293
-
294
- const values: StepValue[] = [];
295
- let depth = 0;
296
- let current = '';
297
-
298
- for (let i = 0; i < inner.length; i++) {
299
- const char = inner[i];
300
-
301
- if (char === '(' || char === '[') {
302
- depth++;
303
- current += char;
304
- } else if (char === ')' || char === ']') {
305
- depth--;
306
- current += char;
307
- } else if (char === ',' && depth === 0) {
308
- values.push(parseStepValue(current));
309
- current = '';
310
- } else {
311
- current += char;
312
- }
313
- }
314
-
315
- if (current.trim()) {
316
- values.push(parseStepValue(current));
317
- }
318
-
319
- return values;
320
- }
321
-
322
- /**
323
- * Unescape a STEP string
324
- */
325
- function unescapeStepString(str: string): string {
326
- return str
327
- .replace(/''/g, "'")
328
- .replace(/\\\\\\\\/g, '\\\\');
58
+ return generateStepFileWithRegistry(SCHEMA_REGISTRY, entities, options);
329
59
  }
330
60
  `;
331
- return code;
332
61
  }
333
62
  //# sourceMappingURL=serialization-generator.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"serialization-generator.js","sourceRoot":"","sources":["../src/serialization-generator.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAW/D;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAqB;IACvD,IAAI,IAAI,GAAG;;;;;;oCAMuB,MAAM,CAAC,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4T9C,CAAC;IAEA,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"serialization-generator.js","sourceRoot":"","sources":["../src/serialization-generator.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAc/D;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAqB;IACvD,OAAO;;;;;;oCAM2B,MAAM,CAAC,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6C9C,CAAC;AACF,CAAC"}