Greasy Fork 还支持 简体中文。

Microsoft Power Platform/Dynamics 365 CE - Generate TypeScript Definitions

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

目前為 2025-04-15 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Microsoft Power Platform/Dynamics 365 CE - Generate TypeScript Definitions
  3. // @namespace https://github.com/gncnpk/xrm-generate-ts-overloads
  4. // @author Gavin Canon-Phratsachack (https://github.com/gncnpk)
  5. // @version 1.988
  6. // @license MIT
  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.  
  12. (function () {
  13. "use strict";
  14.  
  15. const groupItemsByType = (items) => {
  16. return Object.entries(items).reduce((acc, [itemName, itemType]) => {
  17. if (!acc[itemType]) {
  18. acc[itemType] = [];
  19. }
  20. acc[itemType].push(itemName);
  21. return acc;
  22. }, {});
  23. };
  24. const stripNonAlphaNumeric = (str) => {
  25. return str.replace(/\W/g, "");
  26. };
  27. // Create a button element and style it to be fixed in the bottom-right corner.
  28. const btn = document.createElement("button");
  29. btn.textContent = "Generate TypeScript Definitions";
  30. btn.style.position = "fixed";
  31. btn.style.bottom = "20px";
  32. btn.style.right = "20px";
  33. btn.style.padding = "10px";
  34. btn.style.backgroundColor = "#007ACC";
  35. btn.style.color = "#fff";
  36. btn.style.border = "none";
  37. btn.style.borderRadius = "5px";
  38. btn.style.cursor = "pointer";
  39. btn.style.zIndex = 10000;
  40. document.body.appendChild(btn);
  41.  
  42. btn.addEventListener("click", () => {
  43. // Mapping objects for Xrm attribute and control types.
  44. var attributeTypeMapping = {
  45. boolean: "Xrm.Attributes.BooleanAttribute",
  46. datetime: "Xrm.Attributes.DateAttribute",
  47. decimal: "Xrm.Attributes.NumberAttribute",
  48. double: "Xrm.Attributes.NumberAttribute",
  49. integer: "Xrm.Attributes.NumberAttribute",
  50. lookup: "Xrm.Attributes.LookupAttribute",
  51. memo: "Xrm.Attributes.StringAttribute",
  52. money: "Xrm.Attributes.NumberAttribute",
  53. multiselectoptionset: "Xrm.Attributes.MultiselectOptionSetAttribute",
  54. optionset: "Xrm.Attributes.OptionSetAttribute",
  55. string: "Xrm.Attributes.StringAttribute",
  56. };
  57.  
  58. var controlTypeMapping = {
  59. standard: "Xrm.Controls.StandardControl",
  60. iframe: "Xrm.Controls.IframeControl",
  61. lookup: "Xrm.Controls.LookupControl",
  62. optionset: "Xrm.Controls.OptionSetControl",
  63. "customsubgrid:MscrmControls.Grid.GridControl":
  64. "Xrm.Controls.GridControl",
  65. subgrid: "Xrm.Controls.GridControl",
  66. timelinewall: "Xrm.Controls.TimelineWall",
  67. quickform: "Xrm.Controls.QuickFormControl",
  68. };
  69.  
  70. var specificControlTypeMapping = {
  71. boolean: "Xrm.Controls.BooleanControl",
  72. datetime: "Xrm.Controls.DateControl",
  73. decimal: "Xrm.Controls.NumberControl",
  74. double: "Xrm.Controls.NumberControl",
  75. integer: "Xrm.Controls.NumberControl",
  76. lookup: "Xrm.Controls.LookupControl",
  77. memo: "Xrm.Controls.StringControl",
  78. money: "Xrm.Controls.NumberControl",
  79. multiselectoptionset: "Xrm.Controls.MultiselectOptionSetControl",
  80. optionset: "Xrm.Controls.OptionSetControl",
  81. string: "Xrm.Controls.StringControl",
  82. };
  83.  
  84. // Object to hold the type information.
  85. const typeInfo = {
  86. subGrids: {},
  87. quickViews: {},
  88. formAttributes: {},
  89. formControls: {},
  90. possibleEnums: [],
  91. formTabs: {},
  92. formEnums: {},
  93. quickViewControls: {},
  94. quickViewAttributes: {},
  95. };
  96. class Subgrid {
  97. constructor() {
  98. this.attributes = {};
  99. this.enums = {};
  100. }
  101. }
  102. class QuickForm {
  103. constructor() {
  104. this.attributes = {};
  105. this.controls = {};
  106. this.enums = {};
  107. }
  108. }
  109.  
  110. class Tab {
  111. constructor() {
  112. this.sections = {};
  113. }
  114. }
  115.  
  116. const currentFormName = stripNonAlphaNumeric(
  117. Xrm.Page.ui.formSelector.getCurrentItem().getLabel()
  118. );
  119.  
  120. // Loop through all controls on the form.
  121. if (
  122. typeof Xrm !== "undefined" &&
  123. Xrm.Page &&
  124. typeof Xrm.Page.getControl === "function"
  125. ) {
  126. Xrm.Page.getControl().forEach((ctrl) => {
  127. const ctrlType = ctrl.getControlType();
  128. const mappedType = controlTypeMapping[ctrlType];
  129. if (mappedType) {
  130. typeInfo.formControls[ctrl.getName()] = mappedType;
  131. }
  132. });
  133. } else {
  134. alert("Xrm.Page is not available on this page.");
  135. return;
  136. }
  137.  
  138. // Loop through all tabs and sections on the form.
  139. if (typeof Xrm.Page.ui.tabs.get === "function") {
  140. Xrm.Page.ui.tabs.get().forEach((tab) => {
  141. let formTab = (typeInfo.formTabs[stripNonAlphaNumeric(tab.getName())] =
  142. new Tab());
  143. tab.sections.forEach((section) => {
  144. formTab.sections[
  145. stripNonAlphaNumeric(section.getName())
  146. ] = `${stripNonAlphaNumeric(section.getName())}_section`;
  147. });
  148. });
  149. }
  150.  
  151. // Loop through all attributes on the form.
  152. if (typeof Xrm.Page.getAttribute === "function") {
  153. Xrm.Page.getAttribute().forEach((attr) => {
  154. const attrType = attr.getAttributeType();
  155. const attrName = attr.getName();
  156. const mappedType = attributeTypeMapping[attrType];
  157. const mappedControlType = specificControlTypeMapping[attrType];
  158. if (mappedType) {
  159. typeInfo.formAttributes[attrName] = mappedType;
  160. attr.controls.forEach((ctrl) => {
  161. typeInfo.formControls[ctrl.getName()] = mappedControlType;
  162. });
  163. }
  164. if (
  165. attr.getAttributeType() === "optionset" &&
  166. attr.controls.get().length > 0
  167. ) {
  168. const enumValues = attr.getOptions();
  169. if (enumValues) {
  170. typeInfo.formEnums[attrName] = { attribute: "", values: [] };
  171. typeInfo.formEnums[attrName].values = enumValues;
  172. typeInfo.formEnums[attrName].attribute = attrName;
  173. typeInfo.formAttributes[attrName] = `${attrName}_attribute`;
  174. }
  175. }
  176. });
  177. }
  178.  
  179. // Loop through all subgrids on the form.
  180. if (typeof Xrm.Page.getControl === "function") {
  181. Xrm.Page.getControl().forEach((ctrl) => {
  182. if (
  183. ctrl.getControlType() === "subgrid" ||
  184. ctrl.getControlType() ===
  185. "customsubgrid:MscrmControls.Grid.GridControl"
  186. ) {
  187. const gridRow = ctrl.getGrid().getRows().get(0);
  188. const gridName = ctrl.getName();
  189. let subgrid = (typeInfo.subGrids[gridName] = new Subgrid());
  190. typeInfo.formControls[gridName] = `${gridName}_gridcontrol`;
  191. if (gridRow !== null) {
  192. gridRow.data.entity.attributes.forEach((attr) => {
  193. const attrType = attr.getAttributeType();
  194. const attrName = attr.getName();
  195. const mappedType = attributeTypeMapping[attrType];
  196. if (mappedType) {
  197. subgrid.attributes[attrName] = mappedType;
  198. }
  199. if (
  200. attr.getAttributeType() === "optionset" &&
  201. attr.controls.get().length > 0
  202. ) {
  203. const enumValues = attr.getOptions();
  204. if (enumValues) {
  205. subgrid.enums[attrName] = { attribute: "", values: [] };
  206. subgrid.enums[attrName].values = enumValues;
  207. subgrid.enums[attrName].attribute = attrName;
  208. subgrid.attributes[attrName] = `${attrName}_attribute`;
  209. }
  210. }
  211. });
  212. }
  213. }
  214. });
  215. }
  216.  
  217. // Loop through all Quick View controls and attributes on the form.
  218. if (typeof Xrm.Page.ui.quickForms.get === "function") {
  219. Xrm.Page.ui.quickForms.get().forEach((ctrl) => {
  220. const quickViewName = ctrl.getName();
  221. let quickView = (typeInfo.quickViews[quickViewName] = new QuickForm());
  222. const ctrlType = ctrl.getControlType();
  223. typeInfo.formControls[
  224. quickViewName
  225. ] = `${quickViewName}_quickformcontrol`;
  226. ctrl.getControl().forEach((subCtrl) => {
  227. if (typeof subCtrl.getAttribute !== "function") {
  228. return;
  229. }
  230. const subCtrlAttrType = subCtrl.getAttribute().getAttributeType();
  231. const mappedControlType =
  232. specificControlTypeMapping[subCtrlAttrType] ??
  233. controlTypeMapping[subCtrl.getControlType()];
  234. if (mappedControlType) {
  235. quickView.controls[subCtrl.getName()] = mappedControlType;
  236. }
  237. });
  238. ctrl.getAttribute().forEach((attr) => {
  239. const attrType = attr.getAttributeType();
  240. const attrName = attr.getName();
  241. const mappedType = attributeTypeMapping[attrType];
  242. if (mappedType) {
  243. quickView.attributes[attrName] = mappedType;
  244. }
  245. if (attrType === "optionset" && attr.controls.get().length > 0) {
  246. const enumValues = attr.getOptions();
  247. if (enumValues) {
  248. quickView.enums[attrName] = { attribute: "", values: [] };
  249. quickView.enums[attrName].values = enumValues;
  250. quickView.enums[attrName].attribute = attrName;
  251. quickView.attributes[attrName] = `${attrName}_attribute`;
  252. }
  253. }
  254. });
  255. });
  256. }
  257.  
  258. // Build the TypeScript overload string.
  259. let outputTS = `// These TypeScript definitions were generated automatically on: ${new Date().toDateString()}\n`;
  260. for (const [originalEnumName, enumValues] of Object.entries(
  261. typeInfo.formEnums
  262. )) {
  263. if (typeInfo.possibleEnums.includes(originalEnumName)) {
  264. continue;
  265. }
  266. typeInfo.possibleEnums.push(originalEnumName);
  267. let enumName = `${originalEnumName}_enum`;
  268. let enumTemplate = [];
  269. let textLiteralTypes = [];
  270. let valueLiteralTypes = [];
  271. for (const enumValue of enumValues.values) {
  272. enumTemplate.push(
  273. ` ${enumValue.text.replace(/\W/g, "").replace(/[0-9]/g, "")} = ${
  274. enumValue.value
  275. }`
  276. );
  277. textLiteralTypes.push(`"${enumValue.text}"`);
  278. valueLiteralTypes.push(
  279. `${enumName}.${enumValue.text
  280. .replace(/\W/g, "")
  281. .replace(/[0-9]/g, "")}`
  282. );
  283. }
  284. outputTS += `
  285. const enum ${enumName} {
  286. ${enumTemplate.join(",\n")}
  287. }
  288. `;
  289. outputTS += `
  290. interface ${enumValues.attribute}_value extends Xrm.OptionSetValue {
  291. text: ${textLiteralTypes.join(" | ")};
  292. value: ${valueLiteralTypes.join(" | ")};
  293. }
  294. `;
  295. outputTS += `
  296. interface ${enumValues.attribute}_attribute extends Xrm.Attributes.OptionSetAttribute<${enumName}> {
  297. `;
  298. valueLiteralTypes.forEach((value, index) => {
  299. outputTS += `getOption(value: ${value}): {text: ${textLiteralTypes[index]}, value: ${value}};\n`;
  300. });
  301. outputTS += `getOption(value: ${enumValues.attribute}_value['value']): ${enumValues.attribute}_value | null;
  302. getOptions(): ${enumValues.attribute}_value[];
  303. getSelectedOption(): ${enumValues.attribute}_value | null;
  304. getValue(): ${enumName} | null;
  305. setValue(value: ${enumName} | null): void;
  306. getText(): ${enumValues.attribute}_value['text'] | null;
  307. }
  308. `;
  309. }
  310. for (let [subgridName, subgrid] of Object.entries(typeInfo.subGrids)) {
  311. subgridName = subgridName.replace(/\W/g, "");
  312. for (const [enumName, enumValues] of Object.entries(subgrid.enums)) {
  313. if (typeInfo.possibleEnums.includes(enumName)) {
  314. continue;
  315. }
  316. typeInfo.possibleEnums.push(enumName);
  317. let enumTemplate = [];
  318. let textLiteralTypes = [];
  319. let valueLiteralTypes = [];
  320. for (const enumValue of enumValues.values) {
  321. enumTemplate.push(
  322. ` ${enumValue.text.replace(/\W/g, "").replace(/[0-9]/g, "")} = ${
  323. enumValue.value
  324. }`
  325. );
  326. textLiteralTypes.push(`"${enumValue.text}"`);
  327. valueLiteralTypes.push(
  328. `${enumName}_enum.${enumValue.text
  329. .replace(/\W/g, "")
  330. .replace(/[0-9]/g, "")}`
  331. );
  332. }
  333. outputTS += `
  334. const enum ${enumName}_enum {
  335. ${enumTemplate.join(",\n")}
  336. }
  337. `;
  338. outputTS += `
  339. interface ${enumValues.attribute}_value extends Xrm.OptionSetValue {
  340. text: ${textLiteralTypes.join(" | ")};
  341. value: ${valueLiteralTypes.join(" | ")};
  342. }
  343. `;
  344. outputTS += `
  345. interface ${enumValues.attribute}_attribute extends Xrm.Attributes.OptionSetAttribute<${enumName}_enum> {
  346. `;
  347. valueLiteralTypes.forEach((value, index) => {
  348. outputTS += `getOption(value: ${value}): {text: ${textLiteralTypes[index]}, value: ${value}};\n`;
  349. });
  350. outputTS += `
  351. getOption(value: ${enumValues.attribute}_value['value']): ${enumValues.attribute}_value | null;
  352. getOptions(): ${enumValues.attribute}_value[];
  353. getSelectedOption(): ${enumValues.attribute}_value | null;
  354. getValue(): ${enumName}_enum | null;
  355. setValue(value: ${enumName}_enum | null): void;
  356. getText(): ${enumValues.attribute}_value['text'] | null;
  357. }
  358. `;
  359. }
  360.  
  361. outputTS += `
  362. interface ${subgridName}_attributes extends Xrm.Collection.ItemCollection<${subgridName}_attributes_types> {`;
  363. for (const [attrType, attrNames] of Object.entries(
  364. groupItemsByType(subgrid.attributes)
  365. )) {
  366. outputTS += `get(itemName: "${attrNames.join(
  367. '" | "'
  368. )}"): ${attrType};\n`;
  369. }
  370. outputTS += `
  371. get(itemName: ${subgridName}_attributes_literals): ${subgridName}_attributes_types;
  372. get(itemNameOrIndex: string | number): Xrm.Attributes.Attribute | null;
  373. get(delegate?): ${subgridName}_attributes_types[];
  374. }
  375. `;
  376. outputTS += `
  377. type ${subgridName}_attributes_types = ${new Set(
  378. Object.values(subgrid.attributes)
  379. )
  380. .map((attrType) => `${attrType}`)
  381. .join(" | ")}${
  382. Object.keys(subgrid.attributes).length === 0
  383. ? "Xrm.Attributes.Attribute"
  384. : ""
  385. };\n`;
  386.  
  387. outputTS += `
  388. type ${subgridName}_attributes_literals = ${new Set(
  389. Object.keys(subgrid.attributes)
  390. )
  391. .map((attrName) => `"${attrName}"`)
  392. .join(" | ")}${
  393. Object.keys(subgrid.attributes).length === 0 ? '""' : ""
  394. };\n`;
  395.  
  396. outputTS += `
  397. interface ${subgridName}_entity extends Xrm.Entity {
  398. attributes: ${subgridName}_attributes;
  399. }
  400. interface ${subgridName}_data extends Xrm.Data {
  401. entity: ${subgridName}_entity;
  402. }
  403. interface ${subgridName}_gridrow extends Xrm.Controls.Grid.GridRow {
  404. data: ${subgridName}_data;
  405. }
  406. interface ${subgridName}_grid extends Xrm.Controls.Grid {
  407. getRows(): Xrm.Collection.ItemCollection<${subgridName}_gridrow>;
  408. }
  409. interface ${subgridName}_gridcontrol extends Xrm.Controls.GridControl {
  410. getGrid(): ${subgridName}_grid;
  411. }
  412. interface ${subgridName}_context extends Xrm.FormContext {`;
  413. for (const [attrType, attrNames] of Object.entries(
  414. groupItemsByType(subgrid.attributes)
  415. )) {
  416. outputTS += `getAttribute(attributeName: "${attrNames.join(
  417. '" | "'
  418. )}"): ${attrType};\n`;
  419. }
  420. outputTS += `getAttribute(attributeName: ${subgridName}_attributes_literals): ${subgridName}_attributes_types;`;
  421. outputTS += `getAttribute(attributeNameOrIndex: string | number): Xrm.Attributes.Attribute | null;`;
  422. outputTS += `getAttribute(delegateFunction?): ${subgridName}_attributes[];`;
  423. outputTS += `
  424. }
  425. `;
  426. outputTS += `
  427. interface ${subgridName}_eventcontext extends Xrm.Events.EventContext {
  428. getFormContext(): ${subgridName}_context;
  429. }`;
  430. }
  431. for (const [quickViewName, quickView] of Object.entries(
  432. typeInfo.quickViews
  433. )) {
  434. for (const [enumName, enumValues] of Object.entries(quickView.enums)) {
  435. if (typeInfo.possibleEnums.includes(enumName)) {
  436. continue;
  437. }
  438. typeInfo.possibleEnums.push(enumName);
  439. let enumTemplate = [];
  440. let textLiteralTypes = [];
  441. let valueLiteralTypes = [];
  442. for (const enumValue of enumValues.values) {
  443. enumTemplate.push(
  444. ` ${enumValue.text.replace(/\W/g, "").replace(/[0-9]/g, "")} = ${
  445. enumValue.value
  446. }`
  447. );
  448. textLiteralTypes.push(`"${enumValue.text}"`);
  449. valueLiteralTypes.push(
  450. `${enumName}_enum.${enumValue.text
  451. .replace(/\W/g, "")
  452. .replace(/[0-9]/g, "")}`
  453. );
  454. }
  455. outputTS += `
  456. const enum ${enumName}_enum {
  457. ${enumTemplate.join(",\n")}
  458. }
  459. `;
  460. outputTS += `
  461. interface ${enumValues.attribute}_value extends Xrm.OptionSetValue {
  462. text: ${textLiteralTypes.join(" | ")};
  463. value: ${valueLiteralTypes.join(" | ")};
  464. }
  465. `;
  466. outputTS += `
  467. interface ${enumValues.attribute}_attribute extends Xrm.Attributes.OptionSetAttribute<${enumName}_enum> {
  468. `;
  469. valueLiteralTypes.forEach((value, index) => {
  470. outputTS += `getOption(value: ${value}): {text: ${textLiteralTypes[index]}, value: ${value}};\n`;
  471. });
  472. outputTS += `
  473. getOption(value: ${enumValues.attribute}_value['value']): ${enumValues.attribute}_value | null;
  474. getOptions(): ${enumValues.attribute}_value[];
  475. getSelectedOption(): ${enumValues.attribute}_value | null;
  476. getValue(): ${enumName}_enum | null;
  477. setValue(value: ${enumName}_enum | null): void;
  478. getText(): ${enumValues.attribute}_value['text'] | null;
  479. }
  480. `;
  481. }
  482. outputTS += `type ${quickViewName}_controls_types = ${new Set(
  483. Object.values(quickView.controls)
  484. )
  485. .map((controlType) => `${controlType}`)
  486. .join(" | ")}${
  487. Object.keys(quickView.controls).length === 0
  488. ? "Xrm.Controls.Control"
  489. : ""
  490. };\n`;
  491. outputTS += `
  492. type ${quickViewName}_attributes_types = ${new Set(
  493. Object.values(quickView.attributes)
  494. )
  495. .map((attrType) => `${attrType}`)
  496. .join(" | ")}${
  497. Object.keys(quickView.attributes).length === 0
  498. ? "Xrm.Attributes.Attribute"
  499. : ""
  500. };\n`;
  501. outputTS += `type ${quickViewName}_controls_literals = ${new Set(
  502. Object.keys(quickView.controls)
  503. )
  504. .map((controlName) => `"${controlName}"`)
  505. .join(" | ")}${
  506. Object.keys(quickView.controls).length === 0 ? '""' : ""
  507. };\n`;
  508. outputTS += `
  509. type ${quickViewName}_attributes_literals = ${new Set(
  510. Object.keys(quickView.attributes)
  511. )
  512. .map((attrName) => `"${attrName}"`)
  513. .join(" | ")}${
  514. Object.keys(quickView.attributes).length === 0 ? '""' : ""
  515. };\n`;
  516. outputTS += `
  517. interface ${quickViewName}_quickformcontrol extends Xrm.Controls.QuickFormControl {
  518. `;
  519. for (const [controlType, controlNames] of Object.entries(
  520. groupItemsByType(quickView.controls)
  521. )) {
  522. outputTS += `getControl(controlName: "${controlNames.join(
  523. '" | "'
  524. )}"): ${controlType};\n`;
  525. }
  526. outputTS += `
  527. getControl(controlName: ${quickViewName}_controls_literals): ${quickViewName}_controls_types;
  528. getControl(controlNameOrIndex: string | number): Xrm.Controls.Control | null;
  529. getControl(delegateFunction?): ${quickViewName}_controls_types[]
  530. `;
  531. for (const [attrType, attrNames] of Object.entries(
  532. groupItemsByType(quickView.attributes)
  533. )) {
  534. outputTS += `getAttribute(attributeName: "${attrNames.join(
  535. '" | "'
  536. )}"): ${attrType};\n`;
  537. }
  538. outputTS += `
  539. getAttribute(attributeName: ${quickViewName}_attributes_literals): ${quickViewName}_attributes_types;
  540. getAttribute(attributeNameOrIndex: string | number): Xrm.Attributes.Attribute | null;
  541. getAttribute(delegateFunction?): ${quickViewName}_attributes_types[];
  542. }`;
  543. }
  544. for (const [tabName, tab] of Object.entries(typeInfo.formTabs)) {
  545. outputTS += `
  546. type ${tabName}_sections_literals = ${new Set(Object.keys(tab.sections))
  547. .map((sectionName) => `"${sectionName}"`)
  548. .join(" | ")}${Object.keys(tab.sections).length === 0 ? '""' : ""};\n`;
  549. outputTS += `
  550. interface ${tabName}_sections extends Xrm.Collection.ItemCollection<Xrm.Controls.Section> {`;
  551. outputTS += `get(itemName: ${tabName}_sections_literals): Xrm.Controls.Section;\n`;
  552. outputTS += `get(itemNameOrIndex: string | number): Xrm.Controls.Section | null;\n`;
  553. outputTS += `get(delegate?): Xrm.Controls.Section[];\n`;
  554. outputTS += `}`;
  555.  
  556. outputTS += `
  557. interface ${tabName}_tab extends Xrm.Controls.Tab {
  558. sections: ${tabName}_sections;
  559. }`;
  560. }
  561. outputTS += `
  562. type ${currentFormName}_tabs_types = ${new Set(
  563. Object.keys(typeInfo.formTabs).map((tabType) => `${tabType}_tab`)
  564. ).join(" | ")}${
  565. Object.keys(typeInfo.formTabs).length === 0 ? "Xrm.Controls.Tab" : ""
  566. };\n`;
  567. outputTS += `
  568. type ${currentFormName}_tabs_literals = ${new Set(
  569. Object.keys(typeInfo.formTabs)
  570. )
  571. .map((tabName) => `"${tabName}"`)
  572. .join(" | ")}${
  573. Object.keys(typeInfo.formTabs).length === 0 ? '""' : ""
  574. };\n`;
  575. outputTS += `
  576. interface ${currentFormName}_tabs extends Xrm.Collection.ItemCollection<${currentFormName}_tabs_types> {`;
  577. for (const [itemName, itemType] of Object.entries(typeInfo.formTabs)) {
  578. outputTS += `get(itemName:"${itemName}"): ${itemName}_tab;\n`;
  579. }
  580. outputTS += `
  581. get(itemName: ${currentFormName}_tabs_literals): ${currentFormName}_tabs_types;
  582. get(itemNameOrIndex: string | number): Xrm.Controls.Tab | null;
  583. get(delegate?): ${currentFormName}_tabs_types[];
  584. }`;
  585. outputTS += `
  586. type ${currentFormName}_quickforms_types = ${new Set(
  587. Object.keys(typeInfo.quickViews)
  588. )
  589. .map((quickViewName) => `${quickViewName}_quickformcontrol`)
  590. .join(" | ")}${
  591. Object.keys(typeInfo.quickViews).length === 0
  592. ? "Xrm.Controls.QuickFormControl"
  593. : ""
  594. };\n`;
  595. outputTS += `
  596. type ${currentFormName}_quickforms_literals = ${new Set(
  597. Object.keys(typeInfo.quickViews)
  598. )
  599. .map((quickViewName) => `"${quickViewName}"`)
  600. .join(" | ")}${
  601. Object.keys(typeInfo.quickViews).length === 0 ? '""' : ""
  602. };\n`;
  603. outputTS += `
  604. interface ${currentFormName}_quickforms extends Xrm.Collection.ItemCollection<${currentFormName}_quickform_types> {`;
  605. for (const [itemName, itemType] of Object.entries(typeInfo.quickViews)) {
  606. outputTS += `get(itemName:"${itemName}"): ${itemName}_quickformcontrol;\n`;
  607. }
  608. outputTS += `
  609. get(itemName: ${currentFormName}_quickforms_literals): ${currentFormName}_quickforms_types;
  610. get(itemNameOrIndex: string | number): Xrm.Controls.QuickFormControl | null;
  611. get(delegate?): ${currentFormName}_quickforms_types[];
  612. }`;
  613. outputTS += `
  614. type ${currentFormName}_attributes_types = ${new Set(
  615. Object.values(typeInfo.formAttributes)
  616. )
  617. .map((attrType) => `${attrType}`)
  618. .join(" | ")}\n`;
  619. outputTS += `
  620. type ${currentFormName}_attributes_literals = ${new Set(
  621. Object.keys(typeInfo.formAttributes)
  622. )
  623. .map((attrName) => `"${attrName}"`)
  624. .join(" | ")}\n`;
  625.  
  626. outputTS += `
  627. type ${currentFormName}_controls_types = ${new Set(
  628. Object.values(typeInfo.formControls)
  629. )
  630. .map((ctrlType) => `${ctrlType}`)
  631. .join(" | ")}\n`;
  632. outputTS += `
  633. type ${currentFormName}_controls_literals = ${new Set(
  634. Object.keys(typeInfo.formControls)
  635. )
  636. .map((ctrlName) => `"${ctrlName}"`)
  637. .join(" | ")}\n`;
  638. outputTS += `
  639. interface ${currentFormName}_ui extends Xrm.Ui {
  640. quickForms: ${currentFormName}_quickforms | null;
  641. tabs: ${currentFormName}_tabs;
  642.  
  643. }
  644. `;
  645. outputTS += `
  646. interface ${currentFormName}_context extends Xrm.FormContext {
  647. ui: ${currentFormName}_ui;
  648. `;
  649. for (const [controlType, controlNames] of Object.entries(
  650. groupItemsByType(typeInfo.formControls)
  651. )) {
  652. outputTS += `getControl(controlName: "${controlNames.join(
  653. '" | "'
  654. )}"): ${controlType};\n`;
  655. }
  656. outputTS += `
  657. getControl(controlName: ${currentFormName}_controls_literals): ${currentFormName}_controls_types;
  658. getControl(controlNameOrIndex: string | number): Xrm.Controls.Control | null;
  659. getControl(delegateFunction?): ${currentFormName}_controls_types[];
  660. `;
  661. for (const [attrType, attrNames] of Object.entries(
  662. groupItemsByType(typeInfo.formAttributes)
  663. )) {
  664. outputTS += `getAttribute(attributeName: "${attrNames.join(
  665. '" | "'
  666. )}"): ${attrType};\n`;
  667. }
  668. outputTS += `
  669. getAttribute(attributeName: ${currentFormName}_attributes_literals): ${currentFormName}_attributes_types;
  670. getAttribute(attributeNameOrIndex: string | number): Xrm.Attributes.Attribute | null;
  671. getAttribute(delegateFunction?): ${currentFormName}_attributes_types[];
  672. }`;
  673. outputTS += `
  674. interface ${currentFormName}_eventcontext extends Xrm.Events.EventContext {
  675. getFormContext(): ${currentFormName}_context;
  676. }`;
  677.  
  678. // Create a new window with a textarea showing the output.
  679. // The textarea is set to readonly to prevent editing.
  680. const w = window.open(
  681. "",
  682. "_blank",
  683. "width=600,height=400,menubar=no,toolbar=no,location=no,resizable=yes"
  684. );
  685. if (w) {
  686. w.document.write(
  687. "<html><head><title>TypeScript Definitions</title></head><body>"
  688. );
  689. w.document.write(
  690. '<textarea readonly style="width:100%; height:90%;">' +
  691. outputTS +
  692. "</textarea>"
  693. );
  694. w.document.write("</body></html>");
  695. w.document.close();
  696. } else {
  697. // Fallback to prompt if popups are blocked.
  698. prompt("Copy the TypeScript definition:", outputTS);
  699. }
  700. });
  701. })();