@openwebf/webf 0.23.7 → 0.23.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -0
- package/bin/webf.js +12 -1
- package/dist/commands.js +240 -2
- package/dist/generator.js +2 -1
- package/dist/module.js +458 -0
- package/dist/vue.js +17 -2
- package/package.json +2 -2
- package/src/commands.ts +288 -4
- package/src/generator.ts +3 -3
- package/src/module.ts +600 -0
- package/src/vue.ts +17 -2
- package/templates/module.package.json.tpl +36 -0
- package/templates/module.tsconfig.json.tpl +25 -0
- package/templates/module.tsup.config.ts.tpl +13 -0
- package/test/commands.test.ts +10 -8
- package/test/generator.test.ts +16 -14
package/dist/module.js
ADDED
|
@@ -0,0 +1,458 @@
|
|
|
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.generateModuleArtifacts = generateModuleArtifacts;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
10
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
11
|
+
const generator_1 = require("./generator");
|
|
12
|
+
function parseModuleDefinition(modulePath) {
|
|
13
|
+
const sourceText = fs_1.default.readFileSync(modulePath, 'utf-8');
|
|
14
|
+
const sourceFile = typescript_1.default.createSourceFile(modulePath, sourceText, typescript_1.default.ScriptTarget.ES2020, true, typescript_1.default.ScriptKind.TS);
|
|
15
|
+
let interfaceDecl;
|
|
16
|
+
const supporting = [];
|
|
17
|
+
for (const stmt of sourceFile.statements) {
|
|
18
|
+
if (typescript_1.default.isInterfaceDeclaration(stmt)) {
|
|
19
|
+
const name = stmt.name.text;
|
|
20
|
+
if (!interfaceDecl && name.startsWith('WebF')) {
|
|
21
|
+
interfaceDecl = stmt;
|
|
22
|
+
}
|
|
23
|
+
supporting.push(stmt);
|
|
24
|
+
}
|
|
25
|
+
else if (typescript_1.default.isTypeAliasDeclaration(stmt) ||
|
|
26
|
+
typescript_1.default.isEnumDeclaration(stmt) ||
|
|
27
|
+
typescript_1.default.isVariableStatement(stmt)) {
|
|
28
|
+
supporting.push(stmt);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (!interfaceDecl) {
|
|
32
|
+
throw new Error(`No interface starting with "WebF" found in module interface file: ${modulePath}`);
|
|
33
|
+
}
|
|
34
|
+
const interfaceName = interfaceDecl.name.text;
|
|
35
|
+
const moduleName = interfaceName.startsWith('WebF')
|
|
36
|
+
? interfaceName.substring('WebF'.length)
|
|
37
|
+
: interfaceName;
|
|
38
|
+
const printer = typescript_1.default.createPrinter();
|
|
39
|
+
const methods = [];
|
|
40
|
+
for (const member of interfaceDecl.members) {
|
|
41
|
+
if (!typescript_1.default.isMethodSignature(member))
|
|
42
|
+
continue;
|
|
43
|
+
const nameNode = member.name;
|
|
44
|
+
const methodName = nameNode.getText(sourceFile);
|
|
45
|
+
if (!methodName || methodName === 'constructor')
|
|
46
|
+
continue;
|
|
47
|
+
const params = member.parameters.map(param => ({
|
|
48
|
+
name: param.name.getText(sourceFile),
|
|
49
|
+
text: printer.printNode(typescript_1.default.EmitHint.Unspecified, param, sourceFile),
|
|
50
|
+
typeText: param.type
|
|
51
|
+
? printer.printNode(typescript_1.default.EmitHint.Unspecified, param.type, sourceFile)
|
|
52
|
+
: 'any',
|
|
53
|
+
}));
|
|
54
|
+
const returnTypeText = member.type
|
|
55
|
+
? printer.printNode(typescript_1.default.EmitHint.Unspecified, member.type, sourceFile)
|
|
56
|
+
: 'Promise<any>';
|
|
57
|
+
let documentation;
|
|
58
|
+
const jsDocs = member.jsDoc;
|
|
59
|
+
if (jsDocs && jsDocs.length > 0) {
|
|
60
|
+
documentation = jsDocs
|
|
61
|
+
.map(doc => doc.comment)
|
|
62
|
+
.filter(Boolean)
|
|
63
|
+
.join('\n');
|
|
64
|
+
}
|
|
65
|
+
methods.push({
|
|
66
|
+
name: methodName,
|
|
67
|
+
jsName: methodName,
|
|
68
|
+
params,
|
|
69
|
+
returnTypeText,
|
|
70
|
+
documentation,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if (methods.length === 0) {
|
|
74
|
+
throw new Error(`Interface ${interfaceName} in ${modulePath} does not declare any methods`);
|
|
75
|
+
}
|
|
76
|
+
return { interfaceName, moduleName, methods, supportingStatements: supporting, sourceFile };
|
|
77
|
+
}
|
|
78
|
+
function buildTypesFile(def) {
|
|
79
|
+
const printer = typescript_1.default.createPrinter();
|
|
80
|
+
const lines = [];
|
|
81
|
+
lines.push('// AUTO GENERATED FILE, DO NOT EDIT.');
|
|
82
|
+
lines.push('//');
|
|
83
|
+
lines.push('// Generated by `webf module-codegen`');
|
|
84
|
+
lines.push('');
|
|
85
|
+
for (const stmt of def.supportingStatements) {
|
|
86
|
+
// Skip the main module interface (e.g. WebFShare); we only want supporting types.
|
|
87
|
+
if (typescript_1.default.isInterfaceDeclaration(stmt) && stmt.name.text === def.interfaceName) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
let printed = printer.printNode(typescript_1.default.EmitHint.Unspecified, stmt, def.sourceFile);
|
|
91
|
+
// Ensure declarations are exported so types.ts is a proper module.
|
|
92
|
+
if (typescript_1.default.isInterfaceDeclaration(stmt) ||
|
|
93
|
+
typescript_1.default.isTypeAliasDeclaration(stmt) ||
|
|
94
|
+
typescript_1.default.isEnumDeclaration(stmt)) {
|
|
95
|
+
const trimmed = printed.trimStart();
|
|
96
|
+
if (!trimmed.startsWith('export ')) {
|
|
97
|
+
const leadingLength = printed.length - trimmed.length;
|
|
98
|
+
const leading = printed.slice(0, leadingLength);
|
|
99
|
+
printed = `${leading}export ${trimmed}`;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
lines.push(printed);
|
|
103
|
+
}
|
|
104
|
+
lines.push('');
|
|
105
|
+
// Ensure file is treated as a module even if no declarations were emitted.
|
|
106
|
+
lines.push('export {};');
|
|
107
|
+
return lines.join('\n');
|
|
108
|
+
}
|
|
109
|
+
function buildIndexFile(def) {
|
|
110
|
+
const lines = [];
|
|
111
|
+
lines.push('/**');
|
|
112
|
+
lines.push(` * Auto-generated WebF module wrapper for "${def.moduleName}".`);
|
|
113
|
+
lines.push(' *');
|
|
114
|
+
lines.push(' * This file is generated from a TypeScript interface that describes');
|
|
115
|
+
lines.push(' * the module API. It forwards calls to `webf.invokeModuleAsync` at runtime.');
|
|
116
|
+
lines.push(' */');
|
|
117
|
+
lines.push('');
|
|
118
|
+
lines.push(`import { webf } from '@openwebf/webf-enterprise-typings';`);
|
|
119
|
+
// Import option/result types purely as types so this stays tree-shake friendly.
|
|
120
|
+
const typeImportNames = new Set();
|
|
121
|
+
for (const stmt of def.supportingStatements) {
|
|
122
|
+
if (typescript_1.default.isInterfaceDeclaration(stmt) ||
|
|
123
|
+
typescript_1.default.isTypeAliasDeclaration(stmt) ||
|
|
124
|
+
typescript_1.default.isEnumDeclaration(stmt)) {
|
|
125
|
+
const name = stmt.name.text;
|
|
126
|
+
if (name === def.interfaceName)
|
|
127
|
+
continue;
|
|
128
|
+
typeImportNames.add(name);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const typeImportsSorted = Array.from(typeImportNames).sort();
|
|
132
|
+
if (typeImportsSorted.length > 0) {
|
|
133
|
+
lines.push(`import type { ${typeImportsSorted.join(', ')} } from './types';`);
|
|
134
|
+
}
|
|
135
|
+
lines.push('');
|
|
136
|
+
lines.push(`export class ${def.interfaceName} {`);
|
|
137
|
+
lines.push(' static isAvailable(): boolean {');
|
|
138
|
+
lines.push(" return typeof webf !== 'undefined' && typeof (webf as any).invokeModuleAsync === 'function';");
|
|
139
|
+
lines.push(' }');
|
|
140
|
+
lines.push('');
|
|
141
|
+
for (const method of def.methods) {
|
|
142
|
+
if (method.documentation) {
|
|
143
|
+
lines.push(' /**');
|
|
144
|
+
for (const line of method.documentation.split('\n')) {
|
|
145
|
+
lines.push(' * ' + line);
|
|
146
|
+
}
|
|
147
|
+
lines.push(' */');
|
|
148
|
+
}
|
|
149
|
+
const paramsText = method.params.map(p => p.text).join(', ');
|
|
150
|
+
const argNames = method.params.map(p => p.name).join(', ');
|
|
151
|
+
lines.push(` static async ${method.name}(${paramsText}): ${method.returnTypeText} {`);
|
|
152
|
+
lines.push(' if (!this.isAvailable()) {');
|
|
153
|
+
lines.push(` throw new Error('WebF module "${def.moduleName}" is not available. Make sure it is registered via WebF.defineModule().');`);
|
|
154
|
+
lines.push(' }');
|
|
155
|
+
const argsPart = argNames ? `, ${argNames}` : '';
|
|
156
|
+
lines.push(` return webf.invokeModuleAsync('${def.moduleName}', '${method.jsName}'${argsPart});`);
|
|
157
|
+
lines.push(' }');
|
|
158
|
+
lines.push('');
|
|
159
|
+
}
|
|
160
|
+
lines.push('}');
|
|
161
|
+
lines.push('');
|
|
162
|
+
lines.push(`export type {`);
|
|
163
|
+
const typeExportNames = new Set();
|
|
164
|
+
for (const stmt of def.supportingStatements) {
|
|
165
|
+
if (typescript_1.default.isInterfaceDeclaration(stmt) ||
|
|
166
|
+
typescript_1.default.isTypeAliasDeclaration(stmt) ||
|
|
167
|
+
typescript_1.default.isEnumDeclaration(stmt)) {
|
|
168
|
+
const name = stmt.name.text;
|
|
169
|
+
// Do not re-export the main module interface (e.g. WebFShare) to avoid clashes
|
|
170
|
+
// with the generated class of the same name.
|
|
171
|
+
if (name === def.interfaceName)
|
|
172
|
+
continue;
|
|
173
|
+
typeExportNames.add(stmt.name.text);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const sorted = Array.from(typeExportNames).sort();
|
|
177
|
+
if (sorted.length) {
|
|
178
|
+
lines.push(' ' + sorted.join(','));
|
|
179
|
+
}
|
|
180
|
+
lines.push(`} from './types';`);
|
|
181
|
+
lines.push('');
|
|
182
|
+
return lines.join('\n');
|
|
183
|
+
}
|
|
184
|
+
function mapTsReturnTypeToDart(typeText) {
|
|
185
|
+
const raw = typeText.trim();
|
|
186
|
+
// Expect Promise<...> for async module methods
|
|
187
|
+
const promiseMatch = raw.match(/^Promise<(.+)>$/);
|
|
188
|
+
if (!promiseMatch) {
|
|
189
|
+
return 'Future<dynamic>';
|
|
190
|
+
}
|
|
191
|
+
const inner = promiseMatch[1].trim();
|
|
192
|
+
const innerLower = inner.toLowerCase();
|
|
193
|
+
if (innerLower === 'boolean' || innerLower === 'bool') {
|
|
194
|
+
return 'Future<bool>';
|
|
195
|
+
}
|
|
196
|
+
return 'Future<dynamic>';
|
|
197
|
+
}
|
|
198
|
+
function isTsByteArrayUnion(typeText) {
|
|
199
|
+
if (!typeText)
|
|
200
|
+
return false;
|
|
201
|
+
// Remove parentheses and whitespace
|
|
202
|
+
const cleaned = typeText.replace(/[()]/g, '').trim();
|
|
203
|
+
if (!cleaned)
|
|
204
|
+
return false;
|
|
205
|
+
const parts = cleaned.split('|').map(p => p.trim()).filter(Boolean);
|
|
206
|
+
if (parts.length === 0)
|
|
207
|
+
return false;
|
|
208
|
+
const byteTypes = new Set(['ArrayBuffer', 'Uint8Array']);
|
|
209
|
+
const nullable = new Set(['null', 'undefined']);
|
|
210
|
+
let hasByte = false;
|
|
211
|
+
for (const part of parts) {
|
|
212
|
+
if (byteTypes.has(part)) {
|
|
213
|
+
hasByte = true;
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
if (nullable.has(part))
|
|
217
|
+
continue;
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
return hasByte;
|
|
221
|
+
}
|
|
222
|
+
function getBaseTypeName(typeText) {
|
|
223
|
+
if (!typeText)
|
|
224
|
+
return null;
|
|
225
|
+
const cleaned = typeText.replace(/[()]/g, '').trim();
|
|
226
|
+
if (!cleaned)
|
|
227
|
+
return null;
|
|
228
|
+
const parts = cleaned.split('|').map(p => p.trim()).filter(Boolean);
|
|
229
|
+
const nullable = new Set(['null', 'undefined', 'void', 'never']);
|
|
230
|
+
const nonNullable = parts.filter(p => !nullable.has(p));
|
|
231
|
+
if (nonNullable.length !== 1)
|
|
232
|
+
return null;
|
|
233
|
+
const candidate = nonNullable[0];
|
|
234
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(candidate)) {
|
|
235
|
+
return candidate;
|
|
236
|
+
}
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
function mapTsParamTypeToDart(typeText, optionNames) {
|
|
240
|
+
const raw = typeText.trim();
|
|
241
|
+
if (isTsByteArrayUnion(raw)) {
|
|
242
|
+
return { dartType: 'NativeByteData', isByteData: true };
|
|
243
|
+
}
|
|
244
|
+
const base = getBaseTypeName(raw);
|
|
245
|
+
if (base && optionNames.has(base)) {
|
|
246
|
+
return { dartType: `${base}?`, isByteData: false, optionClassName: base };
|
|
247
|
+
}
|
|
248
|
+
return { dartType: 'dynamic', isByteData: false };
|
|
249
|
+
}
|
|
250
|
+
function mapTsPropertyTypeToDart(type) {
|
|
251
|
+
switch (type.kind) {
|
|
252
|
+
case typescript_1.default.SyntaxKind.StringKeyword:
|
|
253
|
+
return 'String?';
|
|
254
|
+
case typescript_1.default.SyntaxKind.NumberKeyword:
|
|
255
|
+
return 'num?';
|
|
256
|
+
case typescript_1.default.SyntaxKind.BooleanKeyword:
|
|
257
|
+
return 'bool?';
|
|
258
|
+
default:
|
|
259
|
+
return 'dynamic';
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
function buildDartBindings(def, command) {
|
|
263
|
+
const dartClassBase = `${def.moduleName}Module`;
|
|
264
|
+
const dartBindingsClass = `${dartClassBase}Bindings`;
|
|
265
|
+
const lines = [];
|
|
266
|
+
lines.push('// AUTO GENERATED FILE, DO NOT EDIT.');
|
|
267
|
+
lines.push('//');
|
|
268
|
+
lines.push('// Generated by `webf module-codegen`');
|
|
269
|
+
lines.push('');
|
|
270
|
+
lines.push("import 'package:webf/module.dart';");
|
|
271
|
+
if (def.methods.some(m => m.params.some(p => isTsByteArrayUnion(p.typeText)))) {
|
|
272
|
+
lines.push("import 'package:webf/bridge.dart';");
|
|
273
|
+
}
|
|
274
|
+
lines.push('');
|
|
275
|
+
// Generate Dart classes for supporting TS interfaces (compound option types).
|
|
276
|
+
const optionInterfaces = [];
|
|
277
|
+
for (const stmt of def.supportingStatements) {
|
|
278
|
+
if (typescript_1.default.isInterfaceDeclaration(stmt) && stmt.name.text !== def.interfaceName) {
|
|
279
|
+
optionInterfaces.push(stmt);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
const optionTypeNames = new Set(optionInterfaces.map(i => i.name.text));
|
|
283
|
+
for (const iface of optionInterfaces) {
|
|
284
|
+
const name = iface.name.text;
|
|
285
|
+
const propInfos = [];
|
|
286
|
+
for (const member of iface.members) {
|
|
287
|
+
if (!typescript_1.default.isPropertySignature(member) || !member.name)
|
|
288
|
+
continue;
|
|
289
|
+
const key = member.name.getText(def.sourceFile).replace(/['"]/g, '');
|
|
290
|
+
const fieldName = key;
|
|
291
|
+
const dartType = member.type ? mapTsPropertyTypeToDart(member.type) : 'dynamic';
|
|
292
|
+
propInfos.push({ fieldName, key, dartType });
|
|
293
|
+
}
|
|
294
|
+
lines.push(`class ${name} {`);
|
|
295
|
+
for (const prop of propInfos) {
|
|
296
|
+
lines.push(` final ${prop.dartType} ${prop.fieldName};`);
|
|
297
|
+
}
|
|
298
|
+
lines.push('');
|
|
299
|
+
const ctorParams = propInfos.map(p => `this.${p.fieldName}`).join(', ');
|
|
300
|
+
lines.push(` const ${name}({${ctorParams}});`);
|
|
301
|
+
lines.push('');
|
|
302
|
+
lines.push(` factory ${name}.fromMap(Map<String, dynamic> map) {`);
|
|
303
|
+
lines.push(` return ${name}(`);
|
|
304
|
+
for (const prop of propInfos) {
|
|
305
|
+
if (prop.dartType.startsWith('String')) {
|
|
306
|
+
lines.push(` ${prop.fieldName}: map['${prop.key}']?.toString(),`);
|
|
307
|
+
}
|
|
308
|
+
else if (prop.dartType.startsWith('bool')) {
|
|
309
|
+
lines.push(` ${prop.fieldName}: map['${prop.key}'] is bool ? map['${prop.key}'] as bool : null,`);
|
|
310
|
+
}
|
|
311
|
+
else if (prop.dartType.startsWith('num')) {
|
|
312
|
+
lines.push(` ${prop.fieldName}: map['${prop.key}'] is num ? map['${prop.key}'] as num : null,`);
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
lines.push(` ${prop.fieldName}: map['${prop.key}'],`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
lines.push(' );');
|
|
319
|
+
lines.push(' }');
|
|
320
|
+
lines.push('');
|
|
321
|
+
lines.push(' Map<String, dynamic> toMap() {');
|
|
322
|
+
lines.push(' final map = <String, dynamic>{};');
|
|
323
|
+
for (const prop of propInfos) {
|
|
324
|
+
lines.push(` if (${prop.fieldName} != null) { map['${prop.key}'] = ${prop.fieldName}; }`);
|
|
325
|
+
}
|
|
326
|
+
lines.push(' return map;');
|
|
327
|
+
lines.push(' }');
|
|
328
|
+
lines.push('');
|
|
329
|
+
lines.push(' Map<String, dynamic> toJson() => toMap();');
|
|
330
|
+
lines.push('}');
|
|
331
|
+
lines.push('');
|
|
332
|
+
}
|
|
333
|
+
lines.push(`abstract class ${dartBindingsClass} extends WebFBaseModule {`);
|
|
334
|
+
lines.push(` ${dartBindingsClass}(super.moduleManager);`);
|
|
335
|
+
lines.push('');
|
|
336
|
+
lines.push(` @override`);
|
|
337
|
+
lines.push(` String get name => '${def.moduleName}';`);
|
|
338
|
+
lines.push('');
|
|
339
|
+
for (const method of def.methods) {
|
|
340
|
+
const dartMethodName = lodash_1.default.camelCase(method.name);
|
|
341
|
+
let dartReturnType = mapTsReturnTypeToDart(method.returnTypeText);
|
|
342
|
+
// If the Promise inner type is one of the option interfaces, map return type to that Dart class.
|
|
343
|
+
const promiseMatch = method.returnTypeText.trim().match(/^Promise<(.+)>$/);
|
|
344
|
+
if (promiseMatch) {
|
|
345
|
+
const inner = promiseMatch[1].trim();
|
|
346
|
+
const baseInner = getBaseTypeName(inner);
|
|
347
|
+
if (baseInner && optionTypeNames.has(baseInner)) {
|
|
348
|
+
dartReturnType = `Future<${baseInner}>`;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
const paramInfos = [];
|
|
352
|
+
method.params.forEach((p, index) => {
|
|
353
|
+
const mapped = mapTsParamTypeToDart(p.typeText, optionTypeNames);
|
|
354
|
+
paramInfos.push({
|
|
355
|
+
name: p.name,
|
|
356
|
+
index,
|
|
357
|
+
dartType: mapped.dartType,
|
|
358
|
+
optionClass: mapped.optionClassName,
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
const dartParams = paramInfos
|
|
362
|
+
.map(info => `${info.dartType} ${info.name}`)
|
|
363
|
+
.join(', ');
|
|
364
|
+
lines.push(` ${dartReturnType} ${dartMethodName}(${dartParams});`);
|
|
365
|
+
}
|
|
366
|
+
lines.push('');
|
|
367
|
+
lines.push(' @override');
|
|
368
|
+
lines.push(' Future<dynamic> invoke(String method, List<dynamic> params) async {');
|
|
369
|
+
lines.push(' switch (method) {');
|
|
370
|
+
for (const method of def.methods) {
|
|
371
|
+
const dartMethodName = lodash_1.default.camelCase(method.name);
|
|
372
|
+
const paramInfos = [];
|
|
373
|
+
method.params.forEach((p, index) => {
|
|
374
|
+
const mapped = mapTsParamTypeToDart(p.typeText, optionTypeNames);
|
|
375
|
+
paramInfos.push({
|
|
376
|
+
name: p.name,
|
|
377
|
+
index,
|
|
378
|
+
dartType: mapped.dartType,
|
|
379
|
+
optionClass: mapped.optionClassName,
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
// Detect if this method returns a structured Dart class (from TS interface),
|
|
383
|
+
// in which case we should convert the result back to a Map for JS.
|
|
384
|
+
let structuredReturnClass = null;
|
|
385
|
+
const retMatch = method.returnTypeText.trim().match(/^Promise<(.+)>$/);
|
|
386
|
+
if (retMatch) {
|
|
387
|
+
const inner = retMatch[1].trim();
|
|
388
|
+
const baseInner = getBaseTypeName(inner);
|
|
389
|
+
if (baseInner && optionTypeNames.has(baseInner)) {
|
|
390
|
+
structuredReturnClass = baseInner;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
lines.push(` case '${method.jsName}': {`);
|
|
394
|
+
// Preprocess option-type parameters (Map -> Dart class instance)
|
|
395
|
+
for (const info of paramInfos) {
|
|
396
|
+
if (!info.optionClass)
|
|
397
|
+
continue;
|
|
398
|
+
const rawVar = `_raw${info.index}`;
|
|
399
|
+
lines.push(` final ${rawVar} = params.length > ${info.index} ? params[${info.index}] : null;`);
|
|
400
|
+
lines.push(` final ${info.name} = ${rawVar} is Map`
|
|
401
|
+
+ ` ? ${info.optionClass}.fromMap(Map<String, dynamic>.from(${rawVar} as Map))`
|
|
402
|
+
+ ` : (${rawVar} as ${info.optionClass}?);`);
|
|
403
|
+
}
|
|
404
|
+
if (paramInfos.length === 0) {
|
|
405
|
+
if (structuredReturnClass) {
|
|
406
|
+
lines.push(` final result = await ${dartMethodName}();`);
|
|
407
|
+
lines.push(' return result.toMap();');
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
lines.push(` return ${dartMethodName}();`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
const callArgs = paramInfos
|
|
415
|
+
.map(info => info.optionClass
|
|
416
|
+
? info.name
|
|
417
|
+
: `params.length > ${info.index} ? params[${info.index}] : null`)
|
|
418
|
+
.join(', ');
|
|
419
|
+
if (structuredReturnClass) {
|
|
420
|
+
lines.push(` final result = await ${dartMethodName}(${callArgs});`);
|
|
421
|
+
lines.push(' return result.toMap();');
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
lines.push(` return ${dartMethodName}(${callArgs});`);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
lines.push(' }');
|
|
428
|
+
}
|
|
429
|
+
lines.push(' default:');
|
|
430
|
+
lines.push(" throw Exception('Unknown method for module ${name}: $method');");
|
|
431
|
+
lines.push(' }');
|
|
432
|
+
lines.push(' }');
|
|
433
|
+
lines.push('}');
|
|
434
|
+
lines.push('');
|
|
435
|
+
return lines.join('\n');
|
|
436
|
+
}
|
|
437
|
+
function generateModuleArtifacts(params) {
|
|
438
|
+
const def = parseModuleDefinition(params.moduleInterfacePath);
|
|
439
|
+
const srcDir = path_1.default.join(params.npmTargetDir, 'src');
|
|
440
|
+
if (!fs_1.default.existsSync(srcDir)) {
|
|
441
|
+
fs_1.default.mkdirSync(srcDir, { recursive: true });
|
|
442
|
+
}
|
|
443
|
+
const typesContent = buildTypesFile(def);
|
|
444
|
+
const typesPath = path_1.default.join(srcDir, 'types.ts');
|
|
445
|
+
(0, generator_1.writeFileIfChanged)(typesPath, typesContent);
|
|
446
|
+
const indexContent = buildIndexFile(def);
|
|
447
|
+
const indexPath = path_1.default.join(srcDir, 'index.ts');
|
|
448
|
+
(0, generator_1.writeFileIfChanged)(indexPath, indexContent);
|
|
449
|
+
const dartBindingsContent = buildDartBindings(def, params.command);
|
|
450
|
+
const dartDir = path_1.default.join(params.flutterPackageDir, 'lib', 'src');
|
|
451
|
+
if (!fs_1.default.existsSync(dartDir)) {
|
|
452
|
+
fs_1.default.mkdirSync(dartDir, { recursive: true });
|
|
453
|
+
}
|
|
454
|
+
const dartFileName = `${lodash_1.default.snakeCase(def.moduleName)}_module_bindings_generated.dart`;
|
|
455
|
+
const dartBindingsPath = path_1.default.join(dartDir, dartFileName);
|
|
456
|
+
(0, generator_1.writeFileIfChanged)(dartBindingsPath, dartBindingsContent);
|
|
457
|
+
return { indexPath, typesPath, dartBindingsPath };
|
|
458
|
+
}
|
package/dist/vue.js
CHANGED
|
@@ -251,9 +251,24 @@ function generateVueTypings(blobs) {
|
|
|
251
251
|
const members = e.members.map(m => m.initializer ? `${m.name} = ${m.initializer}` : `${m.name}`).join(', ');
|
|
252
252
|
return `export declare enum ${e.name} { ${members} }`;
|
|
253
253
|
}).join('\n');
|
|
254
|
+
// Compute relative import path from the generated typings file (index.d.ts at dist root)
|
|
255
|
+
// to the aggregated React types module (src/types.ts) when present.
|
|
256
|
+
let typesImportPath = './src/types';
|
|
257
|
+
try {
|
|
258
|
+
if (blobs.length > 0) {
|
|
259
|
+
const distRoot = blobs[0].dist;
|
|
260
|
+
const typingsDir = distRoot; // index.d.ts is written directly under distRoot
|
|
261
|
+
const typesFilePath = path_1.default.join(distRoot, 'src', 'types');
|
|
262
|
+
const rel = path_1.default.relative(typingsDir, typesFilePath).replace(/\\/g, '/');
|
|
263
|
+
typesImportPath = rel.startsWith('.') ? rel : `./${rel}`;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
catch (_a) {
|
|
267
|
+
typesImportPath = './src/types';
|
|
268
|
+
}
|
|
254
269
|
// Always import the types namespace to support typeof references
|
|
255
|
-
const typesImport = `import * as __webfTypes from '
|
|
256
|
-
(0, logger_1.debug)(`[vue] Generating typings; importing types from
|
|
270
|
+
const typesImport = `import * as __webfTypes from '${typesImportPath}';`;
|
|
271
|
+
(0, logger_1.debug)(`[vue] Generating typings; importing types from ${typesImportPath}`);
|
|
257
272
|
// Build mapping of template tag names to class names for GlobalComponents
|
|
258
273
|
const componentMetas = componentNames.map(className => ({
|
|
259
274
|
className,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openwebf/webf",
|
|
3
|
-
"version": "0.23.
|
|
3
|
+
"version": "0.23.10",
|
|
4
4
|
"description": "Command line tools for WebF",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"keywords": [],
|
|
26
26
|
"author": "",
|
|
27
|
-
"license": "
|
|
27
|
+
"license": "Apache-2.0",
|
|
28
28
|
"type": "commonjs",
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/inquirer": "^8.2.11",
|