@openwebf/webf 0.22.13 → 0.23.2

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/src/dart.ts CHANGED
@@ -83,6 +83,13 @@ ${enumValues};
83
83
  }
84
84
 
85
85
  function generateReturnType(type: ParameterType, enumName?: string) {
86
+ // Handle union types first (e.g., 'left' | 'center' | 'right')
87
+ // so we don't incorrectly treat string literal unions as pointer types.
88
+ if (Array.isArray(type.value)) {
89
+ // If we have an enum name, use it; otherwise fall back to String
90
+ return enumName || 'String';
91
+ }
92
+
86
93
  if (isPointerType(type)) {
87
94
  const pointerType = getPointerType(type);
88
95
  return pointerType;
@@ -91,12 +98,6 @@ function generateReturnType(type: ParameterType, enumName?: string) {
91
98
  return `${getPointerType(type.value)}[]`;
92
99
  }
93
100
 
94
- // Handle union types (e.g., 'left' | 'center' | 'right')
95
- if (Array.isArray(type.value)) {
96
- // If we have an enum name, use it; otherwise fall back to String
97
- return enumName || 'String';
98
- }
99
-
100
101
  // Handle when type.value is a ParameterType object (nested type)
101
102
  if (typeof type.value === 'object' && !Array.isArray(type.value) && type.value !== null) {
102
103
  // This might be a nested ParameterType, recurse
@@ -278,7 +279,7 @@ interface ${object.name} {
278
279
 
279
280
  // Generate enums for union types
280
281
  const enums: { name: string; definition: string }[] = [];
281
- const enumMap: Map<string, string> = new Map(); // prop name -> enum name
282
+ const enumMap: Map<string, string> = new Map(); // camelCase prop name -> enum name
282
283
 
283
284
  if (componentProperties) {
284
285
  for (const prop of componentProperties.props) {
@@ -290,7 +291,8 @@ interface ${object.name} {
290
291
  name: enumName,
291
292
  definition: generateDartEnum(enumName, values)
292
293
  });
293
- enumMap.set(prop.name, enumName);
294
+ // Store by camelCase prop name to match template usage
295
+ enumMap.set(_.camelCase(prop.name), enumName);
294
296
  }
295
297
  }
296
298
  }
@@ -89,3 +89,18 @@ export class TypeAliasObject {
89
89
  name: string;
90
90
  type: string;
91
91
  }
92
+
93
+ export class ConstObject {
94
+ name: string;
95
+ type: string;
96
+ }
97
+
98
+ export class EnumMemberObject {
99
+ name: string;
100
+ initializer?: string;
101
+ }
102
+
103
+ export class EnumObject {
104
+ name: string;
105
+ members: EnumMemberObject[] = [];
106
+ }
package/src/generator.ts CHANGED
@@ -5,12 +5,13 @@ import _ from 'lodash';
5
5
  import { glob } from 'glob';
6
6
  import yaml from 'yaml';
7
7
  import { IDLBlob } from './IDLBlob';
8
- import { ClassObject } from './declaration';
8
+ import { ClassObject, ConstObject, EnumObject, TypeAliasObject } from './declaration';
9
9
  import { analyzer, ParameterType, clearCaches } from './analyzer';
10
10
  import { generateDartClass } from './dart';
11
11
  import { generateReactComponent, generateReactIndex } from './react';
12
12
  import { generateVueTypings } from './vue';
13
13
  import { logger, debug, info, success, warn, error, group, progress, time, timeEnd } from './logger';
14
+ import ts from 'typescript';
14
15
 
15
16
  // Cache for file content to avoid redundant reads
16
17
  const fileContentCache = new Map<string, string>();
@@ -204,13 +205,17 @@ export async function dartGen({ source, target, command, exclude }: GenerateOpti
204
205
  fs.mkdirSync(outputDir, { recursive: true });
205
206
  }
206
207
 
207
- // Generate Dart file
208
+ // Generate Dart file (skip if empty)
208
209
  const genFilePath = path.join(outputDir, _.snakeCase(blob.filename));
209
210
  const fullPath = genFilePath + '_bindings_generated.dart';
210
211
 
211
- if (writeFileIfChanged(fullPath, result)) {
212
- filesChanged++;
213
- debug(`Generated: ${path.basename(fullPath)}`);
212
+ if (result && result.trim().length > 0) {
213
+ if (writeFileIfChanged(fullPath, result)) {
214
+ filesChanged++;
215
+ debug(`Generated: ${path.basename(fullPath)}`);
216
+ }
217
+ } else {
218
+ debug(`Skipped ${path.basename(fullPath)} - empty bindings`);
214
219
  }
215
220
 
216
221
  // Copy the original .d.ts file to the output directory
@@ -224,13 +229,8 @@ export async function dartGen({ source, target, command, exclude }: GenerateOpti
224
229
  }
225
230
  });
226
231
 
227
- // Generate index.d.ts file with references to all .d.ts files
228
- const indexDtsContent = generateTypeScriptIndex(blobs, normalizedTarget);
229
- const indexDtsPath = path.join(normalizedTarget, 'index.d.ts');
230
- if (writeFileIfChanged(indexDtsPath, indexDtsContent)) {
231
- filesChanged++;
232
- debug('Generated: index.d.ts');
233
- }
232
+ // Note: We no longer generate a root index.d.ts for Dart codegen
233
+ // as it is not necessary for the codegen workflow.
234
234
 
235
235
  timeEnd('dartGen');
236
236
  success(`Dart code generation completed. ${filesChanged} files changed.`);
@@ -321,40 +321,155 @@ export async function reactGen({ source, target, exclude, packageName }: Generat
321
321
  }
322
322
  });
