@lwc-bolts/sf2ts 0.0.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.
- package/README.MD +5 -0
- package/lib/converts/_index.mjs +7 -0
- package/lib/converts/apex-class.mjs +590 -0
- package/lib/converts/content-asset.mjs +24 -0
- package/lib/converts/custom-field.mjs +35 -0
- package/lib/converts/custom-labels.mjs +34 -0
- package/lib/converts/custom-object.mjs +30 -0
- package/lib/converts/custom-permission.mjs +30 -0
- package/lib/converts/static-resource.mjs +29 -0
- package/lib/index.mjs +51 -0
- package/lib-cjs/bundle.cjs +838 -0
- package/package.json +47 -0
|
@@ -0,0 +1,838 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var webTreeSitterSfapex = require('web-tree-sitter-sfapex');
|
|
4
|
+
var xml2js = require('xml2js');
|
|
5
|
+
|
|
6
|
+
/** @import Parser from "web-tree-sitter" */
|
|
7
|
+
/** @import { Component, ConvertedComponent } from "../index.mjs" */
|
|
8
|
+
|
|
9
|
+
// TOOD: Apex Continuation
|
|
10
|
+
|
|
11
|
+
/** @type { Parser } */
|
|
12
|
+
let prsr;
|
|
13
|
+
|
|
14
|
+
const getParserPromise = webTreeSitterSfapex.getApexParser().then((newParser) => {
|
|
15
|
+
prsr = newParser;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
*
|
|
20
|
+
* @param {Component} comp
|
|
21
|
+
* @returns {ConvertedComponent}
|
|
22
|
+
*/
|
|
23
|
+
function ApexClass(comp) {
|
|
24
|
+
// generate d.ts
|
|
25
|
+
if (prsr === undefined) {
|
|
26
|
+
/** @type { any } */
|
|
27
|
+
const err = new Error("Apex parser not ready yet");
|
|
28
|
+
err.waitFor = getParserPromise; // smuggle a "ready" signal out to the invoker
|
|
29
|
+
throw err;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const [className, declarationContent, mappingContent] =
|
|
33
|
+
buildDeclarationForApexFile(prsr, comp.path, comp.content, false);
|
|
34
|
+
if (declarationContent === undefined) {
|
|
35
|
+
throw new Error("Problem building declaration content");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return { ...comp, declarationContent: declarationContent, mapData: [] };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @typedef DeltaPoint
|
|
43
|
+
* @property {number} rowDelta
|
|
44
|
+
* @property {number} column
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @typedef MappingPos
|
|
49
|
+
* @property {DeltaPoint} startPosition
|
|
50
|
+
* @property {DeltaPoint} endPosition
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @typedef Mapping
|
|
55
|
+
* @property { MappingPos } dPos
|
|
56
|
+
* @property { {startPosition: Parser.Point, endPosition: Parser.Point }?} sPos
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
/** @type { Record<string, string> } */
|
|
60
|
+
const typeOverrides = {
|
|
61
|
+
string: "string",
|
|
62
|
+
datetime: "Date",
|
|
63
|
+
decimal: "number",
|
|
64
|
+
integer: "number",
|
|
65
|
+
boolean: "boolean",
|
|
66
|
+
id: "string",
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/** @type { string[] } */
|
|
70
|
+
let localNestedClasses = [];
|
|
71
|
+
/** @type { string? } */
|
|
72
|
+
let outerClassName;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
*
|
|
76
|
+
* @param {Parser} parser
|
|
77
|
+
* @param {string} relativeFilePath
|
|
78
|
+
* @param {string} apexText
|
|
79
|
+
* @param {boolean} isSobject
|
|
80
|
+
* @returns { string[] }
|
|
81
|
+
*/
|
|
82
|
+
function buildDeclarationForApexFile(
|
|
83
|
+
parser,
|
|
84
|
+
relativeFilePath,
|
|
85
|
+
apexText,
|
|
86
|
+
isSobject = false
|
|
87
|
+
) {
|
|
88
|
+
try {
|
|
89
|
+
const tree = parser.parse(apexText);
|
|
90
|
+
// step into top-level class, if any
|
|
91
|
+
// if outer-most thing isn't a class the leave
|
|
92
|
+
const classes = tree.rootNode.descendantsOfType("class_declaration");
|
|
93
|
+
|
|
94
|
+
if (classes.length === 0 || !classes[0]) {
|
|
95
|
+
return []; // this isn't a class
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// TODO: build mapping file
|
|
99
|
+
const [className, declarationText, mappings] = processClass(
|
|
100
|
+
classes[0],
|
|
101
|
+
isSobject
|
|
102
|
+
);
|
|
103
|
+
const mappingText = getMapFile(className, relativeFilePath, mappings);
|
|
104
|
+
localNestedClasses = [];
|
|
105
|
+
outerClassName = null;
|
|
106
|
+
return [className, declarationText, mappingText];
|
|
107
|
+
} catch (error) {
|
|
108
|
+
console.error(error);
|
|
109
|
+
|
|
110
|
+
localNestedClasses = [];
|
|
111
|
+
outerClassName = null;
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
*
|
|
118
|
+
* @param {Parser.SyntaxNode} classNode
|
|
119
|
+
* @param {boolean} isSobject
|
|
120
|
+
* @returns { [string, string, Mapping[]] }
|
|
121
|
+
*/
|
|
122
|
+
function processClass(classNode, isSobject) {
|
|
123
|
+
let className = classNode.childForFieldName("name")?.text ?? "";
|
|
124
|
+
if (!outerClassName) {
|
|
125
|
+
outerClassName = className;
|
|
126
|
+
}
|
|
127
|
+
let classBody = classNode.childForFieldName("body");
|
|
128
|
+
/** @type {Mapping[]} */
|
|
129
|
+
const mappings = [];
|
|
130
|
+
/** @type {string[]} */
|
|
131
|
+
let outputText = [];
|
|
132
|
+
if (!classBody) {
|
|
133
|
+
return [className, outputText.join(""), mappings];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const methodsToProcess = [];
|
|
137
|
+
const fieldsToProcess = [];
|
|
138
|
+
const classesToProcess = [];
|
|
139
|
+
|
|
140
|
+
for (const cNode of classBody.children) {
|
|
141
|
+
switch (cNode.type) {
|
|
142
|
+
case "method_declaration":
|
|
143
|
+
// gather methods because AuraEnabled methods create their own exports
|
|
144
|
+
methodsToProcess.push(cNode);
|
|
145
|
+
break;
|
|
146
|
+
case "field_declaration":
|
|
147
|
+
fieldsToProcess.push(cNode);
|
|
148
|
+
break;
|
|
149
|
+
case "class_declaration":
|
|
150
|
+
classesToProcess.push(cNode);
|
|
151
|
+
const innerNameNode = cNode.childForFieldName("name");
|
|
152
|
+
if (innerNameNode) {
|
|
153
|
+
// queue up nested class names so type resolution can use them
|
|
154
|
+
localNestedClasses.push(innerNameNode.text);
|
|
155
|
+
}
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (fieldsToProcess.length > 0) {
|
|
160
|
+
outputText.push(`declare interface ${className} {\n`);
|
|
161
|
+
mappings.push({
|
|
162
|
+
dPos: {
|
|
163
|
+
startPosition: {
|
|
164
|
+
rowDelta: 0,
|
|
165
|
+
column: 0,
|
|
166
|
+
},
|
|
167
|
+
endPosition: {
|
|
168
|
+
rowDelta: 0,
|
|
169
|
+
column: 0,
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
sPos: null,
|
|
173
|
+
});
|
|
174
|
+
for (let fNode of fieldsToProcess) {
|
|
175
|
+
const [newOutputText, newMappings] = transformFieldDeclaration(
|
|
176
|
+
fNode,
|
|
177
|
+
isSobject
|
|
178
|
+
);
|
|
179
|
+
outputText.push(newOutputText);
|
|
180
|
+
mappings.push(...newMappings);
|
|
181
|
+
}
|
|
182
|
+
outputText.push("}\n");
|
|
183
|
+
mappings.push({
|
|
184
|
+
dPos: {
|
|
185
|
+
startPosition: {
|
|
186
|
+
rowDelta: 1,
|
|
187
|
+
column: 0,
|
|
188
|
+
},
|
|
189
|
+
endPosition: {
|
|
190
|
+
rowDelta: 0,
|
|
191
|
+
column: 0,
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
sPos: null,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (classesToProcess.length > 0) {
|
|
199
|
+
outputText.push(`declare namespace ${className} {\n`);
|
|
200
|
+
mappings.push({
|
|
201
|
+
dPos: {
|
|
202
|
+
startPosition: {
|
|
203
|
+
rowDelta: 2,
|
|
204
|
+
column: 0,
|
|
205
|
+
},
|
|
206
|
+
endPosition: {
|
|
207
|
+
rowDelta: 0,
|
|
208
|
+
column: 0,
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
sPos: null,
|
|
212
|
+
});
|
|
213
|
+
for (const cNode of classesToProcess) {
|
|
214
|
+
const [className, newOutputText, newMappings] = processClass(
|
|
215
|
+
cNode,
|
|
216
|
+
isSobject
|
|
217
|
+
);
|
|
218
|
+
if (newOutputText.length > 0) {
|
|
219
|
+
outputText.push(" " + newOutputText.replaceAll(/\n(.)/g, "\n $1"));
|
|
220
|
+
mappings.push(...newMappings);
|
|
221
|
+
}
|
|
222
|
+
// TODO: somehow queue up local classes for type resolution
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
outputText.push(`}\n`);
|
|
226
|
+
mappings.push({
|
|
227
|
+
dPos: {
|
|
228
|
+
startPosition: {
|
|
229
|
+
rowDelta: 2,
|
|
230
|
+
column: 0,
|
|
231
|
+
},
|
|
232
|
+
endPosition: {
|
|
233
|
+
rowDelta: 0,
|
|
234
|
+
column: 0,
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
sPos: null,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
for (const mNode of methodsToProcess) {
|
|
242
|
+
const [newOutputText, newMappings] = transformMethodDeclaration(
|
|
243
|
+
className,
|
|
244
|
+
mNode
|
|
245
|
+
);
|
|
246
|
+
outputText.push(newOutputText);
|
|
247
|
+
mappings.push(...newMappings);
|
|
248
|
+
}
|
|
249
|
+
return [className, outputText.join(""), mappings];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
*
|
|
254
|
+
* @param {string} className
|
|
255
|
+
* @param {Parser.SyntaxNode} methodNode
|
|
256
|
+
* @returns {[string, Mapping[]]}
|
|
257
|
+
*/
|
|
258
|
+
function transformMethodDeclaration(className, methodNode) {
|
|
259
|
+
/** @type {Mapping[]} */
|
|
260
|
+
const mappings = [];
|
|
261
|
+
const methodNameNode = methodNode.childForFieldName("name");
|
|
262
|
+
if (!methodNameNode) {
|
|
263
|
+
return ["", mappings];
|
|
264
|
+
}
|
|
265
|
+
const methodName = methodNameNode.text;
|
|
266
|
+
const returnType = methodNode.childForFieldName("type");
|
|
267
|
+
if (!returnType) {
|
|
268
|
+
return ["", mappings];
|
|
269
|
+
}
|
|
270
|
+
const parameters = getMethodParameters(methodNode);
|
|
271
|
+
|
|
272
|
+
const auraEnabledNode = getAuraEnabledAnnotation(methodNode);
|
|
273
|
+
if (!auraEnabledNode) {
|
|
274
|
+
// method doesn't hold the AuraEnabled Annotation
|
|
275
|
+
return ["", mappings];
|
|
276
|
+
}
|
|
277
|
+
const isCacheableEnabled = getAuraEnabledIsCacheable(auraEnabledNode);
|
|
278
|
+
|
|
279
|
+
const [newOutputText, methodNameMapping] = declarationString(
|
|
280
|
+
className,
|
|
281
|
+
methodName,
|
|
282
|
+
getType(returnType),
|
|
283
|
+
parameters,
|
|
284
|
+
isCacheableEnabled
|
|
285
|
+
);
|
|
286
|
+
const declarationParts = newOutputText.split("\n");
|
|
287
|
+
methodNameMapping.rowDelta;
|
|
288
|
+
|
|
289
|
+
mappings.push({
|
|
290
|
+
dPos: {
|
|
291
|
+
startPosition: methodNameMapping,
|
|
292
|
+
endPosition: {
|
|
293
|
+
rowDelta: methodNameMapping.rowDelta,
|
|
294
|
+
column: methodNameMapping.column + methodName.length,
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
sPos: {
|
|
298
|
+
startPosition: methodNameNode.startPosition,
|
|
299
|
+
endPosition: methodNameNode.endPosition,
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
// add consumption of remaining lines
|
|
303
|
+
mappings.push({
|
|
304
|
+
dPos: {
|
|
305
|
+
startPosition: {
|
|
306
|
+
rowDelta: declarationParts.length - methodNameMapping.rowDelta,
|
|
307
|
+
column: 0,
|
|
308
|
+
},
|
|
309
|
+
endPosition: {
|
|
310
|
+
rowDelta: declarationParts.length - methodNameMapping.rowDelta,
|
|
311
|
+
column: 0,
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
sPos: null,
|
|
315
|
+
});
|
|
316
|
+
return [newOutputText, mappings];
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
*
|
|
321
|
+
* @param {Parser.SyntaxNode} node
|
|
322
|
+
* @param {boolean} isSobject
|
|
323
|
+
* @returns {[string, Mapping[]]}
|
|
324
|
+
*/
|
|
325
|
+
function transformFieldDeclaration(node, isSobject) {
|
|
326
|
+
/** @type {string[]} */
|
|
327
|
+
let deprecatedReasons = [];
|
|
328
|
+
let output = "";
|
|
329
|
+
const modifiers = node.descendantsOfType("modifiers");
|
|
330
|
+
/** @type {Mapping} */
|
|
331
|
+
const mapping = {
|
|
332
|
+
dPos: {
|
|
333
|
+
startPosition: {
|
|
334
|
+
rowDelta: 1,
|
|
335
|
+
column: 2,
|
|
336
|
+
},
|
|
337
|
+
endPosition: {
|
|
338
|
+
// TODO
|
|
339
|
+
rowDelta: 0,
|
|
340
|
+
column: 0,
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
sPos: {
|
|
344
|
+
startPosition: node.startPosition,
|
|
345
|
+
endPosition: node.endPosition,
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
if (modifiers.length === 0 || !modifiers[0]) {
|
|
350
|
+
deprecatedReasons.push("public or global");
|
|
351
|
+
} else {
|
|
352
|
+
// initialize so the generated SObjects don't flag missing AuraEnabled
|
|
353
|
+
let isAuraEnabled = isSobject;
|
|
354
|
+
for (const aNode of modifiers[0].descendantsOfType("annotation")) {
|
|
355
|
+
const aNameNode = aNode.childForFieldName("name");
|
|
356
|
+
if (aNameNode && aNameNode.text.toLowerCase() === "auraenabled") {
|
|
357
|
+
isAuraEnabled = true;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
if (!isAuraEnabled) {
|
|
361
|
+
deprecatedReasons.push("@AuraEnabled");
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
let isAtLeastPublic = false;
|
|
365
|
+
for (const mNode of modifiers[0].descendantsOfType("modifier")) {
|
|
366
|
+
switch (mNode.text.toLowerCase()) {
|
|
367
|
+
case "public":
|
|
368
|
+
case "global":
|
|
369
|
+
isAtLeastPublic = true;
|
|
370
|
+
break;
|
|
371
|
+
case "static":
|
|
372
|
+
deprecatedReasons.push("non-static");
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
if (!isAtLeastPublic) {
|
|
377
|
+
deprecatedReasons.push("public or global");
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
/** TODO:
|
|
381
|
+
* determine if AuraEnabled & public or global & not static
|
|
382
|
+
* otherwise mark as deprecated with note (dynamic based on missing access component)
|
|
383
|
+
* {property}: {getType({type})}?;
|
|
384
|
+
*/
|
|
385
|
+
if (deprecatedReasons.length > 0) {
|
|
386
|
+
mapping.dPos.startPosition.rowDelta++;
|
|
387
|
+
output += ` /** @deprecated not exposed; property must be ${deprecatedReasons.join(
|
|
388
|
+
", "
|
|
389
|
+
)} */\n`;
|
|
390
|
+
}
|
|
391
|
+
output += ` ${
|
|
392
|
+
node.childForFieldName("declarator")?.childForFieldName("name")?.text ?? ""
|
|
393
|
+
}?: ${getType(node.childForFieldName("type"))};\n`;
|
|
394
|
+
return [output, [mapping]];
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
*
|
|
399
|
+
* @param {Parser.SyntaxNode} node
|
|
400
|
+
* @returns {boolean}
|
|
401
|
+
*/
|
|
402
|
+
function getAuraEnabledIsCacheable(node) {
|
|
403
|
+
const args = node.childForFieldName("arguments");
|
|
404
|
+
if (args) {
|
|
405
|
+
for (const a of args.descendantsOfType("annotation_key_value")) {
|
|
406
|
+
const k = a.childForFieldName("key");
|
|
407
|
+
const v = a.childForFieldName("value");
|
|
408
|
+
if (
|
|
409
|
+
k &&
|
|
410
|
+
v &&
|
|
411
|
+
k.text.toLowerCase() === "cacheable" &&
|
|
412
|
+
v.text.toLowerCase() === "true"
|
|
413
|
+
) {
|
|
414
|
+
return true;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
*
|
|
423
|
+
* @param {Parser.SyntaxNode} node
|
|
424
|
+
* @returns {Parser.SyntaxNode | null}
|
|
425
|
+
*/
|
|
426
|
+
function getAuraEnabledAnnotation(node) {
|
|
427
|
+
for (const m of node.descendantsOfType("modifiers")) {
|
|
428
|
+
for (const a of m.descendantsOfType("annotation")) {
|
|
429
|
+
if (a.childForFieldName("name")?.text.toLowerCase() === "auraenabled") {
|
|
430
|
+
return a;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
*
|
|
439
|
+
* @param {Parser.SyntaxNode} node
|
|
440
|
+
* @returns {Map<string, string>}
|
|
441
|
+
*/
|
|
442
|
+
function getMethodParameters(node) {
|
|
443
|
+
const params = new Map();
|
|
444
|
+
const parameters = node.childForFieldName("parameters");
|
|
445
|
+
if (!parameters) {
|
|
446
|
+
return params;
|
|
447
|
+
}
|
|
448
|
+
for (const p of parameters.namedChildren) {
|
|
449
|
+
params.set(
|
|
450
|
+
p.childForFieldName("name")?.text,
|
|
451
|
+
getType(p.childForFieldName("type"))
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
return params;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
*
|
|
459
|
+
* @param {string} className
|
|
460
|
+
* @param {string} methodName
|
|
461
|
+
* @param {string} returnType
|
|
462
|
+
* @param {Map<string, string>} params
|
|
463
|
+
* @param {boolean} isCacheable
|
|
464
|
+
* @returns {[string, DeltaPoint]}
|
|
465
|
+
*/
|
|
466
|
+
function declarationString(
|
|
467
|
+
className,
|
|
468
|
+
methodName,
|
|
469
|
+
returnType,
|
|
470
|
+
params,
|
|
471
|
+
isCacheable
|
|
472
|
+
) {
|
|
473
|
+
let text = `declare module "@salesforce/apex/${className}.${methodName}" {
|
|
474
|
+
const ${methodName}: {
|
|
475
|
+
(${
|
|
476
|
+
params.size === 0
|
|
477
|
+
? ""
|
|
478
|
+
: `param: { ${Array.from(params.entries())
|
|
479
|
+
.map(([name, type]) => `${name}: ${type}`)
|
|
480
|
+
.join("; ")} }`
|
|
481
|
+
}): Promise<${returnType}>;${
|
|
482
|
+
// Apex can only be wired if it is cacheable
|
|
483
|
+
// the adapter is the WireAdapterConstructor that the @wire decorator consumes
|
|
484
|
+
!isCacheable
|
|
485
|
+
? ""
|
|
486
|
+
: `
|
|
487
|
+
adapter: import("lwc").WireAdapterConstructor<
|
|
488
|
+
${
|
|
489
|
+
params.size === 0
|
|
490
|
+
? "never"
|
|
491
|
+
: `{ ${Array.from(params.entries())
|
|
492
|
+
.map(([name, type]) => `${name}: ${type}`)
|
|
493
|
+
.join("; ")} }`
|
|
494
|
+
},
|
|
495
|
+
{ error?: any; data?: ${returnType}; }
|
|
496
|
+
>;`
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
export default `;
|
|
500
|
+
// analyze how much text we're adding before the method name
|
|
501
|
+
const lines = text.split("\n");
|
|
502
|
+
/** @type {DeltaPoint} */
|
|
503
|
+
const m = {
|
|
504
|
+
rowDelta: lines.length - 1,
|
|
505
|
+
column: lines[lines.length - 1]?.length ?? 0,
|
|
506
|
+
};
|
|
507
|
+
// finish building
|
|
508
|
+
text += `${methodName};
|
|
509
|
+
}
|
|
510
|
+
`;
|
|
511
|
+
return [text, m];
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
*
|
|
516
|
+
* @param {string} className
|
|
517
|
+
* @param {string} relativeClassPath
|
|
518
|
+
* @param {Mapping[]} mappings
|
|
519
|
+
* @returns
|
|
520
|
+
*/
|
|
521
|
+
function getMapFile(className, relativeClassPath, mappings) {
|
|
522
|
+
let mappingsString = "";
|
|
523
|
+
for (const m of mappings) {
|
|
524
|
+
mappingsString += ";".repeat(m.dPos.startPosition.rowDelta);
|
|
525
|
+
if (!m.sPos) {
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Generated column
|
|
530
|
+
* Original file this appeared in
|
|
531
|
+
* Original line number relative to last given value in this field
|
|
532
|
+
* Original column relative to last given value in this field
|
|
533
|
+
*/
|
|
534
|
+
// const incrementalColumnCount =
|
|
535
|
+
// m.sPos.startPosition.column - lastMethodColumnNum;
|
|
536
|
+
|
|
537
|
+
// mappingsString += `${encode(m.dPos.startPosition.column)}${encode(
|
|
538
|
+
// 0
|
|
539
|
+
// )}${encode(m.sPos.startPosition.row - lastSourceRow)}${encode(
|
|
540
|
+
// incrementalColumnCount
|
|
541
|
+
// )}`;
|
|
542
|
+
|
|
543
|
+
m.sPos.startPosition.column;
|
|
544
|
+
m.sPos.startPosition.row;
|
|
545
|
+
}
|
|
546
|
+
return JSON.stringify({
|
|
547
|
+
version: 3,
|
|
548
|
+
file: `${className}.d.ts`,
|
|
549
|
+
sourceRoot: "",
|
|
550
|
+
// TODO: not so badly hardcoded
|
|
551
|
+
sources: [`../../../../../${relativeClassPath}`],
|
|
552
|
+
names: [],
|
|
553
|
+
mappings: mappingsString,
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
*
|
|
559
|
+
* @param {Parser.SyntaxNode | null} node
|
|
560
|
+
* @returns {string}
|
|
561
|
+
*/
|
|
562
|
+
function getType(node) {
|
|
563
|
+
if (!node) {
|
|
564
|
+
return "";
|
|
565
|
+
}
|
|
566
|
+
// if generic type, see if it is a list or map
|
|
567
|
+
switch (node.type) {
|
|
568
|
+
case "generic_type":
|
|
569
|
+
const genType = getType(node.namedChildren[0]);
|
|
570
|
+
let typeNode = node.namedChildren[1];
|
|
571
|
+
if (!typeNode) {
|
|
572
|
+
return "";
|
|
573
|
+
}
|
|
574
|
+
switch (genType.toLowerCase()) {
|
|
575
|
+
case "list":
|
|
576
|
+
return getType(typeNode.namedChildren[0]) + "[]";
|
|
577
|
+
case "map":
|
|
578
|
+
return `Record<${getType(typeNode.namedChildren[0])}, ${getType(
|
|
579
|
+
typeNode.namedChildren[1]
|
|
580
|
+
)}>`;
|
|
581
|
+
case "set":
|
|
582
|
+
return `Set<${getType(typeNode.namedChildren[0])}>`;
|
|
583
|
+
|
|
584
|
+
default:
|
|
585
|
+
console.error("Unexpected generic type", genType);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
if (localNestedClasses.includes(node.text)) {
|
|
589
|
+
return `${outerClassName}.${node.text}`;
|
|
590
|
+
}
|
|
591
|
+
return typeOverrides[node.text.toLowerCase()] ?? node.text;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/** @import { Component, ConvertedComponent } from "../index.mjs" */
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
*
|
|
598
|
+
* @param {Component} component
|
|
599
|
+
* @returns {ConvertedComponent}
|
|
600
|
+
*/
|
|
601
|
+
function ContentAsset(component) {
|
|
602
|
+
// generate d.ts
|
|
603
|
+
/** @type {any} */
|
|
604
|
+
let asset;
|
|
605
|
+
xml2js.parseString(component.content, (error, result) => {
|
|
606
|
+
if (error) {
|
|
607
|
+
throw error;
|
|
608
|
+
}
|
|
609
|
+
asset = result;
|
|
610
|
+
});
|
|
611
|
+
const dec = `declare module "@salesforce/contentAssetUrl/${asset.ContentAsset.masterLabel}" {
|
|
612
|
+
var ${asset.ContentAsset.masterLabel}: string;
|
|
613
|
+
export default ${asset.ContentAsset.masterLabel};
|
|
614
|
+
}`;
|
|
615
|
+
return { declarationContent: dec, mapData: [], ...component };
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/** @import { Component, ConvertedComponent } from "../index.mjs" */
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
*
|
|
622
|
+
* @param {Component} component
|
|
623
|
+
* @returns {ConvertedComponent}
|
|
624
|
+
*/
|
|
625
|
+
function CustomPermission(component) {
|
|
626
|
+
// generate d.ts
|
|
627
|
+
/** @type {any} */
|
|
628
|
+
let perm;
|
|
629
|
+
xml2js.parseString(component.content, (error, result) => {
|
|
630
|
+
if (error) {
|
|
631
|
+
throw error;
|
|
632
|
+
}
|
|
633
|
+
perm = result;
|
|
634
|
+
});
|
|
635
|
+
const permName = component.path.split("/").pop()?.split(".")[0];
|
|
636
|
+
const dec = `declare module "@salesforce/customPermission/${permName}" {
|
|
637
|
+
/**
|
|
638
|
+
* ${perm.CustomPermission.label}
|
|
639
|
+
*
|
|
640
|
+
* @description ${perm.CustomPermission.description}
|
|
641
|
+
*/
|
|
642
|
+
const has${permName}:boolean;
|
|
643
|
+
export default has${permName};
|
|
644
|
+
}`;
|
|
645
|
+
return { declarationContent: dec, mapData: [], ...component };
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/** @import { Component, ConvertedComponent } from "../index.mjs" */
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
*
|
|
652
|
+
* @param {Component} component
|
|
653
|
+
* @returns {ConvertedComponent}
|
|
654
|
+
*/
|
|
655
|
+
function CustomField(component) {
|
|
656
|
+
// generate d.ts
|
|
657
|
+
/** @type {any} */
|
|
658
|
+
let field;
|
|
659
|
+
xml2js.parseString(component.content, (error, result) => {
|
|
660
|
+
if (error) {
|
|
661
|
+
throw error;
|
|
662
|
+
}
|
|
663
|
+
field = result;
|
|
664
|
+
});
|
|
665
|
+
const pathParts = component.path.split("/");
|
|
666
|
+
const fieldName = pathParts.pop()?.split(".")[0];
|
|
667
|
+
pathParts?.pop(); // discard `fields`
|
|
668
|
+
const objectName = pathParts.pop();
|
|
669
|
+
const dec = `declare module '@salesforce/schema/${objectName}.${fieldName}' {
|
|
670
|
+
/**
|
|
671
|
+
* @description ${field.CustomField.inlineHelpText}
|
|
672
|
+
* @description ${field.CustomField.description}
|
|
673
|
+
*/
|
|
674
|
+
const ${fieldName}: {
|
|
675
|
+
fieldApiName: '${fieldName}';
|
|
676
|
+
objectApiName: '${objectName}';
|
|
677
|
+
}
|
|
678
|
+
export default ${fieldName};
|
|
679
|
+
}`;
|
|
680
|
+
return { declarationContent: dec, mapData: [], ...component };
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/** @import { Component, ConvertedComponent } from "../index.mjs" */
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
*
|
|
687
|
+
* @param {Component} component
|
|
688
|
+
* @returns {ConvertedComponent}
|
|
689
|
+
*/
|
|
690
|
+
function CustomLabels(component) {
|
|
691
|
+
// generate d.ts
|
|
692
|
+
/** @type {any} */
|
|
693
|
+
let labels;
|
|
694
|
+
xml2js.parseString(component.content, (error, result) => {
|
|
695
|
+
if (error) {
|
|
696
|
+
throw error;
|
|
697
|
+
}
|
|
698
|
+
labels = result;
|
|
699
|
+
});
|
|
700
|
+
if (!Array.isArray(labels.CustomLabels.labels)) {
|
|
701
|
+
labels.CustomLabels.labels = [labels.CustomLabels.labels];
|
|
702
|
+
}
|
|
703
|
+
let dec = "";
|
|
704
|
+
for (let label of labels.CustomLabels.labels) {
|
|
705
|
+
dec += `declare module "@salesforce/label/c.${label.fullName}" {
|
|
706
|
+
/**
|
|
707
|
+
* @description ${label.shortDescription}
|
|
708
|
+
*/
|
|
709
|
+
const lbl${label.fullName} = '${label.value}';
|
|
710
|
+
export default lbl${label.fullName};
|
|
711
|
+
}
|
|
712
|
+
`;
|
|
713
|
+
}
|
|
714
|
+
return { declarationContent: dec, mapData: [], ...component };
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/** @import { Component, ConvertedComponent } from "../index.mjs" */
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
*
|
|
721
|
+
* @param {Component} component
|
|
722
|
+
* @returns {ConvertedComponent}
|
|
723
|
+
*/
|
|
724
|
+
function CustomObject(component) {
|
|
725
|
+
// generate d.ts
|
|
726
|
+
/** @type {any} */
|
|
727
|
+
let obj;
|
|
728
|
+
xml2js.parseString(component.content, (error, result) => {
|
|
729
|
+
if (error) {
|
|
730
|
+
throw error;
|
|
731
|
+
}
|
|
732
|
+
obj = result;
|
|
733
|
+
});
|
|
734
|
+
const objectName = component.path.split("/").pop()?.split(".")[0];
|
|
735
|
+
const dec = `declare module '@salesforce/schema/${objectName}' {
|
|
736
|
+
/**
|
|
737
|
+
* @description ${obj.CustomObject.description}
|
|
738
|
+
*/
|
|
739
|
+
const ${objectName}: {
|
|
740
|
+
objectApiName: '${objectName}';
|
|
741
|
+
}
|
|
742
|
+
export default ${objectName};
|
|
743
|
+
}`;
|
|
744
|
+
return { declarationContent: dec, mapData: [], ...component };
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/** @import { Component, ConvertedComponent } from "../index.mjs" */
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
*
|
|
751
|
+
* @param {Component} component
|
|
752
|
+
* @returns {ConvertedComponent}
|
|
753
|
+
*/
|
|
754
|
+
function StaticResource(component) {
|
|
755
|
+
// generate d.ts
|
|
756
|
+
/** @type {any} */
|
|
757
|
+
let resource;
|
|
758
|
+
xml2js.parseString(component.content, (error, result) => {
|
|
759
|
+
if (error) {
|
|
760
|
+
throw error;
|
|
761
|
+
}
|
|
762
|
+
resource = result;
|
|
763
|
+
});
|
|
764
|
+
const resourceName = component.path.split("/").pop()?.split(".")[0];
|
|
765
|
+
const dec = `declare module "@salesforce/resourceUrl/${resourceName}" {
|
|
766
|
+
/**
|
|
767
|
+
* @description ${resource.StaticResource.description}
|
|
768
|
+
* @access ${resource.StaticResource.cacheControl}
|
|
769
|
+
*/
|
|
770
|
+
const ${resourceName}:string;
|
|
771
|
+
export default ${resourceName};
|
|
772
|
+
}`;
|
|
773
|
+
return { declarationContent: dec, mapData: [], ...component };
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
var converters = /*#__PURE__*/Object.freeze({
|
|
777
|
+
__proto__: null,
|
|
778
|
+
ApexClass: ApexClass,
|
|
779
|
+
ContentAsset: ContentAsset,
|
|
780
|
+
CustomField: CustomField,
|
|
781
|
+
CustomLabels: CustomLabels,
|
|
782
|
+
CustomObject: CustomObject,
|
|
783
|
+
CustomPermission: CustomPermission,
|
|
784
|
+
StaticResource: StaticResource
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
// maybe there is a smarter way to do this
|
|
788
|
+
/** @type {Readonly<{[p in keyof converters]: p}>} */
|
|
789
|
+
const MetadataType = Object.freeze(
|
|
790
|
+
Object.keys(converters).reduce((/** @type {any} */ t, v) => {
|
|
791
|
+
t[v] = v;
|
|
792
|
+
return t;
|
|
793
|
+
}, {})
|
|
794
|
+
);
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* @typedef MapPair
|
|
798
|
+
* @property {number} sourcePos
|
|
799
|
+
* @property {number} destPos
|
|
800
|
+
*/
|
|
801
|
+
|
|
802
|
+
/**
|
|
803
|
+
* @typedef Component
|
|
804
|
+
* @property {string} path
|
|
805
|
+
* @property {keyof converters} type
|
|
806
|
+
* @property {string} content
|
|
807
|
+
*/
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* @typedef { Component & {
|
|
811
|
+
* declarationContent: string;
|
|
812
|
+
* mapData: MapPair[]}} ConvertedComponent
|
|
813
|
+
*/
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
*
|
|
817
|
+
* @param {Component[]} components
|
|
818
|
+
* @returns {ConvertedComponent[]}
|
|
819
|
+
*/
|
|
820
|
+
function convert(components) {
|
|
821
|
+
/** @type {ConvertedComponent[]} */
|
|
822
|
+
const convertedComponents = [];
|
|
823
|
+
for (const c of components) {
|
|
824
|
+
if (!converters[c.type]) {
|
|
825
|
+
throw new Error(
|
|
826
|
+
"Unsupported Metadata Type:" +
|
|
827
|
+
c.type +
|
|
828
|
+
", must be one of " +
|
|
829
|
+
Object.keys(converters)
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
convertedComponents.push(converters[c.type](c));
|
|
833
|
+
}
|
|
834
|
+
return convertedComponents;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
exports.MetadataType = MetadataType;
|
|
838
|
+
exports.convert = convert;
|