@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
package/README.MD
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
The purpose of this library is to take Salesforce Metadata content and transpile it to TypeScript Declaration (d.ts format) content so it can be written to a file or proxied on file reads for editors and language servers.
|
|
2
|
+
|
|
3
|
+
This library does not engage with file systems and is not responsible for discovering or identifying available metadata.
|
|
4
|
+
|
|
5
|
+
Apex should include raw AuraEnabled, AuraEnabled + cacheable, classes with fields (fields not AuraEnabled should show as deprecated with a note), and Apex Continuations, should also carry forward ApexDoc text comments so it can be exposed on definitions.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { ApexClass } from "./apex-class.mjs";
|
|
2
|
+
export { ContentAsset } from "./content-asset.mjs";
|
|
3
|
+
export { CustomPermission } from "./custom-permission.mjs";
|
|
4
|
+
export { CustomField } from "./custom-field.mjs";
|
|
5
|
+
export { CustomLabels } from "./custom-labels.mjs";
|
|
6
|
+
export { CustomObject } from "./custom-object.mjs";
|
|
7
|
+
export { StaticResource } from "./static-resource.mjs";
|
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
/** @import Parser from "web-tree-sitter" */
|
|
2
|
+
/** @import { Component, ConvertedComponent } from "../index.mjs" */
|
|
3
|
+
import { getApexParser } from "web-tree-sitter-sfapex";
|
|
4
|
+
|
|
5
|
+
// TOOD: Apex Continuation
|
|
6
|
+
|
|
7
|
+
/** @type { Parser } */
|
|
8
|
+
let prsr;
|
|
9
|
+
|
|
10
|
+
const getParserPromise = getApexParser().then((newParser) => {
|
|
11
|
+
prsr = newParser;
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
*
|
|
16
|
+
* @param {Component} comp
|
|
17
|
+
* @returns {ConvertedComponent}
|
|
18
|
+
*/
|
|
19
|
+
export function ApexClass(comp) {
|
|
20
|
+
// generate d.ts
|
|
21
|
+
if (prsr === undefined) {
|
|
22
|
+
/** @type { any } */
|
|
23
|
+
const err = new Error("Apex parser not ready yet");
|
|
24
|
+
err.waitFor = getParserPromise; // smuggle a "ready" signal out to the invoker
|
|
25
|
+
throw err;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const [className, declarationContent, mappingContent] =
|
|
29
|
+
buildDeclarationForApexFile(prsr, comp.path, comp.content, false);
|
|
30
|
+
if (declarationContent === undefined) {
|
|
31
|
+
throw new Error("Problem building declaration content");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return { ...comp, declarationContent: declarationContent, mapData: [] };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @typedef DeltaPoint
|
|
39
|
+
* @property {number} rowDelta
|
|
40
|
+
* @property {number} column
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @typedef MappingPos
|
|
45
|
+
* @property {DeltaPoint} startPosition
|
|
46
|
+
* @property {DeltaPoint} endPosition
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @typedef Mapping
|
|
51
|
+
* @property { MappingPos } dPos
|
|
52
|
+
* @property { {startPosition: Parser.Point, endPosition: Parser.Point }?} sPos
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
/** @type { Record<string, string> } */
|
|
56
|
+
const typeOverrides = {
|
|
57
|
+
string: "string",
|
|
58
|
+
datetime: "Date",
|
|
59
|
+
decimal: "number",
|
|
60
|
+
integer: "number",
|
|
61
|
+
boolean: "boolean",
|
|
62
|
+
id: "string",
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/** @type { string[] } */
|
|
66
|
+
let localNestedClasses = [];
|
|
67
|
+
/** @type { string? } */
|
|
68
|
+
let outerClassName;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
*
|
|
72
|
+
* @param {Parser} parser
|
|
73
|
+
* @param {string} relativeFilePath
|
|
74
|
+
* @param {string} apexText
|
|
75
|
+
* @param {boolean} isSobject
|
|
76
|
+
* @returns { string[] }
|
|
77
|
+
*/
|
|
78
|
+
export function buildDeclarationForApexFile(
|
|
79
|
+
parser,
|
|
80
|
+
relativeFilePath,
|
|
81
|
+
apexText,
|
|
82
|
+
isSobject = false
|
|
83
|
+
) {
|
|
84
|
+
try {
|
|
85
|
+
const tree = parser.parse(apexText);
|
|
86
|
+
// step into top-level class, if any
|
|
87
|
+
// if outer-most thing isn't a class the leave
|
|
88
|
+
const classes = tree.rootNode.descendantsOfType("class_declaration");
|
|
89
|
+
|
|
90
|
+
if (classes.length === 0 || !classes[0]) {
|
|
91
|
+
return []; // this isn't a class
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// TODO: build mapping file
|
|
95
|
+
const [className, declarationText, mappings] = processClass(
|
|
96
|
+
classes[0],
|
|
97
|
+
isSobject
|
|
98
|
+
);
|
|
99
|
+
const mappingText = getMapFile(className, relativeFilePath, mappings);
|
|
100
|
+
localNestedClasses = [];
|
|
101
|
+
outerClassName = null;
|
|
102
|
+
return [className, declarationText, mappingText];
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.error(error);
|
|
105
|
+
|
|
106
|
+
localNestedClasses = [];
|
|
107
|
+
outerClassName = null;
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
*
|
|
114
|
+
* @param {Parser.SyntaxNode} classNode
|
|
115
|
+
* @param {boolean} isSobject
|
|
116
|
+
* @returns { [string, string, Mapping[]] }
|
|
117
|
+
*/
|
|
118
|
+
function processClass(classNode, isSobject) {
|
|
119
|
+
let className = classNode.childForFieldName("name")?.text ?? "";
|
|
120
|
+
if (!outerClassName) {
|
|
121
|
+
outerClassName = className;
|
|
122
|
+
}
|
|
123
|
+
let classBody = classNode.childForFieldName("body");
|
|
124
|
+
/** @type {Mapping[]} */
|
|
125
|
+
const mappings = [];
|
|
126
|
+
/** @type {string[]} */
|
|
127
|
+
let outputText = [];
|
|
128
|
+
if (!classBody) {
|
|
129
|
+
return [className, outputText.join(""), mappings];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const methodsToProcess = [];
|
|
133
|
+
const fieldsToProcess = [];
|
|
134
|
+
const classesToProcess = [];
|
|
135
|
+
|
|
136
|
+
for (const cNode of classBody.children) {
|
|
137
|
+
switch (cNode.type) {
|
|
138
|
+
case "method_declaration":
|
|
139
|
+
// gather methods because AuraEnabled methods create their own exports
|
|
140
|
+
methodsToProcess.push(cNode);
|
|
141
|
+
break;
|
|
142
|
+
case "field_declaration":
|
|
143
|
+
fieldsToProcess.push(cNode);
|
|
144
|
+
break;
|
|
145
|
+
case "class_declaration":
|
|
146
|
+
classesToProcess.push(cNode);
|
|
147
|
+
const innerNameNode = cNode.childForFieldName("name");
|
|
148
|
+
if (innerNameNode) {
|
|
149
|
+
// queue up nested class names so type resolution can use them
|
|
150
|
+
localNestedClasses.push(innerNameNode.text);
|
|
151
|
+
}
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (fieldsToProcess.length > 0) {
|
|
156
|
+
outputText.push(`declare interface ${className} {\n`);
|
|
157
|
+
mappings.push({
|
|
158
|
+
dPos: {
|
|
159
|
+
startPosition: {
|
|
160
|
+
rowDelta: 0,
|
|
161
|
+
column: 0,
|
|
162
|
+
},
|
|
163
|
+
endPosition: {
|
|
164
|
+
rowDelta: 0,
|
|
165
|
+
column: 0,
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
sPos: null,
|
|
169
|
+
});
|
|
170
|
+
for (let fNode of fieldsToProcess) {
|
|
171
|
+
const [newOutputText, newMappings] = transformFieldDeclaration(
|
|
172
|
+
fNode,
|
|
173
|
+
isSobject
|
|
174
|
+
);
|
|
175
|
+
outputText.push(newOutputText);
|
|
176
|
+
mappings.push(...newMappings);
|
|
177
|
+
}
|
|
178
|
+
outputText.push("}\n");
|
|
179
|
+
mappings.push({
|
|
180
|
+
dPos: {
|
|
181
|
+
startPosition: {
|
|
182
|
+
rowDelta: 1,
|
|
183
|
+
column: 0,
|
|
184
|
+
},
|
|
185
|
+
endPosition: {
|
|
186
|
+
rowDelta: 0,
|
|
187
|
+
column: 0,
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
sPos: null,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (classesToProcess.length > 0) {
|
|
195
|
+
outputText.push(`declare namespace ${className} {\n`);
|
|
196
|
+
mappings.push({
|
|
197
|
+
dPos: {
|
|
198
|
+
startPosition: {
|
|
199
|
+
rowDelta: 2,
|
|
200
|
+
column: 0,
|
|
201
|
+
},
|
|
202
|
+
endPosition: {
|
|
203
|
+
rowDelta: 0,
|
|
204
|
+
column: 0,
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
sPos: null,
|
|
208
|
+
});
|
|
209
|
+
for (const cNode of classesToProcess) {
|
|
210
|
+
const [className, newOutputText, newMappings] = processClass(
|
|
211
|
+
cNode,
|
|
212
|
+
isSobject
|
|
213
|
+
);
|
|
214
|
+
if (newOutputText.length > 0) {
|
|
215
|
+
outputText.push(" " + newOutputText.replaceAll(/\n(.)/g, "\n $1"));
|
|
216
|
+
mappings.push(...newMappings);
|
|
217
|
+
}
|
|
218
|
+
// TODO: somehow queue up local classes for type resolution
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
outputText.push(`}\n`);
|
|
222
|
+
mappings.push({
|
|
223
|
+
dPos: {
|
|
224
|
+
startPosition: {
|
|
225
|
+
rowDelta: 2,
|
|
226
|
+
column: 0,
|
|
227
|
+
},
|
|
228
|
+
endPosition: {
|
|
229
|
+
rowDelta: 0,
|
|
230
|
+
column: 0,
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
sPos: null,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
for (const mNode of methodsToProcess) {
|
|
238
|
+
const [newOutputText, newMappings] = transformMethodDeclaration(
|
|
239
|
+
className,
|
|
240
|
+
mNode
|
|
241
|
+
);
|
|
242
|
+
outputText.push(newOutputText);
|
|
243
|
+
mappings.push(...newMappings);
|
|
244
|
+
}
|
|
245
|
+
return [className, outputText.join(""), mappings];
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
*
|
|
250
|
+
* @param {string} className
|
|
251
|
+
* @param {Parser.SyntaxNode} methodNode
|
|
252
|
+
* @returns {[string, Mapping[]]}
|
|
253
|
+
*/
|
|
254
|
+
function transformMethodDeclaration(className, methodNode) {
|
|
255
|
+
/** @type {Mapping[]} */
|
|
256
|
+
const mappings = [];
|
|
257
|
+
const methodNameNode = methodNode.childForFieldName("name");
|
|
258
|
+
if (!methodNameNode) {
|
|
259
|
+
return ["", mappings];
|
|
260
|
+
}
|
|
261
|
+
const methodName = methodNameNode.text;
|
|
262
|
+
const returnType = methodNode.childForFieldName("type");
|
|
263
|
+
if (!returnType) {
|
|
264
|
+
return ["", mappings];
|
|
265
|
+
}
|
|
266
|
+
const parameters = getMethodParameters(methodNode);
|
|
267
|
+
|
|
268
|
+
const auraEnabledNode = getAuraEnabledAnnotation(methodNode);
|
|
269
|
+
if (!auraEnabledNode) {
|
|
270
|
+
// method doesn't hold the AuraEnabled Annotation
|
|
271
|
+
return ["", mappings];
|
|
272
|
+
}
|
|
273
|
+
const isCacheableEnabled = getAuraEnabledIsCacheable(auraEnabledNode);
|
|
274
|
+
|
|
275
|
+
const [newOutputText, methodNameMapping] = declarationString(
|
|
276
|
+
className,
|
|
277
|
+
methodName,
|
|
278
|
+
getType(returnType),
|
|
279
|
+
parameters,
|
|
280
|
+
isCacheableEnabled
|
|
281
|
+
);
|
|
282
|
+
const declarationParts = newOutputText.split("\n");
|
|
283
|
+
methodNameMapping.rowDelta;
|
|
284
|
+
|
|
285
|
+
mappings.push({
|
|
286
|
+
dPos: {
|
|
287
|
+
startPosition: methodNameMapping,
|
|
288
|
+
endPosition: {
|
|
289
|
+
rowDelta: methodNameMapping.rowDelta,
|
|
290
|
+
column: methodNameMapping.column + methodName.length,
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
sPos: {
|
|
294
|
+
startPosition: methodNameNode.startPosition,
|
|
295
|
+
endPosition: methodNameNode.endPosition,
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
// add consumption of remaining lines
|
|
299
|
+
mappings.push({
|
|
300
|
+
dPos: {
|
|
301
|
+
startPosition: {
|
|
302
|
+
rowDelta: declarationParts.length - methodNameMapping.rowDelta,
|
|
303
|
+
column: 0,
|
|
304
|
+
},
|
|
305
|
+
endPosition: {
|
|
306
|
+
rowDelta: declarationParts.length - methodNameMapping.rowDelta,
|
|
307
|
+
column: 0,
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
sPos: null,
|
|
311
|
+
});
|
|
312
|
+
return [newOutputText, mappings];
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
*
|
|
317
|
+
* @param {Parser.SyntaxNode} node
|
|
318
|
+
* @param {boolean} isSobject
|
|
319
|
+
* @returns {[string, Mapping[]]}
|
|
320
|
+
*/
|
|
321
|
+
function transformFieldDeclaration(node, isSobject) {
|
|
322
|
+
/** @type {string[]} */
|
|
323
|
+
let deprecatedReasons = [];
|
|
324
|
+
let output = "";
|
|
325
|
+
const modifiers = node.descendantsOfType("modifiers");
|
|
326
|
+
/** @type {Mapping} */
|
|
327
|
+
const mapping = {
|
|
328
|
+
dPos: {
|
|
329
|
+
startPosition: {
|
|
330
|
+
rowDelta: 1,
|
|
331
|
+
column: 2,
|
|
332
|
+
},
|
|
333
|
+
endPosition: {
|
|
334
|
+
// TODO
|
|
335
|
+
rowDelta: 0,
|
|
336
|
+
column: 0,
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
sPos: {
|
|
340
|
+
startPosition: node.startPosition,
|
|
341
|
+
endPosition: node.endPosition,
|
|
342
|
+
},
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
if (modifiers.length === 0 || !modifiers[0]) {
|
|
346
|
+
deprecatedReasons.push("public or global");
|
|
347
|
+
} else {
|
|
348
|
+
// initialize so the generated SObjects don't flag missing AuraEnabled
|
|
349
|
+
let isAuraEnabled = isSobject;
|
|
350
|
+
for (const aNode of modifiers[0].descendantsOfType("annotation")) {
|
|
351
|
+
const aNameNode = aNode.childForFieldName("name");
|
|
352
|
+
if (aNameNode && aNameNode.text.toLowerCase() === "auraenabled") {
|
|
353
|
+
isAuraEnabled = true;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (!isAuraEnabled) {
|
|
357
|
+
deprecatedReasons.push("@AuraEnabled");
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
let isAtLeastPublic = false;
|
|
361
|
+
for (const mNode of modifiers[0].descendantsOfType("modifier")) {
|
|
362
|
+
switch (mNode.text.toLowerCase()) {
|
|
363
|
+
case "public":
|
|
364
|
+
case "global":
|
|
365
|
+
isAtLeastPublic = true;
|
|
366
|
+
break;
|
|
367
|
+
case "static":
|
|
368
|
+
deprecatedReasons.push("non-static");
|
|
369
|
+
break;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (!isAtLeastPublic) {
|
|
373
|
+
deprecatedReasons.push("public or global");
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
/** TODO:
|
|
377
|
+
* determine if AuraEnabled & public or global & not static
|
|
378
|
+
* otherwise mark as deprecated with note (dynamic based on missing access component)
|
|
379
|
+
* {property}: {getType({type})}?;
|
|
380
|
+
*/
|
|
381
|
+
if (deprecatedReasons.length > 0) {
|
|
382
|
+
mapping.dPos.startPosition.rowDelta++;
|
|
383
|
+
output += ` /** @deprecated not exposed; property must be ${deprecatedReasons.join(
|
|
384
|
+
", "
|
|
385
|
+
)} */\n`;
|
|
386
|
+
}
|
|
387
|
+
output += ` ${
|
|
388
|
+
node.childForFieldName("declarator")?.childForFieldName("name")?.text ?? ""
|
|
389
|
+
}?: ${getType(node.childForFieldName("type"))};\n`;
|
|
390
|
+
return [output, [mapping]];
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
*
|
|
395
|
+
* @param {Parser.SyntaxNode} node
|
|
396
|
+
* @returns {boolean}
|
|
397
|
+
*/
|
|
398
|
+
function getAuraEnabledIsCacheable(node) {
|
|
399
|
+
const args = node.childForFieldName("arguments");
|
|
400
|
+
if (args) {
|
|
401
|
+
for (const a of args.descendantsOfType("annotation_key_value")) {
|
|
402
|
+
const k = a.childForFieldName("key");
|
|
403
|
+
const v = a.childForFieldName("value");
|
|
404
|
+
if (
|
|
405
|
+
k &&
|
|
406
|
+
v &&
|
|
407
|
+
k.text.toLowerCase() === "cacheable" &&
|
|
408
|
+
v.text.toLowerCase() === "true"
|
|
409
|
+
) {
|
|
410
|
+
return true;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
*
|
|
419
|
+
* @param {Parser.SyntaxNode} node
|
|
420
|
+
* @returns {Parser.SyntaxNode | null}
|
|
421
|
+
*/
|
|
422
|
+
function getAuraEnabledAnnotation(node) {
|
|
423
|
+
for (const m of node.descendantsOfType("modifiers")) {
|
|
424
|
+
for (const a of m.descendantsOfType("annotation")) {
|
|
425
|
+
if (a.childForFieldName("name")?.text.toLowerCase() === "auraenabled") {
|
|
426
|
+
return a;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
*
|
|
435
|
+
* @param {Parser.SyntaxNode} node
|
|
436
|
+
* @returns {Map<string, string>}
|
|
437
|
+
*/
|
|
438
|
+
function getMethodParameters(node) {
|
|
439
|
+
const params = new Map();
|
|
440
|
+
const parameters = node.childForFieldName("parameters");
|
|
441
|
+
if (!parameters) {
|
|
442
|
+
return params;
|
|
443
|
+
}
|
|
444
|
+
for (const p of parameters.namedChildren) {
|
|
445
|
+
params.set(
|
|
446
|
+
p.childForFieldName("name")?.text,
|
|
447
|
+
getType(p.childForFieldName("type"))
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
return params;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
*
|
|
455
|
+
* @param {string} className
|
|
456
|
+
* @param {string} methodName
|
|
457
|
+
* @param {string} returnType
|
|
458
|
+
* @param {Map<string, string>} params
|
|
459
|
+
* @param {boolean} isCacheable
|
|
460
|
+
* @returns {[string, DeltaPoint]}
|
|
461
|
+
*/
|
|
462
|
+
function declarationString(
|
|
463
|
+
className,
|
|
464
|
+
methodName,
|
|
465
|
+
returnType,
|
|
466
|
+
params,
|
|
467
|
+
isCacheable
|
|
468
|
+
) {
|
|
469
|
+
let text = `declare module "@salesforce/apex/${className}.${methodName}" {
|
|
470
|
+
const ${methodName}: {
|
|
471
|
+
(${
|
|
472
|
+
params.size === 0
|
|
473
|
+
? ""
|
|
474
|
+
: `param: { ${Array.from(params.entries())
|
|
475
|
+
.map(([name, type]) => `${name}: ${type}`)
|
|
476
|
+
.join("; ")} }`
|
|
477
|
+
}): Promise<${returnType}>;${
|
|
478
|
+
// Apex can only be wired if it is cacheable
|
|
479
|
+
// the adapter is the WireAdapterConstructor that the @wire decorator consumes
|
|
480
|
+
!isCacheable
|
|
481
|
+
? ""
|
|
482
|
+
: `
|
|
483
|
+
adapter: import("lwc").WireAdapterConstructor<
|
|
484
|
+
${
|
|
485
|
+
params.size === 0
|
|
486
|
+
? "never"
|
|
487
|
+
: `{ ${Array.from(params.entries())
|
|
488
|
+
.map(([name, type]) => `${name}: ${type}`)
|
|
489
|
+
.join("; ")} }`
|
|
490
|
+
},
|
|
491
|
+
{ error?: any; data?: ${returnType}; }
|
|
492
|
+
>;`
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
export default `;
|
|
496
|
+
// analyze how much text we're adding before the method name
|
|
497
|
+
const lines = text.split("\n");
|
|
498
|
+
/** @type {DeltaPoint} */
|
|
499
|
+
const m = {
|
|
500
|
+
rowDelta: lines.length - 1,
|
|
501
|
+
column: lines[lines.length - 1]?.length ?? 0,
|
|
502
|
+
};
|
|
503
|
+
// finish building
|
|
504
|
+
text += `${methodName};
|
|
505
|
+
}
|
|
506
|
+
`;
|
|
507
|
+
return [text, m];
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
*
|
|
512
|
+
* @param {string} className
|
|
513
|
+
* @param {string} relativeClassPath
|
|
514
|
+
* @param {Mapping[]} mappings
|
|
515
|
+
* @returns
|
|
516
|
+
*/
|
|
517
|
+
function getMapFile(className, relativeClassPath, mappings) {
|
|
518
|
+
let mappingsString = "";
|
|
519
|
+
let lastSourceRow = 0;
|
|
520
|
+
let lastMethodColumnNum = 0;
|
|
521
|
+
for (const m of mappings) {
|
|
522
|
+
mappingsString += ";".repeat(m.dPos.startPosition.rowDelta);
|
|
523
|
+
if (!m.sPos) {
|
|
524
|
+
continue;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Generated column
|
|
528
|
+
* Original file this appeared in
|
|
529
|
+
* Original line number relative to last given value in this field
|
|
530
|
+
* Original column relative to last given value in this field
|
|
531
|
+
*/
|
|
532
|
+
// const incrementalColumnCount =
|
|
533
|
+
// m.sPos.startPosition.column - lastMethodColumnNum;
|
|
534
|
+
|
|
535
|
+
// mappingsString += `${encode(m.dPos.startPosition.column)}${encode(
|
|
536
|
+
// 0
|
|
537
|
+
// )}${encode(m.sPos.startPosition.row - lastSourceRow)}${encode(
|
|
538
|
+
// incrementalColumnCount
|
|
539
|
+
// )}`;
|
|
540
|
+
|
|
541
|
+
lastMethodColumnNum = m.sPos.startPosition.column;
|
|
542
|
+
lastSourceRow = m.sPos.startPosition.row;
|
|
543
|
+
}
|
|
544
|
+
return JSON.stringify({
|
|
545
|
+
version: 3,
|
|
546
|
+
file: `${className}.d.ts`,
|
|
547
|
+
sourceRoot: "",
|
|
548
|
+
// TODO: not so badly hardcoded
|
|
549
|
+
sources: [`../../../../../${relativeClassPath}`],
|
|
550
|
+
names: [],
|
|
551
|
+
mappings: mappingsString,
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
*
|
|
557
|
+
* @param {Parser.SyntaxNode | null} node
|
|
558
|
+
* @returns {string}
|
|
559
|
+
*/
|
|
560
|
+
function getType(node) {
|
|
561
|
+
if (!node) {
|
|
562
|
+
return "";
|
|
563
|
+
}
|
|
564
|
+
// if generic type, see if it is a list or map
|
|
565
|
+
switch (node.type) {
|
|
566
|
+
case "generic_type":
|
|
567
|
+
const genType = getType(node.namedChildren[0]);
|
|
568
|
+
let typeNode = node.namedChildren[1];
|
|
569
|
+
if (!typeNode) {
|
|
570
|
+
return "";
|
|
571
|
+
}
|
|
572
|
+
switch (genType.toLowerCase()) {
|
|
573
|
+
case "list":
|
|
574
|
+
return getType(typeNode.namedChildren[0]) + "[]";
|
|
575
|
+
case "map":
|
|
576
|
+
return `Record<${getType(typeNode.namedChildren[0])}, ${getType(
|
|
577
|
+
typeNode.namedChildren[1]
|
|
578
|
+
)}>`;
|
|
579
|
+
case "set":
|
|
580
|
+
return `Set<${getType(typeNode.namedChildren[0])}>`;
|
|
581
|
+
|
|
582
|
+
default:
|
|
583
|
+
console.error("Unexpected generic type", genType);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
if (localNestedClasses.includes(node.text)) {
|
|
587
|
+
return `${outerClassName}.${node.text}`;
|
|
588
|
+
}
|
|
589
|
+
return typeOverrides[node.text.toLowerCase()] ?? node.text;
|
|
590
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** @import { Component, ConvertedComponent } from "../index.mjs" */
|
|
2
|
+
import { parseString } from "xml2js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param {Component} component
|
|
7
|
+
* @returns {ConvertedComponent}
|
|
8
|
+
*/
|
|
9
|
+
export function ContentAsset(component) {
|
|
10
|
+
// generate d.ts
|
|
11
|
+
/** @type {any} */
|
|
12
|
+
let asset;
|
|
13
|
+
parseString(component.content, (error, result) => {
|
|
14
|
+
if (error) {
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
asset = result;
|
|
18
|
+
});
|
|
19
|
+
const dec = `declare module "@salesforce/contentAssetUrl/${asset.ContentAsset.masterLabel}" {
|
|
20
|
+
var ${asset.ContentAsset.masterLabel}: string;
|
|
21
|
+
export default ${asset.ContentAsset.masterLabel};
|
|
22
|
+
}`;
|
|
23
|
+
return { declarationContent: dec, mapData: [], ...component };
|
|
24
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** @import { Component, ConvertedComponent } from "../index.mjs" */
|
|
2
|
+
import { parseString } from "xml2js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param {Component} component
|
|
7
|
+
* @returns {ConvertedComponent}
|
|
8
|
+
*/
|
|
9
|
+
export function CustomField(component) {
|
|
10
|
+
// generate d.ts
|
|
11
|
+
/** @type {any} */
|
|
12
|
+
let field;
|
|
13
|
+
parseString(component.content, (error, result) => {
|
|
14
|
+
if (error) {
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
field = result;
|
|
18
|
+
});
|
|
19
|
+
const pathParts = component.path.split("/");
|
|
20
|
+
const fieldName = pathParts.pop()?.split(".")[0];
|
|
21
|
+
pathParts?.pop(); // discard `fields`
|
|
22
|
+
const objectName = pathParts.pop();
|
|
23
|
+
const dec = `declare module '@salesforce/schema/${objectName}.${fieldName}' {
|
|
24
|
+
/**
|
|
25
|
+
* @description ${field.CustomField.inlineHelpText}
|
|
26
|
+
* @description ${field.CustomField.description}
|
|
27
|
+
*/
|
|
28
|
+
const ${fieldName}: {
|
|
29
|
+
fieldApiName: '${fieldName}';
|
|
30
|
+
objectApiName: '${objectName}';
|
|
31
|
+
}
|
|
32
|
+
export default ${fieldName};
|
|
33
|
+
}`;
|
|
34
|
+
return { declarationContent: dec, mapData: [], ...component };
|
|
35
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** @import { Component, ConvertedComponent } from "../index.mjs" */
|
|
2
|
+
import { parseString } from "xml2js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param {Component} component
|
|
7
|
+
* @returns {ConvertedComponent}
|
|
8
|
+
*/
|
|
9
|
+
export function CustomLabels(component) {
|
|
10
|
+
// generate d.ts
|
|
11
|
+
/** @type {any} */
|
|
12
|
+
let labels;
|
|
13
|
+
parseString(component.content, (error, result) => {
|
|
14
|
+
if (error) {
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
labels = result;
|
|
18
|
+
});
|
|
19
|
+
if (!Array.isArray(labels.CustomLabels.labels)) {
|
|
20
|
+
labels.CustomLabels.labels = [labels.CustomLabels.labels];
|
|
21
|
+
}
|
|
22
|
+
let dec = "";
|
|
23
|
+
for (let label of labels.CustomLabels.labels) {
|
|
24
|
+
dec += `declare module "@salesforce/label/c.${label.fullName}" {
|
|
25
|
+
/**
|
|
26
|
+
* @description ${label.shortDescription}
|
|
27
|
+
*/
|
|
28
|
+
const lbl${label.fullName} = '${label.value}';
|
|
29
|
+
export default lbl${label.fullName};
|
|
30
|
+
}
|
|
31
|
+
`;
|
|
32
|
+
}
|
|
33
|
+
return { declarationContent: dec, mapData: [], ...component };
|
|
34
|
+
}
|