323
323
 
324
- // Generate index file
325
- // Avoid overriding a user-managed index.ts. Only write when:
326
- // - index.ts does not exist, or
327
- // - it contains the auto-generated marker from our template
324
+ // Generate/merge index file
328
325
  const indexFilePath = path.join(normalizedTarget, 'src', 'index.ts');
326
+ // Always build the full index content string for downstream tooling/logging
329
327
  const newExports = generateReactIndex(blobs);
330
328
 
331
- let shouldWriteIndex = true;
332
- if (fs.existsSync(indexFilePath)) {
333
- try {
334
- const existing = fs.readFileSync(indexFilePath, 'utf-8');
335
- const isAutoGenerated = existing.includes('Generated by TSDL');
336
- if (!isAutoGenerated) {
337
- shouldWriteIndex = false;
338
- warn(`Found existing user-managed index.ts at ${indexFilePath}; skipping overwrite.`);
339
- }
340
- } catch (err) {
341
- // If we cannot read the file for some reason, be conservative and skip overwriting
342
- shouldWriteIndex = false;
343
- warn(`Unable to read existing index.ts; skipping overwrite: ${indexFilePath}`);
344
- }
329
+ // Build desired export map: moduleSpecifier -> Set of names
330
+ const desiredExports = new Map<string, Set<string>>();
331
+ const components = blobs.flatMap(blob => {
332
+ const classObjects = blob.objects.filter(obj => obj instanceof ClassObject) as ClassObject[];
333
+ const properties = classObjects.filter(object => object.name.endsWith('Properties'));
334
+ const events = classObjects.filter(object => object.name.endsWith('Events'));
335
+ const componentMap = new Map<string, boolean>();
336
+ properties.forEach(prop => componentMap.set(prop.name.replace(/Properties$/, ''), true));
337
+ events.forEach(evt => componentMap.set(evt.name.replace(/Events$/, ''), true));
338
+ return Array.from(componentMap.keys()).map(className => ({
339
+ className,
340
+ fileName: blob.filename,
341
+ relativeDir: blob.relativeDir,
342
+ }));
343
+ });
344
+
345
+ // Deduplicate by className
346
+ const unique = new Map<string, { className: string; fileName: string; relativeDir: string }>();
347
+ for (const c of components) {
348
+ if (!unique.has(c.className)) unique.set(c.className, c);
349
+ }
350
+ for (const { className, fileName, relativeDir } of unique.values()) {
351
+ const spec = `./${relativeDir ? `${relativeDir}/` : ''}${fileName}`;
352
+ if (!desiredExports.has(spec)) desiredExports.set(spec, new Set());
353
+ const set = desiredExports.get(spec)!;
354
+ set.add(className);
355
+ set.add(`${className}Element`);
345
356
  }
346
357
 
347
- if (shouldWriteIndex) {
358
+ if (!fs.existsSync(indexFilePath)) {
359
+ // No index.ts -> generate fresh file from template
348
360
  if (writeFileIfChanged(indexFilePath, newExports)) {
349
361
  filesChanged++;
350
362
  debug(`Generated: index.ts`);
351
363
  }
364
+ } else {
365
+ // Merge into existing index.ts without removing user code
366
+ try {
367
+ const existing = fs.readFileSync(indexFilePath, 'utf-8');
368
+ const sourceFile = ts.createSourceFile(indexFilePath, existing, ts.ScriptTarget.ES2020, true, ts.ScriptKind.TS);
369
+
370
+ // Track which names already exported per module
371
+ for (const stmt of sourceFile.statements) {
372
+ if (ts.isExportDeclaration(stmt) && stmt.exportClause && ts.isNamedExports(stmt.exportClause)) {
373
+ const moduleSpecifier = stmt.moduleSpecifier && ts.isStringLiteral(stmt.moduleSpecifier)
374
+ ? stmt.moduleSpecifier.text
375
+ : undefined;
376
+ if (!moduleSpecifier) continue;
377
+ const desired = desiredExports.get(moduleSpecifier);
378
+ if (!desired) continue;
379
+ for (const el of stmt.exportClause.elements) {
380
+ const name = el.name.getText(sourceFile);
381
+ if (desired.has(name)) desired.delete(name);
382
+ }
383
+ }
384
+ }
385
+
386
+ // Prepare new export lines for any remaining names
387
+ const lines: string[] = [];
388
+ for (const [spec, names] of desiredExports) {
389
+ const missing = Array.from(names);
390
+ if (missing.length === 0) continue;
391
+ const specEscaped = spec.replace(/\\/g, '/');
392
+ lines.push(`export { ${missing.join(', ')} } from "${specEscaped}";`);
393
+ }
394
+
395
+ if (lines.length > 0) {
396
+ const appended = (existing.endsWith('\n') ? '' : '\n') + lines.join('\n') + '\n';
397
+ if (writeFileIfChanged(indexFilePath, existing + appended)) {
398
+ filesChanged++;
399
+ debug(`Merged exports into existing index.ts`);
400
+ }
401
+ } else {
402
+ debug(`index.ts is up to date; no merge needed.`);
403
+ }
404
+ } catch (err) {
405
+ warn(`Failed to merge into existing index.ts. Skipping modifications: ${indexFilePath}`);
406
+ }
352
407
  }
353
408
 
354
409
  timeEnd('reactGen');
355
410
  success(`React code generation completed. ${filesChanged} files changed.`);
356
411
  info(`Output directory: ${normalizedTarget}`);
357
412
  info('You can now import these components in your React project.');
413
+
414
+ // Aggregate standalone type declarations (consts/enums/type aliases) into a single types.d.ts
415
+ try {
416
+ const consts = blobs.flatMap(b => b.objects.filter(o => o instanceof ConstObject) as ConstObject[]);
417
+ const enums = blobs.flatMap(b => b.objects.filter(o => o instanceof EnumObject) as EnumObject[]);
418
+ const typeAliases = blobs.flatMap(b => b.objects.filter(o => o instanceof TypeAliasObject) as TypeAliasObject[]);
419
+
420
+ // Deduplicate by name
421
+ const constMap = new Map<string, ConstObject>();
422
+ consts.forEach(c => { if (!constMap.has(c.name)) constMap.set(c.name, c); });
423
+ const typeAliasMap = new Map<string, TypeAliasObject>();
424
+ typeAliases.forEach(t => { if (!typeAliasMap.has(t.name)) typeAliasMap.set(t.name, t); });
425
+
426
+ const hasAny = constMap.size > 0 || enums.length > 0 || typeAliasMap.size > 0;
427
+ if (hasAny) {
428
+ const constDecl = Array.from(constMap.values())
429
+ .map(c => `export declare const ${c.name}: ${c.type};`)
430
+ .join('\n');
431
+ const enumDecl = enums
432
+ .map(e => `export declare enum ${e.name} { ${e.members.map(m => m.initializer ? `${m.name} = ${m.initializer}` : `${m.name}`).join(', ')} }`)
433
+ .join('\n');
434
+ const typeAliasDecl = Array.from(typeAliasMap.values())
435
+ .map(t => `export type ${t.name} = ${t.type};`)
436
+ .join('\n');
437
+
438
+ const typesContent = [
439
+ '/* Generated by WebF CLI - aggregated type declarations */',
440
+ typeAliasDecl,
441
+ constDecl,
442
+ enumDecl,
443
+ ''
444
+ ].filter(Boolean).join('\n');
445
+
446
+ const typesPath = path.join(normalizedTarget, 'src', 'types.d.ts');
447
+ if (writeFileIfChanged(typesPath, typesContent)) {
448
+ filesChanged++;
449
+ debug(`Generated: src/types.d.ts`);
450
+ }
451
+
452
+ // Try to help TypeScript pick up additional declarations by adding a reference comment.
453
+ // This avoids bundler resolution errors from importing a .d.ts file.
454
+ const indexFilePath = path.join(normalizedTarget, 'src', 'index.ts');
455
+ try {
456
+ if (fs.existsSync(indexFilePath)) {
457
+ let current = fs.readFileSync(indexFilePath, 'utf-8');
458
+ const refLine = `/// <reference path="./types.d.ts" />`;
459
+ if (!current.includes(refLine)) {
460
+ // Place the reference at the very top, before any code
461
+ const updated = `${refLine}\n${current}`;
462
+ if (writeFileIfChanged(indexFilePath, updated)) {
463
+ filesChanged++;
464
+ debug(`Updated: src/index.ts with reference to types.d.ts`);
465
+ }
466
+ }
467
+ }
468
+ } catch {}
469
+ }
470
+ } catch (e) {
471
+ warn('Failed to generate aggregated React types.d.ts');
472
+ }
358
473
  }
359
474
 
360
475
  export async function vueGen({ source, target, exclude }: GenerateOptions) {
package/src/react.ts CHANGED
@@ -2,7 +2,7 @@ import _ from "lodash";
2
2
  import fs from 'fs';
3
3
  import path from 'path';
4
4
  import {ParameterType} from "./analyzer";
5
- import {ClassObject, FunctionArgumentType, FunctionDeclaration, TypeAliasObject} from "./declaration";
5
+ import {ClassObject, FunctionArgumentType, FunctionDeclaration, TypeAliasObject, ConstObject, EnumObject} from "./declaration";
6
6
  import {IDLBlob} from "./IDLBlob";
7
7
  import {getPointerType, isPointerType, isUnionType} from "./utils";
8
8
 
@@ -125,6 +125,8 @@ export function toWebFTagName(className: string): string {
125
125
  export function generateReactComponent(blob: IDLBlob, packageName?: string, relativeDir?: string) {
126
126
  const classObjects = blob.objects.filter(obj => obj instanceof ClassObject) as ClassObject[];
127
127
  const typeAliases = blob.objects.filter(obj => obj instanceof TypeAliasObject) as TypeAliasObject[];
128
+ const constObjects = blob.objects.filter(obj => obj instanceof ConstObject) as ConstObject[];
129
+ const enumObjects = blob.objects.filter(obj => obj instanceof EnumObject) as EnumObject[];
128
130
 
129
131
  const classObjectDictionary = Object.fromEntries(
130
132
  classObjects.map(object => {
@@ -153,8 +155,19 @@ export function generateReactComponent(blob: IDLBlob, packageName?: string, rela
153
155
  return `type ${typeAlias.name} = ${typeAlias.type};`;
154
156
  }).join('\n');
155
157
 
158
+ // Include declare const values as ambient exports for type usage (e.g., unique symbol branding)
159
+ const constDeclarations = constObjects.map(c => `export declare const ${c.name}: ${c.type};`).join('\n');
160
+
161
+ // Include enums
162
+ const enumDeclarations = enumObjects.map(e => {
163
+ const members = e.members.map(m => m.initializer ? `${m.name} = ${m.initializer}` : `${m.name}`).join(', ');
164
+ return `export declare enum ${e.name} { ${members} }`;
165
+ }).join('\n');
166
+
156
167
  const dependencies = [
157
168
  typeAliasDeclarations,
169
+ constDeclarations,
170
+ enumDeclarations,
158
171
  // Include Methods interfaces as dependencies
159
172
  methods.map(object => {
160
173
  const methodDeclarations = object.methods.map(method => {
package/src/vue.ts CHANGED
@@ -2,7 +2,7 @@ import _ from "lodash";
2
2
  import fs from 'fs';
3
3
  import path from 'path';
4
4
  import {ParameterType} from "./analyzer";
5
- import {ClassObject, FunctionArgumentType, FunctionDeclaration} from "./declaration";
5
+ import {ClassObject, FunctionArgumentType, FunctionDeclaration, ConstObject, EnumObject} from "./declaration";
6
6
  import {IDLBlob} from "./IDLBlob";
7
7
  import {getPointerType, isPointerType} from "./utils";
8
8
 
@@ -145,6 +145,17 @@ interface ${object.name} {
145
145
  return result;
146
146
  }
147
147
 
148
+ function toVueTagName(className: string): string {
149
+ if (className.startsWith('WebF')) {
150
+ const withoutPrefix = className.substring(4);
151
+ return 'web-f-' + _.kebabCase(withoutPrefix);
152
+ } else if (className.startsWith('Flutter')) {
153
+ const withoutPrefix = className.substring(7);
154
+ return 'flutter-' + _.kebabCase(withoutPrefix);
155
+ }
156
+ return _.kebabCase(className);
157
+ }
158
+
148
159
  export function generateVueTypings(blobs: IDLBlob[]) {
149
160
  const componentNames = blobs.map(blob => {
150
161
  const classObjects = blob.objects as ClassObject[];
@@ -177,13 +188,47 @@ export function generateVueTypings(blobs: IDLBlob[]) {
177
188
  return component.length > 0;
178
189
  }).join('\n\n');
179
190
 
191
+ // Collect declare consts across blobs and render as exported ambient declarations
192
+ const consts = blobs
193
+ .flatMap(blob => blob.objects)
194
+ .filter(obj => obj instanceof ConstObject) as ConstObject[];
195
+
196
+ // Deduplicate by name keeping first occurrence
197
+ const uniqueConsts = new Map<string, ConstObject>();
198
+ consts.forEach(c => {
199
+ if (!uniqueConsts.has(c.name)) uniqueConsts.set(c.name, c);
200
+ });
201
+
202
+ const constDeclarations = Array.from(uniqueConsts.values())
203
+ .map(c => `export declare const ${c.name}: ${c.type};`)
204
+ .join('\n');
205
+
206
+ // Collect declare enums across blobs
207
+ const enums = blobs
208
+ .flatMap(blob => blob.objects)
209
+ .filter(obj => obj instanceof EnumObject) as EnumObject[];
210
+
211
+ const enumDeclarations = enums.map(e => {
212
+ const members = e.members.map(m => m.initializer ? `${m.name} = ${m.initializer}` : `${m.name}`).join(', ');
213
+ return `export declare enum ${e.name} { ${members} }`;
214
+ }).join('\n');
215
+
216
+ // Build mapping of template tag names to class names for GlobalComponents
217
+ const componentMetas = componentNames.map(className => ({
218
+ className,
219
+ tagName: toVueTagName(className),
220
+ }));
221
+
180
222
  const content = _.template(readTemplate('vue.components.d.ts'), {
181
223
  interpolate: /<%=([\s\S]+?)%>/g,
182
224
  evaluate: /<%([\s\S]+?)%>/g,
183
225
  escape: /<%-([\s\S]+?)%>/g
184
226
  })({
185
227
  componentNames,
228
+ componentMetas,
186
229
  components,
230
+ consts: constDeclarations,
231
+ enums: enumDeclarations,
187
232
  });
188
233
 
189
234
  return content.split('\n').filter(str => {
@@ -1,6 +1,6 @@
1
1
  // AUTO GENERATED FILE, DO NOT EDIT.
2
2
  //
3
- // Generated by <%= command %>
3
+ // Generated by `webf codegen`
4
4
 
5
5
  // ignore_for_file: avoid_unused_constructor_parameters
6
6
  // ignore_for_file: non_constant_identifier_names
@@ -23,6 +23,7 @@
23
23
  "devDependencies": {
24
24
  "@types/react": "^19.1.0",
25
25
  "@types/react-dom": "^19.1.2",
26
+ "picomatch": "^4.0.2",
26
27
  "tsup": "^8.5.0",
27
28
  "typescript": "^5.8.3"
28
29
  }
@@ -19,6 +19,9 @@ type VueEventListeners<T extends EventMap> = {
19
19
  [K in keyof T as `on${Capitalize<string & K>}`]?: (event: T[K]) => any
20
20
  }
21
21
 
22
+ <%= consts %>
23
+ <%= enums %>
24
+
22
25
  type DefineCustomElement<
23
26
  ElementType,
24
27
  Events extends EventMap = {},
@@ -44,10 +47,10 @@ type DefineCustomElement<
44
47
 
45
48
  declare module 'vue' {
46
49
  interface GlobalComponents {
47
- <% componentNames.forEach(name => { %>
48
- '<%= name %>': DefineCustomElement<
49
- <%= name %>Props,
50
- <%= name %>Events
50
+ <% componentMetas.forEach(comp => { %>
51
+ '<%= comp.tagName %>': DefineCustomElement<
52
+ <%= comp.className %>Props,
53
+ <%= comp.className %>Events
51
54
  >
52
55
  <% }) %>
53
56
  }
@@ -0,0 +1,30 @@
1
+ import { generateReactComponent } from '../src/react';
2
+ import { IDLBlob } from '../src/IDLBlob';
3
+ import { ConstObject, EnumObject, EnumMemberObject } from '../src/declaration';
4
+
5
+ describe('React generator - declare const support', () => {
6
+ it('emits export declare const for constants from typings', () => {
7
+ const blob = new IDLBlob('/test/source', '/test/target', 'ConstOnly', 'test', '');
8
+ const constObj = new ConstObject();
9
+ constObj.name = 'WEBF_CUPERTINO_SYMBOL';
10
+ constObj.type = 'unique symbol';
11
+
12
+ blob.objects = [constObj as any];
13
+
14
+ const output = generateReactComponent(blob);
15
+ expect(output).toContain('export declare const WEBF_CUPERTINO_SYMBOL: unique symbol;');
16
+ });
17
+
18
+ it('emits export declare enum for enums from typings', () => {
19
+ const blob = new IDLBlob('/test/source', '/test/target', 'EnumOnly', 'test', '');
20
+ const eo = new EnumObject();
21
+ eo.name = 'CupertinoColors';
22
+ const m1 = new EnumMemberObject(); m1.name = "'red'"; m1.initializer = '0x0f';
23
+ const m2 = new EnumMemberObject(); m2.name = "'bbb'"; m2.initializer = '0x00';
24
+ eo.members = [m1, m2];
25
+ blob.objects = [eo as any];
26
+
27
+ const output = generateReactComponent(blob);
28
+ expect(output).toContain("export declare enum CupertinoColors { 'red' = 0x0f, 'bbb' = 0x00 }");
29
+ });
30
+ });
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 } from '../src/declaration';
3
+ import { ClassObject, ClassObjectKind, PropsDeclaration, ConstObject } from '../src/declaration';
4
4
 
5
5
  describe('Vue Generator', () => {
6
6
  describe('generateVueTypings', () => {
@@ -153,5 +153,37 @@ describe('Vue Generator', () => {
153
153
  expect(result).toContain("'class'?: string;");
154
154
  expect(result).toContain("'style'?: string | Record<string, any>;");
155
155
  });
156
+
157
+ it('should include declare const variables as exported declarations', () => {
158
+ const blob = new IDLBlob('/test/source', '/test/target', 'ConstOnly', 'test', '');
159
+
160
+ const constObj = new ConstObject();
161
+ constObj.name = 'WEBF_UNIQUE';
162
+ constObj.type = 'unique symbol';
163
+
164
+ blob.objects = [constObj as any];
165
+
166
+ const result = generateVueTypings([blob]);
167
+
168
+ expect(result).toContain('export declare const WEBF_UNIQUE: unique symbol;');
169
+ });
170
+
171
+ it('should include declare enum as exported declaration', () => {
172
+ const blob = new IDLBlob('/test/source', '/test/target', 'EnumOnly', 'test', '');
173
+ // Build a minimal faux EnumObject via analyzer by simulating ast is heavy; create a shape
174
+ // We'll reuse analyzer classes by importing EnumObject is cumbersome in test; instead
175
+ // craft an object literal compatible with instanceof check by constructing real class.
176
+ const { EnumObject, EnumMemberObject } = require('../src/declaration');
177
+ const e = new EnumObject();
178
+ e.name = 'CupertinoColors';
179
+ const m1 = new EnumMemberObject(); m1.name = "'red'"; m1.initializer = '0x0f';
180
+ const m2 = new EnumMemberObject(); m2.name = "'bbb'"; m2.initializer = '0x00';
181
+ e.members = [m1, m2];
182
+
183
+ blob.objects = [e as any];
184
+
185
+ const result = generateVueTypings([blob]);
186
+ expect(result).toContain("export declare enum CupertinoColors { 'red' = 0x0f, 'bbb' = 0x00 }");
187
+ });
156
188
  });
157
- });
189
+ });
package/tsconfig.json CHANGED
@@ -20,7 +20,10 @@
20
20
  "esModuleInterop": true,
21
21
  "allowSyntheticDefaultImports": true,
22
22
  "declaration": false,
23
- "outDir": "./dist"
23
+ "outDir": "./dist",
24
+ "typeRoots": [
25
+ "./node_modules/@types"
26
+ ]
24
27
  },
25
28
  "include": [
26
29
  "src/**/*.ts"