@openwebf/webf 0.22.13 → 0.23.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/TYPING_GUIDE.md +17 -1
- package/dist/analyzer.js +70 -16
- package/dist/commands.js +64 -6
- package/dist/constants.js +242 -0
- package/dist/dart.js +9 -7
- package/dist/declaration.js +13 -1
- package/dist/generator.js +148 -30
- package/dist/react.js +11 -0
- package/dist/vue.js +40 -0
- package/package.json +3 -2
- package/src/IDLBlob.ts +2 -2
- package/src/analyzer.ts +85 -27
- package/src/commands.ts +73 -6
- package/src/dart.ts +10 -8
- package/src/declaration.ts +15 -0
- package/src/generator.ts +146 -31
- package/src/react.ts +14 -1
- package/src/vue.ts +46 -1
- package/templates/class.dart.tpl +1 -1
- package/templates/react.package.json.tpl +1 -0
- package/templates/vue.components.d.ts.tpl +7 -4
- package/test/react-consts.test.ts +30 -0
- package/test/vue.test.ts +34 -2
- package/tsconfig.json +4 -1
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/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
|
|
73
|
-
//
|
|
74
|
-
sourceFile = cacheEntry
|
|
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,
|
|
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);
|
|
@@ -575,6 +574,8 @@ function walkProgram(blob, statement, sourceFile, definedPropertyCollector, unio
|
|
|
575
574
|
return processVariableStatement(statement, unionTypeCollector);
|
|
576
575
|
case typescript_1.default.SyntaxKind.TypeAliasDeclaration:
|
|
577
576
|
return processTypeAliasDeclaration(statement, blob);
|
|
577
|
+
case typescript_1.default.SyntaxKind.EnumDeclaration:
|
|
578
|
+
return processEnumDeclaration(statement, blob);
|
|
578
579
|
default:
|
|
579
580
|
return null;
|
|
580
581
|
}
|
|
@@ -587,6 +588,47 @@ function processTypeAliasDeclaration(statement, blob) {
|
|
|
587
588
|
typeAlias.type = printer.printNode(typescript_1.default.EmitHint.Unspecified, statement.type, statement.getSourceFile());
|
|
588
589
|
return typeAlias;
|
|
589
590
|
}
|
|
591
|
+
function processEnumDeclaration(statement, blob) {
|
|
592
|
+
const enumObj = new declaration_1.EnumObject();
|
|
593
|
+
enumObj.name = statement.name.text;
|
|
594
|
+
const printer = typescript_1.default.createPrinter();
|
|
595
|
+
enumObj.members = statement.members.map(m => {
|
|
596
|
+
var _a, _b;
|
|
597
|
+
const mem = new declaration_1.EnumMemberObject();
|
|
598
|
+
if (typescript_1.default.isIdentifier(m.name)) {
|
|
599
|
+
mem.name = m.name.text;
|
|
600
|
+
}
|
|
601
|
+
else if (typescript_1.default.isStringLiteral(m.name)) {
|
|
602
|
+
// Preserve quotes in output
|
|
603
|
+
mem.name = `'${m.name.text}'`;
|
|
604
|
+
}
|
|
605
|
+
else if (typescript_1.default.isNumericLiteral(m.name)) {
|
|
606
|
+
// Numeric literal preserves hex form via .text
|
|
607
|
+
mem.name = m.name.text;
|
|
608
|
+
}
|
|
609
|
+
else {
|
|
610
|
+
// Fallback to toString of node kind
|
|
611
|
+
mem.name = m.name.getText ? m.name.getText() : String(m.name);
|
|
612
|
+
}
|
|
613
|
+
if (m.initializer) {
|
|
614
|
+
// Preserve original literal text (e.g., hex) by slicing from the raw source
|
|
615
|
+
try {
|
|
616
|
+
// pos/end are absolute offsets into the source
|
|
617
|
+
const start = (_a = m.initializer.pos) !== null && _a !== void 0 ? _a : 0;
|
|
618
|
+
const end = (_b = m.initializer.end) !== null && _b !== void 0 ? _b : 0;
|
|
619
|
+
if (start >= 0 && end > start) {
|
|
620
|
+
mem.initializer = blob.raw.substring(start, end).trim();
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
catch (_c) {
|
|
624
|
+
// Fallback to printer (may normalize to decimal)
|
|
625
|
+
mem.initializer = printer.printNode(typescript_1.default.EmitHint.Unspecified, m.initializer, statement.getSourceFile());
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
return mem;
|
|
629
|
+
});
|
|
630
|
+
return enumObj;
|
|
631
|
+
}
|
|
590
632
|
function processInterfaceDeclaration(statement, blob, sourceFile, definedPropertyCollector, unionTypeCollector) {
|
|
591
633
|
const interfaceName = statement.name.escapedText.toString();
|
|
592
634
|
const obj = new declaration_1.ClassObject();
|
|
@@ -749,21 +791,33 @@ function processConstructSignature(member, obj, sourceFile, unionTypeCollector)
|
|
|
749
791
|
}
|
|
750
792
|
function processVariableStatement(statement, unionTypeCollector) {
|
|
751
793
|
const declaration = statement.declarationList.declarations[0];
|
|
794
|
+
if (!declaration)
|
|
795
|
+
return null;
|
|
752
796
|
if (!typescript_1.default.isIdentifier(declaration.name)) {
|
|
753
797
|
console.warn('Variable declaration with non-identifier name is not supported');
|
|
754
798
|
return null;
|
|
755
799
|
}
|
|
756
|
-
const
|
|
757
|
-
const
|
|
758
|
-
if (!
|
|
800
|
+
const varName = declaration.name.text;
|
|
801
|
+
const typeNode = declaration.type;
|
|
802
|
+
if (!typeNode) {
|
|
759
803
|
return null;
|
|
760
804
|
}
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
805
|
+
// Handle function type declarations: declare const fn: (args) => ret
|
|
806
|
+
if (typescript_1.default.isFunctionTypeNode(typeNode)) {
|
|
807
|
+
const functionObject = new declaration_1.FunctionObject();
|
|
808
|
+
functionObject.declare = new declaration_1.FunctionDeclaration();
|
|
809
|
+
functionObject.declare.name = varName;
|
|
810
|
+
functionObject.declare.args = typeNode.parameters.map(param => paramsNodeToArguments(param, unionTypeCollector));
|
|
811
|
+
functionObject.declare.returnType = getParameterType(typeNode.type, unionTypeCollector);
|
|
812
|
+
return functionObject;
|
|
813
|
+
}
|
|
814
|
+
// Otherwise, capture as a const declaration with its type text
|
|
815
|
+
const printer = typescript_1.default.createPrinter();
|
|
816
|
+
const typeText = printer.printNode(typescript_1.default.EmitHint.Unspecified, typeNode, typeNode.getSourceFile());
|
|
817
|
+
const constObj = new declaration_1.ConstObject();
|
|
818
|
+
constObj.name = varName;
|
|
819
|
+
constObj.type = typeText;
|
|
820
|
+
return constObj;
|
|
767
821
|
}
|
|
768
822
|
// Clear caches when needed (e.g., between runs)
|
|
769
823
|
function clearCaches() {
|
package/dist/commands.js
CHANGED
|
@@ -18,6 +18,7 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
18
18
|
const path_1 = __importDefault(require("path"));
|
|
19
19
|
const os_1 = __importDefault(require("os"));
|
|
20
20
|
const generator_1 = require("./generator");
|
|
21
|
+
const glob_1 = require("glob");
|
|
21
22
|
const lodash_1 = __importDefault(require("lodash"));
|
|
22
23
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
23
24
|
const yaml_1 = __importDefault(require("yaml"));
|
|
@@ -171,6 +172,43 @@ function readFlutterPackageMetadata(packagePath) {
|
|
|
171
172
|
return null;
|
|
172
173
|
}
|
|
173
174
|
}
|
|
175
|
+
// Copy markdown docs that match .d.ts basenames from source to the built dist folder
|
|
176
|
+
function copyMarkdownDocsToDist(params) {
|
|
177
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
178
|
+
const { sourceRoot, distRoot, exclude } = params;
|
|
179
|
+
// Ensure dist exists
|
|
180
|
+
if (!fs_1.default.existsSync(distRoot)) {
|
|
181
|
+
return { copied: 0, skipped: 0 };
|
|
182
|
+
}
|
|
183
|
+
// Default ignore patterns similar to generator
|
|
184
|
+
const defaultIgnore = ['**/node_modules/**', '**/dist/**', '**/build/**', '**/example/**'];
|
|
185
|
+
const ignore = exclude && exclude.length ? [...defaultIgnore, ...exclude] : defaultIgnore;
|
|
186
|
+
// Find all .d.ts files and check for sibling .md files
|
|
187
|
+
const dtsFiles = glob_1.glob.globSync('**/*.d.ts', { cwd: sourceRoot, ignore });
|
|
188
|
+
let copied = 0;
|
|
189
|
+
let skipped = 0;
|
|
190
|
+
for (const relDts of dtsFiles) {
|
|
191
|
+
if (path_1.default.basename(relDts) === 'global.d.ts') {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
const relMd = relDts.replace(/\.d\.ts$/i, '.md');
|
|
195
|
+
const absMd = path_1.default.join(sourceRoot, relMd);
|
|
196
|
+
if (!fs_1.default.existsSync(absMd)) {
|
|
197
|
+
skipped++;
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
// Copy into dist preserving relative path
|
|
201
|
+
const destPath = path_1.default.join(distRoot, relMd);
|
|
202
|
+
const destDir = path_1.default.dirname(destPath);
|
|
203
|
+
if (!fs_1.default.existsSync(destDir)) {
|
|
204
|
+
fs_1.default.mkdirSync(destDir, { recursive: true });
|
|
205
|
+
}
|
|
206
|
+
fs_1.default.copyFileSync(absMd, destPath);
|
|
207
|
+
copied++;
|
|
208
|
+
}
|
|
209
|
+
return { copied, skipped };
|
|
210
|
+
});
|
|
211
|
+
}
|
|
174
212
|
function validateTypeScriptEnvironment(projectPath) {
|
|
175
213
|
const errors = [];
|
|
176
214
|
// Check for TypeScript configuration
|
|
@@ -233,11 +271,17 @@ function createCommand(target, options) {
|
|
|
233
271
|
fs_1.default.mkdirSync(srcDir, { recursive: true });
|
|
234
272
|
}
|
|
235
273
|
const indexFilePath = path_1.default.join(srcDir, 'index.ts');
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
274
|
+
if (!fs_1.default.existsSync(indexFilePath)) {
|
|
275
|
+
const indexContent = lodash_1.default.template(reactIndexTpl)({
|
|
276
|
+
components: [],
|
|
277
|
+
});
|
|
278
|
+
writeFileIfChanged(indexFilePath, indexContent);
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
// Do not overwrite existing index.ts created by the user
|
|
282
|
+
// Leave merge to the codegen step which appends exports safely
|
|
283
|
+
}
|
|
284
|
+
(0, child_process_1.spawnSync)(NPM, ['install'], {
|
|
241
285
|
cwd: target,
|
|
242
286
|
stdio: 'inherit'
|
|
243
287
|
});
|
|
@@ -503,7 +547,9 @@ function generateCommand(distPath, options) {
|
|
|
503
547
|
target: resolvedDistPath,
|
|
504
548
|
command,
|
|
505
549
|
exclude: options.exclude,
|
|
506
|
-
packageName
|
|
550
|
+
// Prefer CLI-provided packageName (validated/sanitized above),
|
|
551
|
+
// fallback to detected name from package.json
|
|
552
|
+
packageName: packageName || reactPackageName,
|
|
507
553
|
});
|
|
508
554
|
}
|
|
509
555
|
else if (framework === 'vue') {
|
|
@@ -519,6 +565,18 @@ function generateCommand(distPath, options) {
|
|
|
519
565
|
if (framework) {
|
|
520
566
|
try {
|
|
521
567
|
yield buildPackage(resolvedDistPath);
|
|
568
|
+
// After building React package, copy any matching .md docs next to built JS files
|
|
569
|
+
if (framework === 'react' && options.flutterPackageSrc) {
|
|
570
|
+
const distOut = path_1.default.join(resolvedDistPath, 'dist');
|
|
571
|
+
const { copied } = yield copyMarkdownDocsToDist({
|
|
572
|
+
sourceRoot: options.flutterPackageSrc,
|
|
573
|
+
distRoot: distOut,
|
|
574
|
+
exclude: options.exclude,
|
|
575
|
+
});
|
|
576
|
+
if (copied > 0) {
|
|
577
|
+
console.log(`📄 Copied ${copied} markdown docs to dist`);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
522
580
|
}
|
|
523
581
|
catch (error) {
|
|
524
582
|
console.error('\nWarning: Build failed:', error);
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateUnionConstantsValuesTs = generateUnionConstantsValuesTs;
|
|
7
|
+
exports.generateUnionConstantsDts = generateUnionConstantsDts;
|
|
8
|
+
exports.generateDeclaredConstantsValuesTs = generateDeclaredConstantsValuesTs;
|
|
9
|
+
exports.generateDeclaredConstantsDts = generateDeclaredConstantsDts;
|
|
10
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
11
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
12
|
+
const dart_1 = require("./dart");
|
|
13
|
+
// Generate constant name from component (Properties/Bindings trimmed) and prop name
|
|
14
|
+
function getConstName(className, propName) {
|
|
15
|
+
const baseName = className.replace(/Properties$|Bindings$/, '');
|
|
16
|
+
return baseName + lodash_1.default.upperFirst(lodash_1.default.camelCase(propName));
|
|
17
|
+
}
|
|
18
|
+
function collectUnionStringProps(blobs) {
|
|
19
|
+
const results = [];
|
|
20
|
+
for (const blob of blobs) {
|
|
21
|
+
const classObjects = (blob.objects || []);
|
|
22
|
+
const properties = classObjects.filter(obj => obj.name && obj.name.endsWith('Properties'));
|
|
23
|
+
if (!properties.length)
|
|
24
|
+
continue;
|
|
25
|
+
const componentProps = properties[0];
|
|
26
|
+
const componentName = componentProps.name.replace(/Properties$/, '');
|
|
27
|
+
for (const prop of componentProps.props || []) {
|
|
28
|
+
if (!(0, dart_1.isStringUnionType)(prop.type))
|
|
29
|
+
continue;
|
|
30
|
+
const values = (0, dart_1.getUnionStringValues)(prop, blob);
|
|
31
|
+
if (!values || values.length === 0)
|
|
32
|
+
continue;
|
|
33
|
+
const constName = getConstName(componentProps.name, prop.name);
|
|
34
|
+
results.push({ constName, values, className: componentName, propName: prop.name });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return results;
|
|
38
|
+
}
|
|
39
|
+
function generateUnionConstantsValuesTs(blobs) {
|
|
40
|
+
const items = collectUnionStringProps(blobs);
|
|
41
|
+
if (!items.length)
|
|
42
|
+
return '';
|
|
43
|
+
const header = `// Auto-generated by WebF CLI\n// Constants for string-union properties extracted from .d.ts definitions\n`;
|
|
44
|
+
const blocks = items.map(item => {
|
|
45
|
+
const entries = item.values.map(v => ` '${v}': '${v}',`).join('\n');
|
|
46
|
+
return `// ${item.className}.${item.propName}\nexport const ${item.constName} = {\n${entries}\n} as const;`;
|
|
47
|
+
});
|
|
48
|
+
return [header, ...blocks].join('\n\n') + '\n';
|
|
49
|
+
}
|
|
50
|
+
function generateUnionConstantsDts(blobs) {
|
|
51
|
+
const items = collectUnionStringProps(blobs);
|
|
52
|
+
if (!items.length)
|
|
53
|
+
return '';
|
|
54
|
+
const header = `// Auto-generated by WebF CLI\n// Type declarations for constants representing string-union property values\n`;
|
|
55
|
+
const blocks = items.map(item => {
|
|
56
|
+
const entries = item.values.map(v => ` readonly '${v}': '${v}';`).join('\n');
|
|
57
|
+
return `// ${item.className}.${item.propName}\nexport declare const ${item.constName}: {\n${entries}\n};`;
|
|
58
|
+
});
|
|
59
|
+
return [header, ...blocks].join('\n\n') + '\n';
|
|
60
|
+
}
|
|
61
|
+
function parseLiteralFromType(node) {
|
|
62
|
+
if (typescript_1.default.isLiteralTypeNode(node)) {
|
|
63
|
+
const lit = node.literal;
|
|
64
|
+
if (typescript_1.default.isStringLiteral(lit))
|
|
65
|
+
return lit.text;
|
|
66
|
+
if (typescript_1.default.isNumericLiteral(lit))
|
|
67
|
+
return Number(lit.text);
|
|
68
|
+
if (lit.kind === typescript_1.default.SyntaxKind.TrueKeyword)
|
|
69
|
+
return true;
|
|
70
|
+
if (lit.kind === typescript_1.default.SyntaxKind.FalseKeyword)
|
|
71
|
+
return false;
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
if (typescript_1.default.isTypeQueryNode(node)) {
|
|
75
|
+
// typeof Identifier
|
|
76
|
+
if (typescript_1.default.isIdentifier(node.exprName)) {
|
|
77
|
+
return { typeofRef: node.exprName.text };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
function collectDeclaredConstsFromSource(content, fileName = 'index.d.ts') {
|
|
83
|
+
const source = typescript_1.default.createSourceFile(fileName, content, typescript_1.default.ScriptTarget.ES2020, true, typescript_1.default.ScriptKind.TS);
|
|
84
|
+
const results = [];
|
|
85
|
+
const handleVariableStatement = (stmt) => {
|
|
86
|
+
// Only consider const declarations
|
|
87
|
+
if ((stmt.declarationList.flags & typescript_1.default.NodeFlags.Const) === 0)
|
|
88
|
+
return;
|
|
89
|
+
for (const decl of stmt.declarationList.declarations) {
|
|
90
|
+
if (!typescript_1.default.isIdentifier(decl.name) || !decl.type)
|
|
91
|
+
continue;
|
|
92
|
+
const name = decl.name.text;
|
|
93
|
+
const val = parseLiteralFromType(decl.type);
|
|
94
|
+
if (val == null)
|
|
95
|
+
continue;
|
|
96
|
+
results.push({ kind: 'const', name, value: val });
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
const handleClass = (cls) => {
|
|
100
|
+
var _a, _b, _c;
|
|
101
|
+
const container = (_a = cls.name) === null || _a === void 0 ? void 0 : _a.text;
|
|
102
|
+
if (!container)
|
|
103
|
+
return;
|
|
104
|
+
for (const m of cls.members) {
|
|
105
|
+
if (!typescript_1.default.isPropertyDeclaration(m))
|
|
106
|
+
continue;
|
|
107
|
+
const isStatic = (_b = m.modifiers) === null || _b === void 0 ? void 0 : _b.some(mod => mod.kind === typescript_1.default.SyntaxKind.StaticKeyword);
|
|
108
|
+
const isReadonly = (_c = m.modifiers) === null || _c === void 0 ? void 0 : _c.some(mod => mod.kind === typescript_1.default.SyntaxKind.ReadonlyKeyword);
|
|
109
|
+
if (!isStatic || !isReadonly || !m.type)
|
|
110
|
+
continue;
|
|
111
|
+
if (!typescript_1.default.isIdentifier(m.name))
|
|
112
|
+
continue;
|
|
113
|
+
const name = m.name.text;
|
|
114
|
+
const val = parseLiteralFromType(m.type);
|
|
115
|
+
if (val == null)
|
|
116
|
+
continue;
|
|
117
|
+
results.push({ kind: 'container', container, name, value: val });
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
const handleModule = (mod) => {
|
|
121
|
+
const container = mod.name.getText(source).replace(/['"]/g, '');
|
|
122
|
+
if (!mod.body || !typescript_1.default.isModuleBlock(mod.body))
|
|
123
|
+
return;
|
|
124
|
+
for (const stmt of mod.body.statements) {
|
|
125
|
+
if (typescript_1.default.isVariableStatement(stmt)) {
|
|
126
|
+
if ((stmt.declarationList.flags & typescript_1.default.NodeFlags.Const) === 0)
|
|
127
|
+
continue;
|
|
128
|
+
for (const decl of stmt.declarationList.declarations) {
|
|
129
|
+
if (!typescript_1.default.isIdentifier(decl.name) || !decl.type)
|
|
130
|
+
continue;
|
|
131
|
+
const name = decl.name.text;
|
|
132
|
+
const val = parseLiteralFromType(decl.type);
|
|
133
|
+
if (val == null)
|
|
134
|
+
continue;
|
|
135
|
+
results.push({ kind: 'container', container, name, value: val });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
for (const stmt of source.statements) {
|
|
141
|
+
if (typescript_1.default.isVariableStatement(stmt))
|
|
142
|
+
handleVariableStatement(stmt);
|
|
143
|
+
else if (typescript_1.default.isClassDeclaration(stmt))
|
|
144
|
+
handleClass(stmt);
|
|
145
|
+
else if (typescript_1.default.isModuleDeclaration(stmt))
|
|
146
|
+
handleModule(stmt);
|
|
147
|
+
}
|
|
148
|
+
return results;
|
|
149
|
+
}
|
|
150
|
+
function collectDeclaredConsts(blobs) {
|
|
151
|
+
const all = [];
|
|
152
|
+
for (const blob of blobs) {
|
|
153
|
+
const raw = blob.raw || '';
|
|
154
|
+
if (!raw)
|
|
155
|
+
continue;
|
|
156
|
+
try {
|
|
157
|
+
const items = collectDeclaredConstsFromSource(raw, blob.filename + '.d.ts');
|
|
158
|
+
all.push(...items);
|
|
159
|
+
}
|
|
160
|
+
catch (_) {
|
|
161
|
+
// ignore parse errors per file
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return all;
|
|
165
|
+
}
|
|
166
|
+
function literalToTsValue(val) {
|
|
167
|
+
if (typeof val === 'string')
|
|
168
|
+
return `'${val.replace(/'/g, "\\'")}'`;
|
|
169
|
+
if (typeof val === 'number')
|
|
170
|
+
return String(val);
|
|
171
|
+
if (typeof val === 'boolean')
|
|
172
|
+
return val ? 'true' : 'false';
|
|
173
|
+
if (typeof val.typeofRef === 'string')
|
|
174
|
+
return val.typeofRef;
|
|
175
|
+
return 'undefined';
|
|
176
|
+
}
|
|
177
|
+
function generateDeclaredConstantsValuesTs(blobs) {
|
|
178
|
+
const items = collectDeclaredConsts(blobs);
|
|
179
|
+
if (!items.length)
|
|
180
|
+
return '';
|
|
181
|
+
const header = `// Auto-generated by WebF CLI\n// Runtime constants mirrored from .d.ts 'declare const' definitions\n`;
|
|
182
|
+
const topLevel = [];
|
|
183
|
+
const containers = new Map();
|
|
184
|
+
for (const it of items) {
|
|
185
|
+
if (it.kind === 'const') {
|
|
186
|
+
topLevel.push(`export const ${it.name} = ${literalToTsValue(it.value)} as const;`);
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
if (!containers.has(it.container))
|
|
190
|
+
containers.set(it.container, []);
|
|
191
|
+
containers.get(it.container).push(it);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const containerBlocks = [];
|
|
195
|
+
for (const [container, arr] of containers) {
|
|
196
|
+
const lines = arr
|
|
197
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
198
|
+
.map(a => ` ${a.name}: ${literalToTsValue(a.value)},`) // keep plain object
|
|
199
|
+
.join('\n');
|
|
200
|
+
containerBlocks.push(`export const ${container} = {\n${lines}\n} as const;`);
|
|
201
|
+
}
|
|
202
|
+
return [header, ...topLevel, ...containerBlocks].filter(Boolean).join('\n\n') + '\n';
|
|
203
|
+
}
|
|
204
|
+
function generateDeclaredConstantsDts(blobs) {
|
|
205
|
+
const items = collectDeclaredConsts(blobs);
|
|
206
|
+
if (!items.length)
|
|
207
|
+
return '';
|
|
208
|
+
const header = `// Auto-generated by WebF CLI\n// Type declarations for 'declare const' values mirrored into JS runtime\n`;
|
|
209
|
+
const topLevel = [];
|
|
210
|
+
const containers = new Map();
|
|
211
|
+
for (const it of items) {
|
|
212
|
+
if (it.kind === 'const') {
|
|
213
|
+
const val = typeof it.value === 'object' && it.value.typeofRef
|
|
214
|
+
? `typeof ${it.value.typeofRef}`
|
|
215
|
+
: typeof it.value === 'string'
|
|
216
|
+
? `'${it.value.replace(/'/g, "\\'")}'`
|
|
217
|
+
: String(it.value);
|
|
218
|
+
topLevel.push(`export declare const ${it.name}: ${val};`);
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
if (!containers.has(it.container))
|
|
222
|
+
containers.set(it.container, []);
|
|
223
|
+
containers.get(it.container).push(it);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const containerBlocks = [];
|
|
227
|
+
for (const [container, arr] of containers) {
|
|
228
|
+
const lines = arr
|
|
229
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
230
|
+
.map(a => {
|
|
231
|
+
const v = typeof a.value === 'object' && a.value.typeofRef
|
|
232
|
+
? `typeof ${a.value.typeofRef}`
|
|
233
|
+
: typeof a.value === 'string'
|
|
234
|
+
? `'${a.value.replace(/'/g, "\\'")}'`
|
|
235
|
+
: String(a.value);
|
|
236
|
+
return ` readonly ${a.name}: ${v};`;
|
|
237
|
+
})
|
|
238
|
+
.join('\n');
|
|
239
|
+
containerBlocks.push(`export declare const ${container}: {\n${lines}\n};`);
|
|
240
|
+
}
|
|
241
|
+
return [header, ...topLevel, ...containerBlocks].filter(Boolean).join('\n\n') + '\n';
|
|
242
|
+
}
|
package/dist/dart.js
CHANGED
|
@@ -80,6 +80,12 @@ ${enumValues};
|
|
|
80
80
|
}`;
|
|
81
81
|
}
|
|
82
82
|
function generateReturnType(type, enumName) {
|
|
83
|
+
// Handle union types first (e.g., 'left' | 'center' | 'right')
|
|
84
|
+
// so we don't incorrectly treat string literal unions as pointer types.
|
|
85
|
+
if (Array.isArray(type.value)) {
|
|
86
|
+
// If we have an enum name, use it; otherwise fall back to String
|
|
87
|
+
return enumName || 'String';
|
|
88
|
+
}
|
|
83
89
|
if ((0, utils_1.isPointerType)(type)) {
|
|
84
90
|
const pointerType = (0, utils_1.getPointerType)(type);
|
|
85
91
|
return pointerType;
|
|
@@ -87,11 +93,6 @@ function generateReturnType(type, enumName) {
|
|
|
87
93
|
if (type.isArray && typeof type.value === 'object' && !Array.isArray(type.value)) {
|
|
88
94
|
return `${(0, utils_1.getPointerType)(type.value)}[]`;
|
|
89
95
|
}
|
|
90
|
-
// Handle union types (e.g., 'left' | 'center' | 'right')
|
|
91
|
-
if (Array.isArray(type.value)) {
|
|
92
|
-
// If we have an enum name, use it; otherwise fall back to String
|
|
93
|
-
return enumName || 'String';
|
|
94
|
-
}
|
|
95
96
|
// Handle when type.value is a ParameterType object (nested type)
|
|
96
97
|
if (typeof type.value === 'object' && !Array.isArray(type.value) && type.value !== null) {
|
|
97
98
|
// This might be a nested ParameterType, recurse
|
|
@@ -250,7 +251,7 @@ interface ${object.name} {
|
|
|
250
251
|
}
|
|
251
252
|
// Generate enums for union types
|
|
252
253
|
const enums = [];
|
|
253
|
-
const enumMap = new Map(); // prop name -> enum name
|
|
254
|
+
const enumMap = new Map(); // camelCase prop name -> enum name
|
|
254
255
|
if (componentProperties) {
|
|
255
256
|
for (const prop of componentProperties.props) {
|
|
256
257
|
if (isStringUnionType(prop.type)) {
|
|
@@ -261,7 +262,8 @@ interface ${object.name} {
|
|
|
261
262
|
name: enumName,
|
|
262
263
|
definition: generateDartEnum(enumName, values)
|
|
263
264
|
});
|
|
264
|
-
|
|
265
|
+
// Store by camelCase prop name to match template usage
|
|
266
|
+
enumMap.set(lodash_1.default.camelCase(prop.name), enumName);
|
|
265
267
|
}
|
|
266
268
|
}
|
|
267
269
|
}
|
package/dist/declaration.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TypeAliasObject = exports.FunctionObject = exports.ClassObject = exports.ClassObjectKind = exports.FunctionDeclaration = exports.IndexedPropertyDeclaration = exports.PropsDeclaration = exports.ParameterMode = exports.FunctionArguments = exports.FunctionArgumentType = void 0;
|
|
3
|
+
exports.EnumObject = exports.EnumMemberObject = exports.ConstObject = exports.TypeAliasObject = exports.FunctionObject = exports.ClassObject = exports.ClassObjectKind = exports.FunctionDeclaration = exports.IndexedPropertyDeclaration = exports.PropsDeclaration = exports.ParameterMode = exports.FunctionArguments = exports.FunctionArgumentType = void 0;
|
|
4
4
|
var FunctionArgumentType;
|
|
5
5
|
(function (FunctionArgumentType) {
|
|
6
6
|
// Basic types
|
|
@@ -61,3 +61,15 @@ exports.FunctionObject = FunctionObject;
|
|
|
61
61
|
class TypeAliasObject {
|
|
62
62
|
}
|
|
63
63
|
exports.TypeAliasObject = TypeAliasObject;
|
|
64
|
+
class ConstObject {
|
|
65
|
+
}
|
|
66
|
+
exports.ConstObject = ConstObject;
|
|
67
|
+
class EnumMemberObject {
|
|
68
|
+
}
|
|
69
|
+
exports.EnumMemberObject = EnumMemberObject;
|
|
70
|
+
class EnumObject {
|
|
71
|
+
constructor() {
|
|
72
|
+
this.members = [];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.EnumObject = EnumObject;
|