@openwebf/webf 0.23.7 → 0.24.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.
@@ -2,18 +2,20 @@
2
2
  jest.mock('fs');
3
3
  jest.mock('child_process');
4
4
  jest.mock('../src/generator');
5
- jest.mock('glob');
5
+ jest.mock('glob', () => ({
6
+ globSync: jest.fn(),
7
+ }));
6
8
  jest.mock('inquirer');
7
9
  jest.mock('yaml');
8
10
 
9
11
  import fs from 'fs';
10
12
  import path from 'path';
11
13
  import { spawnSync } from 'child_process';
12
- import { glob } from 'glob';
14
+ import { globSync } from 'glob';
13
15
 
14
16
  const mockFs = fs as jest.Mocked<typeof fs>;
15
17
  const mockSpawnSync = spawnSync as jest.MockedFunction<typeof spawnSync>;
16
- const mockGlob = glob as jest.Mocked<typeof glob>;
18
+ const mockGlobSync = globSync as jest.MockedFunction<typeof globSync>;
17
19
 
18
20
  // Set up default mocks before importing commands
19
21
  mockFs.readFileSync = jest.fn().mockImplementation((filePath: any) => {
@@ -87,7 +89,7 @@ describe('Commands', () => {
87
89
  });
88
90
  // Default mock for readdirSync to avoid undefined
89
91
  mockFs.readdirSync.mockReturnValue([] as any);
90
- mockGlob.globSync.mockReturnValue([]);
92
+ mockGlobSync.mockReturnValue([]);
91
93
  });
92
94
 
93
95
 
@@ -224,12 +226,12 @@ describe('Commands', () => {
224
226
  // Mock that required files don't exist
225
227
  mockFs.existsSync.mockReturnValue(false);
226
228
 
227
- await generateCommand(target, options);
229
+ await generateCommand(target, options);
228
230
 
229
231
  expect(mockSpawnSync).toHaveBeenCalledWith(
230
232
  expect.stringMatching(/npm(\.cmd)?/),
231
- ['install', '--omit=peer'],
232
- { cwd: target, stdio: 'inherit' }
233
+ ['install', '--production=false'],
234
+ expect.objectContaining({ cwd: target, stdio: 'inherit' })
233
235
  );
234
236
  });
235
237
  });
@@ -281,18 +283,11 @@ describe('Commands', () => {
281
283
 
282
284
  await generateCommand(target, options);
283
285
 
284
- // Should install WebF typings
285
- expect(mockSpawnSync).toHaveBeenCalledWith(
286
- expect.stringMatching(/npm(\.cmd)?/),
287
- ['install', '@openwebf/webf-enterprise-typings'],
288
- { cwd: target, stdio: 'inherit' }
289
- );
290
-
291
- // Should install Vue 3 as dev dependency
286
+ // Should install dependencies (including devDependencies) from package.json
292
287
  expect(mockSpawnSync).toHaveBeenCalledWith(
293
288
  expect.stringMatching(/npm(\.cmd)?/),
294
- ['install', 'vue', '-D'],
295
- { cwd: target, stdio: 'inherit' }
289
+ ['install', '--production=false'],
290
+ expect.objectContaining({ cwd: target, stdio: 'inherit' })
296
291
  );
297
292
  });
298
293
  });
@@ -461,6 +456,78 @@ describe('Commands', () => {
461
456
  }));
462
457
  });
463
458
 
