Microsoft Power Platform/Dynamics 365 CE - Generate TypeScript Overload Signatures

Automatically creates TypeScript type definitions compatible with @types/xrm by extracting form attributes and controls from Dynamics 365/Power Platform model-driven applications.

目前為 2025-03-12 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Microsoft Power Platform/Dynamics 365 CE - Generate TypeScript Overload Signatures
  3. // @namespace https://github.com/gncnpk/xrm-generate-ts-overloads
  4. // @author Gavin Canon-Phratsachack (https://github.com/gncnpk)
  5. // @version 1.0
  6. // @license GPL3
  7. // @description Automatically creates TypeScript type definitions compatible with @types/xrm by extracting form attributes and controls from Dynamics 365/Power Platform model-driven applications.
  8. // @match https://*.dynamics.com/main.aspx?appid=*&pagetype=entityrecord&etn=*&id=*
  9. // @grant none
  10. // ==/UserScript==
  11. (function() {
  12. 'use strict';
  13. // Create a button element and style it to be fixed in the bottom-right corner.
  14. const btn = document.createElement('button');
  15. btn.textContent = 'Generate TypeScript Signatures';
  16. btn.style.position = 'fixed';
  17. btn.style.bottom = '20px';
  18. btn.style.right = '20px';
  19. btn.style.padding = '10px';
  20. btn.style.backgroundColor = '#007ACC';
  21. btn.style.color = '#fff';
  22. btn.style.border = 'none';
  23. btn.style.borderRadius = '5px';
  24. btn.style.cursor = 'pointer';
  25. btn.style.zIndex = 10000;
  26. document.body.appendChild(btn);
  27. btn.addEventListener('click', () => {
  28. // Mapping objects for Xrm attribute and control types.
  29. var attributeTypeMapping = {
  30. "boolean": "Xrm.Attributes.BooleanAttribute",
  31. "datetime": "Xrm.Attributes.DateAttribute",
  32. "decimal": "Xrm.Attributes.NumberAttribute",
  33. "double": "Xrm.Attributes.NumberAttribute",
  34. "integer": "Xrm.Attributes.NumberAttribute",
  35. "lookup": "Xrm.Attributes.LookupAttribute",
  36. "memo": "Xrm.Attributes.StringAttribute",
  37. "money": "Xrm.Attributes.MoneyAttribute",
  38. "multiselectoptionset": "Xrm.Attributes.MultiselectOptionSetAttribute",
  39. "optionset": "Xrm.Attributes.OptionSetAttribute",
  40. "string": "Xrm.Attributes.StringAttribute"
  41. };
  42. var controlTypeMapping = {
  43. "standard": "Xrm.Controls.StandardControl",
  44. "iframe": "Xrm.Controls.IframeControl",
  45. "lookup": "Xrm.Controls.LookupControl",
  46. "optionset": "Xrm.Controls.OptionSetControl",
  47. "customsubgrid:MscrmControls.Grid.GridControl": "Xrm.Controls.GridControl",
  48. "subgrid": "Xrm.Controls.GridControl",
  49. "timelinewall": "Xrm.Controls.TimelineWall"
  50. };
  51. // Object to hold the type information.
  52. const typeInfo = { attributes: {}, controls: {} };
  53. // Ensure that the Xrm.Page API is available.
  54. if (typeof Xrm !== 'undefined' && Xrm.Page && typeof Xrm.Page.getAttribute === 'function') {
  55. // Loop through all attributes on the form.
  56. Xrm.Page.getAttribute().forEach((attr) => {
  57. const attrType = attr.getAttributeType();
  58. const mappedType = attributeTypeMapping[attrType];
  59. if (mappedType) {
  60. typeInfo.attributes[attr.getName()] = mappedType;
  61. }
  62. });
  63. } else {
  64. alert("Xrm.Page is not available on this page.");
  65. return;
  66. }
  67. // Loop through all controls on the form.
  68. if (typeof Xrm.Page.getControl === 'function') {
  69. Xrm.Page.getControl().forEach((ctrl) => {
  70. const ctrlType = ctrl.getControlType();
  71. const mappedType = controlTypeMapping[ctrlType];
  72. if (mappedType) {
  73. typeInfo.controls[ctrl.getName()] = mappedType;
  74. }
  75. });
  76. }
  77. // Build the TypeScript overload string.
  78. let outputTS = `// This file is generated automatically.
  79. // It extends the Xrm.FormContext interface with overloads for getAttribute and getControl.
  80. // Do not modify this file manually.
  81. declare namespace Xrm {
  82. interface FormContext {
  83. `;
  84. for (const [attributeName, attributeType] of Object.entries(typeInfo.attributes)) {
  85. outputTS += ` getAttribute(attributeName: "${attributeName}"): ${attributeType};\n`;
  86. }
  87. for (const [controlName, controlType] of Object.entries(typeInfo.controls)) {
  88. outputTS += ` getControl(controlName: "${controlName}"): ${controlType};\n`;
  89. }
  90. outputTS += ` }
  91. }
  92. `;
  93. // Create a new window with a textarea showing the output.
  94. // The textarea is set to readonly to prevent editing.
  95. const w = window.open('', '_blank', 'width=600,height=400,menubar=no,toolbar=no,location=no,resizable=yes');
  96. if (w) {
  97. w.document.write('<html><head><title>Xrm Type Info</title></head><body>');
  98. w.document.write('<textarea readonly style="width:100%; height:90%;">' + outputTS + '</textarea>');
  99. w.document.write('</body></html>');
  100. w.document.close();
  101. } else {
  102. // Fallback to prompt if popups are blocked.
  103. prompt("Copy the TypeScript definition:", outputTS);
  104. }
  105. });
  106. })();