@gandalan/weblibs 1.5.23 → 1.5.24
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/index.d.ts +2857 -256
- package/package.json +1 -1
- package/scripts/generate-root-dto-typedefs.mjs +290 -54
package/package.json
CHANGED
|
@@ -356,6 +356,148 @@ function normalizeJSDocBlock(block) {
|
|
|
356
356
|
.trim();
|
|
357
357
|
}
|
|
358
358
|
|
|
359
|
+
function parseBracketedType(rest) {
|
|
360
|
+
if (!rest.startsWith("{")) {
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
let braceDepth = 0;
|
|
365
|
+
let closingBraceIndex = -1;
|
|
366
|
+
|
|
367
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
368
|
+
const character = rest[index];
|
|
369
|
+
|
|
370
|
+
if (character === "{") {
|
|
371
|
+
braceDepth += 1;
|
|
372
|
+
} else if (character === "}") {
|
|
373
|
+
braceDepth -= 1;
|
|
374
|
+
|
|
375
|
+
if (braceDepth === 0) {
|
|
376
|
+
closingBraceIndex = index;
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (closingBraceIndex === -1) {
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return {
|
|
387
|
+
typeExpression: rest.slice(1, closingBraceIndex).trim(),
|
|
388
|
+
remainder: rest.slice(closingBraceIndex + 1).trim()
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function parseTagWithType(line, tagName) {
|
|
393
|
+
const marker = `@${tagName}`;
|
|
394
|
+
const tagIndex = line.indexOf(marker);
|
|
395
|
+
|
|
396
|
+
if (tagIndex === -1) {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const parsed = parseBracketedType(line.slice(tagIndex + marker.length).trimStart());
|
|
401
|
+
|
|
402
|
+
if (!parsed) {
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return parsed;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function parseNameToken(remainder) {
|
|
410
|
+
const nameMatch = remainder.match(/^(\[[^\]]+\]|[^\s-]+)/);
|
|
411
|
+
|
|
412
|
+
if (!nameMatch) {
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const rawName = nameMatch[1];
|
|
417
|
+
const optional = rawName.startsWith("[") && rawName.endsWith("]");
|
|
418
|
+
const normalizedName = optional ? rawName.slice(1, -1).replace(/=.*/, "") : rawName;
|
|
419
|
+
const description = remainder.slice(nameMatch[0].length).trim().replace(/^-/u, "").trim();
|
|
420
|
+
|
|
421
|
+
return {
|
|
422
|
+
name: normalizedName,
|
|
423
|
+
optional,
|
|
424
|
+
description
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function extractPropertyEntries(normalizedBlock) {
|
|
429
|
+
const properties = [];
|
|
430
|
+
|
|
431
|
+
for (const line of normalizedBlock.split("\n")) {
|
|
432
|
+
if (!line.includes("@property")) {
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const parsedTag = parseTagWithType(line, "property");
|
|
437
|
+
|
|
438
|
+
if (!parsedTag) {
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const parsedName = parseNameToken(parsedTag.remainder);
|
|
443
|
+
|
|
444
|
+
if (!parsedName) {
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
properties.push({
|
|
449
|
+
name: parsedName.name,
|
|
450
|
+
optional: parsedName.optional,
|
|
451
|
+
description: parsedName.description,
|
|
452
|
+
typeExpression: parsedTag.typeExpression
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return properties;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function extractParamEntries(normalizedBlock) {
|
|
460
|
+
const params = [];
|
|
461
|
+
|
|
462
|
+
for (const line of normalizedBlock.split("\n")) {
|
|
463
|
+
if (!line.includes("@param")) {
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
const parsedTag = parseTagWithType(line, "param");
|
|
468
|
+
|
|
469
|
+
if (!parsedTag) {
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const parsedName = parseNameToken(parsedTag.remainder);
|
|
474
|
+
|
|
475
|
+
if (!parsedName) {
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
params.push({
|
|
480
|
+
name: parsedName.name,
|
|
481
|
+
optional: parsedName.optional,
|
|
482
|
+
typeExpression: parsedTag.typeExpression
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
return params;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function extractReturnsEntry(normalizedBlock) {
|
|
490
|
+
for (const line of normalizedBlock.split("\n")) {
|
|
491
|
+
if (!line.includes("@returns") && !line.includes("@return")) {
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
return parseTagWithType(line, line.includes("@returns") ? "returns" : "return");
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
return null;
|
|
499
|
+
}
|
|
500
|
+
|
|
359
501
|
function extractTypedefEntry(normalizedBlock) {
|
|
360
502
|
const typedefIndex = normalizedBlock.indexOf("@typedef");
|
|
361
503
|
|
|
@@ -399,10 +541,13 @@ function extractTypedefEntry(normalizedBlock) {
|
|
|
399
541
|
return null;
|
|
400
542
|
}
|
|
401
543
|
|
|
544
|
+
const properties = extractPropertyEntries(normalizedBlock);
|
|
545
|
+
|
|
402
546
|
return {
|
|
403
|
-
kind: "typedef",
|
|
547
|
+
kind: typeExpression === "Object" && properties.length > 0 ? "object" : "typedef",
|
|
404
548
|
name: nameMatch[1],
|
|
405
|
-
typeExpression
|
|
549
|
+
typeExpression,
|
|
550
|
+
properties
|
|
406
551
|
};
|
|
407
552
|
}
|
|
408
553
|
|
|
@@ -416,7 +561,9 @@ function extractCallbackEntry(normalizedBlock) {
|
|
|
416
561
|
return {
|
|
417
562
|
kind: "callback",
|
|
418
563
|
name: callbackMatch[1],
|
|
419
|
-
typeExpression: null
|
|
564
|
+
typeExpression: null,
|
|
565
|
+
params: extractParamEntries(normalizedBlock),
|
|
566
|
+
returns: extractReturnsEntry(normalizedBlock)?.typeExpression ?? "void"
|
|
420
567
|
};
|
|
421
568
|
}
|
|
422
569
|
|
|
@@ -448,6 +595,135 @@ function extractPublicTypeEntries(source, filePath) {
|
|
|
448
595
|
return entries;
|
|
449
596
|
}
|
|
450
597
|
|
|
598
|
+
function splitTopLevel(text, delimiterCharacter) {
|
|
599
|
+
const parts = [];
|
|
600
|
+
let current = "";
|
|
601
|
+
let angleDepth = 0;
|
|
602
|
+
let braceDepth = 0;
|
|
603
|
+
let bracketDepth = 0;
|
|
604
|
+
let parenDepth = 0;
|
|
605
|
+
|
|
606
|
+
for (const character of text) {
|
|
607
|
+
if (character === "<") {
|
|
608
|
+
angleDepth += 1;
|
|
609
|
+
} else if (character === ">") {
|
|
610
|
+
angleDepth = Math.max(0, angleDepth - 1);
|
|
611
|
+
} else if (character === "{") {
|
|
612
|
+
braceDepth += 1;
|
|
613
|
+
} else if (character === "}") {
|
|
614
|
+
braceDepth = Math.max(0, braceDepth - 1);
|
|
615
|
+
} else if (character === "[") {
|
|
616
|
+
bracketDepth += 1;
|
|
617
|
+
} else if (character === "]") {
|
|
618
|
+
bracketDepth = Math.max(0, bracketDepth - 1);
|
|
619
|
+
} else if (character === "(") {
|
|
620
|
+
parenDepth += 1;
|
|
621
|
+
} else if (character === ")") {
|
|
622
|
+
parenDepth = Math.max(0, parenDepth - 1);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
if (character === delimiterCharacter && angleDepth === 0 && braceDepth === 0 && bracketDepth === 0 && parenDepth === 0) {
|
|
626
|
+
parts.push(current.trim());
|
|
627
|
+
current = "";
|
|
628
|
+
continue;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
current += character;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
if (current.trim()) {
|
|
635
|
+
parts.push(current.trim());
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
return parts;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function replaceObjectGenerics(typeExpression, transformTypeExpression) {
|
|
642
|
+
let result = "";
|
|
643
|
+
|
|
644
|
+
for (let index = 0; index < typeExpression.length; index += 1) {
|
|
645
|
+
if (!typeExpression.startsWith("Object<", index)) {
|
|
646
|
+
result += typeExpression[index];
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
let angleDepth = 0;
|
|
651
|
+
let closingIndex = -1;
|
|
652
|
+
|
|
653
|
+
for (let cursor = index + "Object".length; cursor < typeExpression.length; cursor += 1) {
|
|
654
|
+
const character = typeExpression[cursor];
|
|
655
|
+
|
|
656
|
+
if (character === "<") {
|
|
657
|
+
angleDepth += 1;
|
|
658
|
+
} else if (character === ">") {
|
|
659
|
+
angleDepth -= 1;
|
|
660
|
+
|
|
661
|
+
if (angleDepth === 0) {
|
|
662
|
+
closingIndex = cursor;
|
|
663
|
+
break;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
if (closingIndex === -1) {
|
|
669
|
+
result += typeExpression[index];
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
const genericContent = typeExpression.slice(index + "Object<".length, closingIndex);
|
|
674
|
+
const genericParts = splitTopLevel(genericContent, ",");
|
|
675
|
+
|
|
676
|
+
if (genericParts.length === 2) {
|
|
677
|
+
result += `Record<${transformTypeExpression(genericParts[0])}, ${transformTypeExpression(genericParts[1])}>`;
|
|
678
|
+
} else {
|
|
679
|
+
result += "object";
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
index = closingIndex;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
return result;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
function transformTypeExpressionForDts(typeExpression, availableTypeNames) {
|
|
689
|
+
const normalizedExpression = typeExpression.trim();
|
|
690
|
+
|
|
691
|
+
if (normalizedExpression === "*") {
|
|
692
|
+
return "any";
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
let transformedExpression = normalizedExpression.replace(
|
|
696
|
+
/import\((?:"|')[^"']+(?:"|')\)\.([A-Za-z0-9_$]+)(\[[^\]]+\])?/g,
|
|
697
|
+
(fullMatch, typeName, suffix = "") => availableTypeNames.has(typeName) ? `${typeName}${suffix}` : fullMatch
|
|
698
|
+
);
|
|
699
|
+
|
|
700
|
+
transformedExpression = replaceObjectGenerics(transformedExpression, (innerExpression) => transformTypeExpressionForDts(innerExpression, availableTypeNames));
|
|
701
|
+
transformedExpression = transformedExpression.replace(/\bObject\b/g, "object");
|
|
702
|
+
transformedExpression = transformedExpression.replace(/\bfunction\b/g, "Function");
|
|
703
|
+
|
|
704
|
+
return transformedExpression;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
function renderTypeEntryAsDts(entry, availableTypeNames) {
|
|
708
|
+
if (entry.kind === "object") {
|
|
709
|
+
const lines = [`export type ${entry.name} = {`];
|
|
710
|
+
|
|
711
|
+
for (const property of entry.properties) {
|
|
712
|
+
lines.push(` ${property.name}${property.optional ? "?" : ""}: ${transformTypeExpressionForDts(property.typeExpression, availableTypeNames)};`);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
lines.push("};", "");
|
|
716
|
+
return lines;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
if (entry.kind === "callback") {
|
|
720
|
+
const params = entry.params.map((param) => `${param.name}${param.optional ? "?" : ""}: ${transformTypeExpressionForDts(param.typeExpression, availableTypeNames)}`).join(", ");
|
|
721
|
+
return [`export type ${entry.name} = (${params}) => ${transformTypeExpressionForDts(entry.returns, availableTypeNames)};`, ""];
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
return [`export type ${entry.name} = ${transformTypeExpressionForDts(entry.typeExpression, availableTypeNames)};`, ""];
|
|
725
|
+
}
|
|
726
|
+
|
|
451
727
|
function buildTypeExportMap(typeEntries) {
|
|
452
728
|
const exportMap = new Map();
|
|
453
729
|
|
|
@@ -647,61 +923,20 @@ function replaceGeneratedBlock(source, startMarker, endMarker, generatedBlock, t
|
|
|
647
923
|
return `${before}${generatedBlock}${after}`;
|
|
648
924
|
}
|
|
649
925
|
|
|
650
|
-
function buildRootDts(
|
|
651
|
-
const dtoAliasNames = new Set(dtoAliases.map((alias) => alias.name));
|
|
926
|
+
function buildRootDts(publicTypeEntries) {
|
|
652
927
|
const lines = [
|
|
653
928
|
"export * from \"./index.js\";",
|
|
654
929
|
""
|
|
655
930
|
];
|
|
656
931
|
|
|
657
|
-
const
|
|
658
|
-
|
|
659
|
-
for (const directory of sourceDirectories) {
|
|
660
|
-
sourceFiles.push(...allJsFilesByDirectory.get(directory));
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
const discoveredTypeEntries = [];
|
|
664
|
-
|
|
665
|
-
for (const filePath of sourceFiles) {
|
|
666
|
-
const source = sourceByFilePath.get(filePath);
|
|
667
|
-
discoveredTypeEntries.push(...extractPublicTypeEntries(source, filePath));
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
const canonicalizedTypeEntries = discoveredTypeEntries.filter((entry) => {
|
|
671
|
-
const relativeFilePath = toPosixRelativePath(entry.filePath);
|
|
672
|
-
const isDtoLeafFile = relativeFilePath.startsWith("api/dtos/") && relativeFilePath !== "api/dtos/index.js";
|
|
673
|
-
|
|
674
|
-
if (isDtoLeafFile && dtoAliasNames.has(entry.name)) {
|
|
675
|
-
return false;
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
return true;
|
|
679
|
-
});
|
|
680
|
-
|
|
681
|
-
const typeExportMap = buildTypeExportMap(canonicalizedTypeEntries);
|
|
932
|
+
const typeExportMap = buildTypeExportMap(publicTypeEntries);
|
|
682
933
|
const sortedEntries = [...typeExportMap.values()].sort((left, right) => left.name.localeCompare(right.name));
|
|
934
|
+
const availableTypeNames = new Set(sortedEntries.map((entry) => entry.name));
|
|
683
935
|
|
|
684
936
|
for (const entry of sortedEntries) {
|
|
685
|
-
|
|
686
|
-
lines.push(`export type ${entry.name} = import("./api/dtos/index.js").${entry.name};`);
|
|
687
|
-
dtoAliasNames.delete(entry.name);
|
|
688
|
-
continue;
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
const relativeImportPath = `./${toPosixRelativePath(entry.filePath)}`;
|
|
692
|
-
const dtsImportPath = toRelativeDtsImportPath(rootDtsPath, entry.filePath);
|
|
693
|
-
lines.push(`export type ${entry.name} = import("${dtsImportPath}").${entry.name};`);
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
for (const alias of dtoAliases) {
|
|
697
|
-
if (!dtoAliasNames.has(alias.name)) {
|
|
698
|
-
continue;
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
lines.push(`export type ${alias.name} = import("./api/dtos/index.js").${alias.name};`);
|
|
937
|
+
lines.push(...renderTypeEntryAsDts(entry, availableTypeNames));
|
|
702
938
|
}
|
|
703
939
|
|
|
704
|
-
lines.push("");
|
|
705
940
|
return `${lines.join("\n")}\n`;
|
|
706
941
|
}
|
|
707
942
|
|
|
@@ -725,6 +960,12 @@ for (const filePath of [rootIndexPath, ...dtoLeafFiles, ...businessLeafFiles, ..
|
|
|
725
960
|
const dtoEntries = buildTypeExportMap(dtoLeafFiles.flatMap((filePath) => extractPublicTypeEntries(sourceByFilePath.get(filePath), filePath)));
|
|
726
961
|
const dtoAliases = buildDtoAliases(dtoEntries);
|
|
727
962
|
const businessAliases = buildBusinessAliases(businessLeafFiles, sourceByFilePath);
|
|
963
|
+
const publicTypeEntries = [
|
|
964
|
+
...dtoLeafFiles.flatMap((filePath) => extractPublicTypeEntries(sourceByFilePath.get(filePath), filePath)),
|
|
965
|
+
...businessLeafFiles.flatMap((filePath) => extractPublicTypeEntries(sourceByFilePath.get(filePath), filePath)),
|
|
966
|
+
...otherApiJsFiles.flatMap((filePath) => extractPublicTypeEntries(sourceByFilePath.get(filePath), filePath)),
|
|
967
|
+
...uiLeafFiles.filter((filePath) => path.extname(filePath) === ".js").flatMap((filePath) => extractPublicTypeEntries(sourceByFilePath.get(filePath), filePath))
|
|
968
|
+
];
|
|
728
969
|
|
|
729
970
|
const generatedDtoIndexSource = buildDtoIndexSource(dtoAliases);
|
|
730
971
|
const generatedBusinessIndexSource = buildBusinessIndexSource(businessAliases);
|
|
@@ -768,12 +1009,7 @@ const updatedRootIndexSource = replaceGeneratedBlock(rootIndexWithDtoAliases, bu
|
|
|
768
1009
|
await writeFile(rootIndexPath, updatedRootIndexSource);
|
|
769
1010
|
sourceByFilePath.set(rootIndexPath, updatedRootIndexSource);
|
|
770
1011
|
|
|
771
|
-
const
|
|
772
|
-
["api", [...dtoLeafFiles, ...businessLeafFiles, ...otherApiJsFiles].sort()],
|
|
773
|
-
["ui", uiLeafFiles.filter((filePath) => path.extname(filePath) === ".js").sort()]
|
|
774
|
-
]);
|
|
775
|
-
|
|
776
|
-
const generatedRootDts = buildRootDts(dtoAliases, sourceByFilePath, allJsFilesByDirectory);
|
|
1012
|
+
const generatedRootDts = buildRootDts(publicTypeEntries);
|
|
777
1013
|
await writeFile(rootDtsPath, generatedRootDts);
|
|
778
1014
|
|
|
779
1015
|
console.log(`Updated ${toPosixRelativePath(dtoIndexPath)} with ${dtoAliases.length} generated DTO aliases.`);
|