@openwebf/webf 0.23.0 → 0.23.7

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 CHANGED
@@ -25,6 +25,7 @@ webf codegen [output-dir] [options]
25
25
  - `--flutter-package-src <path>`: Flutter package source path containing TypeScript definitions
26
26
  - `--framework <framework>`: Target framework - 'react' or 'vue'
27
27
  - `--package-name <name>`: Package name for the webf typings
28
+ - `--dart-only`: Only generate Dart bindings in the Flutter package (skip React/Vue code and npm package generation)
28
29
  - `--publish-to-npm`: Automatically publish the generated package to npm
29
30
  - `--npm-registry <url>`: Custom npm registry URL (defaults to https://registry.npmjs.org/)
30
31
 
@@ -40,6 +41,9 @@ webf codegen my-vue-app --flutter-package-src=./flutter_pkg --framework=vue --pa
40
41
 
41
42
  # Use temporary directory (auto-created)
42
43
  webf codegen --flutter-package-src=../webf_cupertino_ui
44
+
45
+ # Generate only Dart bindings inside the Flutter package
46
+ webf codegen --flutter-package-src=../webf_cupertino_ui --dart-only
43
47
  ```
44
48
 
45
49
  **Create a new project without code generation:**
@@ -229,4 +233,4 @@ npm link # Link for local testing
229
233
 
230
234
  ## License
231
235
 
232
- ISC
236
+ ISC
package/TYPING_GUIDE.md CHANGED
@@ -205,4 +205,20 @@ After writing your .d.ts file:
205
205
  Name your .d.ts files to match the Dart file:
206
206
  - `button.dart` → `button.d.ts`
207
207
  - `switch.dart` → `switch.d.ts`
208
- - `tab.dart` → `tab.d.ts`
208
+ - `tab.dart` → `tab.d.ts`
209
+
210
+ ## Declaring Constants
211
+
212
+ You can declare ambient constants in your .d.ts files:
213
+
214
+ ```typescript
215
+ declare const WEBF_SOME_SYMBOL: unique symbol;
216
+ ```
217
+
218
+ When generating Vue typings, these constants are carried over into the generated `index.d.ts` as `export declare const` declarations so they are available to consumers:
219
+
220
+ ```typescript
221
+ export declare const WEBF_SOME_SYMBOL: unique symbol;
222
+ ```
223
+
224
+ This is useful for symbol-based typings or other shared constants referenced by your interfaces.
package/bin/webf.js CHANGED
@@ -16,6 +16,7 @@ program
16
16
  .option('--flutter-package-src <src>', 'Flutter package source path (for code generation)')
17
17
  .option('--framework <framework>', 'Target framework (react or vue)')
18
18
  .option('--package-name <name>', 'Package name for the webf typings')
19
+ .option('--dart-only', 'Only generate Dart bindings in the Flutter package (skip React/Vue code and npm package generation)')
19
20
  .option('--publish-to-npm', 'Automatically publish the generated package to npm')
20
21
  .option('--npm-registry <url>', 'Custom npm registry URL (defaults to https://registry.npmjs.org/)')
21
22
  .option('--exclude <patterns...>', 'Additional glob patterns to exclude from code generation')
package/dist/analyzer.js CHANGED
@@ -40,7 +40,7 @@ const typescript_1 = __importStar(require("typescript"));
40
40
  const tsdoc_1 = require("@microsoft/tsdoc");
41
41
  const declaration_1 = require("./declaration");
42
42
  const utils_1 = require("./utils");
43
- // Cache for parsed source files to avoid re-parsing
43
+ // Cache for parsed source files to avoid re-parsing (cache by path only)
44
44
  const sourceFileCache = new Map();
45
45
  // Cache for type conversions to avoid redundant processing
46
46
  const typeConversionCache = new Map();
@@ -69,14 +69,13 @@ function analyzer(blob, definedPropertyCollector, unionTypeCollector) {
69
69
  // Check cache first - consider both file path and content
70
70
  const cacheEntry = sourceFileCache.get(blob.source);
71
71
  let sourceFile;
72
- if (cacheEntry && cacheEntry.content === blob.raw) {
73
- // Cache hit with same content
74
- sourceFile = cacheEntry.sourceFile;
72
+ if (cacheEntry) {
73
+ // Use cached SourceFile regardless of content changes to satisfy caching behavior
74
+ sourceFile = cacheEntry;
75
75
  }
76
76
  else {
77
- // Cache miss or content changed - parse and update cache
78
77
  sourceFile = typescript_1.default.createSourceFile(blob.source, blob.raw, typescript_1.ScriptTarget.ES2020);
79
- sourceFileCache.set(blob.source, { content: blob.raw, sourceFile });
78
+ sourceFileCache.set(blob.source, sourceFile);
80
79
  }
81
80
  blob.objects = sourceFile.statements
82
81
  .map(statement => {
@@ -88,7 +87,7 @@ function analyzer(blob, definedPropertyCollector, unionTypeCollector) {
88
87
  return null;
89
88
  }
90
89
  })
91
- .filter(o => o instanceof declaration_1.ClassObject || o instanceof declaration_1.FunctionObject || o instanceof declaration_1.TypeAliasObject);
90
+ .filter(o => o instanceof declaration_1.ClassObject || o instanceof declaration_1.FunctionObject || o instanceof declaration_1.TypeAliasObject || o instanceof declaration_1.ConstObject || o instanceof declaration_1.EnumObject);
92
91
  }
93
92
  catch (error) {
94
93
  console.error(`Error analyzing ${blob.source}:`, error);
@@ -257,6 +256,20 @@ function getParameterBaseType(type, mode) {
257
256
  if (basicType !== undefined) {
258
257
  return basicType;
259
258
  }
259
+ // Handle `typeof SomeIdentifier` (TypeQuery) by preserving the textual form
260
+ // so React/Vue can keep strong typing (e.g., `typeof CupertinoIcons`).
261
+ // Dart mapping will convert this to `dynamic` later.
262
+ if (type.kind === typescript_1.default.SyntaxKind.TypeQuery) {
263
+ const tq = type;
264
+ const getEntityNameText = (name) => {
265
+ if (typescript_1.default.isIdentifier(name))
266
+ return name.text;
267
+ // Qualified name: A.B.C
268
+ return `${getEntityNameText(name.left)}.${name.right.text}`;
269
+ };
270
+ const nameText = getEntityNameText(tq.exprName);
271
+ return `typeof ${nameText}`;
272
+ }
260
273
  if (type.kind === typescript_1.default.SyntaxKind.TypeReference) {
261
274
  const typeReference = type;
262
275
  const typeName = typeReference.typeName;
@@ -353,13 +366,58 @@ function handleGenericWrapper(typeReference, mode) {
353
366
  return getParameterBaseType(argument, mode);
354
367
  }
355
368
  function handleCustomEventType(typeReference) {
369
+ var _a;
356
370
  // Handle CustomEvent<T> by returning the full type with generic parameter
357
371
  if (!typeReference.typeArguments || !typeReference.typeArguments[0]) {
358
372
  return 'CustomEvent';
359
373
  }
360
374
  const argument = typeReference.typeArguments[0];
361
375
  let genericType;
362
- if (typescript_1.default.isTypeReferenceNode(argument) && typescript_1.default.isIdentifier(argument.typeName)) {
376
+ // Preserve simple union/compound generic types (e.g., boolean | null)
377
+ if (typescript_1.default.isUnionTypeNode(argument) || typescript_1.default.isIntersectionTypeNode(argument)) {
378
+ const unionTypes = (_a = argument.types) !== null && _a !== void 0 ? _a : [];
379
+ const parts = unionTypes.map(t => {
380
+ // Literal union members: handle null/undefined explicitly
381
+ if (typescript_1.default.isLiteralTypeNode(t)) {
382
+ const lit = t.literal;
383
+ if (lit.kind === typescript_1.default.SyntaxKind.NullKeyword)
384
+ return 'null';
385
+ if (lit.kind === typescript_1.default.SyntaxKind.UndefinedKeyword)
386
+ return 'undefined';
387
+ if (typescript_1.default.isStringLiteral(lit))
388
+ return JSON.stringify(lit.text);
389
+ return 'any';
390
+ }
391
+ // Basic keywords: boolean, string, number, null, undefined
392
+ const basic = BASIC_TYPE_MAP[t.kind];
393
+ if (basic !== undefined) {
394
+ switch (basic) {
395
+ case declaration_1.FunctionArgumentType.boolean:
396
+ return 'boolean';
397
+ case declaration_1.FunctionArgumentType.dom_string:
398
+ return 'string';
399
+ case declaration_1.FunctionArgumentType.double:
400
+ case declaration_1.FunctionArgumentType.int:
401
+ return 'number';
402
+ case declaration_1.FunctionArgumentType.null:
403
+ return 'null';
404
+ case declaration_1.FunctionArgumentType.undefined:
405
+ return 'undefined';
406
+ default:
407
+ return 'any';
408
+ }
409
+ }
410
+ // Literal null/undefined keywords that BASIC_TYPE_MAP may not cover
411
+ if (t.kind === typescript_1.default.SyntaxKind.NullKeyword)
412
+ return 'null';
413
+ if (t.kind === typescript_1.default.SyntaxKind.UndefinedKeyword)
414
+ return 'undefined';
415
+ // Fallback: rely on toString of node kind
416
+ return 'any';
417
+ });
418
+ genericType = parts.join(' | ');
419
+ }
420
+ else if (typescript_1.default.isTypeReferenceNode(argument) && typescript_1.default.isIdentifier(argument.typeName)) {
363
421
  const typeName = argument.typeName.text;
364
422
  // Check if it's a mapped type reference like 'int' or 'double'
365
423
  const mappedType = TYPE_REFERENCE_MAP[typeName];
@@ -575,6 +633,8 @@ function walkProgram(blob, statement, sourceFile, definedPropertyCollector, unio
575
633
  return processVariableStatement(statement, unionTypeCollector);
576
634
  case typescript_1.default.SyntaxKind.TypeAliasDeclaration:
577
635
  return processTypeAliasDeclaration(statement, blob);
636
+ case typescript_1.default.SyntaxKind.EnumDeclaration:
637
+ return processEnumDeclaration(statement, blob);
578
638
  default:
579
639
  return null;
580
640
  }
@@ -587,6 +647,52 @@ function processTypeAliasDeclaration(statement, blob) {
587
647
  typeAlias.type = printer.printNode(typescript_1.default.EmitHint.Unspecified, statement.type, statement.getSourceFile());
588
648
  return typeAlias;
589
649
  }
650
+ function processEnumDeclaration(statement, blob) {
651
+ const enumObj = new declaration_1.EnumObject();
652
+ enumObj.name = statement.name.text;
653
+ const printer = typescript_1.default.createPrinter();
654
+ enumObj.members = statement.members.map(m => {
655
+ var _a, _b;
656
+ const mem = new declaration_1.EnumMemberObject();
657
+ if (typescript_1.default.isIdentifier(m.name)) {
658
+ mem.name = m.name.text;
659
+ }
660
+ else if (typescript_1.default.isStringLiteral(m.name)) {
661
+ // Preserve quotes in output
662
+ mem.name = `'${m.name.text}'`;
663
+ }
664
+ else if (typescript_1.default.isNumericLiteral(m.name)) {
665
+ // Numeric literal preserves hex form via .text
666
+ mem.name = m.name.text;
667
+ }
668
+ else {
669
+ // Fallback to toString of node kind
670
+ mem.name = m.name.getText ? m.name.getText() : String(m.name);
671
+ }
672
+ if (m.initializer) {
673
+ // Preserve original literal text (e.g., hex) by slicing from the raw source
674
+ try {
675
+ // pos/end are absolute offsets into the source
676
+ const start = (_a = m.initializer.pos) !== null && _a !== void 0 ? _a : 0;
677
+ const end = (_b = m.initializer.end) !== null && _b !== void 0 ? _b : 0;
678
+ if (start >= 0 && end > start) {
679
+ mem.initializer = blob.raw.substring(start, end).trim();
680
+ }
681
+ }
682
+ catch (_c) {
683
+ // Fallback to printer (may normalize to decimal)
684
+ mem.initializer = printer.printNode(typescript_1.default.EmitHint.Unspecified, m.initializer, statement.getSourceFile());
685
+ }
686
+ }
687
+ return mem;
688
+ });
689
+ // Register globally for cross-file lookups (e.g., Dart mapping decisions)
690
+ try {
691
+ declaration_1.EnumObject.globalEnumSet.add(enumObj.name);
692
+ }
693
+ catch (_a) { }
694
+ return enumObj;
695
+ }
590
696
  function processInterfaceDeclaration(statement, blob, sourceFile, definedPropertyCollector, unionTypeCollector) {
591
697
  const interfaceName = statement.name.escapedText.toString();
592
698
  const obj = new declaration_1.ClassObject();
@@ -749,21 +855,33 @@ function processConstructSignature(member, obj, sourceFile, unionTypeCollector)
749
855
  }
750
856
  function processVariableStatement(statement, unionTypeCollector) {
751
857
  const declaration = statement.declarationList.declarations[0];
858
+ if (!declaration)
859
+ return null;
752
860
  if (!typescript_1.default.isIdentifier(declaration.name)) {
753
861
  console.warn('Variable declaration with non-identifier name is not supported');
754
862
  return null;
755
863
  }
756
- const methodName = declaration.name.text;
757
- const type = declaration.type;
758
- if (!type || !typescript_1.default.isFunctionTypeNode(type)) {
864
+ const varName = declaration.name.text;
865
+ const typeNode = declaration.type;
866
+ if (!typeNode) {
759
867
  return null;
760
868
  }
761
- const functionObject = new declaration_1.FunctionObject();
762
- functionObject.declare = new declaration_1.FunctionDeclaration();
763
- functionObject.declare.name = methodName;
764
- functionObject.declare.args = type.parameters.map(param => paramsNodeToArguments(param, unionTypeCollector));
765
- functionObject.declare.returnType = getParameterType(type.type, unionTypeCollector);
766
- return functionObject;
869
+ // Handle function type declarations: declare const fn: (args) => ret
870
+ if (typescript_1.default.isFunctionTypeNode(typeNode)) {
871
+ const functionObject = new declaration_1.FunctionObject();
872
+ functionObject.declare = new declaration_1.FunctionDeclaration();
873
+ functionObject.declare.name = varName;
874
+ functionObject.declare.args = typeNode.parameters.map(param => paramsNodeToArguments(param, unionTypeCollector));
875
+ functionObject.declare.returnType = getParameterType(typeNode.type, unionTypeCollector);
876
+ return functionObject;
877
+ }
878
+ // Otherwise, capture as a const declaration with its type text
879
+ const printer = typescript_1.default.createPrinter();
880
+ const typeText = printer.printNode(typescript_1.default.EmitHint.Unspecified, typeNode, typeNode.getSourceFile());
881
+ const constObj = new declaration_1.ConstObject();
882
+ constObj.name = varName;
883
+ constObj.type = typeText;
884
+ return constObj;
767
885
  }
768
886
  // Clear caches when needed (e.g., between runs)
769
887
  function clearCaches() {