@lwc-bolts/sf2ts 0.0.4 → 1.0.0
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/LICENSE.md +7 -0
- package/README.md +285 -0
- package/dist/apex/apexdoc.d.ts +19 -0
- package/dist/apex/apexdoc.js +79 -0
- package/dist/apex/file.d.ts +37 -0
- package/dist/apex/file.js +623 -0
- package/dist/apex/library.config.d.ts +23 -0
- package/dist/apex/library.config.js +17 -0
- package/dist/apex/library.d.ts +54 -0
- package/dist/apex/library.generated.d.ts +2 -0
- package/dist/apex/library.generated.js +215 -0
- package/dist/apex/library.js +47 -0
- package/dist/apex/namespace.d.ts +32 -0
- package/dist/apex/namespace.js +41 -0
- package/dist/apex/resolution.d.ts +77 -0
- package/dist/apex/resolution.js +306 -0
- package/dist/apex/sobjects.d.ts +45 -0
- package/dist/apex/sobjects.js +141 -0
- package/dist/context.d.ts +16 -0
- package/dist/context.js +28 -0
- package/dist/converters/apex-class.d.ts +7 -0
- package/dist/converters/apex-class.js +23 -0
- package/dist/converters/content-asset.d.ts +3 -0
- package/dist/converters/content-asset.js +20 -0
- package/dist/converters/custom-field.d.ts +3 -0
- package/dist/converters/custom-field.js +107 -0
- package/dist/converters/custom-label.d.ts +10 -0
- package/dist/converters/custom-label.js +36 -0
- package/dist/converters/custom-labels.d.ts +3 -0
- package/dist/converters/custom-labels.js +10 -0
- package/dist/converters/custom-object.d.ts +3 -0
- package/dist/converters/custom-object.js +37 -0
- package/dist/converters/custom-permission.d.ts +3 -0
- package/dist/converters/custom-permission.js +21 -0
- package/dist/converters/lightning-component-bundle.d.ts +7 -0
- package/dist/converters/lightning-component-bundle.js +31 -0
- package/dist/converters/lightning-message-channel.d.ts +10 -0
- package/dist/converters/lightning-message-channel.js +39 -0
- package/dist/converters/registry.d.ts +22 -0
- package/dist/converters/registry.js +18 -0
- package/dist/converters/static-resource.d.ts +3 -0
- package/dist/converters/static-resource.js +20 -0
- package/dist/diagnostics.d.ts +5 -0
- package/dist/diagnostics.js +18 -0
- package/dist/identifiers.d.ts +12 -0
- package/dist/identifiers.js +19 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +47 -0
- package/dist/jsdoc.d.ts +14 -0
- package/dist/jsdoc.js +28 -0
- package/dist/lightning-component-bundle/component-source.d.ts +6 -0
- package/dist/lightning-component-bundle/component-source.js +21 -0
- package/dist/lightning-component-bundle/metadata.d.ts +36 -0
- package/dist/lightning-component-bundle/metadata.js +93 -0
- package/dist/lightning-component-bundle/templates/ast.d.ts +47 -0
- package/dist/lightning-component-bundle/templates/ast.js +46 -0
- package/dist/lightning-component-bundle/templates/base-components.d.ts +6 -0
- package/dist/lightning-component-bundle/templates/base-components.js +17 -0
- package/dist/lightning-component-bundle/templates/declarations.d.ts +30 -0
- package/dist/lightning-component-bundle/templates/declarations.js +82 -0
- package/dist/lightning-component-bundle/templates/expressions.d.ts +16 -0
- package/dist/lightning-component-bundle/templates/expressions.js +33 -0
- package/dist/lightning-component-bundle/templates/helpers.d.ts +10 -0
- package/dist/lightning-component-bundle/templates/helpers.js +47 -0
- package/dist/lightning-component-bundle/templates/slots.d.ts +24 -0
- package/dist/lightning-component-bundle/templates/slots.js +78 -0
- package/dist/lightning-component-bundle/templates/source.d.ts +36 -0
- package/dist/lightning-component-bundle/templates/source.js +81 -0
- package/dist/lightning-component-bundle/templates/tags.d.ts +41 -0
- package/dist/lightning-component-bundle/templates/tags.js +36 -0
- package/dist/lightning-component-bundle/templates/template.d.ts +53 -0
- package/dist/lightning-component-bundle/templates/template.js +518 -0
- package/dist/lightning-component-bundle/templates/writer.d.ts +9 -0
- package/dist/lightning-component-bundle/templates/writer.js +32 -0
- package/dist/module-declaration.d.ts +27 -0
- package/dist/module-declaration.js +28 -0
- package/dist/names.d.ts +39 -0
- package/dist/names.js +67 -0
- package/dist/text-builder.d.ts +34 -0
- package/dist/text-builder.js +46 -0
- package/dist/types.d.ts +128 -0
- package/dist/types.js +3 -0
- package/dist/xml.d.ts +32 -0
- package/dist/xml.js +80 -0
- package/package.json +37 -26
- package/README.MD +0 -5
- package/lib/converts/_index.mjs +0 -8
- package/lib/converts/apex-class.mjs +0 -469
- package/lib/converts/content-asset.mjs +0 -48
- package/lib/converts/custom-field.mjs +0 -63
- package/lib/converts/custom-label.mjs +0 -62
- package/lib/converts/custom-labels.mjs +0 -34
- package/lib/converts/custom-object.mjs +0 -51
- package/lib/converts/custom-permission.mjs +0 -51
- package/lib/converts/static-resource.mjs +0 -56
- package/lib/index.mjs +0 -51
- package/lib/util.mjs +0 -68
- package/lib-cjs/bundle.cjs +0 -962
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Apex types as LWC receives them: Apex values are serialized to JSON, so dates, times and blobs
|
|
3
|
+
* arrive as strings (ISO-8601 / base64) and every numeric type as a number.
|
|
4
|
+
*/
|
|
5
|
+
export declare const PRIMITIVE_TYPES: Readonly<Record<string, string>>;
|
|
6
|
+
export interface ResolverOptions {
|
|
7
|
+
/** The namespace Apex types are declared in; `null` declares them globally. */
|
|
8
|
+
readonly namespace: string | null;
|
|
9
|
+
/** The project's namespace, or undefined without one. */
|
|
10
|
+
readonly projectNamespace: string | undefined;
|
|
11
|
+
/** The installed package the file's class belongs to, when it isn't the project's. */
|
|
12
|
+
readonly packageNamespace: string | undefined;
|
|
13
|
+
/** The file's top-level type. */
|
|
14
|
+
readonly className: string;
|
|
15
|
+
/** Whether the file is an SObject definition, whose aliases are apart from classes' (see `#alias`). */
|
|
16
|
+
readonly isSobject: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* The top-level type's nested classes, interfaces and enums, keyed by lower-cased name to their declared
|
|
19
|
+
* spelling. The file adds them as it finds them, before its fields refer to them.
|
|
20
|
+
*/
|
|
21
|
+
readonly nestedTypes: ReadonlyMap<string, string>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Resolves one Apex file's type references, and collects what its declarations then need: the library
|
|
25
|
+
* types they use, and the registries they read.
|
|
26
|
+
*/
|
|
27
|
+
export declare class TypeResolver {
|
|
28
|
+
#private;
|
|
29
|
+
constructor(options: ResolverOptions);
|
|
30
|
+
/** The namespace the file's types are declared in: the Apex namespace, or a package's in it. */
|
|
31
|
+
get typesNamespace(): string | null;
|
|
32
|
+
/** A name in the Apex namespace, as a reference: `Apex.Account`, or `Account` without one. */
|
|
33
|
+
qualify(parts: readonly string[]): string;
|
|
34
|
+
/** A type declared with the file's types (its classes), as a reference. */
|
|
35
|
+
qualifyOwn(parts: readonly string[]): string;
|
|
36
|
+
/**
|
|
37
|
+
* Resolves an Apex type reference (`Outer.Inner`, `Inner` from inside `Outer`, `Exception`,
|
|
38
|
+
* `System.Exception`) to its TypeScript type, as Apex looks names up. In order:
|
|
39
|
+
*
|
|
40
|
+
* 1. Through a namespace: the package's (`dlrs.RollupSummary`), which is one of its types, or the
|
|
41
|
+
* project's (`acme.Invoices`), which is the name without it.
|
|
42
|
+
* 2. The file's own types, in their declared spelling, since Apex names are case-insensitive but
|
|
43
|
+
* TypeScript names aren't.
|
|
44
|
+
* 3. A custom object (`Invoice__c`), named with the namespace of the code it's in.
|
|
45
|
+
* 4. A name a library type has (see library.ts): the project's types and SObjects, which shadow it,
|
|
46
|
+
* then the library's namespaces in their configured order, through registries.
|
|
47
|
+
* 5. A library type through its namespace (`System.Exception`). Apex's primitive types are System's
|
|
48
|
+
* (`System.String`), and a name in Schema that's no library type is an SObject (`Schema.Account`).
|
|
49
|
+
* 6. Any other name, as written in the Apex namespace (the project's classes in other files, and
|
|
50
|
+
* standard objects).
|
|
51
|
+
*
|
|
52
|
+
* An installed package's class can't see the project's classes, and its copy may not have what it
|
|
53
|
+
* refers to, so its names are its package's types when the copy has them (`RollupSummary`,
|
|
54
|
+
* `RollupController.Result`), then library types and SObjects, and `unknown` otherwise, never an error
|
|
55
|
+
* in code that isn't the project's to fix.
|
|
56
|
+
*
|
|
57
|
+
* A reference that looks through registries is a conditional type, written where it's used (see
|
|
58
|
+
* `#lookup`).
|
|
59
|
+
*
|
|
60
|
+
* @param base whether the reference is a class's base class, which an interface can only extend by
|
|
61
|
+
* name and which can't be `unknown`: a name a package's class can't find is `{}` instead
|
|
62
|
+
*/
|
|
63
|
+
resolve(parts: readonly string[], { base }?: {
|
|
64
|
+
base?: boolean;
|
|
65
|
+
}): string;
|
|
66
|
+
/**
|
|
67
|
+
* The declarations the file's references need, in a block of their own, or "" without any: the
|
|
68
|
+
* library types they use (and their base classes), the registries they read, and the file's own type
|
|
69
|
+
* in `ownRegistry`, if given. Other files declare the same library types and registries, which merge.
|
|
70
|
+
*/
|
|
71
|
+
declarations(ownName: string, ownRegistry: string | undefined): string;
|
|
72
|
+
/**
|
|
73
|
+
* The SObjects' registry (`Apex.__lwcSObjects`), which every declared SObject adds itself to, by
|
|
74
|
+
* lower-cased name: what `registryLookup` reads to refer to an SObject that may not be declared.
|
|
75
|
+
*/
|
|
76
|
+
get sobjectsRegistry(): string;
|
|
77
|
+
}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
// How an Apex file's type references become TypeScript types: Apex's primitive types, the file's own
|
|
2
|
+
// types, the project's and installed packages' types, SObjects, and the standard library's (library.ts).
|
|
3
|
+
// Which type a name is often depends on the whole project, while sf2ts converts one file at a time, so
|
|
4
|
+
// such references check registries that the project's files add their types to (namespace.ts), and
|
|
5
|
+
// TypeScript picks the type.
|
|
6
|
+
import { apexLibrary } from "./library.js";
|
|
7
|
+
import { CLASSES_REGISTRY, qualifyApex, registration, registryLookup, SOBJECTS_REGISTRY } from "./namespace.js";
|
|
8
|
+
import { recordName } from "../names.js";
|
|
9
|
+
import { RESERVED_WORDS } from "../identifiers.js";
|
|
10
|
+
/**
|
|
11
|
+
* Apex types as LWC receives them: Apex values are serialized to JSON, so dates, times and blobs
|
|
12
|
+
* arrive as strings (ISO-8601 / base64) and every numeric type as a number.
|
|
13
|
+
*/
|
|
14
|
+
export const PRIMITIVE_TYPES = {
|
|
15
|
+
string: "string",
|
|
16
|
+
id: "string",
|
|
17
|
+
date: "string",
|
|
18
|
+
datetime: "string",
|
|
19
|
+
time: "string",
|
|
20
|
+
blob: "string",
|
|
21
|
+
decimal: "number",
|
|
22
|
+
double: "number",
|
|
23
|
+
integer: "number",
|
|
24
|
+
long: "number",
|
|
25
|
+
boolean: "boolean",
|
|
26
|
+
// any value, including null, which TypeScript's `Object` rejects
|
|
27
|
+
object: "unknown",
|
|
28
|
+
// a record of any SObject type, such as a polymorphic lookup (`Owner`, `What`)
|
|
29
|
+
sobject: "Record<string, unknown>",
|
|
30
|
+
// used by the generated SObject definitions for JSON-typed fields
|
|
31
|
+
json: "unknown",
|
|
32
|
+
void: "void",
|
|
33
|
+
};
|
|
34
|
+
/** Names TypeScript's types are, which an alias can't have. */
|
|
35
|
+
const TYPE_KEYWORDS = new Set(["any", "bigint", "boolean", "never", "number", "object", "string", "symbol", "undefined", "unknown"]);
|
|
36
|
+
/**
|
|
37
|
+
* Resolves one Apex file's type references, and collects what its declarations then need: the library
|
|
38
|
+
* types they use, and the registries they read.
|
|
39
|
+
*/
|
|
40
|
+
export class TypeResolver {
|
|
41
|
+
#options;
|
|
42
|
+
/** The library types the declarations reference, by namespace and name; they're declared with them. */
|
|
43
|
+
#libraryTypes = new Map();
|
|
44
|
+
/** The registries references resolve through, which are declared (empty) so they always exist. */
|
|
45
|
+
#registries = new Set();
|
|
46
|
+
/**
|
|
47
|
+
* The resolved types of references that look through registries, by type, to the names of the aliases
|
|
48
|
+
* the file declares for them, so each is written once however many references it has.
|
|
49
|
+
*/
|
|
50
|
+
#aliases = new Map();
|
|
51
|
+
constructor(options) {
|
|
52
|
+
this.#options = options;
|
|
53
|
+
}
|
|
54
|
+
/** The namespace the file's types are declared in: the Apex namespace, or a package's in it. */
|
|
55
|
+
get typesNamespace() {
|
|
56
|
+
const { namespace, packageNamespace } = this.#options;
|
|
57
|
+
if (packageNamespace === undefined) {
|
|
58
|
+
return namespace;
|
|
59
|
+
}
|
|
60
|
+
return namespace === null ? packageNamespace : `${namespace}.${packageNamespace}`;
|
|
61
|
+
}
|
|
62
|
+
/** A name in the Apex namespace, as a reference: `Apex.Account`, or `Account` without one. */
|
|
63
|
+
qualify(parts) {
|
|
64
|
+
return qualifyApex(this.#options.namespace, parts);
|
|
65
|
+
}
|
|
66
|
+
/** A type declared with the file's types (its classes), as a reference. */
|
|
67
|
+
qualifyOwn(parts) {
|
|
68
|
+
const { packageNamespace } = this.#options;
|
|
69
|
+
return this.qualify(packageNamespace === undefined ? parts : [packageNamespace, ...parts]);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Resolves an Apex type reference (`Outer.Inner`, `Inner` from inside `Outer`, `Exception`,
|
|
73
|
+
* `System.Exception`) to its TypeScript type, as Apex looks names up. In order:
|
|
74
|
+
*
|
|
75
|
+
* 1. Through a namespace: the package's (`dlrs.RollupSummary`), which is one of its types, or the
|
|
76
|
+
* project's (`acme.Invoices`), which is the name without it.
|
|
77
|
+
* 2. The file's own types, in their declared spelling, since Apex names are case-insensitive but
|
|
78
|
+
* TypeScript names aren't.
|
|
79
|
+
* 3. A custom object (`Invoice__c`), named with the namespace of the code it's in.
|
|
80
|
+
* 4. A name a library type has (see library.ts): the project's types and SObjects, which shadow it,
|
|
81
|
+
* then the library's namespaces in their configured order, through registries.
|
|
82
|
+
* 5. A library type through its namespace (`System.Exception`). Apex's primitive types are System's
|
|
83
|
+
* (`System.String`), and a name in Schema that's no library type is an SObject (`Schema.Account`).
|
|
84
|
+
* 6. Any other name, as written in the Apex namespace (the project's classes in other files, and
|
|
85
|
+
* standard objects).
|
|
86
|
+
*
|
|
87
|
+
* An installed package's class can't see the project's classes, and its copy may not have what it
|
|
88
|
+
* refers to, so its names are its package's types when the copy has them (`RollupSummary`,
|
|
89
|
+
* `RollupController.Result`), then library types and SObjects, and `unknown` otherwise, never an error
|
|
90
|
+
* in code that isn't the project's to fix.
|
|
91
|
+
*
|
|
92
|
+
* A reference that looks through registries is a conditional type, written where it's used (see
|
|
93
|
+
* `#lookup`).
|
|
94
|
+
*
|
|
95
|
+
* @param base whether the reference is a class's base class, which an interface can only extend by
|
|
96
|
+
* name and which can't be `unknown`: a name a package's class can't find is `{}` instead
|
|
97
|
+
*/
|
|
98
|
+
resolve(parts, { base = false } = {}) {
|
|
99
|
+
const unknown = base ? "{}" : "unknown";
|
|
100
|
+
const [first, ...rest] = parts;
|
|
101
|
+
if (first === undefined) {
|
|
102
|
+
return "";
|
|
103
|
+
}
|
|
104
|
+
const { projectNamespace, packageNamespace, className, nestedTypes } = this.#options;
|
|
105
|
+
// the namespace the code is in, whose custom objects it names without a namespace
|
|
106
|
+
const ownNamespace = packageNamespace ?? projectNamespace;
|
|
107
|
+
const inPackage = packageNamespace !== undefined;
|
|
108
|
+
// 1. through a namespace
|
|
109
|
+
if (inPackage && rest.length > 0 && first.toLowerCase() === packageNamespace.toLowerCase()) {
|
|
110
|
+
// the class itself, or another of the package's, which the copy may not have
|
|
111
|
+
return rest[0].toLowerCase() === className.toLowerCase()
|
|
112
|
+
? this.resolve(rest, { base })
|
|
113
|
+
: this.#lookup(rest, this.#packageLookup(rest, unknown), base);
|
|
114
|
+
}
|
|
115
|
+
if (projectNamespace !== undefined && rest.length > 0 && first.toLowerCase() === projectNamespace.toLowerCase()) {
|
|
116
|
+
return this.resolve(rest, { base });
|
|
117
|
+
}
|
|
118
|
+
// 2. the file's own types
|
|
119
|
+
if (first.toLowerCase() === className.toLowerCase()) {
|
|
120
|
+
return this.qualifyOwn([className, ...rest.map((part) => nestedTypes.get(part.toLowerCase()) ?? part)]);
|
|
121
|
+
}
|
|
122
|
+
const nested = rest.length === 0 ? nestedTypes.get(first.toLowerCase()) : undefined;
|
|
123
|
+
if (nested !== undefined) {
|
|
124
|
+
return this.qualifyOwn([className, nested]);
|
|
125
|
+
}
|
|
126
|
+
if (rest.length === 0) {
|
|
127
|
+
// 3. a custom object, which a package's copy may refer to without its definition
|
|
128
|
+
const custom = recordName(first, ownNamespace);
|
|
129
|
+
if (custom !== first) {
|
|
130
|
+
return inPackage ? this.#lookup([custom], this.#sobjectLookup(custom, unknown), base) : this.qualify([custom]);
|
|
131
|
+
}
|
|
132
|
+
// 4. a name a library type has
|
|
133
|
+
const steps = apexLibrary.resolve(first);
|
|
134
|
+
if (!inPackage) {
|
|
135
|
+
return steps === undefined ? this.qualify([first]) : this.#lookup([first], this.#libraryLookup(first, steps, true), base);
|
|
136
|
+
}
|
|
137
|
+
return this.#lookup([first], this.#packageLookup([first], steps === undefined ? this.#sobjectLookup(first, unknown) : this.#libraryLookup(first, steps, false)), base);
|
|
138
|
+
}
|
|
139
|
+
// 5. a library type through its namespace
|
|
140
|
+
const namespace = apexLibrary.namespace(first);
|
|
141
|
+
const [name] = rest;
|
|
142
|
+
if (namespace !== undefined && name !== undefined && rest.length === 1) {
|
|
143
|
+
const primitive = namespace.name === "System" ? PRIMITIVE_TYPES[name.toLowerCase()] : undefined;
|
|
144
|
+
if (primitive !== undefined) {
|
|
145
|
+
return primitive;
|
|
146
|
+
}
|
|
147
|
+
const ref = apexLibrary.type(namespace.name, name);
|
|
148
|
+
if (ref !== undefined) {
|
|
149
|
+
return this.#libraryReference(ref);
|
|
150
|
+
}
|
|
151
|
+
if (namespace.sobjects) {
|
|
152
|
+
const sobject = recordName(name, ownNamespace);
|
|
153
|
+
return inPackage ? this.#lookup([sobject], this.#sobjectLookup(sobject, unknown), base) : this.qualify([sobject]);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// 6. any other name: in a package, another of its classes' nested types, or another package's type
|
|
157
|
+
return inPackage ? this.#lookup(parts, this.#packageLookup(parts, unknown), base) : this.qualify(parts);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* The declarations the file's references need, in a block of their own, or "" without any: the
|
|
161
|
+
* library types they use (and their base classes), the registries they read, and the file's own type
|
|
162
|
+
* in `ownRegistry`, if given. Other files declare the same library types and registries, which merge.
|
|
163
|
+
*/
|
|
164
|
+
declarations(ownName, ownRegistry) {
|
|
165
|
+
if (this.#libraryTypes.size === 0 && this.#registries.size === 0 && ownRegistry === undefined) {
|
|
166
|
+
// a package's registry is declared by its classes' files themselves, so its aliases can be all there is
|
|
167
|
+
return this.#aliasDeclarations();
|
|
168
|
+
}
|
|
169
|
+
// base classes are declared too
|
|
170
|
+
for (const ref of this.#libraryTypes.values()) {
|
|
171
|
+
const base = ref.type.kind === "class" ? ref.type.extends : undefined;
|
|
172
|
+
const [namespace, name] = base?.split(".") ?? [];
|
|
173
|
+
const baseRef = namespace !== undefined && name !== undefined ? apexLibrary.type(namespace, name) : undefined;
|
|
174
|
+
if (baseRef !== undefined) {
|
|
175
|
+
this.#libraryReference(baseRef);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const { namespace: apexNamespace } = this.#options;
|
|
179
|
+
const indent = apexNamespace === null ? "" : " ";
|
|
180
|
+
const lines = [];
|
|
181
|
+
for (const registry of new Set([...this.#registries, ...(ownRegistry === undefined ? [] : [ownRegistry])])) {
|
|
182
|
+
lines.push(registry === ownRegistry ? registration(indent, registry, [ownName]) : `${indent}interface ${registry} {}\n`);
|
|
183
|
+
}
|
|
184
|
+
const byNamespace = new Map();
|
|
185
|
+
for (const ref of this.#libraryTypes.values()) {
|
|
186
|
+
byNamespace.set(ref.namespace, [...(byNamespace.get(ref.namespace) ?? []), ref]);
|
|
187
|
+
}
|
|
188
|
+
for (const [namespace, refs] of [...byNamespace].sort(([a], [b]) => a.localeCompare(b))) {
|
|
189
|
+
lines.push(`${indent}${indent === "" ? "declare " : ""}namespace ${namespace} {\n`);
|
|
190
|
+
const enums = [];
|
|
191
|
+
for (const { name, type } of refs.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
192
|
+
if (type.kind === "enum") {
|
|
193
|
+
enums.push(`${name}: ${type.values.length === 0 ? "never" : type.values.map((value) => JSON.stringify(value)).join(" | ")};`);
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
const bases = [
|
|
197
|
+
...(type.kind === "class" && type.extends !== undefined ? [this.qualify(type.extends.split("."))] : []),
|
|
198
|
+
// its properties' JSON names aren't known exactly, so any can be read
|
|
199
|
+
...(type.kind === "interface" || (type.kind === "class" && type.open) ? ["Record<string, unknown>"] : []),
|
|
200
|
+
];
|
|
201
|
+
lines.push(`${indent} interface ${name}${bases.length === 0 ? "" : ` extends ${bases.join(", ")}`} {}\n`);
|
|
202
|
+
}
|
|
203
|
+
if (enums.length > 0) {
|
|
204
|
+
lines.push(`${indent} interface __lwcEnums {\n${enums.map((line) => `${indent} ${line}\n`).join("")}${indent} }\n`);
|
|
205
|
+
}
|
|
206
|
+
lines.push(`${indent}}\n`);
|
|
207
|
+
}
|
|
208
|
+
const libraryBlock = apexNamespace === null ? lines.join("") : `declare namespace ${apexNamespace} {\n${lines.join("")}}\n`;
|
|
209
|
+
return libraryBlock + this.#aliasDeclarations();
|
|
210
|
+
}
|
|
211
|
+
/** The file's aliases (see `#alias`), in a namespace named for its class, or "" without any. */
|
|
212
|
+
#aliasDeclarations() {
|
|
213
|
+
if (this.#aliases.size === 0) {
|
|
214
|
+
return "";
|
|
215
|
+
}
|
|
216
|
+
const aliases = [...this.#aliases].map(([type, name]) => ` type ${name} = ${type};\n`).join("");
|
|
217
|
+
return `declare namespace ${this.#aliasNamespace} {\n${aliases}}\n`;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* The namespace the file's aliases are in: one of their own for the file's class, in the namespace of
|
|
221
|
+
* aliases for classes (`Apex.__lwcRefs.Billing`, `Apex.dlrs.__lwcRefs.RollupController`) or for SObject
|
|
222
|
+
* definitions (`Apex.__lwcSObjectRefs.Account`). A class has one file, and so does an SObject
|
|
223
|
+
* definition, so no other file declares aliases in it, even when a class and an SObject share a name.
|
|
224
|
+
* Being apart from the class's own namespace also keeps them out of editors' suggestions for it.
|
|
225
|
+
*/
|
|
226
|
+
get #aliasNamespace() {
|
|
227
|
+
const { isSobject, className } = this.#options;
|
|
228
|
+
const scope = isSobject ? this.qualify(["__lwcSObjectRefs"]) : this.qualifyOwn(["__lwcRefs"]);
|
|
229
|
+
return `${scope}.${className}`;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* A reference that looks through registries, of type `type`: written where it's used, in parentheses
|
|
233
|
+
* so it can be an array's element or part of an intersection; or, for a base class, as an alias, since
|
|
234
|
+
* an interface can only extend a type by name.
|
|
235
|
+
*/
|
|
236
|
+
#lookup(parts, type, base) {
|
|
237
|
+
return base ? this.#alias(parts, type) : `(${type})`;
|
|
238
|
+
}
|
|
239
|
+
/** An alias for `type`, a base class's resolved type, named for the reference (`base`). */
|
|
240
|
+
#alias(parts, type) {
|
|
241
|
+
let name = this.#aliases.get(type);
|
|
242
|
+
if (name === undefined) {
|
|
243
|
+
let base = parts
|
|
244
|
+
.join("_")
|
|
245
|
+
.toLowerCase()
|
|
246
|
+
.replace(/[^\w$]/g, "_");
|
|
247
|
+
// names a type can't have, such as `object`, or a reserved word
|
|
248
|
+
if (TYPE_KEYWORDS.has(base) || RESERVED_WORDS.has(base)) {
|
|
249
|
+
base = `${base}_`;
|
|
250
|
+
}
|
|
251
|
+
const taken = new Set(this.#aliases.values());
|
|
252
|
+
name = base;
|
|
253
|
+
for (let n = 2; taken.has(name); n++) {
|
|
254
|
+
name = `${base}_${n}`;
|
|
255
|
+
}
|
|
256
|
+
this.#aliases.set(type, name);
|
|
257
|
+
}
|
|
258
|
+
return `${this.#aliasNamespace}.${name}`;
|
|
259
|
+
}
|
|
260
|
+
/** A library type, which is declared with the file's declarations. */
|
|
261
|
+
#libraryReference(ref) {
|
|
262
|
+
this.#libraryTypes.set(`${ref.namespace}.${ref.name}`.toLowerCase(), ref);
|
|
263
|
+
return ref.type.kind === "enum"
|
|
264
|
+
? `${this.qualify([ref.namespace, "__lwcEnums"])}[${JSON.stringify(ref.name)}]`
|
|
265
|
+
: this.qualify([ref.namespace, ref.name]);
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* The SObjects' registry (`Apex.__lwcSObjects`), which every declared SObject adds itself to, by
|
|
269
|
+
* lower-cased name: what `registryLookup` reads to refer to an SObject that may not be declared.
|
|
270
|
+
*/
|
|
271
|
+
get sobjectsRegistry() {
|
|
272
|
+
return this.#registry(SOBJECTS_REGISTRY);
|
|
273
|
+
}
|
|
274
|
+
/** A registry in the Apex namespace, which the file's declarations then declare. */
|
|
275
|
+
#registry(name) {
|
|
276
|
+
this.#registries.add(name);
|
|
277
|
+
return this.qualify([name]);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* A name a library type has: the project's type of that name if it has one (unless `projectClasses` is
|
|
281
|
+
* false, in a package's class), else what `steps` find (an SObject, or the library type).
|
|
282
|
+
*/
|
|
283
|
+
#libraryLookup(name, steps, projectClasses) {
|
|
284
|
+
const resolve = ([step, ...rest]) => {
|
|
285
|
+
if (step === undefined) {
|
|
286
|
+
return "never";
|
|
287
|
+
}
|
|
288
|
+
return step.kind === "type"
|
|
289
|
+
? this.#libraryReference(step.ref)
|
|
290
|
+
: registryLookup(this.#registry(SOBJECTS_REGISTRY), name, resolve(rest));
|
|
291
|
+
};
|
|
292
|
+
return projectClasses ? registryLookup(this.#registry(CLASSES_REGISTRY), name, resolve(steps)) : resolve(steps);
|
|
293
|
+
}
|
|
294
|
+
/** An SObject that may not be declared: `unknown` when no file declares it, rather than an error. */
|
|
295
|
+
#sobjectLookup(name, unknown) {
|
|
296
|
+
return registryLookup(this.#registry(SOBJECTS_REGISTRY), name, unknown);
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* In a package's class, the package's type of a name (`RollupSummary`, `RollupController.Result`) when
|
|
300
|
+
* it has one, and otherwise `otherwise`. Its types are in its registry, which each of its classes' files
|
|
301
|
+
* adds itself and its nested types to, by lower-cased dotted name.
|
|
302
|
+
*/
|
|
303
|
+
#packageLookup(path, otherwise) {
|
|
304
|
+
return registryLookup(this.qualifyOwn([CLASSES_REGISTRY]), path.join("."), otherwise);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { TextBuilder, SourceRange } from "../text-builder.ts";
|
|
2
|
+
export interface SObjectProperty {
|
|
3
|
+
name: string;
|
|
4
|
+
/** TypeScript type, with Apex types referenced in the namespace they're declared in (see `qualifyApex`) */
|
|
5
|
+
type: string;
|
|
6
|
+
nameRange?: SourceRange | null;
|
|
7
|
+
typeRange?: SourceRange | null;
|
|
8
|
+
/** JSDoc lines */
|
|
9
|
+
doc?: (string | undefined)[];
|
|
10
|
+
}
|
|
11
|
+
export interface SObjectInterface {
|
|
12
|
+
name: string;
|
|
13
|
+
nameRange?: SourceRange | null;
|
|
14
|
+
/** JSDoc lines */
|
|
15
|
+
doc?: (string | undefined)[];
|
|
16
|
+
properties: SObjectProperty[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The standard fields of an object, by the kind its API name's suffix identifies. They match the types
|
|
20
|
+
* the Salesforce extensions' SObject definitions (`.sfdx/tools/sobjects`) give them, since a property
|
|
21
|
+
* declared in both must have the same type. Standard objects' fields vary, so only `Id` is declared.
|
|
22
|
+
* `owned` is whether records have an owner, which children of a master-detail relationship
|
|
23
|
+
* (`ControlledByParent` sharing) don't; only the owner's Id is declared (see below). The users who
|
|
24
|
+
* created and last modified a record are `User`s in `apexNamespace`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function standardFields(objectName: string, { owned, apexNamespace }: {
|
|
27
|
+
owned: boolean;
|
|
28
|
+
apexNamespace: string | null;
|
|
29
|
+
}): SObjectProperty[];
|
|
30
|
+
/**
|
|
31
|
+
* The TypeScript type of a field, or undefined when it can't be known from the field alone (such as a
|
|
32
|
+
* roll-up summary of a field's minimum) or the type isn't supported. Such fields are left undeclared
|
|
33
|
+
* rather than risk a type that conflicts with the generated SObject definitions.
|
|
34
|
+
*/
|
|
35
|
+
export declare function fieldTsType(fieldType: string | undefined, summaryOperation: string | undefined): string | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Documentation shared by a field's schema module and its Apex property: its description, and the
|
|
38
|
+
* help text users see next to it.
|
|
39
|
+
*/
|
|
40
|
+
export declare function fieldDoc(description: string | undefined, inlineHelpText: string | undefined): (string | undefined)[];
|
|
41
|
+
/**
|
|
42
|
+
* Declares SObject interfaces in the Apex namespace (or globally, when `namespace` is null). Interfaces
|
|
43
|
+
* merge across files, so each object and field file declares the part of a type it knows about.
|
|
44
|
+
*/
|
|
45
|
+
export declare function addSObjectInterfaces(builder: TextBuilder, namespace: string | null, interfaces: SObjectInterface[]): void;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// SObject record types: the interfaces object and field metadata declare in the Apex namespace.
|
|
2
|
+
import { docTag, jsDoc } from "../jsdoc.js";
|
|
3
|
+
import { qualifyApex, registration, SOBJECTS_REGISTRY } from "./namespace.js";
|
|
4
|
+
/**
|
|
5
|
+
* The standard fields of an object, by the kind its API name's suffix identifies. They match the types
|
|
6
|
+
* the Salesforce extensions' SObject definitions (`.sfdx/tools/sobjects`) give them, since a property
|
|
7
|
+
* declared in both must have the same type. Standard objects' fields vary, so only `Id` is declared.
|
|
8
|
+
* `owned` is whether records have an owner, which children of a master-detail relationship
|
|
9
|
+
* (`ControlledByParent` sharing) don't; only the owner's Id is declared (see below). The users who
|
|
10
|
+
* created and last modified a record are `User`s in `apexNamespace`.
|
|
11
|
+
*/
|
|
12
|
+
export function standardFields(objectName, { owned, apexNamespace }) {
|
|
13
|
+
const user = qualifyApex(apexNamespace, ["User"]);
|
|
14
|
+
let fields;
|
|
15
|
+
if (objectName.endsWith("__c")) {
|
|
16
|
+
fields = [
|
|
17
|
+
["Id", "string"],
|
|
18
|
+
["Name", "string"],
|
|
19
|
+
["IsDeleted", "boolean"],
|
|
20
|
+
["CreatedById", "string"],
|
|
21
|
+
["CreatedBy", user],
|
|
22
|
+
["CreatedDate", "string"],
|
|
23
|
+
["LastModifiedById", "string"],
|
|
24
|
+
["LastModifiedBy", user],
|
|
25
|
+
["LastModifiedDate", "string"],
|
|
26
|
+
["SystemModstamp", "string"],
|
|
27
|
+
// The owner (a user or a queue) is left to the SObject definition, whose type for it depends on the
|
|
28
|
+
// org: its targets, when the project has the object's describe, and `SObject` otherwise.
|
|
29
|
+
...(owned ? [["OwnerId", "string"]] : []),
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
else if (objectName.endsWith("__mdt")) {
|
|
33
|
+
fields = [
|
|
34
|
+
["Id", "string"],
|
|
35
|
+
["DeveloperName", "string"],
|
|
36
|
+
["MasterLabel", "string"],
|
|
37
|
+
["Label", "string"],
|
|
38
|
+
["Language", "string"],
|
|
39
|
+
["NamespacePrefix", "string"],
|
|
40
|
+
["QualifiedApiName", "string"],
|
|
41
|
+
["SystemModstamp", "string"],
|
|
42
|
+
];
|
|
43
|
+
}
|
|
44
|
+
else if (objectName.endsWith("__e")) {
|
|
45
|
+
fields = [
|
|
46
|
+
["ReplayId", "string"],
|
|
47
|
+
["EventUuid", "string"],
|
|
48
|
+
["CreatedById", "string"],
|
|
49
|
+
["CreatedBy", user],
|
|
50
|
+
["CreatedDate", "string"],
|
|
51
|
+
];
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
fields = [["Id", "string"]];
|
|
55
|
+
}
|
|
56
|
+
return fields.map(([name, type]) => ({ name, type }));
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* TypeScript types of field types, as LWC receives their values (see the Apex types in resolution.ts).
|
|
60
|
+
* Lookups hold the related record's Id; their related records are declared separately.
|
|
61
|
+
*/
|
|
62
|
+
const FIELD_TYPES = {
|
|
63
|
+
autonumber: "string",
|
|
64
|
+
checkbox: "boolean",
|
|
65
|
+
currency: "number",
|
|
66
|
+
date: "string",
|
|
67
|
+
datetime: "string",
|
|
68
|
+
email: "string",
|
|
69
|
+
encryptedtext: "string",
|
|
70
|
+
hierarchy: "string",
|
|
71
|
+
html: "string",
|
|
72
|
+
lookup: "string",
|
|
73
|
+
masterdetail: "string",
|
|
74
|
+
metadatarelationship: "string",
|
|
75
|
+
multiselectpicklist: "string",
|
|
76
|
+
number: "number",
|
|
77
|
+
percent: "number",
|
|
78
|
+
phone: "string",
|
|
79
|
+
picklist: "string",
|
|
80
|
+
text: "string",
|
|
81
|
+
textarea: "string",
|
|
82
|
+
longtextarea: "string",
|
|
83
|
+
time: "string",
|
|
84
|
+
url: "string",
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* The TypeScript type of a field, or undefined when it can't be known from the field alone (such as a
|
|
88
|
+
* roll-up summary of a field's minimum) or the type isn't supported. Such fields are left undeclared
|
|
89
|
+
* rather than risk a type that conflicts with the generated SObject definitions.
|
|
90
|
+
*/
|
|
91
|
+
export function fieldTsType(fieldType, summaryOperation) {
|
|
92
|
+
const type = fieldType?.toLowerCase();
|
|
93
|
+
if (type === "summary") {
|
|
94
|
+
const operation = summaryOperation?.toLowerCase();
|
|
95
|
+
return operation === "count" || operation === "sum" ? "number" : undefined;
|
|
96
|
+
}
|
|
97
|
+
return type === undefined ? undefined : FIELD_TYPES[type];
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Documentation shared by a field's schema module and its Apex property: its description, and the
|
|
101
|
+
* help text users see next to it.
|
|
102
|
+
*/
|
|
103
|
+
export function fieldDoc(description, inlineHelpText) {
|
|
104
|
+
return [docTag("description", description), docTag("inlineHelpText", inlineHelpText)];
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Declares SObject interfaces in the Apex namespace (or globally, when `namespace` is null). Interfaces
|
|
108
|
+
* merge across files, so each object and field file declares the part of a type it knows about.
|
|
109
|
+
*/
|
|
110
|
+
export function addSObjectInterfaces(builder, namespace, interfaces) {
|
|
111
|
+
const indent = namespace === null ? "" : " ";
|
|
112
|
+
if (namespace !== null) {
|
|
113
|
+
builder.write(`declare namespace ${namespace} {\n`);
|
|
114
|
+
}
|
|
115
|
+
for (const { name, nameRange, doc, properties } of interfaces) {
|
|
116
|
+
builder.write(`${jsDoc(indent, doc ?? [])}${indent}interface `);
|
|
117
|
+
builder.writeMapped(name, nameRange ?? null);
|
|
118
|
+
if (properties.length === 0) {
|
|
119
|
+
builder.write(" {}\n");
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
builder.write(" {\n");
|
|
123
|
+
for (const property of properties) {
|
|
124
|
+
builder.write(`${jsDoc(`${indent} `, property.doc ?? [])}${indent} `);
|
|
125
|
+
builder.writeMapped(property.name, property.nameRange ?? null);
|
|
126
|
+
builder.write("?: ");
|
|
127
|
+
builder.writeMapped(property.type, property.typeRange ?? null);
|
|
128
|
+
builder.write(";\n");
|
|
129
|
+
}
|
|
130
|
+
builder.write(`${indent}}\n`);
|
|
131
|
+
}
|
|
132
|
+
// every SObject registers, so references can look one up without an error when it isn't declared:
|
|
133
|
+
// those whose names library types have shadow them (`Location`, over `System.Location`), and installed
|
|
134
|
+
// packages' classes fall back to them
|
|
135
|
+
if (interfaces.length > 0) {
|
|
136
|
+
builder.write(registration(indent, SOBJECTS_REGISTRY, interfaces.map(({ name }) => name)));
|
|
137
|
+
}
|
|
138
|
+
if (namespace !== null) {
|
|
139
|
+
builder.write("}\n");
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ConvertOptions, InaccessibleApexProperties } from "./types.ts";
|
|
2
|
+
/** `convert`'s options as the converters use them: defaults applied, and an empty namespace as none. */
|
|
3
|
+
export interface ConversionContext {
|
|
4
|
+
/** The namespace Apex types are declared in (`"Apex"` by default), or `null` to declare them globally. */
|
|
5
|
+
readonly apexNamespace: string | null;
|
|
6
|
+
/** What becomes of Apex classes' fields LWC can't read (`"include as deprecated"` by default). */
|
|
7
|
+
readonly inaccessibleApexProperties: InaccessibleApexProperties;
|
|
8
|
+
/** The project's namespace, or undefined without one. */
|
|
9
|
+
readonly projectNamespace: string | undefined;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Applies `convert`'s defaults, and throws for a namespace that would make the declarations invalid: an
|
|
13
|
+
* `apexNamespace` that isn't a dotted TypeScript identifier, or a `projectNamespace` that isn't a
|
|
14
|
+
* namespace prefix, and for an `inaccessibleApexProperties` that isn't one of its values.
|
|
15
|
+
*/
|
|
16
|
+
export declare function conversionContext(options?: ConvertOptions): ConversionContext;
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { DEFAULT_APEX_NAMESPACE } from "./apex/namespace.js";
|
|
2
|
+
import { isIdentifier, NAMESPACE_RESERVED_WORDS } from "./identifiers.js";
|
|
3
|
+
const INACCESSIBLE_APEX_PROPERTIES = ["include", "exclude", "include as deprecated"];
|
|
4
|
+
/**
|
|
5
|
+
* A Salesforce namespace prefix: letters, digits and single underscores, starting with a letter and not
|
|
6
|
+
* ending with an underscore.
|
|
7
|
+
*/
|
|
8
|
+
const NAMESPACE_PREFIX = /^[A-Za-z][A-Za-z0-9]*(?:_[A-Za-z0-9]+)*$/;
|
|
9
|
+
/**
|
|
10
|
+
* Applies `convert`'s defaults, and throws for a namespace that would make the declarations invalid: an
|
|
11
|
+
* `apexNamespace` that isn't a dotted TypeScript identifier, or a `projectNamespace` that isn't a
|
|
12
|
+
* namespace prefix, and for an `inaccessibleApexProperties` that isn't one of its values.
|
|
13
|
+
*/
|
|
14
|
+
export function conversionContext(options) {
|
|
15
|
+
const apexNamespace = options?.apexNamespace === undefined ? DEFAULT_APEX_NAMESPACE : options.apexNamespace;
|
|
16
|
+
if (apexNamespace !== null && !apexNamespace.split(".").every((part) => isIdentifier(part) && !NAMESPACE_RESERVED_WORDS.has(part))) {
|
|
17
|
+
throw new TypeError(`apexNamespace ${JSON.stringify(apexNamespace)} isn't a namespace TypeScript can declare; use dotted identifiers (such as "Acme.Apex"), or null`);
|
|
18
|
+
}
|
|
19
|
+
const inaccessibleApexProperties = options?.inaccessibleApexProperties ?? "include as deprecated";
|
|
20
|
+
if (!INACCESSIBLE_APEX_PROPERTIES.includes(inaccessibleApexProperties)) {
|
|
21
|
+
throw new TypeError(`inaccessibleApexProperties must be one of ${INACCESSIBLE_APEX_PROPERTIES.map((value) => JSON.stringify(value)).join(", ")}, but got ${JSON.stringify(inaccessibleApexProperties)}`);
|
|
22
|
+
}
|
|
23
|
+
const projectNamespace = options?.projectNamespace?.trim() || undefined;
|
|
24
|
+
if (projectNamespace !== undefined && !NAMESPACE_PREFIX.test(projectNamespace)) {
|
|
25
|
+
throw new TypeError(`projectNamespace ${JSON.stringify(projectNamespace)} isn't a Salesforce namespace prefix`);
|
|
26
|
+
}
|
|
27
|
+
return { apexNamespace, inaccessibleApexProperties, projectNamespace };
|
|
28
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ConversionContext } from "../context.ts";
|
|
2
|
+
import type { ApexClassComponent, ConversionOutput } from "../types.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Converts an Apex class. The Apex parser loads on the first class (reading its WebAssembly from disk),
|
|
5
|
+
* once: concurrent conversions share the load, and later ones reuse the parser.
|
|
6
|
+
*/
|
|
7
|
+
export declare function ApexClass(comp: ApexClassComponent, context: ConversionContext): Promise<ConversionOutput>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { getApexParser } from "web-tree-sitter-sfapex";
|
|
2
|
+
import { convertApexFile } from "../apex/file.js";
|
|
3
|
+
import { importNameParts, metadataName } from "../names.js";
|
|
4
|
+
let apexParser;
|
|
5
|
+
/**
|
|
6
|
+
* Converts an Apex class. The Apex parser loads on the first class (reading its WebAssembly from disk),
|
|
7
|
+
* once: concurrent conversions share the load, and later ones reuse the parser.
|
|
8
|
+
*/
|
|
9
|
+
export async function ApexClass(comp, context) {
|
|
10
|
+
apexParser ??= getApexParser();
|
|
11
|
+
const prsr = await apexParser;
|
|
12
|
+
const isSobject = comp.isSobject === true;
|
|
13
|
+
return (convertApexFile(prsr, comp.content, {
|
|
14
|
+
isSobject,
|
|
15
|
+
apexNamespace: context.apexNamespace,
|
|
16
|
+
projectNamespace: context.projectNamespace,
|
|
17
|
+
// a class of another namespace (an installed package's) is named for it, as `dlrs__RollupController.cls`;
|
|
18
|
+
// an SObject definition's name is its object's
|
|
19
|
+
packageNamespace: isSobject ? undefined : importNameParts(metadataName(comp.path), context.projectNamespace).namespace,
|
|
20
|
+
polymorphicTargets: new Map(Object.entries(isSobject ? (comp.polymorphicTargets ?? {}) : {}).map(([name, targets]) => [name.toLowerCase(), targets])),
|
|
21
|
+
inaccessibleProperties: context.inaccessibleApexProperties,
|
|
22
|
+
}) ?? { text: "", mapData: [] });
|
|
23
|
+
}
|