@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 +1 -1
- package/dist/generator.js +25 -4
- package/dist/vue.js +17 -2
- package/package.json +1 -1
- package/src/commands.ts +1 -1
- package/src/generator.ts +26 -5
- package/src/vue.ts +18 -2
- package/templates/vue.components.d.ts.tpl +8 -3
- package/templates/vue.package.json.tpl +7 -1
- package/test/commands.test.ts +2 -2
- package/test/generator.test.ts +30 -1
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', '
|
|
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
|
|
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
|
-
|
|
294
|
-
|
|
295
|
-
|
|
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
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', '
|
|
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
|
|
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
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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
|
-
'<%=
|
|
48
|
+
'<%= name %>': DefineCustomElement<
|
|
44
49
|
<%= name %>Props,
|
|
45
50
|
<%= name %>Events
|
|
46
51
|
>
|
package/test/commands.test.ts
CHANGED
|
@@ -284,10 +284,10 @@ describe('Commands', () => {
|
|
|
284
284
|
{ cwd: target, stdio: 'inherit' }
|
|
285
285
|
);
|
|
286
286
|
|
|
287
|
-
// Should install Vue
|
|
287
|
+
// Should install Vue 3 as dev dependency
|
|
288
288
|
expect(mockSpawnSync).toHaveBeenCalledWith(
|
|
289
289
|
expect.stringMatching(/npm(\.cmd)?/),
|
|
290
|
-
['install', '
|
|
290
|
+
['install', 'vue', '-D'],
|
|
291
291
|
{ cwd: target, stdio: 'inherit' }
|
|
292
292
|
);
|
|
293
293
|
});
|
package/test/generator.test.ts
CHANGED
|
@@ -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
|
+
});
|