@xrmforge/typegen 0.5.0 → 0.5.1

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.
Files changed (2) hide show
  1. package/MIGRATION.md +154 -0
  2. package/package.json +3 -2
package/MIGRATION.md ADDED
@@ -0,0 +1,154 @@
1
+ # XrmForge Migration Guide
2
+
3
+ How to convert legacy Dynamics 365 JavaScript to type-safe TypeScript with XrmForge.
4
+
5
+ ## Step 1: Initialize Project
6
+
7
+ ```bash
8
+ npx @xrmforge/cli init my-project --prefix contoso
9
+ cd my-project
10
+ npm install
11
+ ```
12
+
13
+ ## Step 2: Generate Types from Dataverse
14
+
15
+ ```bash
16
+ npx xrmforge generate \
17
+ --url https://YOUR-ORG.crm4.dynamics.com \
18
+ --auth interactive \
19
+ --tenant-id YOUR-TENANT-ID \
20
+ --client-id YOUR-CLIENT-ID \
21
+ --entities account,contact,opportunity \
22
+ --output ./typings
23
+ ```
24
+
25
+ This generates:
26
+ - `typings/entities/*.d.ts` - Entity interfaces with typed attributes
27
+ - `typings/forms/*.d.ts` - Form interfaces with Fields enum, Tabs enum, Subgrid enum
28
+ - `typings/optionsets/*.d.ts` - OptionSet const enums with labels
29
+ - `typings/entity-names.d.ts` - EntityNames const enum
30
+
31
+ ## Step 3: Convert Form Scripts
32
+
33
+ ### Before (legacy JavaScript):
34
+
35
+ ```javascript
36
+ // account.js - global functions, raw strings, no type safety
37
+ var LM = LM || {};
38
+ LM.Account = {
39
+ onLoad: function(executionContext) {
40
+ var formContext = executionContext.getFormContext();
41
+ var name = formContext.getAttribute("name"); // generic Attribute
42
+ var status = formContext.getAttribute("statuscode");
43
+ if (status.getValue() === 1) { // magic number!
44
+ formContext.getControl("revenue").setVisible(true);
45
+ }
46
+ }
47
+ };
48
+ ```
49
+
50
+ ### After (XrmForge TypeScript):
51
+
52
+ ```typescript
53
+ // account-form.ts - typed, safe, autocomplete everywhere
54
+ import { AccountMainFormFieldsEnum as Fields } from '../../typings/forms/account';
55
+
56
+ export function onLoad(executionContext: Xrm.Events.EventContext): void {
57
+ const form = executionContext.getFormContext() as XrmForge.Forms.Account.AccountMainForm;
58
+
59
+ // Fields enum: compile error on typos, autocomplete in IDE
60
+ const name = form.getAttribute(Fields.AccountName); // StringAttribute, not generic
61
+ const status = form.getAttribute(Fields.StatusCode); // OptionSetAttribute
62
+
63
+ // OptionSet enum: no magic numbers
64
+ if (status.getValue() === XrmForge.OptionSets.Account.StatusCode.Active) {
65
+ form.getControl(Fields.Revenue).setVisible(true);
66
+ }
67
+ }
68
+ ```
69
+
70
+ ### Key Differences:
71
+
72
+ | Legacy | XrmForge |
73
+ |--------|----------|
74
+ | `getAttribute("name")` | `getAttribute(Fields.AccountName)` |
75
+ | `getValue() === 1` | `getValue() === OptionSets.StatusCode.Active` |
76
+ | `formContext` (untyped) | `form as AccountMainForm` (typed) |
77
+ | `getControl("revenue")` | `getControl(Fields.Revenue)` |
78
+ | No compile-time checks | Typos are compile errors |
79
+
80
+ ## Step 4: Replace Common Patterns
81
+
82
+ ### Lookup Values
83
+
84
+ ```typescript
85
+ // Before:
86
+ var value = formContext.getAttribute("primarycontactid").getValue();
87
+ var id = value[0].id.replace("{","").replace("}","");
88
+
89
+ // After: use parseLookup from @xrmforge/typegen
90
+ import { parseLookup } from '@xrmforge/typegen';
91
+ const contact = parseLookup(form.getAttribute(Fields.PrimaryContactId));
92
+ if (contact) {
93
+ console.log(contact.id); // already clean GUID
94
+ }
95
+ ```
96
+
97
+ ### Web API Queries
98
+
99
+ ```typescript
100
+ // Before:
101
+ Xrm.WebApi.retrieveMultipleRecords("account",
102
+ "?$select=name,revenue&$filter=statecode eq 0");
103
+
104
+ // After: use Fields enum for $select
105
+ import { select } from '@xrmforge/typegen';
106
+ import { AccountFields } from '../../typings/entities/account';
107
+ Xrm.WebApi.retrieveMultipleRecords("account",
108
+ `?$select=${select(AccountFields.Name, AccountFields.Revenue)}&$filter=statecode eq 0`);
109
+ ```
110
+
111
+ ### Form Testing
112
+
113
+ ```typescript
114
+ // Before: no tests, or complex manual mocks
115
+
116
+ // After: @xrmforge/testing
117
+ import { createFormMock, fireOnChange } from '@xrmforge/testing';
118
+ import type { AccountMainForm, AccountMainFormMockValues } from '../../typings/forms/account';
119
+
120
+ const mock = createFormMock<AccountMainForm, AccountMainFormMockValues>({
121
+ name: 'Contoso Ltd',
122
+ revenue: 1000000,
123
+ statuscode: 1,
124
+ });
125
+
126
+ onLoad(mock.executionContext);
127
+ expect(mock.formContext.getControl('revenue').getVisible()).toBe(true);
128
+ ```
129
+
130
+ ## Step 5: Build
131
+
132
+ ```bash
133
+ npx xrmforge build # IIFE bundles for D365
134
+ npx xrmforge build --watch # Watch mode
135
+ ```
136
+
137
+ ## Step 6: Replace Magic Numbers
138
+
139
+ Search your code for patterns like:
140
+ - `getValue() === 123` or `getValue() !== 456`
141
+ - `setValue("statuscode", 1)`
142
+ - Raw OptionSet values in if/switch statements
143
+
144
+ Replace with generated const enums from `typings/optionsets/`.
145
+
146
+ ## Checklist
147
+
148
+ - [ ] All `getAttribute("string")` calls use Fields enum
149
+ - [ ] All OptionSet comparisons use const enums (no magic numbers)
150
+ - [ ] All `Xrm.Page` calls replaced with `formContext`
151
+ - [ ] Form scripts export functions (not global namespace objects)
152
+ - [ ] Each form script has tests using `@xrmforge/testing`
153
+ - [ ] `xrmforge build` produces IIFE bundles
154
+ - [ ] `tsc --noEmit` passes with zero errors
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xrmforge/typegen",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "TypeScript declaration generator for Dynamics 365 / Dataverse",
5
5
  "keywords": [
6
6
  "dynamics-365",
@@ -32,7 +32,8 @@
32
32
  }
33
33
  },
34
34
  "files": [
35
- "dist"
35
+ "dist",
36
+ "MIGRATION.md"
36
37
  ],
37
38
  "sideEffects": false,
38
39
  "scripts": {