@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.
Files changed (60) hide show
  1. package/CLAUDE.md +206 -0
  2. package/README-zhCN.md +256 -0
  3. package/README.md +232 -0
  4. package/bin/webf.js +25 -0
  5. package/coverage/clover.xml +1295 -0
  6. package/coverage/coverage-final.json +12 -0
  7. package/coverage/lcov-report/IDLBlob.ts.html +142 -0
  8. package/coverage/lcov-report/analyzer.ts.html +2158 -0
  9. package/coverage/lcov-report/analyzer_original.ts.html +1450 -0
  10. package/coverage/lcov-report/base.css +224 -0
  11. package/coverage/lcov-report/block-navigation.js +87 -0
  12. package/coverage/lcov-report/commands.ts.html +700 -0
  13. package/coverage/lcov-report/dart.ts.html +490 -0
  14. package/coverage/lcov-report/declaration.ts.html +337 -0
  15. package/coverage/lcov-report/favicon.png +0 -0
  16. package/coverage/lcov-report/generator.ts.html +1171 -0
  17. package/coverage/lcov-report/index.html +266 -0
  18. package/coverage/lcov-report/logger.ts.html +424 -0
  19. package/coverage/lcov-report/prettify.css +1 -0
  20. package/coverage/lcov-report/prettify.js +2 -0
  21. package/coverage/lcov-report/react.ts.html +619 -0
  22. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  23. package/coverage/lcov-report/sorter.js +196 -0
  24. package/coverage/lcov-report/utils.ts.html +466 -0
  25. package/coverage/lcov-report/vue.ts.html +613 -0
  26. package/coverage/lcov.info +2149 -0
  27. package/global.d.ts +2 -0
  28. package/jest.config.js +24 -0
  29. package/package.json +36 -0
  30. package/src/IDLBlob.ts +20 -0
  31. package/src/analyzer.ts +692 -0
  32. package/src/commands.ts +645 -0
  33. package/src/dart.ts +170 -0
  34. package/src/declaration.ts +84 -0
  35. package/src/generator.ts +454 -0
  36. package/src/logger.ts +114 -0
  37. package/src/react.ts +186 -0
  38. package/src/utils.ts +127 -0
  39. package/src/vue.ts +176 -0
  40. package/templates/class.dart.tpl +86 -0
  41. package/templates/gitignore.tpl +2 -0
  42. package/templates/react.component.tsx.tpl +53 -0
  43. package/templates/react.createComponent.tpl +286 -0
  44. package/templates/react.index.ts.tpl +8 -0
  45. package/templates/react.package.json.tpl +26 -0
  46. package/templates/react.tsconfig.json.tpl +16 -0
  47. package/templates/react.tsup.config.ts.tpl +10 -0
  48. package/templates/tsconfig.json.tpl +8 -0
  49. package/templates/vue.component.partial.tpl +31 -0
  50. package/templates/vue.components.d.ts.tpl +49 -0
  51. package/templates/vue.package.json.tpl +11 -0
  52. package/templates/vue.tsconfig.json.tpl +15 -0
  53. package/test/IDLBlob.test.ts +75 -0
  54. package/test/analyzer.test.ts +370 -0
  55. package/test/commands.test.ts +1253 -0
  56. package/test/generator.test.ts +460 -0
  57. package/test/logger.test.ts +215 -0
  58. package/test/react.test.ts +49 -0
  59. package/test/utils.test.ts +316 -0
  60. package/tsconfig.json +30 -0
