@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
package/dist/names.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Suffixes of custom API names: objects and fields (`__c`), relationships (`__r`), custom metadata types
|
|
3
|
+
* (`__mdt`), platform events (`__e`), big objects (`__b`) and external objects (`__x`).
|
|
4
|
+
*/
|
|
5
|
+
const CUSTOM_SUFFIXES = new Set(["c", "r", "mdt", "e", "b", "x"]);
|
|
6
|
+
/** Whether a name ends in a custom suffix (`Invoice__c`, `acme__Lines__r`). */
|
|
7
|
+
function isCustomName(name) {
|
|
8
|
+
const parts = name.split("__");
|
|
9
|
+
return parts.length > 1 && CUSTOM_SUFFIXES.has(parts.at(-1).toLowerCase());
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A custom name with the project's namespace (`acme__Invoice__c` for `Invoice__c`), as records and the
|
|
13
|
+
* values of schema imports have it inside the namespace. Standard names (`Account`, `Name`), names that
|
|
14
|
+
* already have a namespace (`other__Invoice__c`), and every name without a project namespace are left as
|
|
15
|
+
* they are.
|
|
16
|
+
*/
|
|
17
|
+
export function recordName(name, namespace) {
|
|
18
|
+
return namespace && isCustomName(name) && name.split("__").length === 2 ? `${namespace}__${name}` : name;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* An API name's namespace and its name without it. Salesforce names can't contain `__` themselves, so a
|
|
22
|
+
* name ending in a custom suffix has a namespace when it has three parts (`dlrs__Log__c`), and any other
|
|
23
|
+
* name when it has two (`dlrs__RollupController`, a label's `dlrs__fflib_error`).
|
|
24
|
+
*/
|
|
25
|
+
export function splitNamespace(apiName) {
|
|
26
|
+
const parts = apiName.split("__");
|
|
27
|
+
const custom = isCustomName(apiName);
|
|
28
|
+
if ((custom && parts.length === 3) || (!custom && parts.length === 2)) {
|
|
29
|
+
return { namespace: parts[0], name: parts.slice(1).join("__") };
|
|
30
|
+
}
|
|
31
|
+
return { namespace: undefined, name: apiName };
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* How a component in the project names an item in an import: inside a namespace, LWC rejects the
|
|
35
|
+
* project's own namespace and requires any other. So `namespace` is the item's namespace when it's
|
|
36
|
+
* another's (an installed package's, such as `dlrs`), and undefined when it's the project's or it has
|
|
37
|
+
* none; `name` is the item's name without its namespace either way.
|
|
38
|
+
*/
|
|
39
|
+
export function importNameParts(apiName, projectNamespace) {
|
|
40
|
+
const { namespace, name } = splitNamespace(apiName);
|
|
41
|
+
return namespace === undefined || namespace.toLowerCase() === projectNamespace?.toLowerCase()
|
|
42
|
+
? { namespace: undefined, name }
|
|
43
|
+
: { namespace, name };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A name without the project's namespace (`Invoice__c` for `acme__Invoice__c`, `logo` for `acme__logo`),
|
|
47
|
+
* as LWC inside the namespace must import it. Other namespaces' names are left as they are.
|
|
48
|
+
*/
|
|
49
|
+
export function importName(name, namespace) {
|
|
50
|
+
const split = splitNamespace(name);
|
|
51
|
+
return namespace !== undefined && split.namespace?.toLowerCase() === namespace.toLowerCase() ? split.name : name;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The name of a metadata file: its file name up to the first `.`, such as `Account` for
|
|
55
|
+
* `objects/Account/Account.object-meta.xml`. Paths can use either separator.
|
|
56
|
+
*/
|
|
57
|
+
export function metadataName(path) {
|
|
58
|
+
const name = path.split(/[\\/]/).pop()?.split(".")[0];
|
|
59
|
+
if (!name) {
|
|
60
|
+
throw new Error(`no metadata name in the file path ${path}`);
|
|
61
|
+
}
|
|
62
|
+
return name;
|
|
63
|
+
}
|
|
64
|
+
/** The import form of a metadata file's name (see `metadataName`), such as `logo` for `acme__logo.resource-meta.xml` in project `acme`. */
|
|
65
|
+
export function fileImportName(component, projectNamespace) {
|
|
66
|
+
return importName(metadataName(component.path), projectNamespace);
|
|
67
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ConversionOutput, MapPair } from "./types.ts";
|
|
2
|
+
/**
|
|
3
|
+
* TypeScript `SpanMapFeature` flags, for a mapped piece that only takes part in some editor features.
|
|
4
|
+
* Diagnostics aren't a feature: they're mapped whatever a piece's features are.
|
|
5
|
+
*/
|
|
6
|
+
export declare const MapFeature: {
|
|
7
|
+
readonly Definition: 8;
|
|
8
|
+
readonly All: 1048575;
|
|
9
|
+
};
|
|
10
|
+
/** A piece of a component's source, from `start` up to `end`, as UTF-16 offsets. */
|
|
11
|
+
export interface SourceRange {
|
|
12
|
+
start: number;
|
|
13
|
+
end: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Builds generated text, recording which pieces of it came from which source text: each mapped piece is
|
|
17
|
+
* a pair of `MapPair`s, its start and its end. Unmapped text isn't recorded.
|
|
18
|
+
*/
|
|
19
|
+
export declare class TextBuilder {
|
|
20
|
+
#private;
|
|
21
|
+
/** The text written so far. */
|
|
22
|
+
get text(): string;
|
|
23
|
+
/** The mapped pieces of the text written so far. */
|
|
24
|
+
get mapData(): MapPair[];
|
|
25
|
+
/** The text written and its mapped pieces, as a converter returns them. */
|
|
26
|
+
output(): ConversionOutput;
|
|
27
|
+
/**
|
|
28
|
+
* Writes text, mapped to the source from `sourceStart` to `sourceEnd` when both are given, for the
|
|
29
|
+
* editor `features` given (see `MapFeature`), or for all of them.
|
|
30
|
+
*/
|
|
31
|
+
write(text: string, sourceStart?: number, sourceEnd?: number, features?: number): void;
|
|
32
|
+
/** Writes text mapped to `range` in the source, or unmapped text when the source has no such range. */
|
|
33
|
+
writeMapped(text: string, range: SourceRange | null | undefined): void;
|
|
34
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript `SpanMapFeature` flags, for a mapped piece that only takes part in some editor features.
|
|
3
|
+
* Diagnostics aren't a feature: they're mapped whatever a piece's features are.
|
|
4
|
+
*/
|
|
5
|
+
export const MapFeature = {
|
|
6
|
+
Definition: 8,
|
|
7
|
+
All: 1048575,
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Builds generated text, recording which pieces of it came from which source text: each mapped piece is
|
|
11
|
+
* a pair of `MapPair`s, its start and its end. Unmapped text isn't recorded.
|
|
12
|
+
*/
|
|
13
|
+
export class TextBuilder {
|
|
14
|
+
#text = "";
|
|
15
|
+
#mapData = [];
|
|
16
|
+
/** The text written so far. */
|
|
17
|
+
get text() {
|
|
18
|
+
return this.#text;
|
|
19
|
+
}
|
|
20
|
+
/** The mapped pieces of the text written so far. */
|
|
21
|
+
get mapData() {
|
|
22
|
+
return this.#mapData;
|
|
23
|
+
}
|
|
24
|
+
/** The text written and its mapped pieces, as a converter returns them. */
|
|
25
|
+
output() {
|
|
26
|
+
return { text: this.#text, mapData: this.#mapData };
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Writes text, mapped to the source from `sourceStart` to `sourceEnd` when both are given, for the
|
|
30
|
+
* editor `features` given (see `MapFeature`), or for all of them.
|
|
31
|
+
*/
|
|
32
|
+
write(text, sourceStart, sourceEnd, features) {
|
|
33
|
+
if (sourceStart !== undefined && sourceEnd !== undefined) {
|
|
34
|
+
this.#mapData.push({ sourcePos: sourceStart, destPos: this.#text.length, ...(features !== undefined && { features }) });
|
|
35
|
+
this.#text += text;
|
|
36
|
+
this.#mapData.push({ sourcePos: sourceEnd, destPos: this.#text.length });
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
this.#text += text;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Writes text mapped to `range` in the source, or unmapped text when the source has no such range. */
|
|
43
|
+
writeMapped(text, range) {
|
|
44
|
+
this.write(text, range?.start, range?.end);
|
|
45
|
+
}
|
|
46
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/** The metadata types sf2ts converts, each by its converter (see converters/registry.ts). */
|
|
2
|
+
export type MetadataType = "ApexClass" | "ContentAsset" | "CustomField" | "CustomLabel" | "CustomLabels" | "CustomObject" | "CustomPermission" | "LightningComponentBundle" | "LightningMessageChannel" | "StaticResource";
|
|
3
|
+
/** What becomes of an Apex class's fields LWC can't read (see `ConvertOptions.inaccessibleApexProperties`). */
|
|
4
|
+
export type InaccessibleApexProperties = "include" | "exclude" | "include as deprecated";
|
|
5
|
+
/**
|
|
6
|
+
* A mapped piece of text is two consecutive pairs, its start and its end: where it is in the source
|
|
7
|
+
* (`sourcePos`) and in the output (`destPos`), as UTF-16 offsets.
|
|
8
|
+
*/
|
|
9
|
+
export interface MapPair {
|
|
10
|
+
sourcePos: number;
|
|
11
|
+
destPos: number;
|
|
12
|
+
/**
|
|
13
|
+
* On a piece's start: the editor features it takes part in, as TypeScript's `SpanMapFeature` flags
|
|
14
|
+
* (see `MapFeature`). Without them, it takes part in all of them.
|
|
15
|
+
*/
|
|
16
|
+
features?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A file to convert, by its metadata type. The options only some types take are on their own
|
|
20
|
+
* components: `ApexClassComponent` and `LightningComponentBundleComponent`.
|
|
21
|
+
*/
|
|
22
|
+
export type Component = ApexClassComponent | LightningComponentBundleComponent | MetadataComponent;
|
|
23
|
+
/** What every component has: its file's path and content. */
|
|
24
|
+
interface ComponentFile {
|
|
25
|
+
path: string;
|
|
26
|
+
content: string;
|
|
27
|
+
}
|
|
28
|
+
/** A component of a type that takes no options of its own. */
|
|
29
|
+
export interface MetadataComponent extends ComponentFile {
|
|
30
|
+
type: Exclude<MetadataType, "ApexClass" | "LightningComponentBundle">;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A file of a Lightning web component bundle. sf2ts only reads the file itself: what it needs to know about
|
|
34
|
+
* the rest of the bundle is given here, which the caller finds out (`bundleApiVersion` and
|
|
35
|
+
* `defaultExportName` read it from the bundle's other files).
|
|
36
|
+
*/
|
|
37
|
+
export interface LightningComponentBundleComponent extends ComponentFile {
|
|
38
|
+
type: "LightningComponentBundle";
|
|
39
|
+
/**
|
|
40
|
+
* For a template (an `.html` file), the module it imports its bundle's component through, relative to the
|
|
41
|
+
* template: `./orders.js` (which TypeScript also resolves to `orders.ts`), or `../orders.js` from a
|
|
42
|
+
* subfolder of the bundle. Templates are matched to their bundle's class, as LWC does, so a bundle's
|
|
43
|
+
* alternate templates (rendered from `render()`) work too. Without it, the template is taken to be in the
|
|
44
|
+
* bundle's folder: `./<bundleName>.js`.
|
|
45
|
+
*/
|
|
46
|
+
componentModule?: string;
|
|
47
|
+
/**
|
|
48
|
+
* For a template, the bundle's name (its folder's: `orders`, or an installed package's `dlrs__orders`),
|
|
49
|
+
* which names the `c/<name>` module it declares its slots under. Without it, the template's folder's name.
|
|
50
|
+
*/
|
|
51
|
+
bundleName?: string;
|
|
52
|
+
/**
|
|
53
|
+
* For a template, the bundle's API version, from its `.js-meta.xml` (see `bundleApiVersion`), which the
|
|
54
|
+
* template is parsed at: the template compiler's latest without it.
|
|
55
|
+
*/
|
|
56
|
+
apiVersion?: number;
|
|
57
|
+
/**
|
|
58
|
+
* For a template, whether the bundle has other templates, any of which may be the one rendered, which
|
|
59
|
+
* makes every `lwc:ref` optional.
|
|
60
|
+
*/
|
|
61
|
+
alternateTemplates?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* For a `.js-meta.xml`, the name of the bundle's module's default export (see `defaultExportName`),
|
|
64
|
+
* which names the `c/<name>` module's export, as editors suggest it when auto-importing: the bundle's
|
|
65
|
+
* name without it.
|
|
66
|
+
*/
|
|
67
|
+
defaultExportName?: string;
|
|
68
|
+
}
|
|
69
|
+
export interface ApexClassComponent extends ComponentFile {
|
|
70
|
+
type: "ApexClass";
|
|
71
|
+
/**
|
|
72
|
+
* Whether it is an SObject definition generated by the Salesforce extensions
|
|
73
|
+
* (`.sfdx/tools/sobjects`). Its fields are exposed without `@AuraEnabled`, and it declares no
|
|
74
|
+
* `@salesforce/apex` modules.
|
|
75
|
+
*/
|
|
76
|
+
isSobject?: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* For an SObject definition, the objects each polymorphic relationship (such as Task's `What`, declared
|
|
79
|
+
* as `SObject`) can refer to, by relationship name: `{ What: ["Account", "Case"] }`. Each is typed as one
|
|
80
|
+
* of its targets once all of them are declared, with the `Name` object's fields when it's declared too;
|
|
81
|
+
* TypeScript finds out which are. Others stay `SObject`.
|
|
82
|
+
*/
|
|
83
|
+
polymorphicTargets?: Readonly<Record<string, readonly string[]>>;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* A problem in a component's file, at a UTF-16 offset and length in its content: `ConversionFailed` when
|
|
87
|
+
* the file couldn't be converted (covering the whole file, with empty output), or `TemplateError` for an
|
|
88
|
+
* error the LWC template compiler reports in a template.
|
|
89
|
+
*/
|
|
90
|
+
export interface Diagnostic {
|
|
91
|
+
kind: "ConversionFailed" | "TemplateError";
|
|
92
|
+
message: string;
|
|
93
|
+
start: number;
|
|
94
|
+
length: number;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* A converted file. Metadata becomes declarations in a global script (`declare module "@salesforce/..."`
|
|
98
|
+
* and `declare namespace Apex`); a template becomes a module that imports its component and uses it the
|
|
99
|
+
* way the template does, so TypeScript checks the template against it. A file that declares nothing
|
|
100
|
+
* (such as an Apex interface) has empty `text`.
|
|
101
|
+
*/
|
|
102
|
+
export type ConvertedComponent = Component & ConversionOutput;
|
|
103
|
+
/** What a file converts to: its TypeScript, the mapped pieces of it, and any problems. */
|
|
104
|
+
export interface ConversionOutput {
|
|
105
|
+
text: string;
|
|
106
|
+
mapData: MapPair[];
|
|
107
|
+
diagnostics?: Diagnostic[];
|
|
108
|
+
}
|
|
109
|
+
export interface ConvertOptions {
|
|
110
|
+
/**
|
|
111
|
+
* The namespace Apex types are declared in, which may be dotted (`Org.Apex`). Defaults to `"Apex"`;
|
|
112
|
+
* `null` declares them globally, where a class can merge with a global type of the same name (such as
|
|
113
|
+
* the DOM's `Response`).
|
|
114
|
+
*/
|
|
115
|
+
apexNamespace?: string | null;
|
|
116
|
+
/**
|
|
117
|
+
* What becomes of an Apex class's fields that LWC can't read (not `@AuraEnabled`, not `public` or
|
|
118
|
+
* `global`, or `static`): `"include as deprecated"` (the default) declares them marked `@deprecated`
|
|
119
|
+
* with what they'd need, so editors flag their use; `"include"` declares them like any other field;
|
|
120
|
+
* `"exclude"` leaves them out. SObject definitions' fields are always declared.
|
|
121
|
+
*/
|
|
122
|
+
inaccessibleApexProperties?: InaccessibleApexProperties;
|
|
123
|
+
/**
|
|
124
|
+
* The project's namespace, used to know when to add or hide namespaces for modules and components
|
|
125
|
+
*/
|
|
126
|
+
projectNamespace?: string;
|
|
127
|
+
}
|
|
128
|
+
export {};
|
package/dist/types.js
ADDED
package/dist/xml.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { SourceRange } from "./text-builder.ts";
|
|
2
|
+
/** An element of metadata XML: its text and children, and where it is in the source. */
|
|
3
|
+
export declare class XmlElement {
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly children: XmlElement[];
|
|
6
|
+
/** The element's own text, with entities and CDATA decoded: `A & B` for `A & B`. */
|
|
7
|
+
text: string;
|
|
8
|
+
/** Where its name is in its start tag. */
|
|
9
|
+
readonly nameRange: SourceRange;
|
|
10
|
+
/**
|
|
11
|
+
* Where its content is, between its tags, as written (`A & B`). It's empty for a self-closing
|
|
12
|
+
* element (`<a/>`), and runs to the end of the file for one that's never closed.
|
|
13
|
+
*/
|
|
14
|
+
contentRange: SourceRange;
|
|
15
|
+
constructor(name: string, nameStart: number, contentStart: number);
|
|
16
|
+
/** Its first child named `name`. */
|
|
17
|
+
child(name: string): XmlElement | undefined;
|
|
18
|
+
/** Its children named `name`. */
|
|
19
|
+
childrenNamed(name: string): XmlElement[];
|
|
20
|
+
/** The text of its first child named `name`, or undefined without one. */
|
|
21
|
+
childText(name: string): string | undefined;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Parses metadata XML into its root element, or undefined when it has none. It never throws: an unclosed
|
|
25
|
+
* element is closed at the end of the file, and a closing tag that matches no open element is ignored.
|
|
26
|
+
*/
|
|
27
|
+
export declare function parseXml(content: string): XmlElement | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Parses a metadata file into its root element, which must be `<root>`: a file without one, or of
|
|
30
|
+
* another type, throws, naming the root it has.
|
|
31
|
+
*/
|
|
32
|
+
export declare function parseMetadataXml(content: string, root: string): XmlElement;
|
package/dist/xml.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Metadata XML, read tolerantly with htmlparser2, so a file being edited (with an unclosed or mismatched
|
|
2
|
+
// tag) converts as far as it goes. Its own module, which only converters import, so htmlparser2 loads with
|
|
3
|
+
// the first of them rather than when sf2ts is imported.
|
|
4
|
+
import { Parser } from "htmlparser2";
|
|
5
|
+
/** An element of metadata XML: its text and children, and where it is in the source. */
|
|
6
|
+
export class XmlElement {
|
|
7
|
+
name;
|
|
8
|
+
children = [];
|
|
9
|
+
/** The element's own text, with entities and CDATA decoded: `A & B` for `A & B`. */
|
|
10
|
+
text = "";
|
|
11
|
+
/** Where its name is in its start tag. */
|
|
12
|
+
nameRange;
|
|
13
|
+
/**
|
|
14
|
+
* Where its content is, between its tags, as written (`A & B`). It's empty for a self-closing
|
|
15
|
+
* element (`<a/>`), and runs to the end of the file for one that's never closed.
|
|
16
|
+
*/
|
|
17
|
+
contentRange;
|
|
18
|
+
constructor(name, nameStart, contentStart) {
|
|
19
|
+
this.name = name;
|
|
20
|
+
this.nameRange = { start: nameStart, end: nameStart + name.length };
|
|
21
|
+
this.contentRange = { start: contentStart, end: contentStart };
|
|
22
|
+
}
|
|
23
|
+
/** Its first child named `name`. */
|
|
24
|
+
child(name) {
|
|
25
|
+
return this.children.find((child) => child.name === name);
|
|
26
|
+
}
|
|
27
|
+
/** Its children named `name`. */
|
|
28
|
+
childrenNamed(name) {
|
|
29
|
+
return this.children.filter((child) => child.name === name);
|
|
30
|
+
}
|
|
31
|
+
/** The text of its first child named `name`, or undefined without one. */
|
|
32
|
+
childText(name) {
|
|
33
|
+
return this.child(name)?.text;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Parses metadata XML into its root element, or undefined when it has none. It never throws: an unclosed
|
|
38
|
+
* element is closed at the end of the file, and a closing tag that matches no open element is ignored.
|
|
39
|
+
*/
|
|
40
|
+
export function parseXml(content) {
|
|
41
|
+
const top = [];
|
|
42
|
+
const open = [];
|
|
43
|
+
// htmlparser2's indices are inclusive: a tag spans `startIndex` to `endIndex`
|
|
44
|
+
const parser = new Parser({
|
|
45
|
+
onopentag(name) {
|
|
46
|
+
const element = new XmlElement(name, parser.startIndex + 1, parser.endIndex + 1);
|
|
47
|
+
(open.at(-1)?.children ?? top).push(element);
|
|
48
|
+
open.push(element);
|
|
49
|
+
},
|
|
50
|
+
ontext(text) {
|
|
51
|
+
const element = open.at(-1);
|
|
52
|
+
if (element !== undefined) {
|
|
53
|
+
element.text += text;
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
onclosetag(_name, isImplied) {
|
|
57
|
+
const element = open.pop();
|
|
58
|
+
if (element === undefined) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
// a self-closing element is closed where it's opened, with no content
|
|
62
|
+
const selfClosing = isImplied && parser.startIndex + 1 === element.nameRange.start;
|
|
63
|
+
element.contentRange = { start: element.contentRange.start, end: selfClosing ? element.contentRange.start : parser.startIndex };
|
|
64
|
+
},
|
|
65
|
+
}, { xmlMode: true });
|
|
66
|
+
parser.write(content);
|
|
67
|
+
parser.end();
|
|
68
|
+
return top[0];
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Parses a metadata file into its root element, which must be `<root>`: a file without one, or of
|
|
72
|
+
* another type, throws, naming the root it has.
|
|
73
|
+
*/
|
|
74
|
+
export function parseMetadataXml(content, root) {
|
|
75
|
+
const element = parseXml(content);
|
|
76
|
+
if (element?.name !== root) {
|
|
77
|
+
throw new Error(`expected a <${root}> root element, ${element === undefined ? "but the file has none" : `found <${element.name}>`}`);
|
|
78
|
+
}
|
|
79
|
+
return element;
|
|
80
|
+
}
|
package/package.json
CHANGED
|
@@ -1,47 +1,58 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lwc-bolts/sf2ts",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"private": false,
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "Converts Salesforce metadata to LWC compatible TypeScript",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "Anthony Heber",
|
|
10
|
+
"email": "anthony@heber.dev",
|
|
11
|
+
"url": "https://github.com/aheber"
|
|
12
|
+
},
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=22.18"
|
|
15
|
+
},
|
|
6
16
|
"exports": {
|
|
7
17
|
".": {
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
18
|
+
"@lwc-bolts/source": "./src/index.ts",
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"default": "./dist/index.js"
|
|
11
21
|
}
|
|
12
22
|
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
13
27
|
"publishConfig": {
|
|
14
28
|
"access": "public"
|
|
15
29
|
},
|
|
16
30
|
"scripts": {
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
31
|
+
"prebuild": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
32
|
+
"build": "tsc -p tsconfig.build.json",
|
|
33
|
+
"prepack": "npm run build",
|
|
34
|
+
"generate:apex-library": "node scripts/generate-apex-library.ts",
|
|
35
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
36
|
+
"test": "node --conditions=@lwc-bolts/source --test 'test/**/*.test.ts'",
|
|
37
|
+
"test:update": "node --conditions=@lwc-bolts/source --test --test-update-snapshots 'test/**/*.test.ts'",
|
|
38
|
+
"test:coverage": "node --conditions=@lwc-bolts/source --test --experimental-test-coverage --test-coverage-include='src/**' --test-coverage-exclude='src/apex/library.generated.ts' 'test/**/*.test.ts'"
|
|
20
39
|
},
|
|
21
40
|
"keywords": [
|
|
22
41
|
"salesforce",
|
|
23
42
|
"apex",
|
|
43
|
+
"lwc",
|
|
24
44
|
"typescript"
|
|
25
45
|
],
|
|
26
|
-
"author": {
|
|
27
|
-
"name": "Anthony Heber",
|
|
28
|
-
"email": "anthony@heber.dev",
|
|
29
|
-
"url": "https://github.com/aheber"
|
|
30
|
-
},
|
|
31
|
-
"license": "MIT",
|
|
32
46
|
"dependencies": {
|
|
33
|
-
"
|
|
34
|
-
"
|
|
47
|
+
"@lwc-bolts/lightning-base-components": "^1.0.0",
|
|
48
|
+
"@lwc/template-compiler": "~9.4.3",
|
|
49
|
+
"@sveltejs/acorn-typescript": "^1.0.13",
|
|
50
|
+
"acorn": "^8.19.0",
|
|
51
|
+
"htmlparser2": "^10.1.0",
|
|
52
|
+
"web-tree-sitter-sfapex": "^3.0.2"
|
|
35
53
|
},
|
|
36
54
|
"devDependencies": {
|
|
37
|
-
"@types/
|
|
38
|
-
"
|
|
39
|
-
}
|
|
40
|
-
"files": [
|
|
41
|
-
"lib",
|
|
42
|
-
"lib-cjs",
|
|
43
|
-
"package.json",
|
|
44
|
-
"package-lock.json",
|
|
45
|
-
"README.MD"
|
|
46
|
-
]
|
|
55
|
+
"@types/node": "^22.20.5",
|
|
56
|
+
"typescript": "7.1.0-beta"
|
|
57
|
+
}
|
|
47
58
|
}
|
package/README.MD
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
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.
|
package/lib/converts/_index.mjs
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
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 { CustomLabel } from "./custom-label.mjs";
|
|
6
|
-
export { CustomLabels } from "./custom-labels.mjs";
|
|
7
|
-
export { CustomObject } from "./custom-object.mjs";
|
|
8
|
-
export { StaticResource } from "./static-resource.mjs";
|