@openwebf/webf 0.22.11 → 0.22.13

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/dist/commands.js CHANGED
@@ -260,7 +260,7 @@ function createCommand(target, options) {
260
260
  cwd: target,
261
261
  stdio: 'inherit'
262
262
  });
263
- (0, child_process_1.spawnSync)(NPM, ['install', '@types/vue', '-D'], {
263
+ (0, child_process_1.spawnSync)(NPM, ['install', 'vue', '-D'], {
264
264
  cwd: target,
265
265
  stdio: 'inherit'
266
266
  });
package/dist/generator.js CHANGED
@@ -287,12 +287,33 @@ function reactGen(_a) {
287
287
  (0, logger_1.error)(`Error generating React component for ${blob.filename}`, err);
288
288
  }
289
289
  }));
290
- // Generate index file (always regenerate to avoid duplicates)
290
+ // Generate index file
291
+ // Avoid overriding a user-managed index.ts. Only write when:
292
+ // - index.ts does not exist, or
293
+ // - it contains the auto-generated marker from our template
291
294
  const indexFilePath = path_1.default.join(normalizedTarget, 'src', 'index.ts');
292
295
  const newExports = (0, react_1.generateReactIndex)(blobs);
293
- if (writeFileIfChanged(indexFilePath, newExports)) {
294
- filesChanged++;
295
- (0, logger_1.debug)(`Generated: index.ts`);
296
+ let shouldWriteIndex = true;
297
+ if (fs_1.default.existsSync(indexFilePath)) {
298
+ try {
299
+ const existing = fs_1.default.readFileSync(indexFilePath, 'utf-8');
300
+ const isAutoGenerated = existing.includes('Generated by TSDL');
301
+ if (!isAutoGenerated) {
302
+ shouldWriteIndex = false;
303
+ (0, logger_1.warn)(`Found existing user-managed index.ts at ${indexFilePath}; skipping overwrite.`);
304
+ }
305
+ }
306
+ catch (err) {
307
+ // If we cannot read the file for some reason, be conservative and skip overwriting
308
+ shouldWriteIndex = false;
309
+ (0, logger_1.warn)(`Unable to read existing index.ts; skipping overwrite: ${indexFilePath}`);
310
+ }
311
+ }
312
+ if (shouldWriteIndex) {
313
+ if (writeFileIfChanged(indexFilePath, newExports)) {
314
+ filesChanged++;
315
+ (0, logger_1.debug)(`Generated: index.ts`);
316
+ }
296
317
  }
297
318
  (0, logger_1.timeEnd)('reactGen');
298
319
  (0, logger_1.success)(`React code generation completed. ${filesChanged} files changed.`);
package/dist/vue.js CHANGED
@@ -51,6 +51,10 @@ function generateEventHandlerType(type) {
51
51
  if (pointerType === 'CustomEvent') {
52
52
  return 'CustomEvent';
53
53
  }
54
+ // Handle generic types like CustomEvent<T>
55
+ if (pointerType.startsWith('CustomEvent<')) {
56
+ return pointerType;
57
+ }
54
58
  throw new Error('Unknown event type: ' + pointerType);
55
59
  }
56
60
  function generateMethodDeclaration(method) {
@@ -65,6 +69,10 @@ function generateMethodDeclaration(method) {
65
69
  }
66
70
  function generateVueComponent(blob) {
67
71
  const classObjects = blob.objects;
72
+ // Skip if no class objects
73
+ if (!classObjects || classObjects.length === 0) {
74
+ return '';
75
+ }
68
76
  const classObjectDictionary = Object.fromEntries(classObjects.map(object => {
69
77
  return [object.name, object];
70
78
  }));
@@ -79,6 +87,9 @@ function generateVueComponent(blob) {
79
87
  && !object.name.endsWith('Events');
80
88
  });
81
89
  const dependencies = others.map(object => {
90
+ if (!object || !object.props) {
91
+ return '';
92
+ }
82
93
  const props = object.props.map(prop => {
83
94
  if (prop.optional) {
84
95
  return `${prop.name}?: ${generateReturnType(prop.type)};`;
@@ -89,7 +100,7 @@ function generateVueComponent(blob) {
89
100
  interface ${object.name} {
90
101
  ${props}
91
102
  }`;
92
- }).join('\n\n');
103
+ }).filter(dep => dep.trim() !== '').join('\n\n');
93
104
  const componentProperties = properties.length > 0 ? properties[0] : undefined;
94
105
  const componentEvents = events.length > 0 ? events[0] : undefined;
95
106
  const className = (() => {
@@ -149,7 +160,11 @@ function generateVueTypings(blobs) {
149
160
  }).filter(component => {
150
161
  return component.length > 0;
151
162
  }).join('\n\n');
152
- const content = lodash_1.default.template(readTemplate('vue.components.d.ts'))({
163
+ const content = lodash_1.default.template(readTemplate('vue.components.d.ts'), {
164
+ interpolate: /<%=([\s\S]+?)%>/g,
165
+ evaluate: /<%([\s\S]+?)%>/g,
166
+ escape: /<%-([\s\S]+?)%>/g
167
+ })({
153
168
  componentNames,
154
169
  components,
155
170
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwebf/webf",
3
- "version": "0.22.11",
3
+ "version": "0.22.13",
4
4
  "description": "Command line tools for WebF",
5
5
  "main": "index.js",
6
6
  "bin": {
package/src/commands.ts CHANGED
@@ -332,7 +332,7 @@ function createCommand(target: string, options: { framework: string; packageName
332
332
  stdio: 'inherit'
333
333
  });
334
334
 
335
- spawnSync(NPM, ['install', '@types/vue', '-D'], {
335
+ spawnSync(NPM, ['install', 'vue', '-D'], {
336
336
  cwd: target,
337
337
  stdio: 'inherit'
338
338
  });
package/src/generator.ts CHANGED
@@ -321,13 +321,34 @@ export async function reactGen({ source, target, exclude, packageName }: Generat
321
321
  }
322
322
  });
323
323
 
324
- // Generate index file (always regenerate to avoid duplicates)
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
325
328
  const indexFilePath = path.join(normalizedTarget, 'src', 'index.ts');
326
329
  const newExports = generateReactIndex(blobs);
327
-
328
- if (writeFileIfChanged(indexFilePath, newExports)) {
329
- filesChanged++;
330
- debug(`Generated: index.ts`);
330
+
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
+ }
345
+ }
346
+
347
+ if (shouldWriteIndex) {
348
+ if (writeFileIfChanged(indexFilePath, newExports)) {
349
+ filesChanged++;
350
+ debug(`Generated: index.ts`);
351
+ }
331
352
  }
332
353
 
333
354
  timeEnd('reactGen');
package/src/vue.ts CHANGED
@@ -50,6 +50,10 @@ function generateEventHandlerType(type: ParameterType) {
50
50
  if (pointerType === 'CustomEvent') {
51
51
  return 'CustomEvent';
52
52
  }
53
+ // Handle generic types like CustomEvent<T>
54
+ if (pointerType.startsWith('CustomEvent<')) {
55
+ return pointerType;
56
+ }
53
57
  throw new Error('Unknown event type: ' + pointerType);
54
58
  }
55
59
 
@@ -66,6 +70,11 @@ function generateMethodDeclaration(method: FunctionDeclaration) {
66
70
 
67
71
  function generateVueComponent(blob: IDLBlob) {
68
72
  const classObjects = blob.objects as ClassObject[];
73
+
74
+ // Skip if no class objects
75
+ if (!classObjects || classObjects.length === 0) {
76
+ return '';
77
+ }
69
78
  const classObjectDictionary = Object.fromEntries(
70
79
  classObjects.map(object => {
71
80
  return [object.name, object];
@@ -85,6 +94,9 @@ function generateVueComponent(blob: IDLBlob) {
85
94
  });
86
95
 
87
96
  const dependencies = others.map(object => {
97
+ if (!object || !object.props) {
98
+ return '';
99
+ }
88
100
  const props = object.props.map(prop => {
89
101
  if (prop.optional) {
90
102
  return `${prop.name}?: ${generateReturnType(prop.type)};`;
@@ -96,7 +108,7 @@ function generateVueComponent(blob: IDLBlob) {
96
108
  interface ${object.name} {
97
109
  ${props}
98
110
  }`;
99
- }).join('\n\n');
111
+ }).filter(dep => dep.trim() !== '').join('\n\n');
100
112
 
101
113
  const componentProperties = properties.length > 0 ? properties[0] : undefined;
102
114
  const componentEvents = events.length > 0 ? events[0] : undefined;
@@ -165,7 +177,11 @@ export function generateVueTypings(blobs: IDLBlob[]) {
165
177
  return component.length > 0;
166
178
  }).join('\n\n');
167
179
 
168
- const content = _.template(readTemplate('vue.components.d.ts'))({
180
+ const content = _.template(readTemplate('vue.components.d.ts'), {
181
+ interpolate: /<%=([\s\S]+?)%>/g,
182
+ evaluate: /<%([\s\S]+?)%>/g,
183
+ escape: /<%-([\s\S]+?)%>/g
184
+ })({
169
185
  componentNames,
170
186
  components,
171
187
  });
@@ -14,18 +14,23 @@ type VueEmit<T extends EventMap> = EmitFn<{
14
14
  [K in keyof T]: (event: T[K]) => void
15
15
  }>
16
16
 
17
+ // Vue 3 event listener properties for template usage
18
+ type VueEventListeners<T extends EventMap> = {
19
+ [K in keyof T as `on${Capitalize<string & K>}`]?: (event: T[K]) => any
20
+ }
21
+
17
22
  type DefineCustomElement<
18
23
  ElementType,
19
24
  Events extends EventMap = {},
20
25
  SelectedAttributes extends keyof ElementType = keyof ElementType
21
- > = new () => ElementType & {
26
+ > = new () => ElementType & VueEventListeners<Events> & {
22
27
  // Use $props to define the properties exposed to template type checking. Vue
23
28
  // specifically reads prop definitions from the `$props` type. Note that we
24
29
  // combine the element's props with the global HTML props and Vue's special
25
30
  // props.
26
31
  /** @deprecated Do not use the $props property on a Custom Element ref,
27
32
  this is for template prop types only. */
28
- $props: Partial<Pick<ElementType, SelectedAttributes>> & PublicProps
33
+ $props: Partial<Pick<ElementType, SelectedAttributes>> & PublicProps & VueEventListeners<Events>
29
34
 
30
35
  // Use $emit to specifically define event types. Vue specifically reads event
31
36
  // types from the `$emit` type. Note that `$emit` expects a particular format
@@ -40,7 +45,7 @@ type DefineCustomElement<
40
45
  declare module 'vue' {
41
46
  interface GlobalComponents {
42
47
  <% componentNames.forEach(name => { %>
43
- '<%= _.kebabCase(name) %>': DefineCustomElement<
48
+ '<%= name %>': DefineCustomElement<
44
49
  <%= name %>Props,
45
50
  <%= name %>Events
46
51
  >
@@ -7,5 +7,11 @@
7
7
  "files": ["index.d.ts"],
8
8
  "keywords": [],
9
9
  "author": "",
10
- "license": "ISC"
10
+ "license": "ISC",
11
+ "peerDependencies": {
12
+ "vue": "^3.0.0"
13
+ },
14
+ "devDependencies": {
15
+ "vue": "^3.0.0"
16
+ }
11
17
  }
@@ -284,10 +284,10 @@ describe('Commands', () => {
284
284
  { cwd: target, stdio: 'inherit' }
285
285
  );
286
286
 
287
- // Should install Vue types as dev dependency
287
+ // Should install Vue 3 as dev dependency
288
288
  expect(mockSpawnSync).toHaveBeenCalledWith(
289
289
  expect.stringMatching(/npm(\.cmd)?/),
290
- ['install', '@types/vue', '-D'],
290
+ ['install', 'vue', '-D'],
291
291
  { cwd: target, stdio: 'inherit' }
292
292
  );
293
293
  });
@@ -336,6 +336,12 @@ describe('Generator', () => {
336
336
  'export { Test, TestElement } from "./lib/src/html/test";\n' +
337
337
  'export { Component, ComponentElement } from "./lib/src/html/component";'
338
338
  );
339
+ // Ensure index.ts does not exist so it will be generated
340
+ mockFs.existsSync.mockImplementation((p: any) => {
341
+ const s = p.toString();
342
+ if (s.includes(path.join('/test/target', 'src', 'index.ts'))) return false;
343
+ return true;
344
+ });
339
345
 
340
346
  await reactGen({
341
347
  source: '/test/source',
@@ -357,6 +363,29 @@ describe('Generator', () => {
357
363
  expect(indexCall![1]).toContain('export { Test, TestElement }');
358
364
  expect(indexCall![1]).toContain('export { Component, ComponentElement }');
359
365
  });
366
+
367
+ it('should not overwrite user-managed index.ts', async () => {
368
+ // Existing index.ts that does not contain auto-generated marker
369
+ mockFs.existsSync.mockImplementation((p: any) => {
370
+ const s = p.toString();
371
+ if (s.includes(path.join('/test/target', 'src', 'index.ts'))) return true;
372
+ return true;
373
+ });
374
+ mockFs.readFileSync.mockImplementation((p: any) => {
375
+ const s = p.toString();
376
+ if (s.includes(path.join('/test/target', 'src', 'index.ts'))) return '// custom index file';
377
+ return 'test content';
378
+ });
379
+
380
+ await reactGen({
381
+ source: '/test/source',
382
+ target: '/test/target',
383
+ command: 'test command'
384
+ });
385
+
386
+ const indexWrite = mockFs.writeFileSync.mock.calls.find(call => call[0].toString().includes('index.ts'));
387
+ expect(indexWrite).toBeUndefined();
388
+ });
360
389
  });
361
390
 
362
391
  describe('vueGen', () => {
@@ -478,4 +507,4 @@ describe('Generator', () => {
478
507
  expect(mockAnalyzer.clearCaches).toHaveBeenCalled();
479
508
  });
480
509
  });
481
- });
510
+ });