@openwebf/webf 0.1.0
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/CLAUDE.md +206 -0
- package/README-zhCN.md +256 -0
- package/README.md +232 -0
- package/bin/webf.js +25 -0
- package/coverage/clover.xml +1295 -0
- package/coverage/coverage-final.json +12 -0
- package/coverage/lcov-report/IDLBlob.ts.html +142 -0
- package/coverage/lcov-report/analyzer.ts.html +2158 -0
- package/coverage/lcov-report/analyzer_original.ts.html +1450 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/commands.ts.html +700 -0
- package/coverage/lcov-report/dart.ts.html +490 -0
- package/coverage/lcov-report/declaration.ts.html +337 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/generator.ts.html +1171 -0
- package/coverage/lcov-report/index.html +266 -0
- package/coverage/lcov-report/logger.ts.html +424 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/react.ts.html +619 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov-report/utils.ts.html +466 -0
- package/coverage/lcov-report/vue.ts.html +613 -0
- package/coverage/lcov.info +2149 -0
- package/global.d.ts +2 -0
- package/jest.config.js +24 -0
- package/package.json +36 -0
- package/src/IDLBlob.ts +20 -0
- package/src/analyzer.ts +692 -0
- package/src/commands.ts +645 -0
- package/src/dart.ts +170 -0
- package/src/declaration.ts +84 -0
- package/src/generator.ts +454 -0
- package/src/logger.ts +114 -0
- package/src/react.ts +186 -0
- package/src/utils.ts +127 -0
- package/src/vue.ts +176 -0
- package/templates/class.dart.tpl +86 -0
- package/templates/gitignore.tpl +2 -0
- package/templates/react.component.tsx.tpl +53 -0
- package/templates/react.createComponent.tpl +286 -0
- package/templates/react.index.ts.tpl +8 -0
- package/templates/react.package.json.tpl +26 -0
- package/templates/react.tsconfig.json.tpl +16 -0
- package/templates/react.tsup.config.ts.tpl +10 -0
- package/templates/tsconfig.json.tpl +8 -0
- package/templates/vue.component.partial.tpl +31 -0
- package/templates/vue.components.d.ts.tpl +49 -0
- package/templates/vue.package.json.tpl +11 -0
- package/templates/vue.tsconfig.json.tpl +15 -0
- package/test/IDLBlob.test.ts +75 -0
- package/test/analyzer.test.ts +370 -0
- package/test/commands.test.ts +1253 -0
- package/test/generator.test.ts +460 -0
- package/test/logger.test.ts +215 -0
- package/test/react.test.ts +49 -0
- package/test/utils.test.ts +316 -0
- package/tsconfig.json +30 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { generateReactComponent } from '../src/react';
|
|
2
|
+
import { IDLBlob } from '../src/IDLBlob';
|
|
3
|
+
import { ClassObject, ClassObjectKind } from '../src/declaration';
|
|
4
|
+
|
|
5
|
+
describe('React Generator', () => {
|
|
6
|
+
describe('generateReactComponent', () => {
|
|
7
|
+
it('should use correct import path for createComponent in subdirectories', () => {
|
|
8
|
+
const blob = new IDLBlob('/test/source', '/test/target', 'TestComponent', 'test', 'components/ui');
|
|
9
|
+
|
|
10
|
+
const properties = new ClassObject();
|
|
11
|
+
properties.name = 'TestComponentProperties';
|
|
12
|
+
properties.kind = ClassObjectKind.interface;
|
|
13
|
+
blob.objects = [properties];
|
|
14
|
+
|
|
15
|
+
const result = generateReactComponent(blob);
|
|
16
|
+
|
|
17
|
+
// Component in components/ui/ needs to go up 2 levels
|
|
18
|
+
expect(result).toContain('import { createComponent } from "../../utils/createComponent"');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should use correct import path for createComponent in root directory', () => {
|
|
22
|
+
const blob = new IDLBlob('/test/source', '/test/target', 'TestComponent', 'test', '');
|
|
23
|
+
|
|
24
|
+
const properties = new ClassObject();
|
|
25
|
+
properties.name = 'TestComponentProperties';
|
|
26
|
+
properties.kind = ClassObjectKind.interface;
|
|
27
|
+
blob.objects = [properties];
|
|
28
|
+
|
|
29
|
+
const result = generateReactComponent(blob);
|
|
30
|
+
|
|
31
|
+
// Component in root needs simple relative path
|
|
32
|
+
expect(result).toContain('import { createComponent } from "./utils/createComponent"');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should use correct import path for single level subdirectory', () => {
|
|
36
|
+
const blob = new IDLBlob('/test/source', '/test/target', 'TestComponent', 'test', 'widgets');
|
|
37
|
+
|
|
38
|
+
const properties = new ClassObject();
|
|
39
|
+
properties.name = 'TestComponentProperties';
|
|
40
|
+
properties.kind = ClassObjectKind.interface;
|
|
41
|
+
blob.objects = [properties];
|
|
42
|
+
|
|
43
|
+
const result = generateReactComponent(blob);
|
|
44
|
+
|
|
45
|
+
// Component in widgets/ needs to go up 1 level
|
|
46
|
+
expect(result).toContain('import { createComponent } from "../utils/createComponent"');
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import {
|
|
2
|
+
addIndent,
|
|
3
|
+
getClassName,
|
|
4
|
+
getWrapperTypeInfoNameOfClassName,
|
|
5
|
+
getMethodName,
|
|
6
|
+
trimNullTypeFromType,
|
|
7
|
+
isUnionType,
|
|
8
|
+
isPointerType,
|
|
9
|
+
getPointerType
|
|
10
|
+
} from '../src/utils';
|
|
11
|
+
import { FunctionArgumentType } from '../src/declaration';
|
|
12
|
+
import { IDLBlob } from '../src/IDLBlob';
|
|
13
|
+
|
|
14
|
+
describe('Utils', () => {
|
|
15
|
+
describe('addIndent', () => {
|
|
16
|
+
it('should add specified spaces to each line', () => {
|
|
17
|
+
const input = 'line1\nline2\nline3';
|
|
18
|
+
const result = addIndent(input, 2);
|
|
19
|
+
expect(result).toBe(' line1\n line2\n line3');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should handle empty string', () => {
|
|
23
|
+
const result = addIndent('', 4);
|
|
24
|
+
expect(result).toBe('');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should handle single line', () => {
|
|
28
|
+
const result = addIndent('hello', 3);
|
|
29
|
+
expect(result).toBe(' hello');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should handle zero indent', () => {
|
|
33
|
+
const input = 'line1\nline2';
|
|
34
|
+
const result = addIndent(input, 0);
|
|
35
|
+
expect(result).toBe('line1\nline2');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('getClassName', () => {
|
|
40
|
+
it('should handle DOM prefixed names', () => {
|
|
41
|
+
const blob = new IDLBlob('', '', 'domElement', '');
|
|
42
|
+
expect(getClassName(blob)).toBe('DOMElement');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should handle DOMMatrixReadonly special case', () => {
|
|
46
|
+
const blob = new IDLBlob('', '', 'domMatrixReadonly', '');
|
|
47
|
+
expect(getClassName(blob)).toBe('DOMMatrixReadOnly');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should handle DOMPointReadonly special case', () => {
|
|
51
|
+
const blob = new IDLBlob('', '', 'domPointReadonly', '');
|
|
52
|
+
expect(getClassName(blob)).toBe('DOMPointReadOnly');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should handle HTML prefixed names', () => {
|
|
56
|
+
const blob = new IDLBlob('', '', 'htmlDivElement', '');
|
|
57
|
+
expect(getClassName(blob)).toBe('HTMLDivElement');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should handle HTMLIFrameElement special case', () => {
|
|
61
|
+
const blob = new IDLBlob('', '', 'htmlIframeElement', '');
|
|
62
|
+
expect(getClassName(blob)).toBe('HTMLIFrameElement');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should handle SVG prefixed names', () => {
|
|
66
|
+
const blob = new IDLBlob('', '', 'svgCircleElement', '');
|
|
67
|
+
expect(getClassName(blob)).toBe('SVGCircleElement');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should handle SVGSVGElement special case', () => {
|
|
71
|
+
const blob = new IDLBlob('', '', 'svgSvgElement', '');
|
|
72
|
+
expect(getClassName(blob)).toBe('SVGSVGElement');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should handle CSS prefixed names', () => {
|
|
76
|
+
const blob = new IDLBlob('', '', 'cssStyleDeclaration', '');
|
|
77
|
+
expect(getClassName(blob)).toBe('CSSStyleDeclaration');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should handle UI prefixed names', () => {
|
|
81
|
+
const blob = new IDLBlob('', '', 'uiEvent', '');
|
|
82
|
+
expect(getClassName(blob)).toBe('UIEvent');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should handle WebF special cases', () => {
|
|
86
|
+
const blob1 = new IDLBlob('', '', 'webfTouchAreaElement', '');
|
|
87
|
+
expect(getClassName(blob1)).toBe('WebFTouchAreaElement');
|
|
88
|
+
|
|
89
|
+
const blob2 = new IDLBlob('', '', 'webfRouterLinkElement', '');
|
|
90
|
+
expect(getClassName(blob2)).toBe('WebFRouterLinkElement');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should handle regular names', () => {
|
|
94
|
+
const blob = new IDLBlob('', '', 'customElement', '');
|
|
95
|
+
expect(getClassName(blob)).toBe('CustomElement');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('getWrapperTypeInfoNameOfClassName', () => {
|
|
100
|
+
it('should convert SVGSVGElement correctly', () => {
|
|
101
|
+
const result = getWrapperTypeInfoNameOfClassName('SVGSVGElement');
|
|
102
|
+
expect(result).toBe('SVG_SVG_ELEMENT');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should convert SVGGElement correctly', () => {
|
|
106
|
+
const result = getWrapperTypeInfoNameOfClassName('SVGGElement');
|
|
107
|
+
expect(result).toBe('SVG_G_ELEMENT');
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should convert regular class names to snake case', () => {
|
|
111
|
+
expect(getWrapperTypeInfoNameOfClassName('HTMLDivElement')).toBe('HTML_DIV_ELEMENT');
|
|
112
|
+
expect(getWrapperTypeInfoNameOfClassName('CustomElement')).toBe('CUSTOM_ELEMENT');
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('getMethodName', () => {
|
|
117
|
+
it('should capitalize first letter', () => {
|
|
118
|
+
expect(getMethodName('method')).toBe('Method');
|
|
119
|
+
expect(getMethodName('getValue')).toBe('GetValue');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should handle single character', () => {
|
|
123
|
+
expect(getMethodName('a')).toBe('A');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should handle empty string', () => {
|
|
127
|
+
expect(getMethodName('')).toBe('');
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe('trimNullTypeFromType', () => {
|
|
132
|
+
it('should remove null from union types', () => {
|
|
133
|
+
const type = {
|
|
134
|
+
isArray: false,
|
|
135
|
+
value: [
|
|
136
|
+
{ isArray: false, value: FunctionArgumentType.dom_string },
|
|
137
|
+
{ isArray: false, value: FunctionArgumentType.null },
|
|
138
|
+
{ isArray: false, value: FunctionArgumentType.double }
|
|
139
|
+
]
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const result = trimNullTypeFromType(type);
|
|
143
|
+
expect(result.value).toHaveLength(2);
|
|
144
|
+
expect(result.value).not.toContainEqual(
|
|
145
|
+
expect.objectContaining({ value: FunctionArgumentType.null })
|
|
146
|
+
);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should return single type if only one remains after trimming', () => {
|
|
150
|
+
const type = {
|
|
151
|
+
isArray: false,
|
|
152
|
+
value: [
|
|
153
|
+
{ isArray: false, value: FunctionArgumentType.dom_string },
|
|
154
|
+
{ isArray: false, value: FunctionArgumentType.null }
|
|
155
|
+
]
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const result = trimNullTypeFromType(type);
|
|
159
|
+
expect(result).toEqual({
|
|
160
|
+
isArray: false,
|
|
161
|
+
value: FunctionArgumentType.dom_string
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should return type unchanged if not an array', () => {
|
|
166
|
+
const type = {
|
|
167
|
+
isArray: false,
|
|
168
|
+
value: FunctionArgumentType.dom_string
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const result = trimNullTypeFromType(type);
|
|
172
|
+
expect(result).toBe(type);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should handle array types', () => {
|
|
176
|
+
const type = {
|
|
177
|
+
isArray: true,
|
|
178
|
+
value: { isArray: false, value: FunctionArgumentType.dom_string }
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const result = trimNullTypeFromType(type);
|
|
182
|
+
expect(result).toBe(type);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
describe('isUnionType', () => {
|
|
187
|
+
it('should return true for union types', () => {
|
|
188
|
+
const type = {
|
|
189
|
+
isArray: false,
|
|
190
|
+
value: [
|
|
191
|
+
{ isArray: false, value: FunctionArgumentType.dom_string },
|
|
192
|
+
{ isArray: false, value: FunctionArgumentType.double }
|
|
193
|
+
]
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
expect(isUnionType(type)).toBe(true);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('should return false for array types', () => {
|
|
200
|
+
const type = {
|
|
201
|
+
isArray: true,
|
|
202
|
+
value: { isArray: false, value: FunctionArgumentType.dom_string }
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
expect(isUnionType(type)).toBe(false);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('should return false for single types', () => {
|
|
209
|
+
const type = {
|
|
210
|
+
isArray: false,
|
|
211
|
+
value: FunctionArgumentType.dom_string
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
expect(isUnionType(type)).toBe(false);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('should check trimmed type for unions', () => {
|
|
218
|
+
const type = {
|
|
219
|
+
isArray: false,
|
|
220
|
+
value: [
|
|
221
|
+
{ isArray: false, value: FunctionArgumentType.dom_string },
|
|
222
|
+
{ isArray: false, value: FunctionArgumentType.null }
|
|
223
|
+
]
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
// After trimming null, only one type remains, so not a union
|
|
227
|
+
expect(isUnionType(type)).toBe(false);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
describe('isPointerType', () => {
|
|
232
|
+
it('should return true for string value types', () => {
|
|
233
|
+
const type = {
|
|
234
|
+
isArray: false,
|
|
235
|
+
value: 'CustomClass'
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
expect(isPointerType(type)).toBe(true);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('should return false for array types', () => {
|
|
242
|
+
const type = {
|
|
243
|
+
isArray: true,
|
|
244
|
+
value: 'CustomClass'
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
expect(isPointerType(type)).toBe(false);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('should return false for function argument types', () => {
|
|
251
|
+
const type = {
|
|
252
|
+
isArray: false,
|
|
253
|
+
value: FunctionArgumentType.dom_string
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
expect(isPointerType(type)).toBe(false);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('should return true if union contains string type', () => {
|
|
260
|
+
const type = {
|
|
261
|
+
isArray: false,
|
|
262
|
+
value: [
|
|
263
|
+
{ isArray: false, value: FunctionArgumentType.dom_string },
|
|
264
|
+
{ isArray: false, value: 'CustomClass' }
|
|
265
|
+
]
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
expect(isPointerType(type)).toBe(true);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
describe('getPointerType', () => {
|
|
273
|
+
it('should return string value directly', () => {
|
|
274
|
+
const type = {
|
|
275
|
+
isArray: false,
|
|
276
|
+
value: 'CustomClass'
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
expect(getPointerType(type)).toBe('CustomClass');
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('should find string value in union type', () => {
|
|
283
|
+
const type = {
|
|
284
|
+
isArray: false,
|
|
285
|
+
value: [
|
|
286
|
+
{ isArray: false, value: FunctionArgumentType.dom_string },
|
|
287
|
+
{ isArray: false, value: 'CustomClass' },
|
|
288
|
+
{ isArray: false, value: FunctionArgumentType.double }
|
|
289
|
+
]
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
expect(getPointerType(type)).toBe('CustomClass');
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('should return empty string if no string type found', () => {
|
|
296
|
+
const type = {
|
|
297
|
+
isArray: false,
|
|
298
|
+
value: FunctionArgumentType.dom_string
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
expect(getPointerType(type)).toBe('');
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('should return first string type in union', () => {
|
|
305
|
+
const type = {
|
|
306
|
+
isArray: false,
|
|
307
|
+
value: [
|
|
308
|
+
{ isArray: false, value: 'FirstClass' },
|
|
309
|
+
{ isArray: false, value: 'SecondClass' }
|
|
310
|
+
]
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
expect(getPointerType(type)).toBe('FirstClass');
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "commonjs",
|
|
4
|
+
"target": "es6",
|
|
5
|
+
"lib": [
|
|
6
|
+
"es6",
|
|
7
|
+
"es7",
|
|
8
|
+
"DOM"
|
|
9
|
+
],
|
|
10
|
+
"allowJs": false,
|
|
11
|
+
"moduleResolution": "node",
|
|
12
|
+
"forceConsistentCasingInFileNames": false,
|
|
13
|
+
"noImplicitReturns": true,
|
|
14
|
+
"noImplicitThis": true,
|
|
15
|
+
"noImplicitAny": true,
|
|
16
|
+
"strictNullChecks": true,
|
|
17
|
+
"noUnusedLocals": false,
|
|
18
|
+
"experimentalDecorators": true,
|
|
19
|
+
"emitDecoratorMetadata": true,
|
|
20
|
+
"esModuleInterop": true,
|
|
21
|
+
"allowSyntheticDefaultImports": true,
|
|
22
|
+
"declaration": false,
|
|
23
|
+
"outDir": "./dist"
|
|
24
|
+
},
|
|
25
|
+
"include": [
|
|
26
|
+
"src/**/*.ts"
|
|
27
|
+
],
|
|
28
|
+
"exclude": [
|
|
29
|
+
]
|
|
30
|
+
}
|