versioned-d.ts-tools 0.7.4 → 0.8.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.
@@ -3,6 +3,7 @@ export interface LinkConfig {
3
3
  globalFunctionTemplate: string;
4
4
  classTemplate: string;
5
5
  classMemberTemplate: string;
6
+ enumTemplate?: string;
6
7
  }
7
8
  export interface WhatsNewConfig {
8
9
  excludedFieldNames?: string[];
@@ -45,17 +45,43 @@ const ts = __importStar(require("typescript"));
45
45
  // {suffix} - "-member(1)" for methods/functions, "-member" for properties
46
46
  // Default link builder using templates
47
47
  function buildLinkFromTemplate(template, relativePath, className, field) {
48
- const basePath = relativePath.substring(0, relativePath.lastIndexOf("."));
49
- const hostName = relativePath.includes("/api/")
50
- ? relativePath.substring(relativePath.indexOf("/api/") + 5, relativePath.lastIndexOf("/"))
51
- : "";
52
- const fileName = relativePath.substring(relativePath.lastIndexOf("/") + 1, relativePath.lastIndexOf("."));
48
+ // Handle relativePath with or without a dot
49
+ const lastDotIndex = relativePath.lastIndexOf(".");
50
+ const basePath = lastDotIndex >= 0
51
+ ? relativePath.substring(0, lastDotIndex)
52
+ : relativePath;
53
+ // Extract hostName - the part after /api/ (e.g., "outlook" from "javascript/api/outlook")
54
+ let hostName = "";
55
+ if (relativePath.includes("/api/")) {
56
+ const afterApi = relativePath.substring(relativePath.indexOf("/api/") + 5);
57
+ // If there's a slash after /api/, take up to that slash; otherwise take the whole thing or up to the dot
58
+ const nextSlashIndex = afterApi.indexOf("/");
59
+ if (nextSlashIndex >= 0) {
60
+ hostName = afterApi.substring(0, nextSlashIndex);
61
+ }
62
+ else if (lastDotIndex >= 0) {
63
+ hostName = afterApi.substring(0, afterApi.indexOf("."));
64
+ }
65
+ else {
66
+ hostName = afterApi;
67
+ }
68
+ }
69
+ const lastSlashIndex = relativePath.lastIndexOf("/");
70
+ const fileName = lastDotIndex >= 0
71
+ ? relativePath.substring(lastSlashIndex + 1, lastDotIndex)
72
+ : relativePath.substring(lastSlashIndex + 1);
53
73
  let result = template
54
74
  .replace(/{basePath}/g, basePath)
55
75
  .replace(/{hostName}/g, hostName)
56
76
  .replace(/{fileName}/g, fileName);
57
77
  if (className && className !== "<global>") {
58
- result = result.replace(/{className}/g, className.toLowerCase());
78
+ const lowerClassName = className.toLowerCase();
79
+ // For classes that already have namespace prefix (e.g., Office.MailboxEnums.ActionType),
80
+ // remove "office." from template since it's already in the className
81
+ if (lowerClassName.startsWith("office.")) {
82
+ result = result.replace(/\/office\.{className}/g, "/{className}");
83
+ }
84
+ result = result.replace(/{className}/g, lowerClassName);
59
85
  }
60
86
  if (field) {
61
87
  const suffix = (field.type === FieldType.Method || field.type === FieldType.Function) ? "-member(1)" : "-member";
@@ -85,8 +111,9 @@ exports.DEFAULT_LINK_CONFIGS = [
85
111
  {
86
112
  pathPattern: ".*", // matches any path as fallback
87
113
  globalFunctionTemplate: "/{basePath}#{hostName}-{fileName}-{fieldName}-function(1)",
88
- classTemplate: "/{basePath}.{className}",
89
- classMemberTemplate: "/{basePath}.{className}#{hostName}-{fileName}-{className}-{fieldName}{suffix}"
114
+ classTemplate: "/{basePath}/office.{className}",
115
+ classMemberTemplate: "/{basePath}/office.{className}#{hostName}-office-{className}-{fieldName}{suffix}",
116
+ enumTemplate: "/{basePath}/office.{className}"
90
117
  }
91
118
  ];
92
119
  // Parsing context to encapsulate state during DTS parsing
@@ -95,11 +122,19 @@ class ParsingContext {
95
122
  this.topClass = null;
96
123
  this.lastItem = null;
97
124
  this.globalFunctionsClass = null;
125
+ this.namespaceStack = [];
98
126
  }
99
127
  reset() {
100
128
  this.topClass = null;
101
129
  this.lastItem = null;
102
130
  this.globalFunctionsClass = null;
131
+ this.namespaceStack = [];
132
+ }
133
+ getFullyQualifiedName(name) {
134
+ if (this.namespaceStack.length === 0) {
135
+ return name;
136
+ }
137
+ return this.namespaceStack.join(".") + "." + name;
103
138
  }
104
139
  ensureGlobalFunctionsClass(allClasses) {
105
140
  if (this.globalFunctionsClass === null) {
@@ -413,8 +448,12 @@ class APISet {
413
448
  output += "|*global*|";
414
449
  }
415
450
  else {
416
- output += "|[" + className + "]("
417
- + buildClassLink(finalOptions.relativePath, className, finalOptions.linkConfigs) + ")|";
451
+ // For enums, display only the simple name but use full qualified name in the link
452
+ const displayName = isEnum && className.includes(".")
453
+ ? className.substring(className.lastIndexOf(".") + 1)
454
+ : className;
455
+ output += "|[" + displayName + "]("
456
+ + buildClassLink(finalOptions.relativePath, className, finalOptions.linkConfigs, isEnum) + ")|";
418
457
  }
419
458
  // Ignore the following:
420
459
  // - Fields matching excluded patterns (configurable, no defaults - all fields included unless explicitly excluded)
@@ -432,7 +471,11 @@ class APISet {
432
471
  }
433
472
  // remove unnecessary parts of the declaration string
434
473
  let newItemText = field.declarationString.replace(/;/g, "");
435
- if (field.type === FieldType.Property) {
474
+ if (field.type === FieldType.Enum) {
475
+ // For enum members, just use the field name as-is
476
+ newItemText = field.name;
477
+ }
478
+ else if (field.type === FieldType.Property) {
436
479
  // Remove the optional modifier and type.
437
480
  newItemText = newItemText.replace(/\?/g, "");
438
481
  newItemText = newItemText.substring(0, newItemText.indexOf(":"));
@@ -453,8 +496,16 @@ class APISet {
453
496
  newItemText = newItemText.replace(/\|/g, "\\|").replace(/\n|\t/gm, "");
454
497
  newItemText = newItemText.replace(/[\s][\s]+/g, " ").replace(/\( /g, "(").replace(/ \)/g, ")").replace(/,\)/g, ")").replace(/([\w]\??: )\\\| /g, "$1"); // dprint formatting quirks
455
498
  newItemText = newItemText.replace(/\<any\>/g, "");
456
- let tableLine = "[" + newItemText + "]("
457
- + buildFieldLink(finalOptions.relativePath, className, field, finalOptions.linkConfigs) + ")|";
499
+ newItemText = newItemText.replace(/</g, "\\<").replace(/>/g, "\\>"); // escape angle brackets for markdown
500
+ let tableLine;
501
+ if (isEnum) {
502
+ // Enum fields are plain text (no link)
503
+ tableLine = newItemText + "|";
504
+ }
505
+ else {
506
+ tableLine = "[" + newItemText + "]("
507
+ + buildFieldLink(finalOptions.relativePath, className, field, finalOptions.linkConfigs) + ")|";
508
+ }
458
509
  tableLine += removeAtLink(extractFirstSentenceFromComment(field.comment));
459
510
  output += tableLine + "|\n";
460
511
  });
@@ -523,9 +574,11 @@ function findMatchingLinkConfig(relativePath, linkConfigs = exports.DEFAULT_LINK
523
574
  const matchingConfig = compiledConfigs.find(({ regex }) => regex.test(relativePath));
524
575
  return matchingConfig ? matchingConfig.config : exports.DEFAULT_LINK_CONFIGS[exports.DEFAULT_LINK_CONFIGS.length - 1];
525
576
  }
526
- function buildClassLink(relativePath, className, linkConfigs = exports.DEFAULT_LINK_CONFIGS) {
577
+ function buildClassLink(relativePath, className, linkConfigs = exports.DEFAULT_LINK_CONFIGS, isEnum = false) {
527
578
  const config = findMatchingLinkConfig(relativePath, linkConfigs);
528
- return buildLinkFromTemplate(config.classTemplate, relativePath, className);
579
+ // Use enumTemplate for enums if available, otherwise fall back to classTemplate
580
+ const template = isEnum && config.enumTemplate ? config.enumTemplate : config.classTemplate;
581
+ return buildLinkFromTemplate(template, relativePath, className);
529
582
  }
530
583
  function buildFieldLink(relativePath, className, field, linkConfigs = exports.DEFAULT_LINK_CONFIGS) {
531
584
  const config = findMatchingLinkConfig(relativePath, linkConfigs);
@@ -577,6 +630,18 @@ function parseDTSInternal(node, allClasses, context) {
577
630
  break;
578
631
  case ts.SyntaxKind.TypeLiteral:
579
632
  return;
633
+ case ts.SyntaxKind.ModuleDeclaration:
634
+ // Handle namespace - push to stack, process children, then pop
635
+ const moduleNode = node;
636
+ if (moduleNode.name && ts.isIdentifier(moduleNode.name)) {
637
+ context.namespaceStack.push(moduleNode.name.text);
638
+ node.getChildren().forEach((element) => {
639
+ parseDTSInternal(element, allClasses, context);
640
+ });
641
+ context.namespaceStack.pop();
642
+ return; // Already processed children
643
+ }
644
+ break;
580
645
  default:
581
646
  // the compiler parses comments after the class/field, therefore this connects to the previous item
582
647
  if (node.getText().indexOf("/**") >= 0 &&
@@ -596,7 +661,11 @@ function parseDTSInternal(node, allClasses, context) {
596
661
  });
597
662
  }
598
663
  function parseDTSTopLevelItem(node, allClasses, type, context) {
599
- context.topClass = new ClassStruct("export " + type.toLowerCase() + " " + node.name.text, "", type);
664
+ // Use fully qualified name for enums to preserve namespace path (e.g., Office.MailboxEnums.ActionType)
665
+ const qualifiedName = type === ClassType.Enum
666
+ ? context.getFullyQualifiedName(node.name.text)
667
+ : node.name.text;
668
+ context.topClass = new ClassStruct("export " + type.toLowerCase() + " " + qualifiedName, "", type);
600
669
  allClasses.addClass(context.topClass);
601
670
  context.lastItem = context.topClass;
602
671
  }
package/dist/whats-new.js CHANGED
@@ -57,7 +57,8 @@ function loadWhatsNewConfig(configFilePath) {
57
57
  pathPattern: item.pathPattern,
58
58
  globalFunctionTemplate: item.globalFunctionTemplate,
59
59
  classTemplate: item.classTemplate,
60
- classMemberTemplate: item.classMemberTemplate
60
+ classMemberTemplate: item.classMemberTemplate,
61
+ enumTemplate: item.enumTemplate // Optional: defaults to classTemplate if not provided
61
62
  };
62
63
  });
63
64
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "versioned-d.ts-tools",
3
- "version": "0.7.4",
3
+ "version": "0.8.0",
4
4
  "description": "Tools for managing versioned TypeScript definition files",
5
5
  "main": "dist/index.js",
6
6
  "bin": {