@openwebf/webf 0.22.1 → 0.22.4
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/bin/webf.js +1 -0
- package/dist/IDLBlob.js +17 -0
- package/dist/analyzer.js +578 -0
- package/dist/analyzer_original.js +467 -0
- package/dist/commands.js +704 -0
- package/dist/dart.js +300 -0
- package/dist/declaration.js +63 -0
- package/dist/generator.js +466 -0
- package/dist/logger.js +103 -0
- package/dist/react.js +283 -0
- package/dist/utils.js +127 -0
- package/dist/vue.js +159 -0
- package/package.json +8 -1
- package/src/IDLBlob.ts +2 -2
- package/src/analyzer.ts +19 -1
- package/src/commands.ts +201 -22
- package/src/dart.ts +172 -11
- package/src/declaration.ts +5 -0
- package/src/generator.ts +82 -14
- package/src/react.ts +197 -62
- package/templates/class.dart.tpl +10 -4
- package/templates/gitignore.tpl +8 -1
- package/templates/react.component.tsx.tpl +78 -26
- package/templates/react.index.ts.tpl +0 -1
- package/templates/react.package.json.tpl +3 -0
- package/test/commands.test.ts +0 -5
- package/test/generator.test.ts +29 -8
- package/test/packageName.test.ts +231 -0
- package/test/react.test.ts +94 -9
- package/CLAUDE.md +0 -206
- package/README-zhCN.md +0 -256
- package/coverage/clover.xml +0 -1295
- package/coverage/coverage-final.json +0 -12
- package/coverage/lcov-report/IDLBlob.ts.html +0 -142
- package/coverage/lcov-report/analyzer.ts.html +0 -2158
- package/coverage/lcov-report/analyzer_original.ts.html +0 -1450
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/commands.ts.html +0 -700
- package/coverage/lcov-report/dart.ts.html +0 -490
- package/coverage/lcov-report/declaration.ts.html +0 -337
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/generator.ts.html +0 -1171
- package/coverage/lcov-report/index.html +0 -266
- package/coverage/lcov-report/logger.ts.html +0 -424
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/react.ts.html +0 -619
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -196
- package/coverage/lcov-report/utils.ts.html +0 -466
- package/coverage/lcov-report/vue.ts.html +0 -613
- package/coverage/lcov.info +0 -2149
- package/global.d.ts +0 -2
- package/jest.config.js +0 -24
- package/templates/react.createComponent.tpl +0 -286
- package/tsconfig.json +0 -30
package/dist/react.js
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
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.toWebFTagName = toWebFTagName;
|
|
7
|
+
exports.generateReactComponent = generateReactComponent;
|
|
8
|
+
exports.generateReactIndex = generateReactIndex;
|
|
9
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
10
|
+
const fs_1 = __importDefault(require("fs"));
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const declaration_1 = require("./declaration");
|
|
13
|
+
const utils_1 = require("./utils");
|
|
14
|
+
function readTemplate(name) {
|
|
15
|
+
return fs_1.default.readFileSync(path_1.default.join(__dirname, '../templates/' + name + '.tpl'), { encoding: 'utf-8' });
|
|
16
|
+
}
|
|
17
|
+
function generateReturnType(type) {
|
|
18
|
+
if ((0, utils_1.isPointerType)(type)) {
|
|
19
|
+
const pointerType = (0, utils_1.getPointerType)(type);
|
|
20
|
+
return pointerType;
|
|
21
|
+
}
|
|
22
|
+
if (type.isArray && typeof type.value === 'object' && !Array.isArray(type.value)) {
|
|
23
|
+
return `${(0, utils_1.getPointerType)(type.value)}[]`;
|
|
24
|
+
}
|
|
25
|
+
switch (type.value) {
|
|
26
|
+
case declaration_1.FunctionArgumentType.int:
|
|
27
|
+
case declaration_1.FunctionArgumentType.double: {
|
|
28
|
+
return 'number';
|
|
29
|
+
}
|
|
30
|
+
case declaration_1.FunctionArgumentType.any: {
|
|
31
|
+
return 'any';
|
|
32
|
+
}
|
|
33
|
+
case declaration_1.FunctionArgumentType.boolean: {
|
|
34
|
+
return 'boolean';
|
|
35
|
+
}
|
|
36
|
+
case declaration_1.FunctionArgumentType.dom_string: {
|
|
37
|
+
return 'string';
|
|
38
|
+
}
|
|
39
|
+
case declaration_1.FunctionArgumentType.void:
|
|
40
|
+
return 'void';
|
|
41
|
+
default:
|
|
42
|
+
return 'void';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function generateEventHandlerType(type) {
|
|
46
|
+
if (!(0, utils_1.isPointerType)(type)) {
|
|
47
|
+
throw new Error('Event type must be an instance of Event');
|
|
48
|
+
}
|
|
49
|
+
const pointerType = (0, utils_1.getPointerType)(type);
|
|
50
|
+
if (pointerType === 'Event') {
|
|
51
|
+
return `EventHandler<SyntheticEvent<Element>>`;
|
|
52
|
+
}
|
|
53
|
+
if (pointerType === 'CustomEvent') {
|
|
54
|
+
return `EventHandler<SyntheticEvent<Element, CustomEvent>>`;
|
|
55
|
+
}
|
|
56
|
+
throw new Error('Unknown event type: ' + pointerType);
|
|
57
|
+
}
|
|
58
|
+
function getEventType(type) {
|
|
59
|
+
if (!(0, utils_1.isPointerType)(type)) {
|
|
60
|
+
return 'Event';
|
|
61
|
+
}
|
|
62
|
+
const pointerType = (0, utils_1.getPointerType)(type);
|
|
63
|
+
if (pointerType === 'CustomEvent') {
|
|
64
|
+
return 'CustomEvent';
|
|
65
|
+
}
|
|
66
|
+
// For specific event types like MouseEvent, TouchEvent, etc.
|
|
67
|
+
if (pointerType.endsWith('Event')) {
|
|
68
|
+
return pointerType;
|
|
69
|
+
}
|
|
70
|
+
return 'Event';
|
|
71
|
+
}
|
|
72
|
+
function generateMethodDeclaration(method) {
|
|
73
|
+
var methodName = method.name;
|
|
74
|
+
var args = method.args.map(arg => {
|
|
75
|
+
var argName = arg.name;
|
|
76
|
+
var argType = generateReturnType(arg.type);
|
|
77
|
+
return `${argName}: ${argType}`;
|
|
78
|
+
}).join(', ');
|
|
79
|
+
var returnType = generateReturnType(method.returnType);
|
|
80
|
+
return `${methodName}(${args}): ${returnType};`;
|
|
81
|
+
}
|
|
82
|
+
function toReactEventName(name) {
|
|
83
|
+
const eventName = 'on-' + name;
|
|
84
|
+
return lodash_1.default.camelCase(eventName);
|
|
85
|
+
}
|
|
86
|
+
function toWebFTagName(className) {
|
|
87
|
+
// Special handling for WebF prefix - treat it as a single unit
|
|
88
|
+
if (className.startsWith('WebF')) {
|
|
89
|
+
// Replace WebF with webf- and then kebab-case the rest
|
|
90
|
+
const withoutPrefix = className.substring(4);
|
|
91
|
+
return 'webf-' + lodash_1.default.kebabCase(withoutPrefix);
|
|
92
|
+
}
|
|
93
|
+
else if (className.startsWith('Flutter')) {
|
|
94
|
+
// Handle Flutter prefix similarly
|
|
95
|
+
const withoutPrefix = className.substring(7);
|
|
96
|
+
return 'flutter-' + lodash_1.default.kebabCase(withoutPrefix);
|
|
97
|
+
}
|
|
98
|
+
// Default kebab-case for other components
|
|
99
|
+
return lodash_1.default.kebabCase(className);
|
|
100
|
+
}
|
|
101
|
+
function generateReactComponent(blob, packageName, relativeDir) {
|
|
102
|
+
const classObjects = blob.objects.filter(obj => obj instanceof declaration_1.ClassObject);
|
|
103
|
+
const typeAliases = blob.objects.filter(obj => obj instanceof declaration_1.TypeAliasObject);
|
|
104
|
+
const classObjectDictionary = Object.fromEntries(classObjects.map(object => {
|
|
105
|
+
return [object.name, object];
|
|
106
|
+
}));
|
|
107
|
+
const properties = classObjects.filter(object => {
|
|
108
|
+
return object.name.endsWith('Properties');
|
|
109
|
+
});
|
|
110
|
+
const events = classObjects.filter(object => {
|
|
111
|
+
return object.name.endsWith('Events');
|
|
112
|
+
});
|
|
113
|
+
const others = classObjects.filter(object => {
|
|
114
|
+
return !object.name.endsWith('Properties')
|
|
115
|
+
&& !object.name.endsWith('Events');
|
|
116
|
+
});
|
|
117
|
+
// Include type aliases
|
|
118
|
+
const typeAliasDeclarations = typeAliases.map(typeAlias => {
|
|
119
|
+
return `type ${typeAlias.name} = ${typeAlias.type};`;
|
|
120
|
+
}).join('\n');
|
|
121
|
+
const dependencies = [
|
|
122
|
+
typeAliasDeclarations,
|
|
123
|
+
others.map(object => {
|
|
124
|
+
const props = object.props.map(prop => {
|
|
125
|
+
if (prop.optional) {
|
|
126
|
+
return `${prop.name}?: ${generateReturnType(prop.type)};`;
|
|
127
|
+
}
|
|
128
|
+
return `${prop.name}: ${generateReturnType(prop.type)};`;
|
|
129
|
+
}).join('\n ');
|
|
130
|
+
return `
|
|
131
|
+
interface ${object.name} {
|
|
132
|
+
${props}
|
|
133
|
+
}`;
|
|
134
|
+
}).join('\n\n')
|
|
135
|
+
].filter(Boolean).join('\n\n');
|
|
136
|
+
// Generate all components from this file
|
|
137
|
+
const components = [];
|
|
138
|
+
// Create a map of component names to their properties and events
|
|
139
|
+
const componentMap = new Map();
|
|
140
|
+
// Process all Properties interfaces
|
|
141
|
+
properties.forEach(prop => {
|
|
142
|
+
const componentName = prop.name.replace(/Properties$/, '');
|
|
143
|
+
if (!componentMap.has(componentName)) {
|
|
144
|
+
componentMap.set(componentName, {});
|
|
145
|
+
}
|
|
146
|
+
componentMap.get(componentName).properties = prop;
|
|
147
|
+
});
|
|
148
|
+
// Process all Events interfaces
|
|
149
|
+
events.forEach(event => {
|
|
150
|
+
const componentName = event.name.replace(/Events$/, '');
|
|
151
|
+
if (!componentMap.has(componentName)) {
|
|
152
|
+
componentMap.set(componentName, {});
|
|
153
|
+
}
|
|
154
|
+
componentMap.get(componentName).events = event;
|
|
155
|
+
});
|
|
156
|
+
// If we have multiple components, we need to generate a combined file
|
|
157
|
+
const componentEntries = Array.from(componentMap.entries());
|
|
158
|
+
if (componentEntries.length === 0) {
|
|
159
|
+
return '';
|
|
160
|
+
}
|
|
161
|
+
if (componentEntries.length === 1) {
|
|
162
|
+
// Single component - use existing template
|
|
163
|
+
const [className, component] = componentEntries[0];
|
|
164
|
+
// Determine the import path for createWebFComponent
|
|
165
|
+
const isReactCoreUI = packageName === '@openwebf/react-core-ui';
|
|
166
|
+
let createWebFComponentImport;
|
|
167
|
+
if (isReactCoreUI && relativeDir) {
|
|
168
|
+
// Calculate relative path from current file to utils/createWebFComponent
|
|
169
|
+
// Files are generated in src/<relativeDir>/ and need to import from src/utils/
|
|
170
|
+
const depth = relativeDir.split('/').filter(p => p).length;
|
|
171
|
+
const upPath = '../'.repeat(depth);
|
|
172
|
+
createWebFComponentImport = `import { createWebFComponent, WebFElementWithMethods } from "${upPath}utils/createWebFComponent";`;
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
createWebFComponentImport = `import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui";`;
|
|
176
|
+
}
|
|
177
|
+
const templateContent = readTemplate('react.component.tsx')
|
|
178
|
+
.replace('import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui";', createWebFComponentImport);
|
|
179
|
+
const content = lodash_1.default.template(templateContent)({
|
|
180
|
+
className: className,
|
|
181
|
+
properties: component.properties,
|
|
182
|
+
events: component.events,
|
|
183
|
+
classObjectDictionary,
|
|
184
|
+
dependencies,
|
|
185
|
+
blob,
|
|
186
|
+
toReactEventName,
|
|
187
|
+
toWebFTagName,
|
|
188
|
+
generateReturnType,
|
|
189
|
+
generateMethodDeclaration,
|
|
190
|
+
generateEventHandlerType,
|
|
191
|
+
getEventType,
|
|
192
|
+
});
|
|
193
|
+
return content.split('\n').filter(str => {
|
|
194
|
+
return str.trim().length > 0;
|
|
195
|
+
}).join('\n');
|
|
196
|
+
}
|
|
197
|
+
// Multiple components - generate with shared imports
|
|
198
|
+
const componentDefinitions = [];
|
|
199
|
+
// Determine the import path for createWebFComponent
|
|
200
|
+
const isReactCoreUI = packageName === '@openwebf/react-core-ui';
|
|
201
|
+
let createWebFComponentImport;
|
|
202
|
+
if (isReactCoreUI && relativeDir) {
|
|
203
|
+
// Calculate relative path from current file to utils/createWebFComponent
|
|
204
|
+
// Files are generated in src/<relativeDir>/ and need to import from src/utils/
|
|
205
|
+
const depth = relativeDir.split('/').filter(p => p).length;
|
|
206
|
+
const upPath = '../'.repeat(depth);
|
|
207
|
+
createWebFComponentImport = `import { createWebFComponent, WebFElementWithMethods } from "${upPath}utils/createWebFComponent";`;
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
createWebFComponentImport = `import { createWebFComponent, WebFElementWithMethods } from "@openwebf/react-core-ui";`;
|
|
211
|
+
}
|
|
212
|
+
componentEntries.forEach(([className, component]) => {
|
|
213
|
+
const content = lodash_1.default.template(readTemplate('react.component.tsx'))({
|
|
214
|
+
className: className,
|
|
215
|
+
properties: component.properties,
|
|
216
|
+
events: component.events,
|
|
217
|
+
classObjectDictionary,
|
|
218
|
+
dependencies: '', // Dependencies will be at the top
|
|
219
|
+
blob,
|
|
220
|
+
toReactEventName,
|
|
221
|
+
toWebFTagName,
|
|
222
|
+
generateReturnType,
|
|
223
|
+
generateMethodDeclaration,
|
|
224
|
+
generateEventHandlerType,
|
|
225
|
+
getEventType,
|
|
226
|
+
});
|
|
227
|
+
// Remove the import statements from all but the first component
|
|
228
|
+
const lines = content.split('\n');
|
|
229
|
+
const withoutImports = lines.filter(line => {
|
|
230
|
+
return !line.startsWith('import ');
|
|
231
|
+
}).join('\n');
|
|
232
|
+
componentDefinitions.push(withoutImports);
|
|
233
|
+
});
|
|
234
|
+
// Combine with shared imports at the top
|
|
235
|
+
const result = [
|
|
236
|
+
'import React from "react";',
|
|
237
|
+
createWebFComponentImport,
|
|
238
|
+
'',
|
|
239
|
+
dependencies,
|
|
240
|
+
'',
|
|
241
|
+
...componentDefinitions
|
|
242
|
+
].filter(line => line !== undefined).join('\n');
|
|
243
|
+
return result.split('\n').filter(str => {
|
|
244
|
+
return str.trim().length > 0;
|
|
245
|
+
}).join('\n');
|
|
246
|
+
}
|
|
247
|
+
function generateReactIndex(blobs) {
|
|
248
|
+
const components = blobs.flatMap(blob => {
|
|
249
|
+
const classObjects = blob.objects.filter(obj => obj instanceof declaration_1.ClassObject);
|
|
250
|
+
const properties = classObjects.filter(object => {
|
|
251
|
+
return object.name.endsWith('Properties');
|
|
252
|
+
});
|
|
253
|
+
const events = classObjects.filter(object => {
|
|
254
|
+
return object.name.endsWith('Events');
|
|
255
|
+
});
|
|
256
|
+
// Create a map of component names
|
|
257
|
+
const componentMap = new Map();
|
|
258
|
+
// Add all components from Properties interfaces
|
|
259
|
+
properties.forEach(prop => {
|
|
260
|
+
const componentName = prop.name.replace(/Properties$/, '');
|
|
261
|
+
componentMap.set(componentName, true);
|
|
262
|
+
});
|
|
263
|
+
// Add all components from Events interfaces
|
|
264
|
+
events.forEach(event => {
|
|
265
|
+
const componentName = event.name.replace(/Events$/, '');
|
|
266
|
+
componentMap.set(componentName, true);
|
|
267
|
+
});
|
|
268
|
+
// Return an array of all components from this file
|
|
269
|
+
return Array.from(componentMap.keys()).map(className => ({
|
|
270
|
+
className: className,
|
|
271
|
+
fileName: blob.filename,
|
|
272
|
+
relativeDir: blob.relativeDir,
|
|
273
|
+
}));
|
|
274
|
+
}).filter(component => {
|
|
275
|
+
return component.className.length > 0;
|
|
276
|
+
});
|
|
277
|
+
const content = lodash_1.default.template(readTemplate('react.index.ts'))({
|
|
278
|
+
components,
|
|
279
|
+
});
|
|
280
|
+
return content.split('\n').filter(str => {
|
|
281
|
+
return str.trim().length > 0;
|
|
282
|
+
}).join('\n');
|
|
283
|
+
}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.addIndent = addIndent;
|
|
4
|
+
exports.getClassName = getClassName;
|
|
5
|
+
exports.getWrapperTypeInfoNameOfClassName = getWrapperTypeInfoNameOfClassName;
|
|
6
|
+
exports.getMethodName = getMethodName;
|
|
7
|
+
exports.trimNullTypeFromType = trimNullTypeFromType;
|
|
8
|
+
exports.isUnionType = isUnionType;
|
|
9
|
+
exports.isPointerType = isPointerType;
|
|
10
|
+
exports.getPointerType = getPointerType;
|
|
11
|
+
const declaration_1 = require("./declaration");
|
|
12
|
+
const lodash_1 = require("lodash");
|
|
13
|
+
function addIndent(str, space) {
|
|
14
|
+
if (!str)
|
|
15
|
+
return str;
|
|
16
|
+
let lines = str.split('\n');
|
|
17
|
+
lines = lines.map(l => {
|
|
18
|
+
for (let i = 0; i < space; i++) {
|
|
19
|
+
l = ' ' + l;
|
|
20
|
+
}
|
|
21
|
+
return l;
|
|
22
|
+
});
|
|
23
|
+
return lines.join('\n');
|
|
24
|
+
}
|
|
25
|
+
function getClassName(blob) {
|
|
26
|
+
let raw = (0, lodash_1.camelCase)(blob.filename);
|
|
27
|
+
if (raw.slice(0, 3) == 'dom') {
|
|
28
|
+
if (raw === 'domMatrixReadonly') {
|
|
29
|
+
return `DOMMatrixReadOnly`;
|
|
30
|
+
}
|
|
31
|
+
else if (raw === 'domPointReadonly') {
|
|
32
|
+
return `DOMPointReadOnly`;
|
|
33
|
+
}
|
|
34
|
+
return 'DOM' + raw.slice(3);
|
|
35
|
+
}
|
|
36
|
+
if (raw.slice(0, 4) == 'html') {
|
|
37
|
+
// Legacy support names.
|
|
38
|
+
if (raw === 'htmlIframeElement') {
|
|
39
|
+
return `HTMLIFrameElement`;
|
|
40
|
+
}
|
|
41
|
+
return 'HTML' + raw.slice(4);
|
|
42
|
+
}
|
|
43
|
+
if (raw.slice(0, 6) == 'svgSvg') {
|
|
44
|
+
// special for SVGSVGElement
|
|
45
|
+
return 'SVGSVG' + raw.slice(6);
|
|
46
|
+
}
|
|
47
|
+
if (raw.slice(0, 3) == 'svg') {
|
|
48
|
+
return 'SVG' + raw.slice(3);
|
|
49
|
+
}
|
|
50
|
+
if (raw.slice(0, 3) == 'css') {
|
|
51
|
+
return 'CSS' + raw.slice(3);
|
|
52
|
+
}
|
|
53
|
+
if (raw.slice(0, 2) == 'ui') {
|
|
54
|
+
return 'UI' + raw.slice(2);
|
|
55
|
+
}
|
|
56
|
+
if (raw == 'webfTouchAreaElement') {
|
|
57
|
+
return 'WebFTouchAreaElement';
|
|
58
|
+
}
|
|
59
|
+
if (raw == 'webfRouterLinkElement') {
|
|
60
|
+
return 'WebFRouterLinkElement';
|
|
61
|
+
}
|
|
62
|
+
return `${raw[0].toUpperCase() + raw.slice(1)}`;
|
|
63
|
+
}
|
|
64
|
+
function getWrapperTypeInfoNameOfClassName(className) {
|
|
65
|
+
if (className.slice(0, 6) === 'SVGSVG') {
|
|
66
|
+
// special for SVGSVGElement
|
|
67
|
+
className = `SVGSvg${className.slice(6)}`;
|
|
68
|
+
}
|
|
69
|
+
else if (className === 'SVGGElement') {
|
|
70
|
+
// TODO: use more better way
|
|
71
|
+
className = `SVG_G_Element`;
|
|
72
|
+
}
|
|
73
|
+
return (0, lodash_1.snakeCase)(className).toUpperCase();
|
|
74
|
+
}
|
|
75
|
+
function getMethodName(name) {
|
|
76
|
+
if (!name || name.length === 0)
|
|
77
|
+
return '';
|
|
78
|
+
return name[0].toUpperCase() + name.slice(1);
|
|
79
|
+
}
|
|
80
|
+
function trimNullTypeFromType(type) {
|
|
81
|
+
let types = type.value;
|
|
82
|
+
if (!Array.isArray(types))
|
|
83
|
+
return type;
|
|
84
|
+
let trimed = types.filter(t => t.value != declaration_1.FunctionArgumentType.null);
|
|
85
|
+
if (trimed.length === 1) {
|
|
86
|
+
return {
|
|
87
|
+
isArray: false,
|
|
88
|
+
value: trimed[0].value
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
isArray: type.isArray,
|
|
93
|
+
value: trimed
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function isUnionType(type) {
|
|
97
|
+
if (type.isArray || !Array.isArray(type.value)) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
const trimedType = trimNullTypeFromType(type);
|
|
101
|
+
return Array.isArray(trimedType.value);
|
|
102
|
+
}
|
|
103
|
+
function isPointerType(type) {
|
|
104
|
+
if (type.isArray)
|
|
105
|
+
return false;
|
|
106
|
+
if (typeof type.value === 'string') {
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
if (Array.isArray(type.value)) {
|
|
110
|
+
return type.value.some(t => typeof t.value === 'string');
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
function getPointerType(type) {
|
|
115
|
+
if (typeof type.value === 'string') {
|
|
116
|
+
return type.value;
|
|
117
|
+
}
|
|
118
|
+
if (Array.isArray(type.value)) {
|
|
119
|
+
for (let i = 0; i < type.value.length; i++) {
|
|
120
|
+
let childValue = type.value[i];
|
|
121
|
+
if (typeof childValue.value === 'string') {
|
|
122
|
+
return childValue.value;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return '';
|
|
127
|
+
}
|
package/dist/vue.js
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
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.generateVueTypings = generateVueTypings;
|
|
7
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const declaration_1 = require("./declaration");
|
|
11
|
+
const utils_1 = require("./utils");
|
|
12
|
+
function readTemplate(name) {
|
|
13
|
+
return fs_1.default.readFileSync(path_1.default.join(__dirname, '../templates/' + name + '.tpl'), { encoding: 'utf-8' });
|
|
14
|
+
}
|
|
15
|
+
function generateReturnType(type) {
|
|
16
|
+
if ((0, utils_1.isPointerType)(type)) {
|
|
17
|
+
const pointerType = (0, utils_1.getPointerType)(type);
|
|
18
|
+
return pointerType;
|
|
19
|
+
}
|
|
20
|
+
if (type.isArray && typeof type.value === 'object' && !Array.isArray(type.value)) {
|
|
21
|
+
return `${(0, utils_1.getPointerType)(type.value)}[]`;
|
|
22
|
+
}
|
|
23
|
+
switch (type.value) {
|
|
24
|
+
case declaration_1.FunctionArgumentType.int:
|
|
25
|
+
case declaration_1.FunctionArgumentType.double: {
|
|
26
|
+
return 'number';
|
|
27
|
+
}
|
|
28
|
+
case declaration_1.FunctionArgumentType.any: {
|
|
29
|
+
return 'any';
|
|
30
|
+
}
|
|
31
|
+
case declaration_1.FunctionArgumentType.boolean: {
|
|
32
|
+
return 'boolean';
|
|
33
|
+
}
|
|
34
|
+
case declaration_1.FunctionArgumentType.dom_string: {
|
|
35
|
+
return 'string';
|
|
36
|
+
}
|
|
37
|
+
case declaration_1.FunctionArgumentType.void:
|
|
38
|
+
return 'void';
|
|
39
|
+
default:
|
|
40
|
+
return 'void';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function generateEventHandlerType(type) {
|
|
44
|
+
if (!(0, utils_1.isPointerType)(type)) {
|
|
45
|
+
throw new Error('Event type must be an instance of Event');
|
|
46
|
+
}
|
|
47
|
+
const pointerType = (0, utils_1.getPointerType)(type);
|
|
48
|
+
if (pointerType === 'Event') {
|
|
49
|
+
return 'Event';
|
|
50
|
+
}
|
|
51
|
+
if (pointerType === 'CustomEvent') {
|
|
52
|
+
return 'CustomEvent';
|
|
53
|
+
}
|
|
54
|
+
throw new Error('Unknown event type: ' + pointerType);
|
|
55
|
+
}
|
|
56
|
+
function generateMethodDeclaration(method) {
|
|
57
|
+
var methodName = method.name;
|
|
58
|
+
var args = method.args.map(arg => {
|
|
59
|
+
var argName = arg.name;
|
|
60
|
+
var argType = generateReturnType(arg.type);
|
|
61
|
+
return `${argName}: ${argType}`;
|
|
62
|
+
}).join(', ');
|
|
63
|
+
var returnType = generateReturnType(method.returnType);
|
|
64
|
+
return `${methodName}(${args}): ${returnType};`;
|
|
65
|
+
}
|
|
66
|
+
function generateVueComponent(blob) {
|
|
67
|
+
const classObjects = blob.objects;
|
|
68
|
+
const classObjectDictionary = Object.fromEntries(classObjects.map(object => {
|
|
69
|
+
return [object.name, object];
|
|
70
|
+
}));
|
|
71
|
+
const properties = classObjects.filter(object => {
|
|
72
|
+
return object.name.endsWith('Properties');
|
|
73
|
+
});
|
|
74
|
+
const events = classObjects.filter(object => {
|
|
75
|
+
return object.name.endsWith('Events');
|
|
76
|
+
});
|
|
77
|
+
const others = classObjects.filter(object => {
|
|
78
|
+
return !object.name.endsWith('Properties')
|
|
79
|
+
&& !object.name.endsWith('Events');
|
|
80
|
+
});
|
|
81
|
+
const dependencies = others.map(object => {
|
|
82
|
+
const props = object.props.map(prop => {
|
|
83
|
+
if (prop.optional) {
|
|
84
|
+
return `${prop.name}?: ${generateReturnType(prop.type)};`;
|
|
85
|
+
}
|
|
86
|
+
return `${prop.name}: ${generateReturnType(prop.type)};`;
|
|
87
|
+
}).join('\n ');
|
|
88
|
+
return `
|
|
89
|
+
interface ${object.name} {
|
|
90
|
+
${props}
|
|
91
|
+
}`;
|
|
92
|
+
}).join('\n\n');
|
|
93
|
+
const componentProperties = properties.length > 0 ? properties[0] : undefined;
|
|
94
|
+
const componentEvents = events.length > 0 ? events[0] : undefined;
|
|
95
|
+
const className = (() => {
|
|
96
|
+
if (componentProperties) {
|
|
97
|
+
return componentProperties.name.replace(/Properties$/, '');
|
|
98
|
+
}
|
|
99
|
+
if (componentEvents) {
|
|
100
|
+
return componentEvents.name.replace(/Events$/, '');
|
|
101
|
+
}
|
|
102
|
+
return '';
|
|
103
|
+
})();
|
|
104
|
+
if (!className) {
|
|
105
|
+
return '';
|
|
106
|
+
}
|
|
107
|
+
const content = lodash_1.default.template(readTemplate('vue.component.partial'))({
|
|
108
|
+
className: className,
|
|
109
|
+
properties: componentProperties,
|
|
110
|
+
events: componentEvents,
|
|
111
|
+
classObjectDictionary,
|
|
112
|
+
dependencies,
|
|
113
|
+
blob,
|
|
114
|
+
generateReturnType,
|
|
115
|
+
generateMethodDeclaration,
|
|
116
|
+
generateEventHandlerType,
|
|
117
|
+
});
|
|
118
|
+
const result = content.split('\n').filter(str => {
|
|
119
|
+
return str.trim().length > 0;
|
|
120
|
+
}).join('\n');
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
function generateVueTypings(blobs) {
|
|
124
|
+
const componentNames = blobs.map(blob => {
|
|
125
|
+
const classObjects = blob.objects;
|
|
126
|
+
const properties = classObjects.filter(object => {
|
|
127
|
+
return object.name.endsWith('Properties');
|
|
128
|
+
});
|
|
129
|
+
const events = classObjects.filter(object => {
|
|
130
|
+
return object.name.endsWith('Events');
|
|
131
|
+
});
|
|
132
|
+
const componentProperties = properties.length > 0 ? properties[0] : undefined;
|
|
133
|
+
const componentEvents = events.length > 0 ? events[0] : undefined;
|
|
134
|
+
const className = (() => {
|
|
135
|
+
if (componentProperties) {
|
|
136
|
+
return componentProperties.name.replace(/Properties$/, '');
|
|
137
|
+
}
|
|
138
|
+
if (componentEvents) {
|
|
139
|
+
return componentEvents.name.replace(/Events$/, '');
|
|
140
|
+
}
|
|
141
|
+
return '';
|
|
142
|
+
})();
|
|
143
|
+
return className;
|
|
144
|
+
}).filter(name => {
|
|
145
|
+
return name.length > 0;
|
|
146
|
+
});
|
|
147
|
+
const components = blobs.map(blob => {
|
|
148
|
+
return generateVueComponent(blob);
|
|
149
|
+
}).filter(component => {
|
|
150
|
+
return component.length > 0;
|
|
151
|
+
}).join('\n\n');
|
|
152
|
+
const content = lodash_1.default.template(readTemplate('vue.components.d.ts'))({
|
|
153
|
+
componentNames,
|
|
154
|
+
components,
|
|
155
|
+
});
|
|
156
|
+
return content.split('\n').filter(str => {
|
|
157
|
+
return str.trim().length > 0;
|
|
158
|
+
}).join('\n');
|
|
159
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openwebf/webf",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.4",
|
|
4
4
|
"description": "Command line tools for WebF",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"webf": "./bin/webf.js"
|
|
8
8
|
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"dist",
|
|
12
|
+
"src",
|
|
13
|
+
"templates",
|
|
14
|
+
"test"
|
|
15
|
+
],
|
|
9
16
|
"scripts": {
|
|
10
17
|
"build": "tsc",
|
|
11
18
|
"test": "jest",
|
package/src/IDLBlob.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {ClassObject, FunctionObject} from "./declaration";
|
|
1
|
+
import {ClassObject, FunctionObject, TypeAliasObject} from "./declaration";
|
|
2
2
|
|
|
3
3
|
export class IDLBlob {
|
|
4
4
|
raw: string = '';
|
|
@@ -7,7 +7,7 @@ export class IDLBlob {
|
|
|
7
7
|
filename: string;
|
|
8
8
|
implement: string;
|
|
9
9
|
relativeDir: string = '';
|
|
10
|
-
objects: (ClassObject | FunctionObject)[] = [];
|
|
10
|
+
objects: (ClassObject | FunctionObject | TypeAliasObject)[] = [];
|
|
11
11
|
|
|
12
12
|
constructor(source: string, dist: string, filename: string, implement: string, relativeDir: string = '') {
|
|
13
13
|
this.source = source;
|
package/src/analyzer.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
IndexedPropertyDeclaration,
|
|
11
11
|
ParameterMode,
|
|
12
12
|
PropsDeclaration,
|
|
13
|
+
TypeAliasObject,
|
|
13
14
|
} from './declaration';
|
|
14
15
|
import {isUnionType} from "./utils";
|
|
15
16
|
|
|
@@ -67,7 +68,7 @@ export function analyzer(blob: IDLBlob, definedPropertyCollector: DefinedPropert
|
|
|
67
68
|
return null;
|
|
68
69
|
}
|
|
69
70
|
})
|
|
70
|
-
.filter(o => o instanceof ClassObject || o instanceof FunctionObject) as (FunctionObject | ClassObject)[];
|
|
71
|
+
.filter(o => o instanceof ClassObject || o instanceof FunctionObject || o instanceof TypeAliasObject) as (FunctionObject | ClassObject | TypeAliasObject)[];
|
|
71
72
|
} catch (error) {
|
|
72
73
|
console.error(`Error analyzing ${blob.source}:`, error);
|
|
73
74
|
throw new Error(`Failed to analyze ${blob.source}: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -418,11 +419,28 @@ function walkProgram(blob: IDLBlob, statement: ts.Statement, definedPropertyColl
|
|
|
418
419
|
case ts.SyntaxKind.VariableStatement:
|
|
419
420
|
return processVariableStatement(statement as VariableStatement, unionTypeCollector);
|
|
420
421
|
|
|
422
|
+
case ts.SyntaxKind.TypeAliasDeclaration:
|
|
423
|
+
return processTypeAliasDeclaration(statement as ts.TypeAliasDeclaration, blob);
|
|
424
|
+
|
|
421
425
|
default:
|
|
422
426
|
return null;
|
|
423
427
|
}
|
|
424
428
|
}
|
|
425
429
|
|
|
430
|
+
function processTypeAliasDeclaration(
|
|
431
|
+
statement: ts.TypeAliasDeclaration,
|
|
432
|
+
blob: IDLBlob
|
|
433
|
+
): TypeAliasObject {
|
|
434
|
+
const typeAlias = new TypeAliasObject();
|
|
435
|
+
typeAlias.name = statement.name.text;
|
|
436
|
+
|
|
437
|
+
// Convert the type to a string representation
|
|
438
|
+
const printer = ts.createPrinter();
|
|
439
|
+
typeAlias.type = printer.printNode(ts.EmitHint.Unspecified, statement.type, statement.getSourceFile());
|
|
440
|
+
|
|
441
|
+
return typeAlias;
|
|
442
|
+
}
|
|
443
|
+
|
|
426
444
|
function processInterfaceDeclaration(
|
|
427
445
|
statement: ts.InterfaceDeclaration,
|
|
428
446
|
blob: IDLBlob,
|