@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/README.md +9 -1
- package/bin/webf.js +9 -1
- package/dist/agents.js +245 -0
- package/dist/analyzer.js +183 -121
- package/dist/commands.js +20 -9
- package/dist/generator.js +39 -16
- package/package.json +2 -1
- package/src/agents.ts +267 -0
- package/src/analyzer.ts +186 -114
- package/src/commands.ts +22 -12
- package/src/generator.ts +32 -12
- package/templates/module.package.json.tpl +17 -6
- package/templates/{module.tsup.config.ts.tpl → module.tsdown.config.ts.tpl} +2 -7
- package/templates/react.component.tsx.tpl +18 -2
- package/templates/react.package.json.tpl +16 -4
- package/templates/{react.tsup.config.ts.tpl → react.tsdown.config.ts.tpl} +2 -4
- package/test/agents-init.test.ts +80 -0
- package/test/analyzer.test.ts +45 -1
- package/test/commands.test.ts +4 -4
- package/test/generator.test.ts +37 -0
- package/test/react.test.ts +5 -0
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
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
|
180
|
-
path.resolve(__dirname, '../templates/module.
|
|
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
|
|
194
|
-
path.resolve(__dirname, '../templates/react.
|
|
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
|
|
456
|
-
const
|
|
457
|
-
writeFileIfChanged(
|
|
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
|
|
535
|
-
const
|
|
536
|
-
writeFileIfChanged(
|
|
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,
|
|
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
|
|
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 (!
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
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
|
|
380
|
-
|
|
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
|
-
|
|
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
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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
|
-
|
|
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.
|
|
5
|
+
"main": "dist/index.cjs",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
7
|
-
"types": "dist/index.d.
|
|
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": "
|
|
11
|
-
"dev": "
|
|
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
|
-
"
|
|
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
|
-
|
|
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.
|
|
5
|
+
"main": "dist/index.cjs",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
7
|
-
"types": "dist/index.d.
|
|
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": "
|
|
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
|
-
"
|
|
38
|
+
"tsdown": "^0.19.0",
|
|
27
39
|
"typescript": "^5.8.3"
|
|
28
40
|
}
|
|
29
41
|
}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
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
|
+
};
|