@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/react.js CHANGED
@@ -11,19 +11,58 @@ const fs_1 = __importDefault(require("fs"));
11
11
  const path_1 = __importDefault(require("path"));
12
12
  const declaration_1 = require("./declaration");
13
13
  const utils_1 = require("./utils");
14
+ const logger_1 = require("./logger");
14
15
  function readTemplate(name) {
15
16
  return fs_1.default.readFileSync(path_1.default.join(__dirname, '../templates/' + name + '.tpl'), { encoding: 'utf-8' });
16
17
  }
17
18
  function generateReturnType(type) {
18
19
  if ((0, utils_1.isUnionType)(type)) {
19
- return type.value.map(v => `'${v.value}'`).join(' | ');
20
+ const values = type.value;
21
+ return values.map(v => {
22
+ if (v.value === declaration_1.FunctionArgumentType.null) {
23
+ return 'null';
24
+ }
25
+ // String literal unions: 'left' | 'center' | 'right'
26
+ if (typeof v.value === 'string') {
27
+ return `'${v.value}'`;
28
+ }
29
+ return 'any';
30
+ }).join(' | ');
31
+ }
32
+ // Handle non-literal unions such as boolean | null, number | null, CustomType | null
33
+ if (Array.isArray(type.value)) {
34
+ const values = type.value;
35
+ const hasNull = values.some(v => v.value === declaration_1.FunctionArgumentType.null);
36
+ if (hasNull) {
37
+ const nonNulls = values.filter(v => v.value !== declaration_1.FunctionArgumentType.null);
38
+ if (nonNulls.length === 0) {
39
+ return 'null';
40
+ }
41
+ const parts = nonNulls.map(v => generateReturnType(v));
42
+ // Deduplicate and append null
43
+ const unique = Array.from(new Set(parts));
44
+ unique.push('null');
45
+ return unique.join(' | ');
46
+ }
47
+ // Complex non-null unions are rare for React typings; fall back to any
48
+ return 'any';
20
49
  }
21
50
  if ((0, utils_1.isPointerType)(type)) {
22
51
  const pointerType = (0, utils_1.getPointerType)(type);
52
+ // Map Dart's `Type` (from TS typeof) to TS `any`
53
+ if (pointerType === 'Type')
54
+ return 'any';
23
55
  return pointerType;
24
56
  }
25
57
  if (type.isArray && typeof type.value === 'object' && !Array.isArray(type.value)) {
26
- return `${(0, utils_1.getPointerType)(type.value)}[]`;
58
+ const elemType = (0, utils_1.getPointerType)(type.value);
59
+ // Map arrays of Dart `Type` to `any[]` in TS; parenthesize typeof
60
+ if (elemType === 'Type')
61
+ return 'any[]';
62
+ if (typeof elemType === 'string' && elemType.startsWith('typeof ')) {
63
+ return `(${elemType})[]`;
64
+ }
65
+ return `${elemType}[]`;
27
66
  }
28
67
  switch (type.value) {
29
68
  case declaration_1.FunctionArgumentType.int:
@@ -146,11 +185,18 @@ function generateReactComponent(blob, packageName, relativeDir) {
146
185
  }).join('\n');
147
186
  // Include declare const values as ambient exports for type usage (e.g., unique symbol branding)
148
187
  const constDeclarations = constObjects.map(c => `export declare const ${c.name}: ${c.type};`).join('\n');
149
- // Include enums
188
+ // Include enums as concrete exports (no declare) so they are usable as values
150
189
  const enumDeclarations = enumObjects.map(e => {
151
190
  const members = e.members.map(m => m.initializer ? `${m.name} = ${m.initializer}` : `${m.name}`).join(', ');
152
- return `export declare enum ${e.name} { ${members} }`;
191
+ return `export enum ${e.name} { ${members} }`;
153
192
  }).join('\n');
193
+ // Names declared within this blob (so we shouldn't prefix them with __webfTypes)
194
+ const localTypeNames = new Set([
195
+ ...others.map(o => o.name),
196
+ ...typeAliases.map(t => t.name),
197
+ ...constObjects.map(c => c.name),
198
+ ...enumObjects.map(e => e.name),
199
+ ]);
154
200
  const dependencies = [
155
201
  typeAliasDeclarations,
156
202
  constDeclarations,
@@ -160,26 +206,49 @@ function generateReactComponent(blob, packageName, relativeDir) {
160
206
  const methodDeclarations = object.methods.map(method => {
161
207
  return generateMethodDeclarationWithDocs(method, ' ');
162
208
  }).join('\n');
163
- let interfaceDoc = '';
164
- if (object.documentation) {
165
- interfaceDoc = `/**\n${object.documentation.split('\n').map(line => ` * ${line}`).join('\n')}\n */\n`;
209
+ const lines = [];
210
+ if (object.documentation && object.documentation.trim().length > 0) {
211
+ lines.push('/**');
212
+ object.documentation.split('\n').forEach(line => {
213
+ lines.push(` * ${line}`);
214
+ });
215
+ lines.push(' */');
166
216
  }
167
- return `${interfaceDoc}interface ${object.name} {
168
- ${methodDeclarations}
169
- }`;
217
+ lines.push(`interface ${object.name} {`);
218
+ lines.push(methodDeclarations);
219
+ lines.push('}');
220
+ return lines.join('\n');
170
221
  }).join('\n\n'),
171
222
  others.map(object => {
172
- const props = object.props.map(prop => {
173
- if (prop.optional) {
174
- return `${prop.name}?: ${generateReturnType(prop.type)};`;
223
+ if (!object || !object.props || object.props.length === 0) {
224
+ return '';
225
+ }
226
+ const interfaceLines = [];
227
+ if (object.documentation && object.documentation.trim().length > 0) {
228
+ interfaceLines.push('/**');
229
+ object.documentation.split('\n').forEach(line => {
230
+ interfaceLines.push(` * ${line}`);
231
+ });
232
+ interfaceLines.push(' */');
233
+ }
234
+ interfaceLines.push(`interface ${object.name} {`);
235
+ const propLines = object.props.map(prop => {
236
+ const lines = [];
237
+ if (prop.documentation && prop.documentation.trim().length > 0) {
238
+ lines.push(' /**');
239
+ prop.documentation.split('\n').forEach(line => {
240
+ lines.push(` * ${line}`);
241
+ });
242
+ lines.push(' */');
175
243
  }
176
- return `${prop.name}: ${generateReturnType(prop.type)};`;
177
- }).join('\n ');
178
- return `
179
- interface ${object.name} {
180
- ${props}
181
- }`;
182
- }).join('\n\n')
244
+ const optionalToken = prop.optional ? '?' : '';
245
+ lines.push(` ${prop.name}${optionalToken}: ${generateReturnType(prop.type)};`);
246
+ return lines.join('\n');
247
+ });
248
+ interfaceLines.push(propLines.join('\n'));
249
+ interfaceLines.push('}');
250
+ return interfaceLines.join('\n');
251
+ }).filter(Boolean).join('\n\n')
183
252
  ].filter(Boolean).join('\n\n');