@@ -0,0 +1,370 @@
1
+ import ts from 'typescript';
2
+ import { analyzer, buildClassRelationship, clearCaches, ParameterType } from '../src/analyzer';
3
+ import { IDLBlob } from '../src/IDLBlob';
4
+ import { ClassObject, FunctionArgumentType } from '../src/declaration';
5
+
6
+ describe('Analyzer', () => {
7
+ beforeEach(() => {
8
+ // Clear caches and global state before each test
9
+ clearCaches();
10
+ ClassObject.globalClassMap = Object.create(null);
11
+ ClassObject.globalClassRelationMap = Object.create(null);
12
+ });
13
+
14
+ describe('analyzer function', () => {
15
+ it('should analyze interface declaration', () => {
16
+ const blob = new IDLBlob('/test/source.d.ts', '/test/target', 'test', 'test');
17
+ blob.raw = `
18
+ interface TestInterface {
19
+ name: string;
20
+ age: number;
21
+ isActive?: boolean;
22
+ }
23
+ `;
24
+
25
+ const propertyCollector = {
26
+ properties: new Set<string>(),
27
+ files: new Set<string>(),
28
+ interfaces: new Set<string>()
29
+ };
30
+ const unionTypeCollector = {
31
+ types: new Set<ParameterType[]>()
32
+ };
33
+
34
+ analyzer(blob, propertyCollector, unionTypeCollector);
35
+
36
+ expect(blob.objects).toHaveLength(1);
37
+ expect(blob.objects[0]).toBeInstanceOf(ClassObject);
38
+
39
+ const classObj = blob.objects[0] as ClassObject;
40
+ expect(classObj.name).toBe('TestInterface');
41
+ expect(classObj.props).toHaveLength(3);
42
+ expect(classObj.props[0].name).toBe('name');
43
+ expect(classObj.props[1].name).toBe('age');
44
+ expect(classObj.props[2].name).toBe('isActive');
45
+ expect(classObj.props[2].optional).toBe(true);
46
+ });
47
+
48
+ it('should handle interface inheritance', () => {
49
+ const blob = new IDLBlob('/test/source.d.ts', '/test/target', 'test', 'test');
50
+ blob.raw = `
51
+ interface ParentInterface {
52
+ parentProp: string;
53
+ }
54
+
55
+ interface ChildInterface extends ParentInterface {
56
+ childProp: number;
57
+ }
58
+ `;
59
+
60
+ const propertyCollector = {
61
+ properties: new Set<string>(),
62
+ files: new Set<string>(),
63
+ interfaces: new Set<string>()
64
+ };
65
+ const unionTypeCollector = {
66
+ types: new Set<ParameterType[]>()
67
+ };
68
+
69
+ analyzer(blob, propertyCollector, unionTypeCollector);
70
+
71
+ expect(blob.objects).toHaveLength(2);
72
+
73
+ const childObj = blob.objects.find(o => (o as ClassObject).name === 'ChildInterface') as ClassObject;
74
+ expect(childObj.parent).toBe('ParentInterface');
75
+ });
76
+
77
+ it('should handle method signatures', () => {
78
+ const blob = new IDLBlob('/test/source.d.ts', '/test/target', 'test', 'test');
79
+ blob.raw = `
80
+ interface TestInterface {
81
+ getName(): string;
82
+ setAge(age: number): void;
83
+ calculate(a: number, b: number): number;
84
+ }
85
+ `;
86
+
87
+ const propertyCollector = {
88
+ properties: new Set<string>(),
89
+ files: new Set<string>(),
90
+ interfaces: new Set<string>()
91
+ };
92
+ const unionTypeCollector = {
93
+ types: new Set<ParameterType[]>()
94
+ };
95
+
96
+ analyzer(blob, propertyCollector, unionTypeCollector);
97
+
98
+ const classObj = blob.objects[0] as ClassObject;
99
+ expect(classObj.methods).toHaveLength(3);
100
+
101
+ const getName = classObj.methods[0];
102
+ expect(getName.name).toBe('getName');
103
+ expect(getName.args).toHaveLength(0);
104
+ expect(getName.returnType?.value).toBe(FunctionArgumentType.dom_string);
105
+
106
+ const setAge = classObj.methods[1];
107
+ expect(setAge.name).toBe('setAge');
108
+ expect(setAge.args).toHaveLength(1);
109
+ expect(setAge.args[0].name).toBe('age');
110
+ expect(setAge.returnType?.value).toBe(FunctionArgumentType.void);
111
+ });
112
+
113
+ it('should handle union types', () => {
114
+ const blob = new IDLBlob('/test/source.d.ts', '/test/target', 'test', 'test');
115
+ blob.raw = `
116
+ interface TestInterface {
117
+ value: string | number | null;
118
+ }
119
+ `;
120
+
121
+ const propertyCollector = {
122
+ properties: new Set<string>(),
123
+ files: new Set<string>(),
124
+ interfaces: new Set<string>()
125
+ };
126
+ const unionTypeCollector = {
127
+ types: new Set<ParameterType[]>()
128
+ };
129
+
130
+ analyzer(blob, propertyCollector, unionTypeCollector);
131
+
132
+ const classObj = blob.objects[0] as ClassObject;
133
+ const prop = classObj.props[0];
134
+ expect(Array.isArray(prop.type.value)).toBe(true);
135
+ expect(prop.type.value).toHaveLength(3);
136
+ expect(unionTypeCollector.types.size).toBeGreaterThan(0);
137
+ });
138
+
139
+ it('should handle array types', () => {
140
+ const blob = new IDLBlob('/test/source.d.ts', '/test/target', 'test', 'test');
141
+ blob.raw = `
142
+ interface TestInterface {
143
+ items: string[];
144
+ numbers: number[];
145
+ }
146
+ `;
147
+
148
+ const propertyCollector = {
149
+ properties: new Set<string>(),
150
+ files: new Set<string>(),
151
+ interfaces: new Set<string>()
152
+ };
153
+ const unionTypeCollector = {
154
+ types: new Set<ParameterType[]>()
155
+ };
156
+
157
+ analyzer(blob, propertyCollector, unionTypeCollector);
158
+
159
+ const classObj = blob.objects[0] as ClassObject;
160
+ expect(classObj.props[0].type.isArray).toBe(true);
161
+ expect(classObj.props[1].type.isArray).toBe(true);
162
+ });
163
+
164
+ it('should handle function types as properties', () => {
165
+ const blob = new IDLBlob('/test/source.d.ts', '/test/target', 'test', 'test');
166
+ blob.raw = `
167
+ interface TestInterface {
168
+ onClick: Function;
169
+ onChange: Function;
170
+ }
171
+ `;
172
+
173
+ const propertyCollector = {
174
+ properties: new Set<string>(),
175
+ files: new Set<string>(),
176
+ interfaces: new Set<string>()
177
+ };
178
+ const unionTypeCollector = {
179
+ types: new Set<ParameterType[]>()
180
+ };
181
+
182
+ analyzer(blob, propertyCollector, unionTypeCollector);
183
+
184
+ const classObj = blob.objects[0] as ClassObject;
185
+ // Function type properties are stored as props with function type
186
+ expect(classObj.props).toHaveLength(2);
187
+ expect(classObj.props[0].name).toBe('onClick');
188
+ expect(classObj.props[0].type.value).toBe(FunctionArgumentType.function);
189
+ expect(classObj.props[1].name).toBe('onChange');
190
+ expect(classObj.props[1].type.value).toBe(FunctionArgumentType.function);
191
+ });
192
+
193
+ it('should handle errors gracefully', () => {
194
+ const blob = new IDLBlob('/test/source.d.ts', '/test/target', 'test', 'test');
195
+ blob.raw = `
196
+ interface TestInterface {
197
+ // This is invalid TypeScript
198
+ prop:
199
+ }
200
+ `;
201
+
202
+ const propertyCollector = {
203
+ properties: new Set<string>(),
204
+ files: new Set<string>(),
205
+ interfaces: new Set<string>()
206
+ };
207
+ const unionTypeCollector = {
208
+ types: new Set<ParameterType[]>()
209
+ };
210
+
211
+ // Should not throw, but objects array will be empty or contain partial results
212
+ expect(() => analyzer(blob, propertyCollector, unionTypeCollector)).not.toThrow();
213
+ });
214
+
215
+ it('should cache parsed source files', () => {
216
+ const blob1 = new IDLBlob('/test/source.d.ts', '/test/target', 'test', 'test');
217
+ blob1.raw = `interface Test1 { prop: string; }`;
218
+
219
+ const blob2 = new IDLBlob('/test/source.d.ts', '/test/target', 'test', 'test');
220
+ blob2.raw = `interface Test2 { prop: number; }`; // Different content, same path
221
+
222
+ const propertyCollector = {
223
+ properties: new Set<string>(),
224
+ files: new Set<string>(),
225
+ interfaces: new Set<string>()
226
+ };
227
+ const unionTypeCollector = {
228
+ types: new Set<ParameterType[]>()
229
+ };
230
+
231
+ analyzer(blob1, propertyCollector, unionTypeCollector);
232
+ analyzer(blob2, propertyCollector, unionTypeCollector);
233
+
234
+ // Second analyzer should use cached source file (with first content)
235
+ const classObj = blob2.objects[0] as ClassObject;
236
+ expect(classObj.name).toBe('Test1'); // Not Test2, because it used cached version
237
+ });
238
+ });
239
+
240
+ describe('buildClassRelationship', () => {
241
+ it('should build parent-child relationships', () => {
242
+ ClassObject.globalClassMap = {
243
+ Parent: { name: 'Parent', parent: null } as any,
244
+ Child1: { name: 'Child1', parent: 'Parent' } as any,
245
+ Child2: { name: 'Child2', parent: 'Parent' } as any,
246
+ GrandChild: { name: 'GrandChild', parent: 'Child1' } as any,
247
+ };
248
+
249
+ buildClassRelationship();
250
+
251
+ expect(ClassObject.globalClassRelationMap['Parent']).toEqual(['Child1', 'Child2']);
252
+ expect(ClassObject.globalClassRelationMap['Child1']).toEqual(['GrandChild']);
253
+ expect(ClassObject.globalClassRelationMap['Child2']).toBeUndefined();
254
+ });
255
+
256
+ it('should handle empty class map', () => {
257
+ ClassObject.globalClassMap = {};
258
+
259
+ expect(() => buildClassRelationship()).not.toThrow();
260
+ expect(ClassObject.globalClassRelationMap).toEqual({});
261
+ });
262
+ });
263
+
264
+ describe('clearCaches', () => {
265
+ it('should clear all caches', () => {
266
+ // First add some data to cache by analyzing
267
+ const blob = new IDLBlob('/test/source.d.ts', '/test/target', 'test', 'test');
268
+ blob.raw = `interface Test { prop: string; }`;
269
+
270
+ const propertyCollector = {
271
+ properties: new Set<string>(),
272
+ files: new Set<string>(),
273
+ interfaces: new Set<string>()
274
+ };
275
+ const unionTypeCollector = {
276
+ types: new Set<ParameterType[]>()
277
+ };
278
+
279
+ analyzer(blob, propertyCollector, unionTypeCollector);
280
+
281
+ // Clear caches
282
+ clearCaches();
283
+
284
+ // Analyze again with different content
285
+ blob.raw = `interface Test { prop: number; }`;
286
+ analyzer(blob, propertyCollector, unionTypeCollector);
287
+
288
+ // Should get new result, not cached
289
+ const classObj = blob.objects[0] as ClassObject;
290
+ expect(classObj.props[0].type.value).toBe(FunctionArgumentType.double);
291
+ });
292
+ });
293
+
294
+ describe('Type mapping', () => {
295
+ it('should map basic types correctly', () => {
296
+ const blob = new IDLBlob('/test/source.d.ts', '/test/target', 'test', 'test');
297
+ blob.raw = `
298
+ interface TestInterface {
299
+ str: string;
300
+ num: number;
301
+ bool: boolean;
302
+ obj: object;
303
+ any: any;
304
+ void: void;
305
+ undef: undefined;
306
+ nil: null;
307
+ }
308
+ `;
309
+
310
+ const propertyCollector = {
311
+ properties: new Set<string>(),
312
+ files: new Set<string>(),
313
+ interfaces: new Set<string>()
314
+ };
315
+ const unionTypeCollector = {
316
+ types: new Set<ParameterType[]>()
317
+ };
318
+
319
+ analyzer(blob, propertyCollector, unionTypeCollector);
320
+
321
+ const classObj = blob.objects[0] as ClassObject;
322
+ // Note: void properties might be filtered out or handled differently
323
+ expect(classObj.props.length).toBeGreaterThanOrEqual(7);
324
+ expect(classObj.props[0].type.value).toBe(FunctionArgumentType.dom_string);
325
+ expect(classObj.props[1].type.value).toBe(FunctionArgumentType.double);
326
+ expect(classObj.props[2].type.value).toBe(FunctionArgumentType.boolean);
327
+ expect(classObj.props[3].type.value).toBe(FunctionArgumentType.object);
328
+ expect(classObj.props[4].type.value).toBe(FunctionArgumentType.any);
329
+ // void type might be at index 5
330
+ const voidProp = classObj.props.find(p => p.name === 'void');
331
+ if (voidProp) {
332
+ expect(voidProp.type.value).toBe(FunctionArgumentType.void);
333
+ }
334
+ const undefProp = classObj.props.find(p => p.name === 'undef');
335
+ expect(undefProp?.type.value).toBe(FunctionArgumentType.undefined);
336
+ const nilProp = classObj.props.find(p => p.name === 'nil');
337
+ expect(nilProp).toBeDefined();
338
+ if (nilProp) {
339
+ expect(nilProp.type.value).toBe(FunctionArgumentType.null);
340
+ }
341
+ });
342
+
343
+ it('should handle special type references', () => {
344
+ const blob = new IDLBlob('/test/source.d.ts', '/test/target', 'test', 'test');
345
+ blob.raw = `
346
+ interface TestInterface {
347
+ func: Function;
348
+ promise: Promise<string>;
349
+ int: int;
350
+ }
351
+ `;
352
+
353
+ const propertyCollector = {
354
+ properties: new Set<string>(),
355
+ files: new Set<string>(),
356
+ interfaces: new Set<string>()
357
+ };
358
+ const unionTypeCollector = {
359
+ types: new Set<ParameterType[]>()
360
+ };
361
+
362
+ analyzer(blob, propertyCollector, unionTypeCollector);
363
+
364
+ const classObj = blob.objects[0] as ClassObject;
365
+ expect(classObj.props[0].type.value).toBe(FunctionArgumentType.function);
366
+ expect(classObj.props[1].type.value).toBe(FunctionArgumentType.promise);
367
+ expect(classObj.props[2].type.value).toBe(FunctionArgumentType.int);
368
+ });
369
+ });
370
+ });