@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/README.md +33 -1
- package/bin/webf.js +13 -1
- package/dist/analyzer.js +65 -1
- package/dist/commands.js +437 -99
- package/dist/dart.js +91 -25
- package/dist/declaration.js +1 -0
- package/dist/generator.js +28 -18
- package/dist/module.js +458 -0
- package/dist/react.js +272 -25
- package/dist/vue.js +89 -11
- package/package.json +2 -2
- package/src/analyzer.ts +58 -2
- package/src/commands.ts +587 -199
- package/src/dart.ts +95 -20
- package/src/declaration.ts +1 -0
- package/src/generator.ts +27 -19
- package/src/module.ts +600 -0
- package/src/react.ts +288 -29
- package/src/vue.ts +100 -13
- package/templates/class.dart.tpl +1 -1
- package/templates/module.package.json.tpl +36 -0
- package/templates/module.tsconfig.json.tpl +25 -0
- package/templates/module.tsup.config.ts.tpl +13 -0
- package/templates/vue.components.d.ts.tpl +2 -0
- package/test/commands.test.ts +86 -4
- package/test/dart-nullable-props.test.ts +58 -0
- package/test/generator.test.ts +16 -14
- package/test/react-consts.test.ts +1 -1
- package/test/react-vue-nullable-props.test.ts +66 -0
- package/test/react.test.ts +46 -4
package/src/react.ts
CHANGED
|
@@ -4,22 +4,61 @@ import path from 'path';
|
|
|
4
4
|
import {ParameterType} from "./analyzer";
|
|
5
5
|
import {ClassObject, FunctionArgumentType, FunctionDeclaration, TypeAliasObject, ConstObject, EnumObject} from "./declaration";
|
|
6
6
|
import {IDLBlob} from "./IDLBlob";
|
|
7
|
-
import {getPointerType, isPointerType, isUnionType} from "./utils";
|
|
7
|
+
import {getPointerType, isPointerType, isUnionType, trimNullTypeFromType} from "./utils";
|
|
8
|
+
import { debug } from './logger';
|
|
8
9
|
|
|
9
10
|
function readTemplate(name: string) {
|
|
10
11
|
return fs.readFileSync(path.join(__dirname, '../templates/' + name + '.tpl'), {encoding: 'utf-8'});
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
function generateReturnType(type: ParameterType) {
|
|
14
|
+
function generateReturnType(type: ParameterType): string {
|
|
14
15
|
if (isUnionType(type)) {
|
|
15
|
-
|
|
16
|
+
const values = type.value as ParameterType[];
|
|
17
|
+
return values.map(v => {
|
|
18
|
+
if (v.value === FunctionArgumentType.null) {
|
|
19
|
+
return 'null';
|
|
20
|
+
}
|
|
21
|
+
// String literal unions: 'left' | 'center' | 'right'
|
|
22
|
+
if (typeof v.value === 'string') {
|
|
23
|
+
return `'${v.value}'`;
|
|
24
|
+
}
|
|
25
|
+
return 'any';
|
|
26
|
+
}).join(' | ');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Handle non-literal unions such as boolean | null, number | null, CustomType | null
|
|
30
|
+
if (Array.isArray(type.value)) {
|
|
31
|
+
const values = type.value as ParameterType[];
|
|
32
|
+
const hasNull = values.some(v => v.value === FunctionArgumentType.null);
|
|
33
|
+
if (hasNull) {
|
|
34
|
+
const nonNulls = values.filter(v => v.value !== FunctionArgumentType.null);
|
|
35
|
+
if (nonNulls.length === 0) {
|
|
36
|
+
return 'null';
|
|
37
|
+
}
|
|
38
|
+
const parts: string[] = nonNulls.map(v => generateReturnType(v));
|
|
39
|
+
// Deduplicate and append null
|
|
40
|
+
const unique: string[] = Array.from(new Set(parts));
|
|
41
|
+
unique.push('null');
|
|
42
|
+
return unique.join(' | ');
|
|
43
|
+
}
|
|
44
|
+
// Complex non-null unions are rare for React typings; fall back to any
|
|
45
|
+
return 'any';
|
|
16
46
|
}
|
|
47
|
+
|
|
17
48
|
if (isPointerType(type)) {
|
|
18
49
|
const pointerType = getPointerType(type);
|
|
50
|
+
// Map Dart's `Type` (from TS typeof) to TS `any`
|
|
51
|
+
if (pointerType === 'Type') return 'any';
|
|
19
52
|
return pointerType;
|
|
20
53
|
}
|
|
21
54
|
if (type.isArray && typeof type.value === 'object' && !Array.isArray(type.value)) {
|
|
22
|
-
|
|
55
|
+
const elemType = getPointerType(type.value);
|
|
56
|
+
// Map arrays of Dart `Type` to `any[]` in TS; parenthesize typeof
|
|
57
|
+
if (elemType === 'Type') return 'any[]';
|
|
58
|
+
if (typeof elemType === 'string' && elemType.startsWith('typeof ')) {
|
|
59
|
+
return `(${elemType})[]`;
|
|
60
|
+
}
|
|
61
|
+
return `${elemType}[]`;
|
|
23
62
|
}
|
|
24
63
|
switch (type.value) {
|
|
25
64
|
case FunctionArgumentType.int:
|
|
@@ -158,12 +197,20 @@ export function generateReactComponent(blob: IDLBlob, packageName?: string, rela
|
|
|
158
197
|
// Include declare const values as ambient exports for type usage (e.g., unique symbol branding)
|
|
159
198
|
const constDeclarations = constObjects.map(c => `export declare const ${c.name}: ${c.type};`).join('\n');
|
|
160
199
|
|
|
161
|
-
// Include enums
|
|
200
|
+
// Include enums as concrete exports (no declare) so they are usable as values
|
|
162
201
|
const enumDeclarations = enumObjects.map(e => {
|
|
163
202
|
const members = e.members.map(m => m.initializer ? `${m.name} = ${m.initializer}` : `${m.name}`).join(', ');
|
|
164
|
-
return `export
|
|
203
|
+
return `export enum ${e.name} { ${members} }`;
|
|
165
204
|
}).join('\n');
|
|
166
205
|
|
|
206
|
+
// Names declared within this blob (so we shouldn't prefix them with __webfTypes)
|
|
207
|
+
const localTypeNames = new Set<string>([
|
|
208
|
+
...others.map(o => o.name),
|
|
209
|
+
...typeAliases.map(t => t.name),
|
|
210
|
+
...constObjects.map(c => c.name),
|
|
211
|
+
...enumObjects.map(e => e.name),
|
|
212
|
+
]);
|
|
213
|
+
|
|
167
214
|
const dependencies = [
|
|
168
215
|
typeAliasDeclarations,
|
|
169
216
|
constDeclarations,
|
|
@@ -174,28 +221,60 @@ export function generateReactComponent(blob: IDLBlob, packageName?: string, rela
|
|
|
174
221
|
return generateMethodDeclarationWithDocs(method, ' ');
|
|
175
222
|
}).join('\n');
|
|
176
223
|
|
|
177
|
-
|
|
178
|
-
if (object.documentation) {
|
|
179
|
-
|
|
224
|
+
const lines: string[] = [];
|
|
225
|
+
if (object.documentation && object.documentation.trim().length > 0) {
|
|
226
|
+
lines.push('/**');
|
|
227
|
+
object.documentation.split('\n').forEach(line => {
|
|
228
|
+
lines.push(` * ${line}`);
|
|
229
|
+
});
|
|
230
|
+
lines.push(' */');
|
|
180
231
|
}
|
|
181
232
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
233
|
+
lines.push(`interface ${object.name} {`);
|
|
234
|
+
lines.push(methodDeclarations);
|
|
235
|
+
lines.push('}');
|
|
236
|
+
|
|
237
|
+
return lines.join('\n');
|
|
185
238
|
}).join('\n\n'),
|
|
186
239
|
others.map(object => {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
240
|
+
if (!object || !object.props || object.props.length === 0) {
|
|
241
|
+
return '';
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const interfaceLines: string[] = [];
|
|
245
|
+
|
|
246
|
+
if (object.documentation && object.documentation.trim().length > 0) {
|
|
247
|
+
interfaceLines.push('/**');
|
|
248
|
+
object.documentation.split('\n').forEach(line => {
|
|
249
|
+
interfaceLines.push(` * ${line}`);
|
|
250
|
+
});
|
|
251
|
+
interfaceLines.push(' */');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
interfaceLines.push(`interface ${object.name} {`);
|
|
255
|
+
|
|
256
|
+
const propLines = object.props.map(prop => {
|
|
257
|
+
const lines: string[] = [];
|
|
258
|
+
|
|
259
|
+
if (prop.documentation && prop.documentation.trim().length > 0) {
|
|
260
|
+
lines.push(' /**');
|
|
261
|
+
prop.documentation.split('\n').forEach(line => {
|
|
262
|
+
lines.push(` * ${line}`);
|
|
263
|
+
});
|
|
264
|
+
lines.push(' */');
|
|
190
265
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
266
|
+
|
|
267
|
+
const optionalToken = prop.optional ? '?' : '';
|
|
268
|
+
lines.push(` ${prop.name}${optionalToken}: ${generateReturnType(prop.type)};`);
|
|
269
|
+
|
|
270
|
+
return lines.join('\n');
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
interfaceLines.push(propLines.join('\n'));
|
|
274
|
+
interfaceLines.push('}');
|
|
275
|
+
|
|
276
|
+
return interfaceLines.join('\n');
|
|
277
|
+
}).filter(Boolean).join('\n\n')
|
|
199
278
|
].filter(Boolean).join('\n\n');
|
|
200
279
|
|
|
201
280
|
// Generate all components from this file
|
|
@@ -262,17 +341,115 @@ interface ${object.name} {
|
|
|
262
341
|
createWebFComponentImport
|
|
263
342
|
);
|
|
264
343
|
|
|
265
|
-
|
|
344
|
+
// Generate return type mapping; always use __webfTypes namespace for typeof
|
|
345
|
+
const genRT = (type: ParameterType): string => {
|
|
346
|
+
if (isUnionType(type)) {
|
|
347
|
+
const values = type.value as ParameterType[];
|
|
348
|
+
return values.map(v => {
|
|
349
|
+
if (v.value === FunctionArgumentType.null) {
|
|
350
|
+
return 'null';
|
|
351
|
+
}
|
|
352
|
+
if (typeof v.value === 'string') {
|
|
353
|
+
return `'${v.value}'`;
|
|
354
|
+
}
|
|
355
|
+
return 'any';
|
|
356
|
+
}).join(' | ');
|
|
357
|
+
}
|
|
358
|
+
if (Array.isArray(type.value)) {
|
|
359
|
+
const values = type.value as ParameterType[];
|
|
360
|
+
const hasNull = values.some(v => v.value === FunctionArgumentType.null);
|
|
361
|
+
if (hasNull) {
|
|
362
|
+
const nonNulls = values.filter(v => v.value !== FunctionArgumentType.null);
|
|
363
|
+
if (nonNulls.length === 0) {
|
|
364
|
+
return 'null';
|
|
365
|
+
}
|
|
366
|
+
const parts: string[] = nonNulls.map(v => genRT(v));
|
|
367
|
+
const unique: string[] = Array.from(new Set(parts));
|
|
368
|
+
unique.push('null');
|
|
369
|
+
return unique.join(' | ');
|
|
370
|
+
}
|
|
371
|
+
return 'any';
|
|
372
|
+
}
|
|
373
|
+
if (isPointerType(type)) {
|
|
374
|
+
const pointerType = getPointerType(type);
|
|
375
|
+
if (pointerType === 'Type') return 'any';
|
|
376
|
+
if (typeof pointerType === 'string' && pointerType.startsWith('typeof ')) {
|
|
377
|
+
const ident = pointerType.substring('typeof '.length).trim();
|
|
378
|
+
return `typeof __webfTypes.${ident}`;
|
|
379
|
+
}
|
|
380
|
+
// Prefix external pointer types with __webfTypes unless locally declared
|
|
381
|
+
if (typeof pointerType === 'string' && /^(?:[A-Za-z_][A-Za-z0-9_]*)(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(pointerType)) {
|
|
382
|
+
const base = pointerType.split('.')[0];
|
|
383
|
+
if (!localTypeNames.has(base)) {
|
|
384
|
+
return `__webfTypes.${pointerType}`;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return pointerType;
|
|
388
|
+
}
|
|
389
|
+
if (type.isArray && typeof type.value === 'object' && !Array.isArray(type.value)) {
|
|
390
|
+
const elemType = getPointerType(type.value);
|
|
391
|
+
if (elemType === 'Type') return 'any[]';
|
|
392
|
+
if (typeof elemType === 'string' && elemType.startsWith('typeof ')) {
|
|
393
|
+
const ident = elemType.substring('typeof '.length).trim();
|
|
394
|
+
return `(typeof __webfTypes.${ident})[]`;
|
|
395
|
+
}
|
|
396
|
+
if (typeof elemType === 'string' && /^(?:[A-Za-z_][A-Za-z0-9_]*)(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(elemType)) {
|
|
397
|
+
const base = elemType.split('.')[0];
|
|
398
|
+
if (!localTypeNames.has(base)) {
|
|
399
|
+
return `__webfTypes.${elemType}[]`;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return `${elemType}[]`;
|
|
403
|
+
}
|
|
404
|
+
switch (type.value) {
|
|
405
|
+
case FunctionArgumentType.int:
|
|
406
|
+
case FunctionArgumentType.double:
|
|
407
|
+
return 'number';
|
|
408
|
+
case FunctionArgumentType.any:
|
|
409
|
+
return 'any';
|
|
410
|
+
case FunctionArgumentType.boolean:
|
|
411
|
+
return 'boolean';
|
|
412
|
+
case FunctionArgumentType.dom_string:
|
|
413
|
+
return 'string';
|
|
414
|
+
case FunctionArgumentType.void:
|
|
415
|
+
default:
|
|
416
|
+
return 'void';
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
// Compute relative import path to src/types
|
|
421
|
+
const depth = (relativeDir || '').split('/').filter(p => p).length;
|
|
422
|
+
const upPath = '../'.repeat(depth);
|
|
423
|
+
// Always import the types namespace for typeof references
|
|
424
|
+
const typesImport = `import * as __webfTypes from "${upPath}types";\n\n`;
|
|
425
|
+
|
|
426
|
+
// Debug: collect typeof references from props for this component
|
|
427
|
+
const typeofRefs = new Set<string>();
|
|
428
|
+
if (component.properties) {
|
|
429
|
+
component.properties.props.forEach(p => {
|
|
430
|
+
const t = p.type;
|
|
431
|
+
if (!t) return;
|
|
432
|
+
if (!t.isArray && typeof (t.value as any) === 'string' && String((t.value as any)).startsWith('typeof ')) {
|
|
433
|
+
const ident = String((t.value as any)).substring('typeof '.length).trim();
|
|
434
|
+
typeofRefs.add(ident);
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
debug(`[react] Generating ${className} (${blob.relativeDir}/${blob.filename}.tsx) typeof refs: ${Array.from(typeofRefs).join(', ') || '(none)'}; types import: ${upPath}types`);
|
|
439
|
+
|
|
440
|
+
const dependenciesWithImports = `${typesImport}${dependencies}`;
|
|
441
|
+
|
|
442
|
+
let content = _.template(templateContent)({
|
|
266
443
|
className: className,
|
|
267
444
|
properties: component.properties,
|
|
268
445
|
events: component.events,
|
|
269
446
|
methods: component.methods,
|
|
270
447
|
classObjectDictionary,
|
|
271
|
-
dependencies,
|
|
448
|
+
dependencies: dependenciesWithImports,
|
|
272
449
|
blob,
|
|
273
450
|
toReactEventName,
|
|
274
451
|
toWebFTagName,
|
|
275
|
-
generateReturnType,
|
|
452
|
+
generateReturnType: genRT,
|
|
276
453
|
generateMethodDeclaration,
|
|
277
454
|
generateMethodDeclarationWithDocs,
|
|
278
455
|
generateEventHandlerType,
|
|
@@ -302,6 +479,80 @@ interface ${object.name} {
|
|
|
302
479
|
}
|
|
303
480
|
|
|
304
481
|
componentEntries.forEach(([className, component]) => {
|
|
482
|
+
const genRT = (type: ParameterType): string => {
|
|
483
|
+
if (isUnionType(type)) {
|
|
484
|
+
const values = type.value as ParameterType[];
|
|
485
|
+
return values.map(v => {
|
|
486
|
+
if (v.value === FunctionArgumentType.null) {
|
|
487
|
+
return 'null';
|
|
488
|
+
}
|
|
489
|
+
if (typeof v.value === 'string') {
|
|
490
|
+
return `'${v.value}'`;
|
|
491
|
+
}
|
|
492
|
+
return 'any';
|
|
493
|
+
}).join(' | ');
|
|
494
|
+
}
|
|
495
|
+
if (Array.isArray(type.value)) {
|
|
496
|
+
const values = type.value as ParameterType[];
|
|
497
|
+
const hasNull = values.some(v => v.value === FunctionArgumentType.null);
|
|
498
|
+
if (hasNull) {
|
|
499
|
+
const nonNulls = values.filter(v => v.value !== FunctionArgumentType.null);
|
|
500
|
+
if (nonNulls.length === 0) {
|
|
501
|
+
return 'null';
|
|
502
|
+
}
|
|
503
|
+
const parts: string[] = nonNulls.map(v => genRT(v));
|
|
504
|
+
const unique: string[] = Array.from(new Set(parts));
|
|
505
|
+
unique.push('null');
|
|
506
|
+
return unique.join(' | ');
|
|
507
|
+
}
|
|
508
|
+
return 'any';
|
|
509
|
+
}
|
|
510
|
+
if (isPointerType(type)) {
|
|
511
|
+
const pointerType = getPointerType(type);
|
|
512
|
+
if (pointerType === 'Type') return 'any';
|
|
513
|
+
if (typeof pointerType === 'string' && pointerType.startsWith('typeof ')) {
|
|
514
|
+
const ident = pointerType.substring('typeof '.length).trim();
|
|
515
|
+
return `typeof __webfTypes.${ident}`;
|
|
516
|
+
}
|
|
517
|
+
if (typeof pointerType === 'string' && /^(?:[A-Za-z_][A-Za-z0-9_]*)(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(pointerType)) {
|
|
518
|
+
const base = pointerType.split('.')[0];
|
|
519
|
+
if (!localTypeNames.has(base)) {
|
|
520
|
+
return `__webfTypes.${pointerType}`;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return pointerType;
|
|
524
|
+
}
|
|
525
|
+
if (type.isArray && typeof type.value === 'object' && !Array.isArray(type.value)) {
|
|
526
|
+
const elemType = getPointerType(type.value);
|
|
527
|
+
if (elemType === 'Type') return 'any[]';
|
|
528
|
+
if (typeof elemType === 'string' && elemType.startsWith('typeof ')) {
|
|
529
|
+
const ident = elemType.substring('typeof '.length).trim();
|
|
530
|
+
return `(typeof __webfTypes.${ident})[]`;
|
|
531
|
+
}
|
|
532
|
+
if (typeof elemType === 'string' && /^(?:[A-Za-z_][A-Za-z0-9_]*)(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(elemType)) {
|
|
533
|
+
const base = elemType.split('.')[0];
|
|
534
|
+
if (!localTypeNames.has(base)) {
|
|
535
|
+
return `__webfTypes.${elemType}[]`;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return `${elemType}[]`;
|
|
539
|
+
}
|
|
540
|
+
switch (type.value) {
|
|
541
|
+
case FunctionArgumentType.int:
|
|
542
|
+
case FunctionArgumentType.double:
|
|
543
|
+
return 'number';
|
|
544
|
+
case FunctionArgumentType.any:
|
|
545
|
+
return 'any';
|
|
546
|
+
case FunctionArgumentType.boolean:
|
|
547
|
+
return 'boolean';
|
|
548
|
+
case FunctionArgumentType.dom_string:
|
|
549
|
+
return 'string';
|
|
550
|
+
case FunctionArgumentType.void:
|
|
551
|
+
default:
|
|
552
|
+
return 'void';
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
|
|
305
556
|
const content = _.template(readTemplate('react.component.tsx'))({
|
|
306
557
|
className: className,
|
|
307
558
|
properties: component.properties,
|
|
@@ -312,7 +563,7 @@ interface ${object.name} {
|
|
|
312
563
|
blob,
|
|
313
564
|
toReactEventName,
|
|
314
565
|
toWebFTagName,
|
|
315
|
-
generateReturnType,
|
|
566
|
+
generateReturnType: genRT,
|
|
316
567
|
generateMethodDeclaration,
|
|
317
568
|
generateMethodDeclarationWithDocs,
|
|
318
569
|
generateEventHandlerType,
|
|
@@ -329,15 +580,23 @@ interface ${object.name} {
|
|
|
329
580
|
});
|
|
330
581
|
|
|
331
582
|
// Combine with shared imports at the top
|
|
332
|
-
|
|
583
|
+
// Compute relative import path to src/types and always include namespace import
|
|
584
|
+
const depth = (relativeDir || '').split('/').filter(p => p).length;
|
|
585
|
+
const upPath = '../'.repeat(depth);
|
|
586
|
+
const typesImport = `import * as __webfTypes from "${upPath}types";`;
|
|
587
|
+
|
|
588
|
+
debug(`[react] Generating combined components for ${blob.filename}.tsx; types import: ${upPath}types`);
|
|
589
|
+
|
|
590
|
+
let result = [
|
|
333
591
|
'import React from "react";',
|
|
334
592
|
createWebFComponentImport,
|
|
593
|
+
typesImport,
|
|
335
594
|
'',
|
|
336
595
|
dependencies,
|
|
337
596
|
'',
|
|
338
597
|
...componentDefinitions
|
|
339
598
|
].filter(line => line !== undefined).join('\n');
|
|
340
|
-
|
|
599
|
+
|
|
341
600
|
return result.split('\n').filter(str => {
|
|
342
601
|
return str.trim().length > 0;
|
|
343
602
|
}).join('\n');
|
package/src/vue.ts
CHANGED
|
@@ -4,19 +4,63 @@ import path from 'path';
|
|
|
4
4
|
import {ParameterType} from "./analyzer";
|
|
5
5
|
import {ClassObject, FunctionArgumentType, FunctionDeclaration, ConstObject, EnumObject} from "./declaration";
|
|
6
6
|
import {IDLBlob} from "./IDLBlob";
|
|
7
|
-
import {
|
|
7
|
+
import { debug } from './logger';
|
|
8
|
+
import {getPointerType, isPointerType, isUnionType, trimNullTypeFromType} from "./utils";
|
|
8
9
|
|
|
9
10
|
function readTemplate(name: string) {
|
|
10
11
|
return fs.readFileSync(path.join(__dirname, '../templates/' + name + '.tpl'), {encoding: 'utf-8'});
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
function generateReturnType(type: ParameterType) {
|
|
14
|
+
function generateReturnType(type: ParameterType): string {
|
|
15
|
+
if (isUnionType(type)) {
|
|
16
|
+
const values = type.value as ParameterType[];
|
|
17
|
+
return values.map(v => {
|
|
18
|
+
if (v.value === FunctionArgumentType.null) {
|
|
19
|
+
return 'null';
|
|
20
|
+
}
|
|
21
|
+
if (typeof v.value === 'string') {
|
|
22
|
+
return `'${v.value}'`;
|
|
23
|
+
}
|
|
24
|
+
return 'any';
|
|
25
|
+
}).join(' | ');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Handle unions like boolean | null, number | null, CustomType | null
|
|
29
|
+
if (Array.isArray(type.value)) {
|
|
30
|
+
const values = type.value as ParameterType[];
|
|
31
|
+
const hasNull = values.some(v => v.value === FunctionArgumentType.null);
|
|
32
|
+
if (hasNull) {
|
|
33
|
+
const nonNulls = values.filter(v => v.value !== FunctionArgumentType.null);
|
|
34
|
+
if (nonNulls.length === 0) {
|
|
35
|
+
return 'null';
|
|
36
|
+
}
|
|
37
|
+
const parts: string[] = nonNulls.map(v => generateReturnType(v));
|
|
38
|
+
const unique: string[] = Array.from(new Set(parts));
|
|
39
|
+
unique.push('null');
|
|
40
|
+
return unique.join(' | ');
|
|
41
|
+
}
|
|
42
|
+
// Complex non-null unions are rare; fall back to any
|
|
43
|
+
return 'any';
|
|
44
|
+
}
|
|
45
|
+
|
|
14
46
|
if (isPointerType(type)) {
|
|
15
47
|
const pointerType = getPointerType(type);
|
|
48
|
+
// Map Dart's `Type` (from TS typeof) to TS `any`
|
|
49
|
+
if (pointerType === 'Type') return 'any';
|
|
50
|
+
if (typeof pointerType === 'string' && pointerType.startsWith('typeof ')) {
|
|
51
|
+
const ident = pointerType.substring('typeof '.length).trim();
|
|
52
|
+
return `typeof __webfTypes.${ident}`;
|
|
53
|
+
}
|
|
16
54
|
return pointerType;
|
|
17
55
|
}
|
|
18
56
|
if (type.isArray && typeof type.value === 'object' && !Array.isArray(type.value)) {
|
|
19
|
-
|
|
57
|
+
const elemType = getPointerType(type.value);
|
|
58
|
+
if (elemType === 'Type') return 'any[]';
|
|
59
|
+
if (typeof elemType === 'string' && elemType.startsWith('typeof ')) {
|
|
60
|
+
const ident = elemType.substring('typeof '.length).trim();
|
|
61
|
+
return `(typeof __webfTypes.${ident})[]`;
|
|
62
|
+
}
|
|
63
|
+
return `${elemType}[]`;
|
|
20
64
|
}
|
|
21
65
|
switch (type.value) {
|
|
22
66
|
case FunctionArgumentType.int:
|
|
@@ -94,20 +138,43 @@ function generateVueComponent(blob: IDLBlob) {
|
|
|
94
138
|
});
|
|
95
139
|
|
|
96
140
|
const dependencies = others.map(object => {
|
|
97
|
-
if (!object || !object.props) {
|
|
141
|
+
if (!object || !object.props || object.props.length === 0) {
|
|
98
142
|
return '';
|
|
99
143
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
144
|
+
|
|
145
|
+
const interfaceLines: string[] = [];
|
|
146
|
+
|
|
147
|
+
if (object.documentation && object.documentation.trim().length > 0) {
|
|
148
|
+
interfaceLines.push('/**');
|
|
149
|
+
object.documentation.split('\n').forEach(line => {
|
|
150
|
+
interfaceLines.push(` * ${line}`);
|
|
151
|
+
});
|
|
152
|
+
interfaceLines.push(' */');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
interfaceLines.push(`interface ${object.name} {`);
|
|
156
|
+
|
|
157
|
+
const propLines = object.props.map(prop => {
|
|
158
|
+
const lines: string[] = [];
|
|
159
|
+
|
|
160
|
+
if (prop.documentation && prop.documentation.trim().length > 0) {
|
|
161
|
+
lines.push(' /**');
|
|
162
|
+
prop.documentation.split('\n').forEach(line => {
|
|
163
|
+
lines.push(` * ${line}`);
|
|
164
|
+
});
|
|
165
|
+
lines.push(' */');
|
|
103
166
|
}
|
|
104
|
-
return `${prop.name}: ${generateReturnType(prop.type)};`;
|
|
105
|
-
}).join('\n ');
|
|
106
167
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
168
|
+
const optionalToken = prop.optional ? '?' : '';
|
|
169
|
+
lines.push(` ${prop.name}${optionalToken}: ${generateReturnType(prop.type)};`);
|
|
170
|
+
|
|
171
|
+
return lines.join('\n');
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
interfaceLines.push(propLines.join('\n'));
|
|
175
|
+
interfaceLines.push('}');
|
|
176
|
+
|
|
177
|
+
return interfaceLines.join('\n');
|
|
111
178
|
}).filter(dep => dep.trim() !== '').join('\n\n');
|
|
112
179
|
|
|
113
180
|
const componentProperties = properties.length > 0 ? properties[0] : undefined;
|
|
@@ -213,6 +280,25 @@ export function generateVueTypings(blobs: IDLBlob[]) {
|
|
|
213
280
|
return `export declare enum ${e.name} { ${members} }`;
|
|
214
281
|
}).join('\n');
|
|
215
282
|
|
|
283
|
+
// Compute relative import path from the generated typings file (index.d.ts at dist root)
|
|
284
|
+
// to the aggregated React types module (src/types.ts) when present.
|
|
285
|
+
let typesImportPath = './src/types';
|
|
286
|
+
try {
|
|
287
|
+
if (blobs.length > 0) {
|
|
288
|
+
const distRoot = blobs[0].dist;
|
|
289
|
+
const typingsDir = distRoot; // index.d.ts is written directly under distRoot
|
|
290
|
+
const typesFilePath = path.join(distRoot, 'src', 'types');
|
|
291
|
+
const rel = path.relative(typingsDir, typesFilePath).replace(/\\/g, '/');
|
|
292
|
+
typesImportPath = rel.startsWith('.') ? rel : `./${rel}`;
|
|
293
|
+
}
|
|
294
|
+
} catch {
|
|
295
|
+
typesImportPath = './src/types';
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Always import the types namespace to support typeof references
|
|
299
|
+
const typesImport = `import * as __webfTypes from '${typesImportPath}';`;
|
|
300
|
+
debug(`[vue] Generating typings; importing types from ${typesImportPath}`);
|
|
301
|
+
|
|
216
302
|
// Build mapping of template tag names to class names for GlobalComponents
|
|
217
303
|
const componentMetas = componentNames.map(className => ({
|
|
218
304
|
className,
|
|
@@ -229,6 +315,7 @@ export function generateVueTypings(blobs: IDLBlob[]) {
|
|
|
229
315
|
components,
|
|
230
316
|
consts: constDeclarations,
|
|
231
317
|
enums: enumDeclarations,
|
|
318
|
+
typesImport,
|
|
232
319
|
});
|
|
233
320
|
|
|
234
321
|
return content.split('\n').filter(str => {
|
package/templates/class.dart.tpl
CHANGED
|
@@ -38,7 +38,7 @@ abstract class <%= className %>Bindings extends WidgetElement {
|
|
|
38
38
|
<% var attributeName = _.kebabCase(prop.name); %>
|
|
39
39
|
<% var propName = _.camelCase(prop.name); %>
|
|
40
40
|
attributes['<%= attributeName %>'] = ElementAttributeProperty(
|
|
41
|
-
getter: () => <%= generateAttributeGetter(propName, prop.type, prop.optional) %>,
|
|
41
|
+
getter: () => <%= generateAttributeGetter(propName, prop.type, prop.optional, prop) %>,
|
|
42
42
|
setter: (value) => <%= generateAttributeSetter(propName, prop.type) %>,
|
|
43
43
|
deleter: () => <%= generateAttributeDeleter(propName, prop.type, prop.optional) %>
|
|
44
44
|
);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "<%= packageName %>",
|
|
3
|
+
"version": "<%= version %>",
|
|
4
|
+
"description": "<%= description %>",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": ["dist"],
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsup",
|
|
11
|
+
"dev": "tsup --watch",
|
|
12
|
+
"clean": "rimraf dist",
|
|
13
|
+
"prepublishOnly": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"webf",
|
|
17
|
+
"flutter",
|
|
18
|
+
"native",
|
|
19
|
+
"module"
|
|
20
|
+
],
|
|
21
|
+
"author": "",
|
|
22
|
+
"license": "Apache-2.0",
|
|
23
|
+
"type": "commonjs",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@openwebf/webf-enterprise-typings": "^0.23.7"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"rimraf": "^5.0.0",
|
|
29
|
+
"tsup": "^8.5.0",
|
|
30
|
+
"typescript": "^5.8.3"
|
|
31
|
+
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"lib": ["ES2020", "DOM"],
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "node",
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"allowSyntheticDefaultImports": true,
|
|
9
|
+
"strict": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"declaration": true,
|
|
13
|
+
"declarationMap": true,
|
|
14
|
+
"outDir": "dist",
|
|
15
|
+
"rootDir": "src"
|
|
16
|
+
},
|
|
17
|
+
"include": [
|
|
18
|
+
"src/**/*"
|
|
19
|
+
],
|
|
20
|
+
"exclude": [
|
|
21
|
+
"node_modules",
|
|
22
|
+
"dist"
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
|