184
253
  // Generate all components from this file
185
254
  const components = [];
@@ -232,17 +301,114 @@ interface ${object.name} {
232
301
  }
233
302
  const templateContent = readTemplate('react.component.tsx')
234
303
  .replace('import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui";', createWebFComponentImport);
235
- const content = lodash_1.default.template(templateContent)({
304
+ // Generate return type mapping; always use __webfTypes namespace for typeof
305
+ const genRT = (type) => {
306
+ if ((0, utils_1.isUnionType)(type)) {
307
+ const values = type.value;
308
+ return values.map(v => {
309
+ if (v.value === declaration_1.FunctionArgumentType.null) {
310
+ return 'null';
311
+ }
312
+ if (typeof v.value === 'string') {
313
+ return `'${v.value}'`;
314
+ }
315
+ return 'any';
316
+ }).join(' | ');
317
+ }
318
+ if (Array.isArray(type.value)) {
319
+ const values = type.value;
320
+ const hasNull = values.some(v => v.value === declaration_1.FunctionArgumentType.null);
321
+ if (hasNull) {
322
+ const nonNulls = values.filter(v => v.value !== declaration_1.FunctionArgumentType.null);
323
+ if (nonNulls.length === 0) {
324
+ return 'null';
325
+ }
326
+ const parts = nonNulls.map(v => genRT(v));
327
+ const unique = Array.from(new Set(parts));
328
+ unique.push('null');
329
+ return unique.join(' | ');
330
+ }
331
+ return 'any';
332
+ }
333
+ if ((0, utils_1.isPointerType)(type)) {
334
+ const pointerType = (0, utils_1.getPointerType)(type);
335
+ if (pointerType === 'Type')
336
+ return 'any';
337
+ if (typeof pointerType === 'string' && pointerType.startsWith('typeof ')) {
338
+ const ident = pointerType.substring('typeof '.length).trim();
339
+ return `typeof __webfTypes.${ident}`;
340
+ }
341
+ // Prefix external pointer types with __webfTypes unless locally declared
342
+ if (typeof pointerType === 'string' && /^(?:[A-Za-z_][A-Za-z0-9_]*)(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(pointerType)) {
343
+ const base = pointerType.split('.')[0];
344
+ if (!localTypeNames.has(base)) {
345
+ return `__webfTypes.${pointerType}`;
346
+ }
347
+ }
348
+ return pointerType;
349
+ }
350
+ if (type.isArray && typeof type.value === 'object' && !Array.isArray(type.value)) {
351
+ const elemType = (0, utils_1.getPointerType)(type.value);
352
+ if (elemType === 'Type')
353
+ return 'any[]';
354
+ if (typeof elemType === 'string' && elemType.startsWith('typeof ')) {
355
+ const ident = elemType.substring('typeof '.length).trim();
356
+ return `(typeof __webfTypes.${ident})[]`;
357
+ }
358
+ if (typeof elemType === 'string' && /^(?:[A-Za-z_][A-Za-z0-9_]*)(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(elemType)) {
359
+ const base = elemType.split('.')[0];
360
+ if (!localTypeNames.has(base)) {
361
+ return `__webfTypes.${elemType}[]`;
362
+ }
363
+ }
364
+ return `${elemType}[]`;
365
+ }
366
+ switch (type.value) {
367
+ case declaration_1.FunctionArgumentType.int:
368
+ case declaration_1.FunctionArgumentType.double:
369
+ return 'number';
370
+ case declaration_1.FunctionArgumentType.any:
371
+ return 'any';
372
+ case declaration_1.FunctionArgumentType.boolean:
373
+ return 'boolean';
374
+ case declaration_1.FunctionArgumentType.dom_string:
375
+ return 'string';
376
+ case declaration_1.FunctionArgumentType.void:
377
+ default:
378
+ return 'void';
379
+ }
380
+ };
381
+ // Compute relative import path to src/types
382
+ const depth = (relativeDir || '').split('/').filter(p => p).length;
383
+ const upPath = '../'.repeat(depth);
384
+ // Always import the types namespace for typeof references
385
+ const typesImport = `import * as __webfTypes from "${upPath}types";\n\n`;
386
+ // Debug: collect typeof references from props for this component
387
+ const typeofRefs = new Set();
388
+ if (component.properties) {
389
+ component.properties.props.forEach(p => {
390
+ const t = p.type;
391
+ if (!t)
392
+ return;
393
+ if (!t.isArray && typeof t.value === 'string' && String(t.value).startsWith('typeof ')) {
394
+ const ident = String(t.value).substring('typeof '.length).trim();
395
+ typeofRefs.add(ident);
396
+ }
397
+ });
398
+ }
399
+ (0, logger_1.debug)(`[react] Generating ${className} (${blob.relativeDir}/${blob.filename}.tsx) typeof refs: ${Array.from(typeofRefs).join(', ') || '(none)'}; types import: ${upPath}types`);
400
+ const dependenciesWithImports = `${typesImport}${dependencies}`;
401
+ let content = lodash_1.default.template(templateContent)({
236
402
  className: className,
237
403
  properties: component.properties,
238
404
  events: component.events,
239
405
  methods: component.methods,
240
406
  classObjectDictionary,
241
- dependencies,
407
+ dependencies: dependenciesWithImports,
242
408
  blob,
243
409
  toReactEventName,
244
410
  toWebFTagName,
245
- generateReturnType,
411
+ generateReturnType: genRT,
246
412
  generateMethodDeclaration,
247
413
  generateMethodDeclarationWithDocs,
248
414
  generateEventHandlerType,
@@ -268,6 +434,81 @@ interface ${object.name} {
268
434
  createWebFComponentImport = `import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui";`;
269
435
  }
270
436
  componentEntries.forEach(([className, component]) => {
437
+ const genRT = (type) => {
438
+ if ((0, utils_1.isUnionType)(type)) {
439
+ const values = type.value;
440
+ return values.map(v => {
441
+ if (v.value === declaration_1.FunctionArgumentType.null) {
442
+ return 'null';
443
+ }
444
+ if (typeof v.value === 'string') {
445
+ return `'${v.value}'`;
446
+ }
447
+ return 'any';
448
+ }).join(' | ');
449
+ }
450
+ if (Array.isArray(type.value)) {
451
+ const values = type.value;
452
+ const hasNull = values.some(v => v.value === declaration_1.FunctionArgumentType.null);
453
+ if (hasNull) {
454
+ const nonNulls = values.filter(v => v.value !== declaration_1.FunctionArgumentType.null);
455
+ if (nonNulls.length === 0) {
456
+ return 'null';
457
+ }
458
+ const parts = nonNulls.map(v => genRT(v));
459
+ const unique = Array.from(new Set(parts));
460
+ unique.push('null');
461
+ return unique.join(' | ');
462
+ }
463
+ return 'any';
464
+ }
465
+ if ((0, utils_1.isPointerType)(type)) {
466
+ const pointerType = (0, utils_1.getPointerType)(type);
467
+ if (pointerType === 'Type')
468
+ return 'any';
469
+ if (typeof pointerType === 'string' && pointerType.startsWith('typeof ')) {
470
+ const ident = pointerType.substring('typeof '.length).trim();
471
+ return `typeof __webfTypes.${ident}`;
472
+ }
473
+ if (typeof pointerType === 'string' && /^(?:[A-Za-z_][A-Za-z0-9_]*)(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(pointerType)) {
474
+ const base = pointerType.split('.')[0];
475
+ if (!localTypeNames.has(base)) {
476
+ return `__webfTypes.${pointerType}`;
477
+ }
478
+ }
479
+ return pointerType;
480
+ }
481
+ if (type.isArray && typeof type.value === 'object' && !Array.isArray(type.value)) {
482
+ const elemType = (0, utils_1.getPointerType)(type.value);
483
+ if (elemType === 'Type')
484
+ return 'any[]';
485
+ if (typeof elemType === 'string' && elemType.startsWith('typeof ')) {
486
+ const ident = elemType.substring('typeof '.length).trim();
487
+ return `(typeof __webfTypes.${ident})[]`;
488
+ }
489
+ if (typeof elemType === 'string' && /^(?:[A-Za-z_][A-Za-z0-9_]*)(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(elemType)) {
490
+ const base = elemType.split('.')[0];
491
+ if (!localTypeNames.has(base)) {
492
+ return `__webfTypes.${elemType}[]`;
493
+ }
494
+ }
495
+ return `${elemType}[]`;
496
+ }
497
+ switch (type.value) {
498
+ case declaration_1.FunctionArgumentType.int:
499
+ case declaration_1.FunctionArgumentType.double:
500
+ return 'number';
501
+ case declaration_1.FunctionArgumentType.any:
502
+ return 'any';
503
+ case declaration_1.FunctionArgumentType.boolean:
504
+ return 'boolean';
505
+ case declaration_1.FunctionArgumentType.dom_string:
506
+ return 'string';
507
+ case declaration_1.FunctionArgumentType.void:
508
+ default:
509
+ return 'void';
510
+ }
511
+ };
271
512
  const content = lodash_1.default.template(readTemplate('react.component.tsx'))({
272
513
  className: className,
273
514
  properties: component.properties,
@@ -278,7 +519,7 @@ interface ${object.name} {
278
519
  blob,
279
520
  toReactEventName,
280
521
  toWebFTagName,
281
- generateReturnType,
522
+ generateReturnType: genRT,
282
523
  generateMethodDeclaration,
283
524
  generateMethodDeclarationWithDocs,
284
525
  generateEventHandlerType,
@@ -292,9 +533,15 @@ interface ${object.name} {
292
533
  componentDefinitions.push(withoutImports);
293
534
  });
294
535
  // Combine with shared imports at the top
295
- const result = [
536
+ // Compute relative import path to src/types and always include namespace import
537
+ const depth = (relativeDir || '').split('/').filter(p => p).length;
538
+ const upPath = '../'.repeat(depth);
539
+ const typesImport = `import * as __webfTypes from "${upPath}types";`;
540
+ (0, logger_1.debug)(`[react] Generating combined components for ${blob.filename}.tsx; types import: ${upPath}types`);
541
+ let result = [
296
542
  'import React from "react";',
297
543
  createWebFComponentImport,
544
+ typesImport,
298
545
  '',
299
546
  dependencies,
300
547
  '',
package/dist/vue.js CHANGED
@@ -8,17 +8,61 @@ const lodash_1 = __importDefault(require("lodash"));
8
8
  const fs_1 = __importDefault(require("fs"));
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const declaration_1 = require("./declaration");
11
+ const logger_1 = require("./logger");
11
12
  const utils_1 = require("./utils");
12
13
  function readTemplate(name) {
13
14
  return fs_1.default.readFileSync(path_1.default.join(__dirname, '../templates/' + name + '.tpl'), { encoding: 'utf-8' });
14
15
  }
15
16
  function generateReturnType(type) {
17
+ if ((0, utils_1.isUnionType)(type)) {
18
+ const values = type.value;
19
+ return values.map(v => {
20
+ if (v.value === declaration_1.FunctionArgumentType.null) {
21
+ return 'null';
22
+ }
23
+ if (typeof v.value === 'string') {
24
+ return `'${v.value}'`;
25
+ }
26
+ return 'any';
27
+ }).join(' | ');
28
+ }
29
+ // Handle unions like boolean | null, number | null, CustomType | null
30
+ if (Array.isArray(type.value)) {
31
+ const values = type.value;
32
+ const hasNull = values.some(v => v.value === declaration_1.FunctionArgumentType.null);
33
+ if (hasNull) {
34
+ const nonNulls = values.filter(v => v.value !== declaration_1.FunctionArgumentType.null);
35
+ if (nonNulls.length === 0) {
36
+ return 'null';
37
+ }
38
+ const parts = nonNulls.map(v => generateReturnType(v));
39
+ const unique = Array.from(new Set(parts));
40
+ unique.push('null');
41
+ return unique.join(' | ');
42
+ }
43
+ // Complex non-null unions are rare; fall back to any
44
+ return 'any';
45
+ }
16
46
  if ((0, utils_1.isPointerType)(type)) {
17
47
  const pointerType = (0, utils_1.getPointerType)(type);
48
+ // Map Dart's `Type` (from TS typeof) to TS `any`
49
+ if (pointerType === 'Type')
50
+ return 'any';
51
+ if (typeof pointerType === 'string' && pointerType.startsWith('typeof ')) {
52
+ const ident = pointerType.substring('typeof '.length).trim();
53
+ return `typeof __webfTypes.${ident}`;
54
+ }
18
55
  return pointerType;
19
56
  }
20
57
  if (type.isArray && typeof type.value === 'object' && !Array.isArray(type.value)) {
21
- return `${(0, utils_1.getPointerType)(type.value)}[]`;
58
+ const elemType = (0, utils_1.getPointerType)(type.value);
59
+ if (elemType === 'Type')
60
+ return 'any[]';
61
+ if (typeof elemType === 'string' && elemType.startsWith('typeof ')) {
62
+ const ident = elemType.substring('typeof '.length).trim();
63
+ return `(typeof __webfTypes.${ident})[]`;
64
+ }
65
+ return `${elemType}[]`;
22
66
  }
23
67
  switch (type.value) {
24
68
  case declaration_1.FunctionArgumentType.int:
@@ -87,19 +131,34 @@ function generateVueComponent(blob) {
87
131
  && !object.name.endsWith('Events');
88
132
  });
89
133
  const dependencies = others.map(object => {
90
- if (!object || !object.props) {
134
+ if (!object || !object.props || object.props.length === 0) {
91
135
  return '';
92
136
  }
93
- const props = object.props.map(prop => {
94
- if (prop.optional) {
95
- return `${prop.name}?: ${generateReturnType(prop.type)};`;
137
+ const interfaceLines = [];
138
+ if (object.documentation && object.documentation.trim().length > 0) {
139
+ interfaceLines.push('/**');
140
+ object.documentation.split('\n').forEach(line => {
141
+ interfaceLines.push(` * ${line}`);
142
+ });
143
+ interfaceLines.push(' */');
144
+ }
145
+ interfaceLines.push(`interface ${object.name} {`);
146
+ const propLines = object.props.map(prop => {
147
+ const lines = [];
148
+ if (prop.documentation && prop.documentation.trim().length > 0) {
149
+ lines.push(' /**');
150
+ prop.documentation.split('\n').forEach(line => {
151
+ lines.push(` * ${line}`);
152
+ });
153
+ lines.push(' */');
96
154
  }
97
- return `${prop.name}: ${generateReturnType(prop.type)};`;
98
- }).join('\n ');
99
- return `
100
- interface ${object.name} {
101
- ${props}
102
- }`;
155
+ const optionalToken = prop.optional ? '?' : '';
156
+ lines.push(` ${prop.name}${optionalToken}: ${generateReturnType(prop.type)};`);
157
+ return lines.join('\n');
158
+ });
159
+ interfaceLines.push(propLines.join('\n'));
160
+ interfaceLines.push('}');
161
+ return interfaceLines.join('\n');
103
162
  }).filter(dep => dep.trim() !== '').join('\n\n');
104
163
  const componentProperties = properties.length > 0 ? properties[0] : undefined;
105
164
  const componentEvents = events.length > 0 ? events[0] : undefined;
@@ -192,6 +251,24 @@ function generateVueTypings(blobs) {
192
251
  const members = e.members.map(m => m.initializer ? `${m.name} = ${m.initializer}` : `${m.name}`).join(', ');
193
252
  return `export declare enum ${e.name} { ${members} }`;
194
253
  }).join('\n');
254
+ // Compute relative import path from the generated typings file (index.d.ts at dist root)
255
+ // to the aggregated React types module (src/types.ts) when present.
256
+ let typesImportPath = './src/types';
257
+ try {
258
+ if (blobs.length > 0) {
259
+ const distRoot = blobs[0].dist;
260
+ const typingsDir = distRoot; // index.d.ts is written directly under distRoot
261
+ const typesFilePath = path_1.default.join(distRoot, 'src', 'types');
262
+ const rel = path_1.default.relative(typingsDir, typesFilePath).replace(/\\/g, '/');
263
+ typesImportPath = rel.startsWith('.') ? rel : `./${rel}`;
264
+ }
265
+ }
266
+ catch (_a) {
267
+ typesImportPath = './src/types';
268
+ }
269
+ // Always import the types namespace to support typeof references
270
+ const typesImport = `import * as __webfTypes from '${typesImportPath}';`;
271
+ (0, logger_1.debug)(`[vue] Generating typings; importing types from ${typesImportPath}`);
195
272
  // Build mapping of template tag names to class names for GlobalComponents
196
273
  const componentMetas = componentNames.map(className => ({
197
274
  className,
@@ -207,6 +284,7 @@ function generateVueTypings(blobs) {
207
284
  components,
208
285
  consts: constDeclarations,
209
286
  enums: enumDeclarations,
287
+ typesImport,
210
288
  });
211
289
  return content.split('\n').filter(str => {
212
290
  return str.trim().length > 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwebf/webf",
3
- "version": "0.23.2",
3
+ "version": "0.23.10",
4
4
  "description": "Command line tools for WebF",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "keywords": [],
26
26
  "author": "",
27
- "license": "MIT",
27
+ "license": "Apache-2.0",
28
28
  "type": "commonjs",
29
29
  "devDependencies": {
30
30
  "@types/inquirer": "^8.2.11",
package/src/analyzer.ts CHANGED
@@ -286,6 +286,20 @@ function getParameterBaseType(type: ts.TypeNode, mode?: ParameterMode): Paramete
286
286
  return basicType;
287
287
  }
288
288
 
289
+ // Handle `typeof SomeIdentifier` (TypeQuery) by preserving the textual form
290
+ // so React/Vue can keep strong typing (e.g., `typeof CupertinoIcons`).
291
+ // Dart mapping will convert this to `dynamic` later.
292
+ if (type.kind === ts.SyntaxKind.TypeQuery) {
293
+ const tq = type as ts.TypeQueryNode;
294
+ const getEntityNameText = (name: ts.EntityName): string => {
295
+ if (ts.isIdentifier(name)) return name.text;
296
+ // Qualified name: A.B.C
297
+ return `${getEntityNameText(name.left)}.${name.right.text}`;
298
+ };
299
+ const nameText = getEntityNameText(tq.exprName);
300
+ return `typeof ${nameText}`;
301
+ }
302
+
289
303
  if (type.kind === ts.SyntaxKind.TypeReference) {
290
304
  const typeReference = type as ts.TypeReferenceNode;
291
305
  const typeName = typeReference.typeName;
@@ -405,7 +419,45 @@ function handleCustomEventType(typeReference: ts.TypeReferenceNode): ParameterBa
405
419
  const argument = typeReference.typeArguments[0];
406
420
  let genericType: string;
407
421
 
408
- if (ts.isTypeReferenceNode(argument) && ts.isIdentifier(argument.typeName)) {
422
+ // Preserve simple union/compound generic types (e.g., boolean | null)
423
+ if (ts.isUnionTypeNode(argument) || ts.isIntersectionTypeNode(argument)) {
424
+ const unionTypes = (argument as ts.UnionTypeNode | ts.IntersectionTypeNode).types ?? [];
425
+ const parts = unionTypes.map(t => {
426
+ // Literal union members: handle null/undefined explicitly
427
+ if (ts.isLiteralTypeNode(t)) {
428
+ const lit = t.literal;
429
+ if (lit.kind === ts.SyntaxKind.NullKeyword) return 'null';
430
+ if (lit.kind === ts.SyntaxKind.UndefinedKeyword) return 'undefined';
431
+ if (ts.isStringLiteral(lit)) return JSON.stringify(lit.text);
432
+ return 'any';
433
+ }
434
+ // Basic keywords: boolean, string, number, null, undefined
435
+ const basic = BASIC_TYPE_MAP[t.kind];
436
+ if (basic !== undefined) {
437
+ switch (basic) {
438
+ case FunctionArgumentType.boolean:
439
+ return 'boolean';
440
+ case FunctionArgumentType.dom_string:
441
+ return 'string';
442
+ case FunctionArgumentType.double:
443
+ case FunctionArgumentType.int:
444
+ return 'number';
445
+ case FunctionArgumentType.null:
446
+ return 'null';
447
+ case FunctionArgumentType.undefined:
448
+ return 'undefined';
449
+ default:
450
+ return 'any';
451
+ }
452
+ }
453
+ // Literal null/undefined keywords that BASIC_TYPE_MAP may not cover
454
+ if (t.kind === ts.SyntaxKind.NullKeyword) return 'null';
455
+ if (t.kind === ts.SyntaxKind.UndefinedKeyword) return 'undefined';
456
+ // Fallback: rely on toString of node kind
457
+ return 'any';
458
+ });
459
+ genericType = parts.join(' | ');
460
+ } else if (ts.isTypeReferenceNode(argument) && ts.isIdentifier(argument.typeName)) {
409
461
  const typeName = argument.typeName.text;
410
462
 
411
463
  // Check if it's a mapped type reference like 'int' or 'double'
@@ -699,7 +751,11 @@ function processEnumDeclaration(
699
751
  }
700
752
  return mem;
701
753
  });
702
-
754
+
755
+ // Register globally for cross-file lookups (e.g., Dart mapping decisions)
756
+ try {
757
+ EnumObject.globalEnumSet.add(enumObj.name);
758
+ } catch {}
703
759
  return enumObj;
704
760
  }
705
761