459
+ it('should copy Flutter README.md into generated React package root', async () => {
460
+ const options = {
461
+ flutterPackageSrc: '/flutter/src',
462
+ framework: 'react',
463
+ packageName: 'test-package'
464
+ };
465
+
466
+ // Mock TypeScript validation
467
+ mockTypeScriptValidation('/flutter/src');
468
+
469
+ const originalExistsSync = mockFs.existsSync as jest.Mock;
470
+ mockFs.existsSync = jest.fn().mockImplementation((filePath: any) => {
471
+ const pathStr = filePath.toString();
472
+ if (pathStr === '/flutter/src/README.md') return true;
473
+ if (pathStr === path.join(path.resolve('/dist'), 'README.md')) return false;
474
+ return originalExistsSync(filePath);
475
+ });
476
+
477
+ const originalReadFileSync = mockFs.readFileSync as jest.Mock;
478
+ mockFs.readFileSync = jest.fn().mockImplementation((filePath: any, encoding?: any) => {
479
+ const pathStr = filePath.toString();
480
+ if (pathStr === '/flutter/src/README.md') {
481
+ return '# Flutter README\n\nHello';
482
+ }
483
+ return originalReadFileSync(filePath, encoding);
484
+ });
485
+
486
+ await generateCommand('/dist', options);
487
+
488
+ expect(mockFs.writeFileSync).toHaveBeenCalledWith(
489
+ path.join(path.resolve('/dist'), 'README.md'),
490
+ '# Flutter README\n\nHello',
491
+ 'utf-8'
492
+ );
493
+ });
494
+
495
+ it('should copy Flutter README.md into generated Vue package root', async () => {
496
+ const options = {
497
+ flutterPackageSrc: '/flutter/src',
498
+ framework: 'vue',
499
+ packageName: 'test-package'
500
+ };
501
+
502
+ // Mock TypeScript validation
503
+ mockTypeScriptValidation('/flutter/src');
504
+
505
+ const originalExistsSync = mockFs.existsSync as jest.Mock;
506
+ mockFs.existsSync = jest.fn().mockImplementation((filePath: any) => {
507
+ const pathStr = filePath.toString();
508
+ if (pathStr === '/flutter/src/README.md') return true;
509
+ if (pathStr === path.join(path.resolve('/dist'), 'README.md')) return false;
510
+ return originalExistsSync(filePath);
511
+ });
512
+
513
+ const originalReadFileSync = mockFs.readFileSync as jest.Mock;
514
+ mockFs.readFileSync = jest.fn().mockImplementation((filePath: any, encoding?: any) => {
515
+ const pathStr = filePath.toString();
516
+ if (pathStr === '/flutter/src/README.md') {
517
+ return '# Flutter README\n\nHello';
518
+ }
519
+ return originalReadFileSync(filePath, encoding);
520
+ });
521
+
522
+ await generateCommand('/dist', options);
523
+
524
+ expect(mockFs.writeFileSync).toHaveBeenCalledWith(
525
+ path.join(path.resolve('/dist'), 'README.md'),
526
+ '# Flutter README\n\nHello',
527
+ 'utf-8'
528
+ );
529
+ });
530
+
464
531
  it('should generate an aggregated README in dist from markdown docs', async () => {
465
532
  const options = {
466
533
  flutterPackageSrc: '/flutter/src',
@@ -471,8 +538,8 @@ describe('Commands', () => {
471
538
  // Mock TypeScript validation
472
539
  mockTypeScriptValidation('/flutter/src');
473
540
 
474
- // Mock .d.ts files so copyMarkdownDocsToDist sees at least one entry
475
- mockGlob.globSync.mockReturnValue(['lib/src/alert.d.ts'] as any);
541
+ // Mock .d.ts files so copyMarkdownDocsToDist sees at least one entry
542
+ mockGlobSync.mockReturnValue(['lib/src/alert.d.ts'] as any);
476
543
 
477
544
  const originalExistsSync = mockFs.existsSync as jest.Mock;
478
545
  mockFs.existsSync = jest.fn().mockImplementation((filePath: any) => {
@@ -1,6 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import { glob } from 'glob';
3
+ import { globSync } from 'glob';
4
4
  import { dartGen, reactGen, vueGen, clearAllCaches } from '../src/generator';
5
5
  import * as analyzer from '../src/analyzer';
6
6
  import * as dartGenerator from '../src/dart';
@@ -10,7 +10,9 @@ import { ClassObject } from '../src/declaration';
10
10
 
11
11
  // Mock dependencies
12
12
  jest.mock('fs');
13
- jest.mock('glob');
13
+ jest.mock('glob', () => ({
14
+ globSync: jest.fn(),
15
+ }));
14
16
  jest.mock('../src/analyzer');
15
17
  jest.mock('../src/dart');
16
18
  jest.mock('../src/react');
@@ -47,7 +49,7 @@ jest.mock('../src/logger', () => ({
47
49
  }));
48
50
 
49
51
  const mockFs = fs as jest.Mocked<typeof fs>;
50
- const mockGlob = glob as jest.Mocked<typeof glob>;
52
+ const mockGlobSync = globSync as jest.MockedFunction<typeof globSync>;
51
53
  const mockAnalyzer = analyzer as jest.Mocked<typeof analyzer>;
52
54
  const mockDartGenerator = dartGenerator as jest.Mocked<typeof dartGenerator>;
53
55
  const mockReactGenerator = reactGenerator as jest.Mocked<typeof reactGenerator>;
@@ -65,7 +67,7 @@ describe('Generator', () => {
65
67
  mockFs.writeFileSync.mockImplementation(() => undefined);
66
68
  mockFs.mkdirSync.mockImplementation(() => undefined);
67
69
 
68
- mockGlob.globSync.mockReturnValue(['test.d.ts', 'component.d.ts']);
70
+ mockGlobSync.mockReturnValue(['test.d.ts', 'component.d.ts']);
69
71
 
70
72
  mockAnalyzer.analyzer.mockImplementation(() => undefined);
71
73
  mockAnalyzer.clearCaches.mockImplementation(() => undefined);
@@ -84,7 +86,7 @@ describe('Generator', () => {
84
86
  command: 'test command'
85
87
  });
86
88
 
87
- expect(mockGlob.globSync).toHaveBeenCalledWith('**/*.d.ts', {
89
+ expect(mockGlobSync).toHaveBeenCalledWith('**/*.d.ts', {
88
90
  cwd: '/test/source',
89
91
  ignore: ['**/node_modules/**', '**/dist/**', '**/build/**', '**/example/**']
90
92
  });
@@ -101,14 +103,14 @@ describe('Generator', () => {
101
103
  command: 'test command'
102
104
  });
103
105
 
104
- expect(mockGlob.globSync).toHaveBeenCalledWith('**/*.d.ts', {
106
+ expect(mockGlobSync).toHaveBeenCalledWith('**/*.d.ts', {
105
107
  cwd: expect.stringContaining('relative/source'),
106
108
  ignore: ['**/node_modules/**', '**/dist/**', '**/build/**', '**/example/**']
107
109
  });
108
110
  });
109
111
 
110
112
  it('should filter out global.d.ts files', async () => {
111
- mockGlob.globSync.mockReturnValue(['test.d.ts', 'global.d.ts', 'component.d.ts']);
113
+ mockGlobSync.mockReturnValue(['test.d.ts', 'global.d.ts', 'component.d.ts']);
112
114
 
113
115
  await dartGen({
114
116
  source: '/test/source',
@@ -122,7 +124,7 @@ describe('Generator', () => {
122
124
  });
123
125
 
124
126
  it('should handle empty type files', async () => {
125
- mockGlob.globSync.mockReturnValue([]);
127
+ mockGlobSync.mockReturnValue([]);
126
128
 
127
129
  await dartGen({
128
130
  source: '/test/source',
@@ -135,7 +137,7 @@ describe('Generator', () => {
135
137
  });
136
138
 
137
139
  it('should continue processing if one file fails', async () => {
138
- mockGlob.globSync.mockReturnValue(['test1.d.ts', 'test2.d.ts']);
140
+ mockGlobSync.mockReturnValue(['test1.d.ts', 'test2.d.ts']);
139
141
  mockAnalyzer.analyzer
140
142
  .mockImplementationOnce(() => { throw new Error('Parse error'); })
141
143
  .mockImplementationOnce(() => undefined);
@@ -216,7 +218,7 @@ describe('Generator', () => {
216
218
  });
217
219
 
218
220
  it('should generate index.d.ts with references and exports', async () => {
219
- mockGlob.globSync.mockReturnValue(['components/button.d.ts', 'widgets/card.d.ts']);
221
+ mockGlobSync.mockReturnValue(['components/button.d.ts', 'widgets/card.d.ts']);
220
222
  mockFs.readFileSync.mockReturnValue('interface Test {}');
221
223
  mockFs.existsSync.mockImplementation((path) => {
222
224
  // Source directory exists
@@ -231,23 +233,17 @@ describe('Generator', () => {
231
233
  command: 'test command'
232
234
  });
233
235
 
234
- // Check that index.d.ts was written
236
+ // Dart codegen does not write a root index.d.ts; it copies .d.ts files alongside generated Dart bindings.
235
237
  const writeFileCalls = mockFs.writeFileSync.mock.calls;
236
238
  const indexDtsCall = writeFileCalls.find(call =>
237
239
  call[0].toString().endsWith('index.d.ts')
238
240
  );
239
241
 
240
- expect(indexDtsCall).toBeDefined();
241
- expect(indexDtsCall![1]).toContain('/// <reference path="./global.d.ts" />');
242
- expect(indexDtsCall![1]).toContain('/// <reference path="./components/button.d.ts" />');
243
- expect(indexDtsCall![1]).toContain('/// <reference path="./widgets/card.d.ts" />');
244
- expect(indexDtsCall![1]).toContain("export * from './components/button';");
245
- expect(indexDtsCall![1]).toContain("export * from './widgets/card';");
246
- expect(indexDtsCall![1]).toContain('TypeScript Definitions');
242
+ expect(indexDtsCall).toBeUndefined();
247
243
  });
248
244
 
249
245
  it('should copy original .d.ts files to output directory', async () => {
250
- mockGlob.globSync.mockReturnValue(['test.d.ts']);
246
+ mockGlobSync.mockReturnValue(['test.d.ts']);
251
247
  const originalContent = 'interface Original {}';
252
248
  mockFs.readFileSync.mockReturnValue(originalContent);
253
249
  mockFs.existsSync.mockImplementation((path) => {
@@ -279,7 +275,7 @@ describe('Generator', () => {
279
275
  exclude: ['**/test/**', '**/docs/**']
280
276
  });
281
277
 
282
- expect(mockGlob.globSync).toHaveBeenCalledWith('**/*.d.ts', {
278
+ expect(mockGlobSync).toHaveBeenCalledWith('**/*.d.ts', {
283
279
  cwd: '/test/source',
284
280
  ignore: ['**/node_modules/**', '**/dist/**', '**/build/**', '**/example/**', '**/test/**', '**/docs/**']
285
281
  });
@@ -299,6 +295,19 @@ describe('Generator', () => {
299
295
  expect(mockFs.writeFileSync).toHaveBeenCalled();
300
296
  });
301
297
 
298
+ it('should always generate src/types.ts for React output', async () => {
299
+ await reactGen({
300
+ source: '/test/source',
301
+ target: '/test/target',
302
+ command: 'test command'
303
+ });
304
+
305
+ const writeCalls = mockFs.writeFileSync.mock.calls;
306
+ const typesCall = writeCalls.find(call => /[\\/]src[\\/]types\.ts$/.test(call[0].toString()));
307
+ expect(typesCall).toBeDefined();
308
+ expect(typesCall![1]).toContain('export {};');
309
+ });
310
+
302
311
  it('should use the exact target directory specified', async () => {
303
312
  await reactGen({
304
313
  source: '/test/source',
@@ -419,7 +428,7 @@ describe('Generator', () => {
419
428
  });
420
429
 
421
430
  it('should only generate one index.d.ts file', async () => {
422
- mockGlob.globSync.mockReturnValue(['comp1.d.ts', 'comp2.d.ts', 'comp3.d.ts']);
431
+ mockGlobSync.mockReturnValue(['comp1.d.ts', 'comp2.d.ts', 'comp3.d.ts']);
423
432
 
424
433
  await vueGen({
425
434
  source: '/test/source',
@@ -436,7 +445,7 @@ describe('Generator', () => {
436
445
 
437
446
  describe('Error handling', () => {
438
447
  it('should handle glob errors', async () => {
439
- mockGlob.globSync.mockImplementation(() => {
448
+ mockGlobSync.mockImplementation(() => {
440
449
  throw new Error('Glob error');
441
450
  });
442
451
 
@@ -476,8 +485,8 @@ describe('Generator', () => {
476
485
  // First file fails (no writes), second file succeeds (1 dart + 1 .d.ts), plus index.d.ts = 3 total
477
486
  // But since the error happens in dartGen, the .d.ts copy might not happen
478
487
  const writeCalls = mockFs.writeFileSync.mock.calls;
479
- // Should have at least written the successful dart file and index.d.ts
480
- expect(writeCalls.length).toBeGreaterThanOrEqual(2);
488
+ // Should have at least written the successful Dart bindings file
489
+ expect(writeCalls.length).toBeGreaterThanOrEqual(1);
481
490
  });
482
491
  });
483
492
 
@@ -0,0 +1,30 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ import { getPackageTypesFileFromDir, isPackageTypesReady } from '../src/peerDeps';
5
+
6
+ function makeTempDir(prefix: string): string {
7
+ return fs.mkdtempSync(path.join(process.cwd(), 'test', prefix));
8
+ }
9
+
10
+ describe('peerDeps', () => {
11
+ it('treats package as not ready when declared types file is missing', () => {
12
+ const dir = makeTempDir('peerDeps-');
13
+ const distDir = path.join(dir, 'dist');
14
+ try {
15
+ fs.mkdirSync(distDir, { recursive: true });
16
+ fs.writeFileSync(
17
+ path.join(dir, 'package.json'),
18
+ JSON.stringify({ name: '@openwebf/react-core-ui', types: 'dist/index.d.ts' }, null, 2)
19
+ );
20
+
21
+ expect(getPackageTypesFileFromDir(dir)).toBe(path.join(dir, 'dist', 'index.d.ts'));
22
+ expect(isPackageTypesReady(dir)).toBe(false);
23
+
24
+ fs.writeFileSync(path.join(distDir, 'index.d.ts'), 'export {};');
25
+ expect(isPackageTypesReady(dir)).toBe(true);
26
+ } finally {
27
+ fs.rmSync(dir, { recursive: true, force: true });
28
+ }
29
+ });
30
+ });
@@ -1,15 +1,18 @@
1
1
  import { generateReactComponent } from '../src/react';
2
2
  import { IDLBlob } from '../src/IDLBlob';
3
- import { ConstObject, EnumObject, EnumMemberObject } from '../src/declaration';
3
+ import { ClassObject, ClassObjectKind, ConstObject, EnumObject, EnumMemberObject } from '../src/declaration';
4
4
 
5
5
  describe('React generator - declare const support', () => {
6
6
  it('emits export declare const for constants from typings', () => {
7
7
  const blob = new IDLBlob('/test/source', '/test/target', 'ConstOnly', 'test', '');
8
+ const properties = new ClassObject();
9
+ properties.name = 'TestComponentProperties';
10
+ properties.kind = ClassObjectKind.interface;
8
11
  const constObj = new ConstObject();
9
12
  constObj.name = 'WEBF_CUPERTINO_SYMBOL';
10
13
  constObj.type = 'unique symbol';
11
14
 
12
- blob.objects = [constObj as any];
15
+ blob.objects = [properties, constObj as any];
13
16
 
14
17
  const output = generateReactComponent(blob);
15
18
  expect(output).toContain('export declare const WEBF_CUPERTINO_SYMBOL: unique symbol;');
@@ -17,12 +20,15 @@ describe('React generator - declare const support', () => {
17
20
 
18
21
  it('emits export declare enum for enums from typings', () => {
19
22
  const blob = new IDLBlob('/test/source', '/test/target', 'EnumOnly', 'test', '');
23
+ const properties = new ClassObject();
24
+ properties.name = 'TestComponentProperties';
25
+ properties.kind = ClassObjectKind.interface;
20
26
  const eo = new EnumObject();
21
27
  eo.name = 'CupertinoColors';
22
28
  const m1 = new EnumMemberObject(); m1.name = "'red'"; m1.initializer = '0x0f';
23
29
  const m2 = new EnumMemberObject(); m2.name = "'bbb'"; m2.initializer = '0x00';
24
30
  eo.members = [m1, m2];
25
- blob.objects = [eo as any];
31
+ blob.objects = [properties, eo as any];
26
32
 
27
33
  const output = generateReactComponent(blob);
28
34
  expect(output).toContain("export enum CupertinoColors { 'red' = 0x0f, 'bbb' = 0x00 }");
@@ -49,13 +49,13 @@ describe('Standard HTML Props Generation', () => {
49
49
  const propsContent = result.substring(propsStart, propsEnd);
50
50
 
51
51
  // Verify order: custom props, event handlers, then standard HTML props
52
- const labelIndex = propsContent.indexOf('label: dom_string;');
53
- const variantIndex = propsContent.indexOf('variant?: dom_string;');
54
- const onClickIndex = propsContent.indexOf('onClick?: (event: Event) => void;');
55
- const idIndex = propsContent.indexOf('id?: string;');
56
- const styleIndex = propsContent.indexOf('style?: React.CSSProperties;');
57
- const childrenIndex = propsContent.indexOf('children?: React.ReactNode;');
58
- const classNameIndex = propsContent.indexOf('className?: string;');
52
+ const labelIndex = propsContent.search(/\n\s*label\??:\s*/);
53
+ const variantIndex = propsContent.search(/\n\s*variant\??:\s*/);
54
+ const onClickIndex = propsContent.search(/\n\s*onClick\??:\s*/);
55
+ const idIndex = propsContent.search(/\n\s*id\??:\s*/);
56
+ const styleIndex = propsContent.search(/\n\s*style\??:\s*/);
57
+ const childrenIndex = propsContent.search(/\n\s*children\??:\s*/);
58
+ const classNameIndex = propsContent.search(/\n\s*className\??:\s*/);
59
59
 
60
60
  // All props should exist
61
61
  expect(labelIndex).toBeGreaterThan(-1);
@@ -131,8 +131,8 @@ describe('Standard HTML Props Generation', () => {
131
131
 
132
132
  // Standard HTML props
133
133
  expect(propsContent).toContain("'id'?: string;");
134
- expect(propsContent).toContain("'class'?: string;");
135
- expect(propsContent).toContain("'style'?: string | Record<string, any>;");
134
+ expect(propsContent).toContain("'class'?: ClassValue;");
135
+ expect(propsContent).toContain("'style'?: StyleValue;");
136
136
  });
137
137
 
138
138
  it('should handle Vue style prop with both string and object types', () => {
@@ -145,8 +145,8 @@ describe('Standard HTML Props Generation', () => {
145
145
 
146
146
  const result = generateVueTypings([blob]);
147
147
 
148
- // Vue style prop should accept both string and object
149
- expect(result).toMatch(/'style'\?: string \| Record<string, any>;/);
148
+ // Vue style prop should accept Vue's supported style value forms
149
+ expect(result).toMatch(/'style'\?: StyleValue;/);
150
150
  });
151
151
  });
152
152
 
@@ -176,15 +176,15 @@ describe('Standard HTML Props Generation', () => {
176
176
 
177
177
  // Both should have style prop (with appropriate types)
178
178
  expect(reactResult).toContain('style?: React.CSSProperties;');
179
- expect(vueResult).toContain("'style'?: string | Record<string, any>;");
179
+ expect(vueResult).toContain("'style'?: StyleValue;");
180
180
 
181
181
  // React has className, Vue has class
182
182
  expect(reactResult).toContain('className?: string;');
183
- expect(vueResult).toContain("'class'?: string;");
183
+ expect(vueResult).toContain("'class'?: ClassValue;");
184
184
 
185
185
  // React has children, Vue uses slots (not in props)
186
186
  expect(reactResult).toContain('children?: React.ReactNode;');
187
187
  expect(vueResult).not.toContain('children');
188
188
  });
189
189
  });
190
- });
190
+ });
@@ -0,0 +1,17 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ describe('Templates', () => {
5
+ it('react tsconfig template pins React type resolution to root @types', () => {
6
+ const templatePath = path.resolve(__dirname, '../templates/react.tsconfig.json.tpl');
7
+ const template = fs.readFileSync(templatePath, 'utf8');
8
+
9
+ expect(template).toContain('"baseUrl": "."');
10
+ expect(template).toContain('"paths"');
11
+ expect(template).toContain('"react": ["./node_modules/@types/react/index.d.ts"]');
12
+ expect(template).toContain('"react-dom": ["./node_modules/@types/react-dom/index.d.ts"]');
13
+ expect(template).toContain('"react/jsx-runtime": ["./node_modules/@types/react/jsx-runtime.d.ts"]');
14
+ expect(template).toContain('"react/jsx-dev-runtime": ["./node_modules/@types/react/jsx-dev-runtime.d.ts"]');
15
+ });
16
+ });
17
+
package/test/vue.test.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { generateVueTypings } from '../src/vue';
2
2
  import { IDLBlob } from '../src/IDLBlob';
3
- import { ClassObject, ClassObjectKind, PropsDeclaration, ConstObject } from '../src/declaration';
3
+ import { ClassObject, ClassObjectKind, PropsDeclaration, ConstObject, TypeAliasObject } from '../src/declaration';
4
4
 
5
5
  describe('Vue Generator', () => {
6
6
  describe('generateVueTypings', () => {
@@ -16,13 +16,16 @@ describe('Vue Generator', () => {
16
16
 
17
17
  // Should include standard HTML props in Props type
18
18
  expect(result).toContain("'id'?: string;");
19
- expect(result).toContain("'class'?: string;");
20
- expect(result).toContain("'style'?: string | Record<string, any>;");
19
+ expect(result).toContain("'class'?: ClassValue;");
20
+ expect(result).toContain("'style'?: StyleValue;");
21
21
 
22
22
  // Should generate proper type exports
23
23
  expect(result).toContain('export type TestComponentProps = {');
24
24
  expect(result).toContain('export interface TestComponentElement {');
25
25
  expect(result).toContain('export type TestComponentEvents = {');
26
+
27
+ // Should not rely on a non-existent internal __webfTypes import
28
+ expect(result).not.toContain('__webfTypes');
26
29
  });
27
30
 
28
31
  it('should include standard HTML props along with custom properties', () => {
@@ -58,8 +61,8 @@ describe('Vue Generator', () => {
58
61
 
59
62
  // And still include standard HTML props
60
63
  expect(result).toContain("'id'?: string;");
61
- expect(result).toContain("'class'?: string;");
62
- expect(result).toContain("'style'?: string | Record<string, any>;");
64
+ expect(result).toContain("'class'?: ClassValue;");
65
+ expect(result).toContain("'style'?: StyleValue;");
63
66
  });
64
67
 
65
68
  it('should generate proper Vue component declarations', () => {
@@ -73,11 +76,20 @@ describe('Vue Generator', () => {
73
76
  const result = generateVueTypings([blob]);
74
77
 
75
78
  // Should generate proper component declarations
76
- expect(result).toContain("declare module 'vue' {");
79
+ expect(result).toContain("declare module '@vue/runtime-core' {");
80
+ expect(result).toContain("interface GlobalDirectives {");
81
+ expect(result).toContain('vFlutterAttached: typeof flutterAttached;');
77
82
  expect(result).toContain("interface GlobalComponents {");
78
- expect(result).toContain("'web-f-list-view': DefineCustomElement<");
83
+ expect(result).toContain("'webf-list-view': DefineCustomElement<");
84
+ expect(result).not.toContain("'web-f-list-view': DefineCustomElement<");
85
+ expect(result).toContain("WebFListViewElement,");
79
86
  expect(result).toContain("WebFListViewProps,");
80
87
  expect(result).toContain("WebFListViewEvents");
88
+
89
+ // Compatibility module for older tooling
90
+ expect(result).toContain("declare module 'vue' {");
91
+ expect(result).toContain("interface GlobalDirectives extends import('@vue/runtime-core').GlobalDirectives {}");
92
+ expect(result).toContain("interface GlobalComponents extends import('@vue/runtime-core').GlobalComponents {}");
81
93
  });
82
94
 
83
95
  it('should handle multiple components', () => {
@@ -145,13 +157,13 @@ describe('Vue Generator', () => {
145
157
 
146
158
  // Should include event types
147
159
  expect(result).toContain('export type TestComponentEvents = {');
148
- expect(result).toContain('close?: Event;');
149
- expect(result).toContain('refresh?: CustomEvent;');
160
+ expect(result).toContain('close: Event;');
161
+ expect(result).toContain('refresh: CustomEvent;');
150
162
 
151
163
  // Props should still have standard HTML props
152
164
  expect(result).toContain("'id'?: string;");
153
- expect(result).toContain("'class'?: string;");
154
- expect(result).toContain("'style'?: string | Record<string, any>;");
165
+ expect(result).toContain("'class'?: ClassValue;");
166
+ expect(result).toContain("'style'?: StyleValue;");
155
167
  });
156
168
 
157
169
  it('should include declare const variables as exported declarations', () => {
@@ -168,6 +180,19 @@ describe('Vue Generator', () => {
168
180
  expect(result).toContain('export declare const WEBF_UNIQUE: unique symbol;');
169
181
  });
170
182
 
183
+ it('should include type aliases as exported declarations', () => {
184
+ const blob = new IDLBlob('/test/source', '/test/target', 'TypeAliasOnly', 'test', '');
185
+
186
+ const typeAlias = new TypeAliasObject();
187
+ typeAlias.name = 'MyAlias';
188
+ typeAlias.type = 'string | number';
189
+
190
+ blob.objects = [typeAlias as any];
191
+
192
+ const result = generateVueTypings([blob]);
193
+ expect(result).toContain('export type MyAlias = string | number;');
194
+ });
195
+
171
196
  it('should include declare enum as exported declaration', () => {
172
197
  const blob = new IDLBlob('/test/source', '/test/target', 'EnumOnly', 'test', '');
173
198
  // Build a minimal faux EnumObject via analyzer by simulating ast is heavy; create a shape