@openwebf/webf 0.22.13 → 0.23.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.
- package/dist/commands.js +10 -4
- package/dist/dart.js +9 -7
- package/dist/generator.js +79 -20
- package/package.json +3 -2
- package/src/commands.ts +9 -4
- package/src/dart.ts +10 -8
- package/src/generator.ts +75 -20
- package/templates/class.dart.tpl +1 -1
- package/tsconfig.json +4 -1
package/dist/commands.js
CHANGED
|
@@ -233,10 +233,16 @@ function createCommand(target, options) {
|
|
|
233
233
|
fs_1.default.mkdirSync(srcDir, { recursive: true });
|
|
234
234
|
}
|
|
235
235
|
const indexFilePath = path_1.default.join(srcDir, 'index.ts');
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
236
|
+
if (!fs_1.default.existsSync(indexFilePath)) {
|
|
237
|
+
const indexContent = lodash_1.default.template(reactIndexTpl)({
|
|
238
|
+
components: [],
|
|
239
|
+
});
|
|
240
|
+
writeFileIfChanged(indexFilePath, indexContent);
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
// Do not overwrite existing index.ts created by the user
|
|
244
|
+
// Leave merge to the codegen step which appends exports safely
|
|
245
|
+
}
|
|
240
246
|
(0, child_process_1.spawnSync)(NPM, ['install', '--omit=peer'], {
|
|
241
247
|
cwd: target,
|
|
242
248
|
stdio: 'inherit'
|
package/dist/dart.js
CHANGED
|
@@ -80,6 +80,12 @@ ${enumValues};
|
|
|
80
80
|
}`;
|
|
81
81
|
}
|
|
82
82
|
function generateReturnType(type, enumName) {
|
|
83
|
+
// Handle union types first (e.g., 'left' | 'center' | 'right')
|
|
84
|
+
// so we don't incorrectly treat string literal unions as pointer types.
|
|
85
|
+
if (Array.isArray(type.value)) {
|
|
86
|
+
// If we have an enum name, use it; otherwise fall back to String
|
|
87
|
+
return enumName || 'String';
|
|
88
|
+
}
|
|
83
89
|
if ((0, utils_1.isPointerType)(type)) {
|
|
84
90
|
const pointerType = (0, utils_1.getPointerType)(type);
|
|
85
91
|
return pointerType;
|
|
@@ -87,11 +93,6 @@ function generateReturnType(type, enumName) {
|
|
|
87
93
|
if (type.isArray && typeof type.value === 'object' && !Array.isArray(type.value)) {
|
|
88
94
|
return `${(0, utils_1.getPointerType)(type.value)}[]`;
|
|
89
95
|
}
|
|
90
|
-
// Handle union types (e.g., 'left' | 'center' | 'right')
|
|
91
|
-
if (Array.isArray(type.value)) {
|
|
92
|
-
// If we have an enum name, use it; otherwise fall back to String
|
|
93
|
-
return enumName || 'String';
|
|
94
|
-
}
|
|
95
96
|
// Handle when type.value is a ParameterType object (nested type)
|
|
96
97
|
if (typeof type.value === 'object' && !Array.isArray(type.value) && type.value !== null) {
|
|
97
98
|
// This might be a nested ParameterType, recurse
|
|
@@ -250,7 +251,7 @@ interface ${object.name} {
|
|
|
250
251
|
}
|
|
251
252
|
// Generate enums for union types
|
|
252
253
|
const enums = [];
|
|
253
|
-
const enumMap = new Map(); // prop name -> enum name
|
|
254
|
+
const enumMap = new Map(); // camelCase prop name -> enum name
|
|
254
255
|
if (componentProperties) {
|
|
255
256
|
for (const prop of componentProperties.props) {
|
|
256
257
|
if (isStringUnionType(prop.type)) {
|
|
@@ -261,7 +262,8 @@ interface ${object.name} {
|
|
|
261
262
|
name: enumName,
|
|
262
263
|
definition: generateDartEnum(enumName, values)
|
|
263
264
|
});
|
|
264
|
-
|
|
265
|
+
// Store by camelCase prop name to match template usage
|
|
266
|
+
enumMap.set(lodash_1.default.camelCase(prop.name), enumName);
|
|
265
267
|
}
|
|
266
268
|
}
|
|
267
269
|
}
|
package/dist/generator.js
CHANGED
|
@@ -29,6 +29,7 @@ const dart_1 = require("./dart");
|
|
|
29
29
|
const react_1 = require("./react");
|
|
30
30
|
const vue_1 = require("./vue");
|
|
31
31
|
const logger_1 = require("./logger");
|
|
32
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
32
33
|
// Cache for file content to avoid redundant reads
|
|
33
34
|
const fileContentCache = new Map();
|
|
34
35
|
// Cache for generated content to detect changes
|
|
@@ -287,32 +288,90 @@ function reactGen(_a) {
|
|
|
287
288
|
(0, logger_1.error)(`Error generating React component for ${blob.filename}`, err);
|
|
288
289
|
}
|
|
289
290
|
}));
|
|
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
|
+
// Generate/merge index file
|
|
294
292
|
const indexFilePath = path_1.default.join(normalizedTarget, 'src', 'index.ts');
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
293
|
+
// Build desired export map: moduleSpecifier -> Set of names
|
|
294
|
+
const desiredExports = new Map();
|
|
295
|
+
const components = blobs.flatMap(blob => {
|
|
296
|
+
const classObjects = blob.objects.filter(obj => obj instanceof declaration_1.ClassObject);
|
|
297
|
+
const properties = classObjects.filter(object => object.name.endsWith('Properties'));
|
|
298
|
+
const events = classObjects.filter(object => object.name.endsWith('Events'));
|
|
299
|
+
const componentMap = new Map();
|
|
300
|
+
properties.forEach(prop => componentMap.set(prop.name.replace(/Properties$/, ''), true));
|
|
301
|
+
events.forEach(evt => componentMap.set(evt.name.replace(/Events$/, ''), true));
|
|
302
|
+
return Array.from(componentMap.keys()).map(className => ({
|
|
303
|
+
className,
|
|
304
|
+
fileName: blob.filename,
|
|
305
|
+
relativeDir: blob.relativeDir,
|
|
306
|
+
}));
|
|
307
|
+
});
|
|
308
|
+
// Deduplicate by className
|
|
309
|
+
const unique = new Map();
|
|
310
|
+
for (const c of components) {
|
|
311
|
+
if (!unique.has(c.className))
|
|
312
|
+
unique.set(c.className, c);
|
|
313
|
+
}
|
|
314
|
+
for (const { className, fileName, relativeDir } of unique.values()) {
|
|
315
|
+
const spec = `./${relativeDir ? `${relativeDir}/` : ''}${fileName}`;
|
|
316
|
+
if (!desiredExports.has(spec))
|
|
317
|
+
desiredExports.set(spec, new Set());
|
|
318
|
+
const set = desiredExports.get(spec);
|
|
319
|
+
set.add(className);
|
|
320
|
+
set.add(`${className}Element`);
|
|
321
|
+
}
|
|
322
|
+
if (!fs_1.default.existsSync(indexFilePath)) {
|
|
323
|
+
// No index.ts -> generate fresh file from template
|
|
324
|
+
const newExports = (0, react_1.generateReactIndex)(blobs);
|
|
325
|
+
if (writeFileIfChanged(indexFilePath, newExports)) {
|
|
326
|
+
filesChanged++;
|
|
327
|
+
(0, logger_1.debug)(`Generated: index.ts`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
// Merge into existing index.ts without removing user code
|
|
298
332
|
try {
|
|
299
333
|
const existing = fs_1.default.readFileSync(indexFilePath, 'utf-8');
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
(
|
|
334
|
+
const sourceFile = typescript_1.default.createSourceFile(indexFilePath, existing, typescript_1.default.ScriptTarget.ES2020, true, typescript_1.default.ScriptKind.TS);
|
|
335
|
+
// Track which names already exported per module
|
|
336
|
+
for (const stmt of sourceFile.statements) {
|
|
337
|
+
if (typescript_1.default.isExportDeclaration(stmt) && stmt.exportClause && typescript_1.default.isNamedExports(stmt.exportClause)) {
|
|
338
|
+
const moduleSpecifier = stmt.moduleSpecifier && typescript_1.default.isStringLiteral(stmt.moduleSpecifier)
|
|
339
|
+
? stmt.moduleSpecifier.text
|
|
340
|
+
: undefined;
|
|
341
|
+
if (!moduleSpecifier)
|
|
342
|
+
continue;
|
|
343
|
+
const desired = desiredExports.get(moduleSpecifier);
|
|
344
|
+
if (!desired)
|
|
345
|
+
continue;
|
|
346
|
+
for (const el of stmt.exportClause.elements) {
|
|
347
|
+
const name = el.name.getText(sourceFile);
|
|
348
|
+
if (desired.has(name))
|
|
349
|
+
desired.delete(name);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
// Prepare new export lines for any remaining names
|
|
354
|
+
const lines = [];
|
|
355
|
+
for (const [spec, names] of desiredExports) {
|
|
356
|
+
const missing = Array.from(names);
|
|
357
|
+
if (missing.length === 0)
|
|
358
|
+
continue;
|
|
359
|
+
const specEscaped = spec.replace(/\\/g, '/');
|
|
360
|
+
lines.push(`export { ${missing.join(', ')} } from "${specEscaped}";`);
|
|
361
|
+
}
|
|
362
|
+
if (lines.length > 0) {
|
|
363
|
+
const appended = (existing.endsWith('\n') ? '' : '\n') + lines.join('\n') + '\n';
|
|
364
|
+
if (writeFileIfChanged(indexFilePath, existing + appended)) {
|
|
365
|
+
filesChanged++;
|
|
366
|
+
(0, logger_1.debug)(`Merged exports into existing index.ts`);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
(0, logger_1.debug)(`index.ts is up to date; no merge needed.`);
|
|
304
371
|
}
|
|
305
372
|
}
|
|
306
373
|
catch (err) {
|
|
307
|
-
|
|
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`);
|
|
374
|
+
(0, logger_1.warn)(`Failed to merge into existing index.ts. Skipping modifications: ${indexFilePath}`);
|
|
316
375
|
}
|
|
317
376
|
}
|
|
318
377
|
(0, logger_1.timeEnd)('reactGen');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openwebf/webf",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Command line tools for WebF",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"@types/inquirer": "^8.2.11",
|
|
31
31
|
"@types/jest": "^29.5.12",
|
|
32
32
|
"@types/lodash": "^4.17.17",
|
|
33
|
+
"@types/minimatch": "^5.1.2",
|
|
33
34
|
"@types/node": "^22.15.21",
|
|
34
35
|
"@types/yaml": "^1.9.6",
|
|
35
36
|
"jest": "^29.7.0",
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
"@microsoft/tsdoc": "^0.15.1",
|
|
40
41
|
"@microsoft/tsdoc-config": "^0.17.1",
|
|
41
42
|
"commander": "^14.0.0",
|
|
42
|
-
"glob": "^10.
|
|
43
|
+
"glob": "^10.4.5",
|
|
43
44
|
"inquirer": "^8.2.6",
|
|
44
45
|
"lodash": "^4.17.21",
|
|
45
46
|
"typescript": "^5.8.3",
|
package/src/commands.ts
CHANGED
|
@@ -300,10 +300,15 @@ function createCommand(target: string, options: { framework: string; packageName
|
|
|
300
300
|
}
|
|
301
301
|
|
|
302
302
|
const indexFilePath = path.join(srcDir, 'index.ts');
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
303
|
+
if (!fs.existsSync(indexFilePath)) {
|
|
304
|
+
const indexContent = _.template(reactIndexTpl)({
|
|
305
|
+
components: [],
|
|
306
|
+
});
|
|
307
|
+
writeFileIfChanged(indexFilePath, indexContent);
|
|
308
|
+
} else {
|
|
309
|
+
// Do not overwrite existing index.ts created by the user
|
|
310
|
+
// Leave merge to the codegen step which appends exports safely
|
|
311
|
+
}
|
|
307
312
|
|
|
308
313
|
spawnSync(NPM, ['install', '--omit=peer'], {
|
|
309
314
|
cwd: target,
|
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
|
-
|
|
294
|
+
// Store by camelCase prop name to match template usage
|
|
295
|
+
enumMap.set(_.camelCase(prop.name), enumName);
|
|
294
296
|
}
|
|
295
297
|
}
|
|
296
298
|
}
|
package/src/generator.ts
CHANGED
|
@@ -11,6 +11,7 @@ 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>();
|
|
@@ -321,34 +322,88 @@ export async function reactGen({ source, target, exclude, packageName }: Generat
|
|
|
321
322
|
}
|
|
322
323
|
});
|
|
323
324
|
|
|
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
|
+
// Generate/merge index file
|
|
328
326
|
const indexFilePath = path.join(normalizedTarget, 'src', 'index.ts');
|
|
329
|
-
const newExports = generateReactIndex(blobs);
|
|
330
327
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
}
|
|
328
|
+
// Build desired export map: moduleSpecifier -> Set of names
|
|
329
|
+
const desiredExports = new Map<string, Set<string>>();
|
|
330
|
+
const components = blobs.flatMap(blob => {
|
|
331
|
+
const classObjects = blob.objects.filter(obj => obj instanceof ClassObject) as ClassObject[];
|
|
332
|
+
const properties = classObjects.filter(object => object.name.endsWith('Properties'));
|
|
333
|
+
const events = classObjects.filter(object => object.name.endsWith('Events'));
|
|
334
|
+
const componentMap = new Map<string, boolean>();
|
|
335
|
+
properties.forEach(prop => componentMap.set(prop.name.replace(/Properties$/, ''), true));
|
|
336
|
+
events.forEach(evt => componentMap.set(evt.name.replace(/Events$/, ''), true));
|
|
337
|
+
return Array.from(componentMap.keys()).map(className => ({
|
|
338
|
+
className,
|
|
339
|
+
fileName: blob.filename,
|
|
340
|
+
relativeDir: blob.relativeDir,
|
|
341
|
+
}));
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// Deduplicate by className
|
|
345
|
+
const unique = new Map<string, { className: string; fileName: string; relativeDir: string }>();
|
|
346
|
+
for (const c of components) {
|
|
347
|
+
if (!unique.has(c.className)) unique.set(c.className, c);
|
|
348
|
+
}
|
|
349
|
+
for (const { className, fileName, relativeDir } of unique.values()) {
|
|
350
|
+
const spec = `./${relativeDir ? `${relativeDir}/` : ''}${fileName}`;
|
|
351
|
+
if (!desiredExports.has(spec)) desiredExports.set(spec, new Set());
|
|
352
|
+
const set = desiredExports.get(spec)!;
|
|
353
|
+
set.add(className);
|
|
354
|
+
set.add(`${className}Element`);
|
|
345
355
|
}
|
|
346
356
|
|
|
347
|
-
if (
|
|
357
|
+
if (!fs.existsSync(indexFilePath)) {
|
|
358
|
+
// No index.ts -> generate fresh file from template
|
|
359
|
+
const newExports = generateReactIndex(blobs);
|
|
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');
|
package/templates/class.dart.tpl
CHANGED
package/tsconfig.json
CHANGED