@openwebf/webf 0.24.0 → 0.24.2

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/src/analyzer.ts CHANGED
@@ -410,6 +410,188 @@ function handleGenericWrapper(typeReference: ts.TypeReferenceNode, mode?: Parame
410
410
  return getParameterBaseType(argument, mode);
411
411
  }
412
412
 
413
+ const customEventTypePrinter = ts.createPrinter({ removeComments: true });
414
+
415
+ function mapTypeReferenceIdentifierToTsType(identifier: string): string | null {
416
+ const mappedType = TYPE_REFERENCE_MAP[identifier];
417
+ if (mappedType === undefined) return null;
418
+ switch (mappedType) {
419
+ case FunctionArgumentType.boolean:
420
+ return 'boolean';
421
+ case FunctionArgumentType.dom_string:
422
+ return 'string';
423
+ case FunctionArgumentType.double:
424
+ case FunctionArgumentType.int:
425
+ return 'number';
426
+ case FunctionArgumentType.any:
427
+ return 'any';
428
+ case FunctionArgumentType.void:
429
+ return 'void';
430
+ case FunctionArgumentType.function:
431
+ return 'Function';
432
+ case FunctionArgumentType.promise:
433
+ return 'Promise<any>';
434
+ default:
435
+ return null;
436
+ }
437
+ }
438
+
439
+ function getBasicTypeKindAsTsType(kind: ts.SyntaxKind): string | null {
440
+ const basicType = BASIC_TYPE_MAP[kind];
441
+ if (basicType === undefined) return null;
442
+ switch (basicType) {
443
+ case FunctionArgumentType.boolean:
444
+ return 'boolean';
445
+ case FunctionArgumentType.dom_string:
446
+ return 'string';
447
+ case FunctionArgumentType.double:
448
+ case FunctionArgumentType.int:
449
+ return 'number';
450
+ case FunctionArgumentType.any:
451
+ return 'any';
452
+ case FunctionArgumentType.void:
453
+ return 'void';
454
+ case FunctionArgumentType.null:
455
+ return 'null';
456
+ case FunctionArgumentType.undefined:
457
+ return 'undefined';
458
+ default:
459
+ return null;
460
+ }
461
+ }
462
+
463
+ function stringifyEntityName(name: ts.EntityName): string {
464
+ if (ts.isIdentifier(name)) return name.text;
465
+ return `${stringifyEntityName(name.left)}.${name.right.text}`;
466
+ }
467
+
468
+ function safePrintCustomEventNode(node: ts.Node): string {
469
+ const sourceFile = node.getSourceFile();
470
+ const printed = customEventTypePrinter.printNode(ts.EmitHint.Unspecified, node, sourceFile);
471
+ // Ensure WebF IDL-like aliases used in type definitions do not leak into generated TypeScript packages.
472
+ return printed.replace(/\bint\b/g, 'number').replace(/\bdouble\b/g, 'number');
473
+ }
474
+
475
+ function stringifyCustomEventGenericTypeNode(typeNode: ts.TypeNode): string | null {
476
+ if (ts.isParenthesizedTypeNode(typeNode)) {
477
+ const inner = stringifyCustomEventGenericTypeNode(typeNode.type);
478
+ return inner ? `(${inner})` : null;
479
+ }
480
+
481
+ if (ts.isUnionTypeNode(typeNode)) {
482
+ const parts = typeNode.types.map(t => stringifyCustomEventGenericTypeNode(t)).filter((t): t is string => Boolean(t));
483
+ return parts.length === typeNode.types.length ? parts.join(' | ') : null;
484
+ }
485
+
486
+ if (ts.isIntersectionTypeNode(typeNode)) {
487
+ const parts = typeNode.types.map(t => stringifyCustomEventGenericTypeNode(t)).filter((t): t is string => Boolean(t));
488
+ return parts.length === typeNode.types.length ? parts.join(' & ') : null;
489
+ }
490
+
491
+ if (ts.isArrayTypeNode(typeNode)) {
492
+ const element = stringifyCustomEventGenericTypeNode(typeNode.elementType);
493
+ return element ? `${element}[]` : null;
494
+ }
495
+
496
+ if (ts.isTupleTypeNode(typeNode)) {
497
+ const elements = typeNode.elements.map(e => stringifyCustomEventGenericTypeNode(e)).filter((t): t is string => Boolean(t));
498
+ return elements.length === typeNode.elements.length ? `[${elements.join(', ')}]` : null;
499
+ }
500
+
501
+ if (ts.isLiteralTypeNode(typeNode)) {
502
+ const literal = typeNode.literal;
503
+ if (literal.kind === ts.SyntaxKind.NullKeyword) return 'null';
504
+ if (literal.kind === ts.SyntaxKind.UndefinedKeyword) return 'undefined';
505
+ if (literal.kind === ts.SyntaxKind.TrueKeyword) return 'true';
506
+ if (literal.kind === ts.SyntaxKind.FalseKeyword) return 'false';
507
+ if (ts.isStringLiteral(literal)) return JSON.stringify(literal.text);
508
+ if (ts.isNumericLiteral(literal)) return literal.text;
509
+ return null;
510
+ }
511
+
512
+ const basic = getBasicTypeKindAsTsType(typeNode.kind);
513
+ if (basic) return basic;
514
+
515
+ if (ts.isTypeReferenceNode(typeNode)) {
516
+ const typeName = stringifyEntityName(typeNode.typeName);
517
+
518
+ // Unwrap internal helpers used by WebF typings.
519
+ if (typeName === 'DartImpl' && typeNode.typeArguments && typeNode.typeArguments[0]) {
520
+ return stringifyCustomEventGenericTypeNode(typeNode.typeArguments[0]);
521
+ }
522
+
523
+ if (typeName === 'Promise') {
524
+ if (!typeNode.typeArguments || !typeNode.typeArguments[0]) return 'Promise<any>';
525
+ const inner = stringifyCustomEventGenericTypeNode(typeNode.typeArguments[0]);
526
+ return inner ? `Promise<${inner}>` : null;
527
+ }
528
+
529
+ const mapped = mapTypeReferenceIdentifierToTsType(typeName);
530
+ if (mapped) return mapped;
531
+
532
+ if (!typeNode.typeArguments || typeNode.typeArguments.length === 0) {
533
+ return typeName;
534
+ }
535
+
536
+ const args = typeNode.typeArguments
537
+ .map(arg => stringifyCustomEventGenericTypeNode(arg))
538
+ .filter((t): t is string => Boolean(t));
539
+ if (args.length !== typeNode.typeArguments.length) return null;
540
+ return `${typeName}<${args.join(', ')}>`;
541
+ }
542
+
543
+ if (ts.isTypeLiteralNode(typeNode)) {
544
+ const members: string[] = [];
545
+ for (const member of typeNode.members) {
546
+ if (ts.isPropertySignature(member) && member.type) {
547
+ const typeString = stringifyCustomEventGenericTypeNode(member.type);
548
+ if (!typeString) return null;
549
+ let nameText: string;
550
+ if (ts.isIdentifier(member.name)) nameText = member.name.text;
551
+ else if (ts.isStringLiteral(member.name)) nameText = JSON.stringify(member.name.text);
552
+ else if (ts.isNumericLiteral(member.name)) nameText = member.name.text;
553
+ else nameText = member.name.getText();
554
+ const optional = member.questionToken ? '?' : '';
555
+ members.push(`${nameText}${optional}: ${typeString}`);
556
+ continue;
557
+ }
558
+ if (ts.isIndexSignatureDeclaration(member) && member.type && member.parameters.length === 1) {
559
+ const param = member.parameters[0];
560
+ const paramName = ts.isIdentifier(param.name) ? param.name.text : param.name.getText();
561
+ const paramType = param.type ? stringifyCustomEventGenericTypeNode(param.type) : 'string';
562
+ const valueType = stringifyCustomEventGenericTypeNode(member.type);
563
+ if (!paramType || !valueType) return null;
564
+ members.push(`[${paramName}: ${paramType}]: ${valueType}`);
565
+ continue;
566
+ }
567
+ // Fallback for uncommon members (call signatures, method signatures, etc.).
568
+ members.push(safePrintCustomEventNode(member));
569
+ }
570
+ return `{ ${members.join('; ')} }`;
571
+ }
572
+
573
+ if (ts.isTypeOperatorNode(typeNode)) {
574
+ const inner = stringifyCustomEventGenericTypeNode(typeNode.type);
575
+ if (!inner) return null;
576
+ const operator =
577
+ typeNode.operator === ts.SyntaxKind.KeyOfKeyword ? 'keyof' :
578
+ typeNode.operator === ts.SyntaxKind.ReadonlyKeyword ? 'readonly' :
579
+ typeNode.operator === ts.SyntaxKind.UniqueKeyword ? 'unique' :
580
+ null;
581
+ return operator ? `${operator} ${inner}` : null;
582
+ }
583
+
584
+ if (ts.isIndexedAccessTypeNode(typeNode)) {
585
+ const objectType = stringifyCustomEventGenericTypeNode(typeNode.objectType);
586
+ const indexType = stringifyCustomEventGenericTypeNode(typeNode.indexType);
587
+ if (!objectType || !indexType) return null;
588
+ return `${objectType}[${indexType}]`;
589
+ }
590
+
591
+ // As a last resort, keep the original syntax but normalize known WebF aliases.
592
+ return safePrintCustomEventNode(typeNode);
593
+ }
594
+
413
595
  function handleCustomEventType(typeReference: ts.TypeReferenceNode): ParameterBaseType {
414
596
  // Handle CustomEvent<T> by returning the full type with generic parameter
415
597
  if (!typeReference.typeArguments || !typeReference.typeArguments[0]) {
@@ -417,121 +599,11 @@ function handleCustomEventType(typeReference: ts.TypeReferenceNode): ParameterBa
417
599
  }
418
600
 
419
601
  const argument = typeReference.typeArguments[0];
420
- let genericType: string;
421
-
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)) {
461
- const typeName = argument.typeName.text;
462
-
463
- // Check if it's a mapped type reference like 'int' or 'double'
464
- const mappedType = TYPE_REFERENCE_MAP[typeName];
465
- if (mappedType !== undefined) {
466
- switch (mappedType) {
467
- case FunctionArgumentType.boolean:
468
- genericType = 'boolean';
469
- break;
470
- case FunctionArgumentType.dom_string:
471
- genericType = 'string';
472
- break;
473
- case FunctionArgumentType.double:
474
- case FunctionArgumentType.int:
475
- genericType = 'number';
476
- break;
477
- case FunctionArgumentType.any:
478
- genericType = 'any';
479
- break;
480
- case FunctionArgumentType.void:
481
- genericType = 'void';
482
- break;
483
- case FunctionArgumentType.function:
484
- genericType = 'Function';
485
- break;
486
- case FunctionArgumentType.promise:
487
- genericType = 'Promise<any>';
488
- break;
489
- default:
490
- genericType = typeName;
491
- }
492
- } else {
493
- // For other type references, use the type name directly
494
- genericType = typeName;
495
- }
496
- } else if (ts.isLiteralTypeNode(argument) && ts.isStringLiteral(argument.literal)) {
497
- genericType = argument.literal.text;
498
- } else {
499
- // Handle basic types (boolean, string, number, etc.)
500
- const basicType = BASIC_TYPE_MAP[argument.kind];
501
- if (basicType !== undefined) {
502
- switch (basicType) {
503
- case FunctionArgumentType.boolean:
504
- genericType = 'boolean';
505
- break;
506
- case FunctionArgumentType.dom_string:
507
- genericType = 'string';
508
- break;
509
- case FunctionArgumentType.double:
510
- case FunctionArgumentType.int:
511
- genericType = 'number';
512
- break;
513
- case FunctionArgumentType.any:
514
- genericType = 'any';
515
- break;
516
- case FunctionArgumentType.void:
517
- genericType = 'void';
518
- break;
519
- case FunctionArgumentType.null:
520
- genericType = 'null';
521
- break;
522
- case FunctionArgumentType.undefined:
523
- genericType = 'undefined';
524
- break;
525
- default:
526
- genericType = 'any';
527
- }
528
- } else {
529
- // For truly complex types, fallback to 'any' to avoid errors
530
- console.warn('Complex generic type in CustomEvent, using any');
531
- genericType = 'any';
532
- }
602
+ const genericType = stringifyCustomEventGenericTypeNode(argument);
603
+ if (!genericType) {
604
+ console.warn('Complex generic type in CustomEvent, using any');
605
+ return 'CustomEvent<any>';
533
606
  }
534
-
535
607
  return `CustomEvent<${genericType}>`;
536
608
  }
