@openwebf/webf 0.23.2 → 0.23.10

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/dist/dart.js CHANGED
@@ -79,19 +79,55 @@ ${enumValues};
79
79
  String toString() => value;
80
80
  }`;
81
81
  }
82
+ function hasNullInUnion(type) {
83
+ if (!Array.isArray(type.value))
84
+ return false;
85
+ return type.value.some(t => t.value === declaration_1.FunctionArgumentType.null);
86
+ }
87
+ function isBooleanType(type) {
88
+ if (Array.isArray(type.value)) {
89
+ return type.value.some(t => t.value === declaration_1.FunctionArgumentType.boolean);
90
+ }
91
+ return type.value === declaration_1.FunctionArgumentType.boolean;
92
+ }
82
93
  function generateReturnType(type, enumName) {
83
94
  // Handle union types first (e.g., 'left' | 'center' | 'right')
84
95
  // so we don't incorrectly treat string literal unions as pointer types.
85
96
  if (Array.isArray(type.value)) {
86
- // If we have an enum name, use it; otherwise fall back to String
87
- return enumName || 'String';
97
+ // If we have an enum name, always use it (nullable handled separately)
98
+ if (enumName) {
99
+ return enumName;
100
+ }
101
+ // If this is a union that includes null and exactly one non-null type,
102
+ // generate the Dart type from the non-null part instead of falling back to String.
103
+ const trimmed = (0, utils_1.trimNullTypeFromType)(type);
104
+ if (!Array.isArray(trimmed.value)) {
105
+ return generateReturnType(trimmed, enumName);
106
+ }
107
+ // Fallback for complex unions: use String
108
+ return 'String';
88
109
  }
89
110
  if ((0, utils_1.isPointerType)(type)) {
90
111
  const pointerType = (0, utils_1.getPointerType)(type);
112
+ // Map TS typeof expressions to Dart dynamic
113
+ if (typeof pointerType === 'string' && pointerType.startsWith('typeof ')) {
114
+ return 'dynamic';
115
+ }
116
+ // Map references to known string enums to String in Dart
117
+ if (typeof pointerType === 'string' && declaration_1.EnumObject.globalEnumSet.has(pointerType)) {
118
+ return 'String';
119
+ }
91
120
  return pointerType;
92
121
  }
93
122
  if (type.isArray && typeof type.value === 'object' && !Array.isArray(type.value)) {
94
- return `${(0, utils_1.getPointerType)(type.value)}[]`;
123
+ const elem = (0, utils_1.getPointerType)(type.value);
124
+ if (typeof elem === 'string' && elem.startsWith('typeof ')) {
125
+ return `dynamic[]`;
126
+ }
127
+ if (typeof elem === 'string' && declaration_1.EnumObject.globalEnumSet.has(elem)) {
128
+ return 'String[]';
129
+ }
130
+ return `${elem}[]`;
95
131
  }
96
132
  // Handle when type.value is a ParameterType object (nested type)
97
133
  if (typeof type.value === 'object' && !Array.isArray(type.value) && type.value !== null) {
@@ -106,7 +142,8 @@ function generateReturnType(type, enumName) {
106
142
  return 'double';
107
143
  }
108
144
  case declaration_1.FunctionArgumentType.any: {
109
- return 'any';
145
+ // Dart doesn't have `any`; use `dynamic`.
146
+ return 'dynamic';
110
147
  }
111
148
  case declaration_1.FunctionArgumentType.boolean: {
112
149
  return 'bool';
@@ -135,41 +172,59 @@ function generateEventHandlerType(type) {
135
172
  }
136
173
  function generateAttributeSetter(propName, type, enumName) {
137
174
  // Attributes from HTML are always strings, so we need to convert them
175
+ const unionHasNull = hasNullInUnion(type);
138
176
  // Handle enum types
139
177
  if (enumName && Array.isArray(type.value)) {
178
+ if (unionHasNull) {
179
+ return `${propName} = value == 'null' ? null : ${enumName}.parse(value)`;
180
+ }
140
181
  return `${propName} = ${enumName}.parse(value)`;
141
182
  }
142
- switch (type.value) {
143
- case declaration_1.FunctionArgumentType.boolean:
144
- return `${propName} = value == 'true' || value == ''`;
145
- case declaration_1.FunctionArgumentType.int:
146
- return `${propName} = int.tryParse(value) ?? 0`;
147
- case declaration_1.FunctionArgumentType.double:
148
- return `${propName} = double.tryParse(value) ?? 0.0`;
149
- default:
150
- // String and other types can be assigned directly
151
- return `${propName} = value`;
183
+ const effectiveType = Array.isArray(type.value) && unionHasNull
184
+ ? (0, utils_1.trimNullTypeFromType)(type)
185
+ : type;
186
+ const baseSetter = (() => {
187
+ switch (effectiveType.value) {
188
+ case declaration_1.FunctionArgumentType.boolean:
189
+ return `${propName} = value == 'true' || value == ''`;
190
+ case declaration_1.FunctionArgumentType.int:
191
+ return `${propName} = int.tryParse(value) ?? 0`;
192
+ case declaration_1.FunctionArgumentType.double:
193
+ return `${propName} = double.tryParse(value) ?? 0.0`;
194
+ default:
195
+ // String and other types can be assigned directly
196
+ return `${propName} = value`;
197
+ }
198
+ })();
199
+ if (unionHasNull) {
200
+ const assignmentPrefix = `${propName} = `;
201
+ const rhs = baseSetter.startsWith(assignmentPrefix)
202
+ ? baseSetter.slice(assignmentPrefix.length)
203
+ : 'value';
204
+ return `${propName} = value == 'null' ? null : (${rhs})`;
152
205
  }
206
+ return baseSetter;
153
207
  }
154
- function generateAttributeGetter(propName, type, optional, enumName) {
208
+ function generateAttributeGetter(propName, type, isNullable, enumName) {
155
209
  // Handle enum types
156
210
  if (enumName && Array.isArray(type.value)) {
157
- return optional ? `${propName}?.value` : `${propName}.value`;
211
+ return isNullable ? `${propName}?.value` : `${propName}.value`;
158
212
  }
159
213
  // Handle nullable properties - they should return null if the value is null
160
- if (optional && type.value !== declaration_1.FunctionArgumentType.boolean) {
214
+ if (isNullable) {
161
215
  // For nullable properties, we need to handle null values properly
162
216
  return `${propName}?.toString()`;
163
217
  }
164
- // For non-nullable properties (including booleans), always convert to string
218
+ // For non-nullable properties, always convert to string
165
219
  return `${propName}.toString()`;
166
220
  }
167
221
  function generateAttributeDeleter(propName, type, optional) {
168
222
  // When deleting an attribute, we should reset it to its default value
223
+ if (isBooleanType(type)) {
224
+ // Booleans (including unions with null) default to false
225
+ return `${propName} = false`;
226
+ }
169
227
  switch (type.value) {
170
- case declaration_1.FunctionArgumentType.boolean:
171
- // Booleans default to false
172
- return `${propName} = false`;
173
228
  case declaration_1.FunctionArgumentType.int:
174
229
  // Integers default to 0
175
230
  return `${propName} = 0`;
@@ -201,10 +256,20 @@ function generateMethodDeclaration(method) {
201
256
  return `${methodName}(${args}): ${returnType};`;
202
257
  }
203
258
  function shouldMakeNullable(prop) {
204
- // Boolean properties should never be nullable in Dart, even if optional in TypeScript
205
- if (prop.type.value === declaration_1.FunctionArgumentType.boolean) {
259
+ const type = prop.type;
260
+ // Boolean properties are only nullable in Dart when explicitly unioned with `null`.
261
+ if (isBooleanType(type)) {
262
+ return hasNullInUnion(type);
263
+ }
264
+ // Dynamic (any) should not use nullable syntax; dynamic already allows null
265
+ if (type.value === declaration_1.FunctionArgumentType.any) {
206
266
  return false;
207
267
  }
268
+ // Properties with an explicit `null` in their type should be nullable,
269
+ // even if they are not marked optional in TypeScript.
270
+ if (hasNullInUnion(type)) {
271
+ return true;
272
+ }
208
273
  // Other optional properties remain nullable
209
274
  return prop.optional;
210
275
  }
@@ -287,8 +352,9 @@ interface ${object.name} {
287
352
  generateAttributeSetter: (propName, type) => {
288
353
  return generateAttributeSetter(propName, type, enumMap.get(propName));
289
354
  },
290
- generateAttributeGetter: (propName, type, optional) => {
291
- return generateAttributeGetter(propName, type, optional, enumMap.get(propName));
355
+ generateAttributeGetter: (propName, type, optional, prop) => {
356
+ const isNullable = prop ? shouldMakeNullable(prop) : optional;
357
+ return generateAttributeGetter(propName, type, isNullable, enumMap.get(propName));
292
358
  },
293
359
  generateAttributeDeleter,
294
360
  shouldMakeNullable,
@@ -73,3 +73,4 @@ class EnumObject {
73
73
  }
74
74
  }
75
75
  exports.EnumObject = EnumObject;
76
+ EnumObject.globalEnumSet = new Set();
package/dist/generator.js CHANGED
@@ -12,6 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.writeFileIfChanged = writeFileIfChanged;
15
16
  exports.dartGen = dartGen;
16
17
  exports.reactGen = reactGen;
17
18
  exports.vueGen = vueGen;
@@ -101,7 +102,7 @@ function getTypeFiles(source, excludePatterns) {
101
102
  try {
102
103
  const defaultIgnore = ['**/node_modules/**', '**/dist/**', '**/build/**', '**/example/**'];
103
104
  const ignore = excludePatterns ? [...defaultIgnore, ...excludePatterns] : defaultIgnore;
104
- const files = glob_1.glob.globSync("**/*.d.ts", {
105
+ const files = (0, glob_1.globSync)("**/*.d.ts", {
105
106
  cwd: source,
106
107
  ignore: ignore
107
108
  });
@@ -282,6 +283,8 @@ function reactGen(_a) {
282
283
  if (writeFileIfChanged(fullPath, result)) {
283
284
  filesChanged++;
284
285
  (0, logger_1.debug)(`Generated: ${path_1.default.basename(fullPath)}`);
286
+ // Emit a short preview for debugging when WEBF_DEBUG is on
287
+ (0, logger_1.debug)(`Preview (${path_1.default.basename(fullPath)}):\n` + result.split('\n').slice(0, 12).join('\n'));
285
288
  }
286
289
  }
287
290
  catch (err) {
@@ -379,7 +382,7 @@ function reactGen(_a) {
379
382
  (0, logger_1.success)(`React code generation completed. ${filesChanged} files changed.`);
380
383
  (0, logger_1.info)(`Output directory: ${normalizedTarget}`);
381
384
  (0, logger_1.info)('You can now import these components in your React project.');
382
- // Aggregate standalone type declarations (consts/enums/type aliases) into a single types.d.ts
385
+ // Aggregate standalone type declarations (consts/enums/type aliases) into a single types.ts
383
386
  try {
384
387
  const consts = blobs.flatMap(b => b.objects.filter(o => o instanceof declaration_1.ConstObject));
385
388
  const enums = blobs.flatMap(b => b.objects.filter(o => o instanceof declaration_1.EnumObject));
@@ -397,7 +400,7 @@ function reactGen(_a) {
397
400
  .map(c => `export declare const ${c.name}: ${c.type};`)
398
401
  .join('\n');
399
402
  const enumDecl = enums
400
- .map(e => `export declare enum ${e.name} { ${e.members.map(m => m.initializer ? `${m.name} = ${m.initializer}` : `${m.name}`).join(', ')} }`)
403
+ .map(e => `export enum ${e.name} { ${e.members.map(m => m.initializer ? `${m.name} = ${m.initializer}` : `${m.name}`).join(', ')} }`)
401
404
  .join('\n');
402
405
  const typeAliasDecl = Array.from(typeAliasMap.values())
403
406
  .map(t => `export type ${t.name} = ${t.type};`)
@@ -409,33 +412,40 @@ function reactGen(_a) {
409
412
  enumDecl,
410
413
  ''
411
414
  ].filter(Boolean).join('\n');
412
- const typesPath = path_1.default.join(normalizedTarget, 'src', 'types.d.ts');
415
+ const typesPath = path_1.default.join(normalizedTarget, 'src', 'types.ts');
413
416
  if (writeFileIfChanged(typesPath, typesContent)) {
414
417
  filesChanged++;
415
- (0, logger_1.debug)(`Generated: src/types.d.ts`);
418
+ (0, logger_1.debug)(`Generated: src/types.ts`);
419
+ try {
420
+ const constNames = Array.from(constMap.keys());
421
+ const aliasNames = Array.from(typeAliasMap.keys());
422
+ const enumNames = enums.map(e => e.name);
423
+ (0, logger_1.debug)(`[react] Aggregated types - consts: ${constNames.join(', ') || '(none)'}; typeAliases: ${aliasNames.join(', ') || '(none)'}; enums: ${enumNames.join(', ') || '(none)'}\n`);
424
+ (0, logger_1.debug)(`[react] src/types.ts preview:\n` + typesContent.split('\n').slice(0, 20).join('\n'));
425
+ }
426
+ catch (_b) { }
416
427
  }
417
- // Try to help TypeScript pick up additional declarations by adding a reference comment.
418
- // This avoids bundler resolution errors from importing a .d.ts file.
428
+ // Ensure index.ts re-exports these types so consumers get them on import.
419
429
  const indexFilePath = path_1.default.join(normalizedTarget, 'src', 'index.ts');
420
430
  try {
431
+ let current = '';
421
432
  if (fs_1.default.existsSync(indexFilePath)) {
422
- let current = fs_1.default.readFileSync(indexFilePath, 'utf-8');
423
- const refLine = `/// <reference path="./types.d.ts" />`;
424
- if (!current.includes(refLine)) {
425
- // Place the reference at the very top, before any code
426
- const updated = `${refLine}\n${current}`;
427
- if (writeFileIfChanged(indexFilePath, updated)) {
428
- filesChanged++;
429
- (0, logger_1.debug)(`Updated: src/index.ts with reference to types.d.ts`);
430
- }
433
+ current = fs_1.default.readFileSync(indexFilePath, 'utf-8');
434
+ }
435
+ const exportLine = `export * from './types';`;
436
+ if (!current.includes(exportLine)) {
437
+ const updated = current.trim().length ? `${current.trim()}\n${exportLine}\n` : `${exportLine}\n`;
438
+ if (writeFileIfChanged(indexFilePath, updated)) {
439
+ filesChanged++;
440
+ (0, logger_1.debug)(`Updated: src/index.ts to export aggregated types`);
431
441
  }
432
442
  }
433
443
  }
434
- catch (_b) { }
444
+ catch (_c) { }
435
445
  }
436
446
  }
437
447
  catch (e) {
438
- (0, logger_1.warn)('Failed to generate aggregated React types.d.ts');
448
+ (0, logger_1.warn)('Failed to generate aggregated React types');
439
449
  }
440
450
  });
441
451
  }