537
609
 
package/src/commands.ts CHANGED
@@ -9,6 +9,7 @@ import { globSync } from 'glob';
9
9
  import _ from 'lodash';
10
10
  import inquirer from 'inquirer';
11
11
  import yaml from 'yaml';
12
+ import { agentsInitCommand } from './agents';
12
13
 
13
14
  interface GenerateOptions {
14
15
  flutterPackageSrc?: string;
@@ -176,8 +177,8 @@ const moduleTsConfig = fs.readFileSync(
176
177
  'utf-8'
177
178
  );
178
179
 
179
- const moduleTsUpConfig = fs.readFileSync(
180
- path.resolve(__dirname, '../templates/module.tsup.config.ts.tpl'),
180
+ const moduleTsDownConfig = fs.readFileSync(
181
+ path.resolve(__dirname, '../templates/module.tsdown.config.ts.tpl'),
181
182
  'utf-8'
182
183
  );
183
184
  const reactPackageJson = fs.readFileSync(
@@ -190,8 +191,8 @@ const reactTsConfig = fs.readFileSync(
190
191
  'utf-8'
191
192
  );
192
193
 
193
- const reactTsUpConfig = fs.readFileSync(
194
- path.resolve(__dirname, '../templates/react.tsup.config.ts.tpl'),
194
+ const reactTsDownConfig = fs.readFileSync(
195
+ path.resolve(__dirname, '../templates/react.tsdown.config.ts.tpl'),
195
196
  'utf-8'
196
197
  );
197
198
 
@@ -452,9 +453,9 @@ function createCommand(target: string, options: { framework: string; packageName
452
453
  const tsConfigContent = _.template(reactTsConfig)({});
453
454
  writeFileIfChanged(tsConfigPath, tsConfigContent);
454
455
 
455
- const tsupConfigPath = path.join(target, 'tsup.config.ts');
456
- const tsupConfigContent = _.template(reactTsUpConfig)({});
457
- writeFileIfChanged(tsupConfigPath, tsupConfigContent);
456
+ const tsdownConfigPath = path.join(target, 'tsdown.config.ts');
457
+ const tsdownConfigContent = _.template(reactTsDownConfig)({});
458
+ writeFileIfChanged(tsdownConfigPath, tsdownConfigContent);
458
459
 
459
460
  const gitignorePath = path.join(target, '.gitignore');
460
461
  const gitignoreContent = _.template(gitignore)({});
@@ -531,9 +532,9 @@ function createModuleProject(target: string, options: { packageName: string; met
531
532
  const tsConfigContent = _.template(moduleTsConfig)({});
532
533
  writeFileIfChanged(tsConfigPath, tsConfigContent);
533
534
 
534
- const tsupConfigPath = path.join(target, 'tsup.config.ts');
535
- const tsupConfigContent = _.template(moduleTsUpConfig)({});
536
- writeFileIfChanged(tsupConfigPath, tsupConfigContent);
535
+ const tsdownConfigPath = path.join(target, 'tsdown.config.ts');
536
+ const tsdownConfigContent = _.template(moduleTsDownConfig)({});
537
+ writeFileIfChanged(tsdownConfigPath, tsdownConfigContent);
537
538
 
538
539
  if (!skipGitignore) {
539
540
  const gitignorePath = path.join(target, '.gitignore');
@@ -1063,7 +1064,7 @@ async function generateModuleCommand(distPath: string, options: GenerateOptions)
1063
1064
  packageName = packageNameAnswer.packageName;
1064
1065
  }
1065
1066
 
1066
- // Prevent npm scaffolding (package.json, tsup.config.ts, etc.) from being written into
1067
+ // Prevent npm scaffolding (package.json, tsdown.config.ts, etc.) from being written into
1067
1068
  // the Flutter package itself. Force users to choose a separate output directory.
1068
1069
  if (resolvedDistPath === flutterPackageSrc) {
1069
1070
  console.error('\n❌ Output directory must not be the Flutter package root.');
@@ -1114,6 +1115,15 @@ async function generateModuleCommand(distPath: string, options: GenerateOptions)
1114
1115
  command,
1115
1116
  });
1116
1117
 
1118
+ // Copy README.md from the source Flutter package into the npm package root
1119
+ const { copied } = copyReadmeToPackageRoot({
1120
+ sourceRoot: flutterPackageSrc,
1121
+ targetRoot: resolvedDistPath,
1122
+ });
1123
+ if (copied) {
1124
+ console.log('📄 Copied README.md to package root');
1125
+ }
1126
+
1117
1127
  console.log('\nModule code generation completed successfully!');
1118
1128
 
1119
1129
  try {
@@ -1446,4 +1456,4 @@ async function buildAndPublishPackage(packagePath: string, registry?: string, is
1446
1456
  }
1447
1457
  }
1448
1458
 
1449
- export { generateCommand, generateModuleCommand };
1459
+ export { generateCommand, generateModuleCommand, agentsInitCommand };
package/src/generator.ts CHANGED
@@ -329,7 +329,8 @@ export async function reactGen({ source, target, exclude, packageName }: Generat
329
329
  const newExports = generateReactIndex(blobs);
330
330
 
331
331
  // Build desired export map: moduleSpecifier -> Set of names
332
- const desiredExports = new Map<string, Set<string>>();
332
+ const desiredValueExports = new Map<string, Set<string>>();
333
+ const desiredTypeExports = new Map<string, Set<string>>();
333
334
  const components = blobs.flatMap(blob => {
334
335
  const classObjects = blob.objects.filter(obj => obj instanceof ClassObject) as ClassObject[];
335
336
  const properties = classObjects.filter(object => object.name.endsWith('Properties'));
@@ -351,10 +352,10 @@ export async function reactGen({ source, target, exclude, packageName }: Generat
351
352
  }
352
353
  for (const { className, fileName, relativeDir } of unique.values()) {
353
354
  const spec = `./${relativeDir ? `${relativeDir}/` : ''}${fileName}`;
354
- if (!desiredExports.has(spec)) desiredExports.set(spec, new Set());
355
- const set = desiredExports.get(spec)!;
356
- set.add(className);
357
- set.add(`${className}Element`);
355
+ if (!desiredValueExports.has(spec)) desiredValueExports.set(spec, new Set());
356
+ if (!desiredTypeExports.has(spec)) desiredTypeExports.set(spec, new Set());
357
+ desiredValueExports.get(spec)!.add(className);
358
+ desiredTypeExports.get(spec)!.add(`${className}Element`);
358
359
  }
359
360
 
360
361
  if (!fs.existsSync(indexFilePath)) {
@@ -376,22 +377,41 @@ export async function reactGen({ source, target, exclude, packageName }: Generat
376
377
  ? stmt.moduleSpecifier.text
377
378
  : undefined;
378
379
  if (!moduleSpecifier) continue;
379
- const desired = desiredExports.get(moduleSpecifier);
380
- if (!desired) continue;
380
+ const desiredValues = desiredValueExports.get(moduleSpecifier);
381
+ const desiredTypes = desiredTypeExports.get(moduleSpecifier);
382
+ if (!desiredValues && !desiredTypes) continue;
383
+ const declIsTypeOnly = Boolean((stmt as unknown as { isTypeOnly?: boolean }).isTypeOnly);
381
384
  for (const el of stmt.exportClause.elements) {
382
385
  const name = el.name.getText(sourceFile);
383
- if (desired.has(name)) desired.delete(name);
386
+ const specIsTypeOnly = Boolean((el as unknown as { isTypeOnly?: boolean }).isTypeOnly);
387
+ const isTypeOnly = declIsTypeOnly || specIsTypeOnly;
388
+ if (isTypeOnly) {
389
+ if (desiredTypes?.has(name)) desiredTypes.delete(name);
390
+ } else {
391
+ if (desiredValues?.has(name)) desiredValues.delete(name);
392
+ }
384
393
  }
385
394
  }
386
395
  }
387
396
 
388
397
  // Prepare new export lines for any remaining names
389
398
  const lines: string[] = [];
390
- for (const [spec, names] of desiredExports) {
391
- const missing = Array.from(names);
392
- if (missing.length === 0) continue;
399
+ const specs = new Set<string>([
400
+ ...desiredValueExports.keys(),
401
+ ...desiredTypeExports.keys()
402
+ ]);
403
+
404
+ for (const spec of specs) {
405
+ const missingValues = Array.from(desiredValueExports.get(spec) ?? []);
406
+ const missingTypes = Array.from(desiredTypeExports.get(spec) ?? []);
407
+ if (missingValues.length === 0 && missingTypes.length === 0) continue;
393
408
  const specEscaped = spec.replace(/\\/g, '/');
394
- lines.push(`export { ${missing.join(', ')} } from "${specEscaped}";`);
409
+ if (missingValues.length > 0) {
410
+ lines.push(`export { ${missingValues.join(', ')} } from "${specEscaped}";`);
411
+ }
412
+ if (missingTypes.length > 0) {
413
+ lines.push(`export type { ${missingTypes.join(', ')} } from "${specEscaped}";`);
414
+ }
395
415
  }
396
416
 
397
417
  if (lines.length > 0) {
@@ -2,13 +2,25 @@
2
2
  "name": "<%= packageName %>",
3
3
  "version": "<%= version %>",
4
4
  "description": "<%= description %>",
5
- "main": "dist/index.js",
5
+ "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
7
- "types": "dist/index.d.ts",
7
+ "types": "dist/index.d.mts",
8
+ "exports": {
9
+ ".": {
10
+ "import": {
11
+ "types": "./dist/index.d.mts",
12
+ "default": "./dist/index.mjs"
13
+ },
14
+ "require": {
15
+ "types": "./dist/index.d.cts",
16
+ "default": "./dist/index.cjs"
17
+ }
18
+ }
19
+ },
8
20
  "files": ["dist"],
9
21
  "scripts": {
10
- "build": "tsup",
11
- "dev": "tsup --watch",
22
+ "build": "tsdown",
23
+ "dev": "tsdown --watch",
12
24
  "clean": "rimraf dist",
13
25
  "prepublishOnly": "npm run build"
14
26
  },
@@ -26,11 +38,10 @@
26
38
  },
27
39
  "devDependencies": {
28
40
  "rimraf": "^5.0.0",
29
- "tsup": "^8.5.0",
41
+ "tsdown": "^0.19.0",
30
42
  "typescript": "^5.8.3"
31
43
  },
32
44
  "publishConfig": {
33
45
  "access": "public"
34
46
  }
35
47
  }
36
-
@@ -1,13 +1,8 @@
1
- import { defineConfig } from 'tsup';
2
-
3
- export default defineConfig({
1
+ module.exports = {
4
2
  entry: ['src/index.ts'],
5
3
  format: ['cjs', 'esm'],
6
4
  dts: true,
7
5
  clean: true,
8
- splitting: false,
9
6
  sourcemap: true,
10
- minify: false,
11
7
  external: [],
12
- });
13
-
8
+ };
@@ -63,12 +63,16 @@ export interface <%= className %>Props {
63
63
 
64
64
  <% if (methods && methods.methods.length > 0) { %>
65
65
  /**
66
- * Element interface with methods accessible via ref
66
+ * Element interface with methods/properties accessible via ref
67
67
  * @example
68
68
  * ```tsx
69
69
  * const ref = useRef<<%= className %>Element>(null);
70
70
  * // Call methods on the element
71
71
  * ref.current?.finishRefresh('success');
72
+ <% if (properties && properties.props && properties.props.length > 0) { %>
73
+ * // Access properties
74
+ * console.log(ref.current?.<%= _.camelCase(properties.props[0].name || 'someProp') %>);
75
+ <% } %>
72
76
  * ```
73
77
  */
74
78
  <% } %>
@@ -76,7 +80,19 @@ export interface <%= className %>Element extends WebFElementWithMethods<{
76
80
  <% _.forEach(methods?.methods, function(method, index) { %>
77
81
  <%= generateMethodDeclarationWithDocs(method, ' ') %>
78
82
  <% }); %>
79
- }> {}
83
+ }> {
84
+ <% _.forEach(properties?.props, function(prop, index) { %>
85
+ <% var propName = _.camelCase(prop.name); %>
86
+ <% if (prop.documentation) { %>
87
+ /** <%= prop.documentation.split('\n')[0] %> */
88
+ <% } %>
89
+ <% if (prop.readonly) { %>
90
+ readonly <%= propName %><% if (prop.optional) { %>?<% } %>: <%= generateReturnType(prop.type) %>;
91
+ <% } else { %>
92
+ <%= propName %><% if (prop.optional) { %>?<% } %>: <%= generateReturnType(prop.type) %>;
93
+ <% } %>
94
+ <% }); %>
95
+ }
80
96
 
81
97
  <% if (properties?.documentation || methods?.documentation || events?.documentation) { %>
82
98
  <% const docs = properties?.documentation || methods?.documentation || events?.documentation; %>
@@ -2,12 +2,24 @@
2
2
  "name": "<%= packageName %>",
3
3
  "version": "<%= version %>",
4
4
  "description": "<%= description %>",
5
- "main": "dist/index.js",
5
+ "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
7
- "types": "dist/index.d.ts",
7
+ "types": "dist/index.d.mts",
8
+ "exports": {
9
+ ".": {
10
+ "import": {
11
+ "types": "./dist/index.d.mts",
12
+ "default": "./dist/index.mjs"
13
+ },
14
+ "require": {
15
+ "types": "./dist/index.d.cts",
16
+ "default": "./dist/index.cjs"
17
+ }
18
+ }
19
+ },
8
20
  "files": ["dist", "README.md"],
9
21
  "scripts": {
10
- "build": "tsup"
22
+ "build": "tsdown"
11
23
  },
12
24
  "keywords": [],
13
25
  "author": "",
@@ -23,7 +35,7 @@
23
35
  "@types/react": "^19.1.0",
24
36
  "@types/react-dom": "^19.1.2",
25
37
  "picomatch": "^4.0.2",
26
- "tsup": "^8.5.0",
38
+ "tsdown": "^0.19.0",
27
39
  "typescript": "^5.8.3"
28
40
  }
29
41
  }
@@ -1,10 +1,8 @@
1
- import { defineConfig } from 'tsup'
2
-
3
- export default defineConfig({
1
+ module.exports = {
4
2
  entry: ['src/index.ts'],
5
3
  format: ['esm', 'cjs'],
6
4
  dts: true,
7
5
  sourcemap: true,
8
6
  clean: true,
9
7
  external: ['react', 'react-dom', '@openwebf/react-core-ui'],
10
- })
8
+ };