@ryanstark24/sfgraph-core 1.1.0 → 1.1.3
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/dist/analyze/__tests__/audit-graph.test.d.ts +2 -0
- package/dist/analyze/__tests__/audit-graph.test.d.ts.map +1 -0
- package/dist/analyze/__tests__/audit-graph.test.js +122 -0
- package/dist/analyze/__tests__/audit-graph.test.js.map +1 -0
- package/dist/analyze/audit-graph.d.ts +48 -0
- package/dist/analyze/audit-graph.d.ts.map +1 -0
- package/dist/analyze/audit-graph.js +50 -0
- package/dist/analyze/audit-graph.js.map +1 -0
- package/dist/analyze/index.d.ts +1 -0
- package/dist/analyze/index.d.ts.map +1 -1
- package/dist/analyze/index.js +1 -0
- package/dist/analyze/index.js.map +1 -1
- package/dist/domain/rel-types.d.ts +7 -0
- package/dist/domain/rel-types.d.ts.map +1 -1
- package/dist/domain/rel-types.js +7 -0
- package/dist/domain/rel-types.js.map +1 -1
- package/dist/extractors/live-org/__tests__/rate-limit.test.js +46 -1
- package/dist/extractors/live-org/__tests__/rate-limit.test.js.map +1 -1
- package/dist/extractors/live-org/capabilities.d.ts.map +1 -1
- package/dist/extractors/live-org/capabilities.js +21 -3
- package/dist/extractors/live-org/capabilities.js.map +1 -1
- package/dist/extractors/live-org/discovery.d.ts.map +1 -1
- package/dist/extractors/live-org/discovery.js +2 -4
- package/dist/extractors/live-org/discovery.js.map +1 -1
- package/dist/extractors/live-org/extractors/apex.d.ts.map +1 -1
- package/dist/extractors/live-org/extractors/apex.js +4 -4
- package/dist/extractors/live-org/extractors/apex.js.map +1 -1
- package/dist/extractors/live-org/extractors/generic-sobject.d.ts.map +1 -1
- package/dist/extractors/live-org/extractors/generic-sobject.js +2 -2
- package/dist/extractors/live-org/extractors/generic-sobject.js.map +1 -1
- package/dist/extractors/live-org/extractors/lwc.d.ts.map +1 -1
- package/dist/extractors/live-org/extractors/lwc.js +3 -3
- package/dist/extractors/live-org/extractors/lwc.js.map +1 -1
- package/dist/extractors/live-org/extractors/object.d.ts.map +1 -1
- package/dist/extractors/live-org/extractors/object.js +4 -4
- package/dist/extractors/live-org/extractors/object.js.map +1 -1
- package/dist/extractors/live-org/extractors/omnistudio.d.ts.map +1 -1
- package/dist/extractors/live-org/extractors/omnistudio.js +3 -3
- package/dist/extractors/live-org/extractors/omnistudio.js.map +1 -1
- package/dist/extractors/live-org/rate-limit.d.ts +18 -11
- package/dist/extractors/live-org/rate-limit.d.ts.map +1 -1
- package/dist/extractors/live-org/rate-limit.js +51 -19
- package/dist/extractors/live-org/rate-limit.js.map +1 -1
- package/dist/extractors/live-org/source-member.d.ts.map +1 -1
- package/dist/extractors/live-org/source-member.js +3 -3
- package/dist/extractors/live-org/source-member.js.map +1 -1
- package/dist/extractors/live-org/vlocity/runner.d.ts.map +1 -1
- package/dist/extractors/live-org/vlocity/runner.js +71 -38
- package/dist/extractors/live-org/vlocity/runner.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/ingest/__tests__/multi-org-orchestrator.test.js +4 -0
- package/dist/ingest/__tests__/multi-org-orchestrator.test.js.map +1 -1
- package/dist/ingest/live-ingest.d.ts +16 -0
- package/dist/ingest/live-ingest.d.ts.map +1 -1
- package/dist/ingest/live-ingest.js +93 -0
- package/dist/ingest/live-ingest.js.map +1 -1
- package/dist/parsers/__tests__/lwc-bindings.test.d.ts +2 -0
- package/dist/parsers/__tests__/lwc-bindings.test.d.ts.map +1 -0
- package/dist/parsers/__tests__/lwc-bindings.test.js +74 -0
- package/dist/parsers/__tests__/lwc-bindings.test.js.map +1 -0
- package/dist/parsers/apex/__tests__/arity-resolver.test.d.ts +2 -0
- package/dist/parsers/apex/__tests__/arity-resolver.test.d.ts.map +1 -0
- package/dist/parsers/apex/__tests__/arity-resolver.test.js +135 -0
- package/dist/parsers/apex/__tests__/arity-resolver.test.js.map +1 -0
- package/dist/parsers/apex/__tests__/ast-extractor.test.d.ts +2 -0
- package/dist/parsers/apex/__tests__/ast-extractor.test.d.ts.map +1 -0
- package/dist/parsers/apex/__tests__/ast-extractor.test.js +146 -0
- package/dist/parsers/apex/__tests__/ast-extractor.test.js.map +1 -0
- package/dist/parsers/apex/arity-resolver.d.ts +42 -0
- package/dist/parsers/apex/arity-resolver.d.ts.map +1 -0
- package/dist/parsers/apex/arity-resolver.js +111 -0
- package/dist/parsers/apex/arity-resolver.js.map +1 -0
- package/dist/parsers/apex/ast-extractor.d.ts +42 -0
- package/dist/parsers/apex/ast-extractor.d.ts.map +1 -0
- package/dist/parsers/apex/ast-extractor.js +402 -0
- package/dist/parsers/apex/ast-extractor.js.map +1 -0
- package/dist/parsers/apex/class.d.ts.map +1 -1
- package/dist/parsers/apex/class.js +56 -2
- package/dist/parsers/apex/class.js.map +1 -1
- package/dist/parsers/flow/__tests__/invocable-resolver.test.d.ts +2 -0
- package/dist/parsers/flow/__tests__/invocable-resolver.test.d.ts.map +1 -0
- package/dist/parsers/flow/__tests__/invocable-resolver.test.js +91 -0
- package/dist/parsers/flow/__tests__/invocable-resolver.test.js.map +1 -0
- package/dist/parsers/flow/invocable-resolver.d.ts +30 -0
- package/dist/parsers/flow/invocable-resolver.d.ts.map +1 -0
- package/dist/parsers/flow/invocable-resolver.js +67 -0
- package/dist/parsers/flow/invocable-resolver.js.map +1 -0
- package/dist/parsers/index.d.ts +2 -0
- package/dist/parsers/index.d.ts.map +1 -1
- package/dist/parsers/index.js +2 -0
- package/dist/parsers/index.js.map +1 -1
- package/dist/parsers/lwc/bundle.d.ts.map +1 -1
- package/dist/parsers/lwc/bundle.js +19 -4
- package/dist/parsers/lwc/bundle.js.map +1 -1
- package/dist/parsers/lwc/html-visitor.d.ts +2 -1
- package/dist/parsers/lwc/html-visitor.d.ts.map +1 -1
- package/dist/parsers/lwc/html-visitor.js +67 -3
- package/dist/parsers/lwc/html-visitor.js.map +1 -1
- package/dist/parsers/lwc/js-visitor.d.ts +22 -0
- package/dist/parsers/lwc/js-visitor.d.ts.map +1 -1
- package/dist/parsers/lwc/js-visitor.js +86 -2
- package/dist/parsers/lwc/js-visitor.js.map +1 -1
- package/dist/storage/interfaces.d.ts +9 -0
- package/dist/storage/interfaces.d.ts.map +1 -1
- package/dist/storage/sqlite/graph-store.d.ts +10 -0
- package/dist/storage/sqlite/graph-store.d.ts.map +1 -1
- package/dist/storage/sqlite/graph-store.js +56 -0
- package/dist/storage/sqlite/graph-store.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { asOrgId, asQualifiedName, asSha256 } from "@ryanstark24/sfgraph-shared";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { REL_TYPES } from "../../../domain/index.js";
|
|
4
|
+
import { SqliteGraphStore } from "../../../storage/sqlite/graph-store.js";
|
|
5
|
+
import { makeTestCtx } from "../../__tests__/_harness.js";
|
|
6
|
+
import { resolveFlowApexMethods } from "../invocable-resolver.js";
|
|
7
|
+
async function seed() {
|
|
8
|
+
const store = new SqliteGraphStore({ dbPath: ":memory:" });
|
|
9
|
+
await store.init();
|
|
10
|
+
const orgId = asOrgId("org_test");
|
|
11
|
+
store.upsertOrg({
|
|
12
|
+
id: orgId,
|
|
13
|
+
alias: "test",
|
|
14
|
+
instanceUrl: "https://example.test",
|
|
15
|
+
apiVersion: "59.0",
|
|
16
|
+
createdAt: Date.now(),
|
|
17
|
+
});
|
|
18
|
+
return { store, orgId };
|
|
19
|
+
}
|
|
20
|
+
function node(orgId, qn, label, attrs = {}) {
|
|
21
|
+
const ts = Date.now();
|
|
22
|
+
return {
|
|
23
|
+
orgId,
|
|
24
|
+
qualifiedName: asQualifiedName(qn),
|
|
25
|
+
label,
|
|
26
|
+
attributes: attrs,
|
|
27
|
+
sourceHash: asSha256(qn),
|
|
28
|
+
firstSeenAt: ts,
|
|
29
|
+
lastSeenAt: ts,
|
|
30
|
+
lastModifiedAt: ts,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
describe("resolveFlowApexMethods", () => {
|
|
34
|
+
it("emits FLOW_INVOKES_APEX_METHOD when the class has exactly one @InvocableMethod", async () => {
|
|
35
|
+
const { store, orgId } = await seed();
|
|
36
|
+
const ts = Date.now();
|
|
37
|
+
store.mergeNodes([
|
|
38
|
+
node(orgId, "FlowVersion:DoStuff/1", "FlowVersion"),
|
|
39
|
+
node(orgId, "ApexClass:DoStuffHandler", "ApexClass"),
|
|
40
|
+
node(orgId, "ApexMethod:DoStuffHandler.run(1)", "ApexMethod", { isInvocable: true }),
|
|
41
|
+
node(orgId, "ApexMethod:DoStuffHandler.helper(0)", "ApexMethod", { isInvocable: false }),
|
|
42
|
+
]);
|
|
43
|
+
store.mergeEdges([
|
|
44
|
+
{
|
|
45
|
+
orgId,
|
|
46
|
+
srcQualifiedName: asQualifiedName("FlowVersion:DoStuff/1"),
|
|
47
|
+
dstQualifiedName: asQualifiedName("ApexClass:DoStuffHandler"),
|
|
48
|
+
relType: REL_TYPES.FLOW_INVOKES_APEX,
|
|
49
|
+
attributes: { actionName: "DoStuffHandler" },
|
|
50
|
+
firstSeenAt: ts,
|
|
51
|
+
lastSeenAt: ts,
|
|
52
|
+
},
|
|
53
|
+
]);
|
|
54
|
+
const result = resolveFlowApexMethods(store, orgId, makeTestCtx());
|
|
55
|
+
expect(result).toMatchObject({ scanned: 1, resolved: 1, ambiguous: 0, missing: 0 });
|
|
56
|
+
const edges = store.listEdgesFrom(orgId, asQualifiedName("FlowVersion:DoStuff/1"), REL_TYPES.FLOW_INVOKES_APEX_METHOD);
|
|
57
|
+
expect(edges).toHaveLength(1);
|
|
58
|
+
expect(edges[0]?.dstQualifiedName).toBe("ApexMethod:DoStuffHandler.run(1)");
|
|
59
|
+
expect(edges[0]?.attributes.resolvedBy).toBe("flow-invocable-resolver");
|
|
60
|
+
// Class-level edge is left in place — additive resolution.
|
|
61
|
+
const classEdges = store.listEdgesFrom(orgId, asQualifiedName("FlowVersion:DoStuff/1"), REL_TYPES.FLOW_INVOKES_APEX);
|
|
62
|
+
expect(classEdges).toHaveLength(1);
|
|
63
|
+
await store.close();
|
|
64
|
+
});
|
|
65
|
+
it("reports missing when the target class has no invocable method", async () => {
|
|
66
|
+
const { store, orgId } = await seed();
|
|
67
|
+
const ts = Date.now();
|
|
68
|
+
store.mergeNodes([
|
|
69
|
+
node(orgId, "FlowVersion:Stale/1", "FlowVersion"),
|
|
70
|
+
node(orgId, "ApexClass:NotInvocable", "ApexClass"),
|
|
71
|
+
node(orgId, "ApexMethod:NotInvocable.run(0)", "ApexMethod", { isInvocable: false }),
|
|
72
|
+
]);
|
|
73
|
+
store.mergeEdges([
|
|
74
|
+
{
|
|
75
|
+
orgId,
|
|
76
|
+
srcQualifiedName: asQualifiedName("FlowVersion:Stale/1"),
|
|
77
|
+
dstQualifiedName: asQualifiedName("ApexClass:NotInvocable"),
|
|
78
|
+
relType: REL_TYPES.FLOW_INVOKES_APEX,
|
|
79
|
+
attributes: {},
|
|
80
|
+
firstSeenAt: ts,
|
|
81
|
+
lastSeenAt: ts,
|
|
82
|
+
},
|
|
83
|
+
]);
|
|
84
|
+
const result = resolveFlowApexMethods(store, orgId, makeTestCtx());
|
|
85
|
+
expect(result).toMatchObject({ scanned: 1, resolved: 0, missing: 1 });
|
|
86
|
+
const edges = store.listEdgesFrom(orgId, asQualifiedName("FlowVersion:Stale/1"), REL_TYPES.FLOW_INVOKES_APEX_METHOD);
|
|
87
|
+
expect(edges).toHaveLength(0);
|
|
88
|
+
await store.close();
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
//# sourceMappingURL=invocable-resolver.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invocable-resolver.test.js","sourceRoot":"","sources":["../../../../src/parsers/flow/__tests__/invocable-resolver.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { OrgId } from "@ryanstark24/sfgraph-shared";
|
|
2
|
+
import type { GraphStore } from "../../storage/interfaces.js";
|
|
3
|
+
import type { ParseContext } from "../contract.js";
|
|
4
|
+
/**
|
|
5
|
+
* Resolve `FLOW_INVOKES_APEX → ApexClass:Foo` edges to the specific
|
|
6
|
+
* `@InvocableMethod` defined on the class. Flow XML only exposes the class
|
|
7
|
+
* (and any input parameter mapping), not the method name — but because Apex
|
|
8
|
+
* allows at most one `@InvocableMethod` per class, we can pair them up
|
|
9
|
+
* unambiguously when there is exactly one such method.
|
|
10
|
+
*
|
|
11
|
+
* 0 invocable methods on the class → leave the class-level edge alone,
|
|
12
|
+
* count as `missing` (likely the class doesn't actually implement
|
|
13
|
+
* Invocable; could be a stale flow ref).
|
|
14
|
+
* 1 invocable method → emit a new FLOW_INVOKES_APEX_METHOD edge.
|
|
15
|
+
* >1 invocable methods → ambiguous (legal in Apex? technically the compiler
|
|
16
|
+
* forbids two `@InvocableMethod`s in one class, but we defend anyway).
|
|
17
|
+
*/
|
|
18
|
+
export interface FlowInvocableResolveResult {
|
|
19
|
+
/** Flow→ApexClass edges scanned. */
|
|
20
|
+
scanned: number;
|
|
21
|
+
/** Edges that produced exactly one method-level edge. */
|
|
22
|
+
resolved: number;
|
|
23
|
+
/** Classes with multiple invocable methods — should never happen but
|
|
24
|
+
* tracked defensively. */
|
|
25
|
+
ambiguous: number;
|
|
26
|
+
/** Class qnames that didn't contain any `@InvocableMethod` annotation. */
|
|
27
|
+
missing: number;
|
|
28
|
+
}
|
|
29
|
+
export declare function resolveFlowApexMethods(store: GraphStore, orgIdIn: OrgId | string, ctx: ParseContext): FlowInvocableResolveResult;
|
|
30
|
+
//# sourceMappingURL=invocable-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invocable-resolver.d.ts","sourceRoot":"","sources":["../../../src/parsers/flow/invocable-resolver.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAGzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,0BAA0B;IACzC,oCAAoC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB;+BAC2B;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC;CACjB;AAID,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,KAAK,GAAG,MAAM,EACvB,GAAG,EAAE,YAAY,GAChB,0BAA0B,CAyE5B"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { asOrgId, asQualifiedName } from "@ryanstark24/sfgraph-shared";
|
|
2
|
+
import { REL_TYPES } from "../../domain/index.js";
|
|
3
|
+
import { makeEdge } from "../common.js";
|
|
4
|
+
const METHOD_QNAME_RE = /^ApexMethod:(.+)\.([A-Za-z_][\w]*)\(\d+\)$/;
|
|
5
|
+
export function resolveFlowApexMethods(store, orgIdIn, ctx) {
|
|
6
|
+
const orgId = typeof orgIdIn === "string" ? asOrgId(orgIdIn) : orgIdIn;
|
|
7
|
+
const result = {
|
|
8
|
+
scanned: 0,
|
|
9
|
+
resolved: 0,
|
|
10
|
+
ambiguous: 0,
|
|
11
|
+
missing: 0,
|
|
12
|
+
};
|
|
13
|
+
// Build className → ApexMethod[] of invocable methods (one query, walked once).
|
|
14
|
+
const apexMethods = [
|
|
15
|
+
...store.listNodesByLabel(orgId, "ApexMethod"),
|
|
16
|
+
...store.listNodesByLabel(orgId, "TestMethod"),
|
|
17
|
+
];
|
|
18
|
+
const invocableByClass = new Map();
|
|
19
|
+
for (const n of apexMethods) {
|
|
20
|
+
if (n.attributes?.isInvocable !== true)
|
|
21
|
+
continue;
|
|
22
|
+
const m = METHOD_QNAME_RE.exec(String(n.qualifiedName));
|
|
23
|
+
if (!m)
|
|
24
|
+
continue;
|
|
25
|
+
const cls = m[1] ?? "";
|
|
26
|
+
const bucket = invocableByClass.get(cls) ?? [];
|
|
27
|
+
bucket.push(n);
|
|
28
|
+
invocableByClass.set(cls, bucket);
|
|
29
|
+
}
|
|
30
|
+
// Find every FLOW_INVOKES_APEX edge and try to resolve it.
|
|
31
|
+
// listEdgesByDstLike("ApexClass:%") is cheap given how many ApexClass nodes
|
|
32
|
+
// exist in a typical org.
|
|
33
|
+
const flowEdges = store.listEdgesByDstLike(orgId, "ApexClass:%", REL_TYPES.FLOW_INVOKES_APEX);
|
|
34
|
+
const newEdges = [];
|
|
35
|
+
for (const edge of flowEdges) {
|
|
36
|
+
result.scanned += 1;
|
|
37
|
+
const className = String(edge.dstQualifiedName).slice("ApexClass:".length);
|
|
38
|
+
const candidates = invocableByClass.get(className) ?? [];
|
|
39
|
+
if (candidates.length === 0) {
|
|
40
|
+
result.missing += 1;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (candidates.length > 1) {
|
|
44
|
+
result.ambiguous += 1;
|
|
45
|
+
// Still emit edges to all of them so downstream impact analysis sees both.
|
|
46
|
+
}
|
|
47
|
+
result.resolved += 1;
|
|
48
|
+
for (const m of candidates) {
|
|
49
|
+
const e = makeEdge(ctx, String(edge.srcQualifiedName), REL_TYPES.FLOW_INVOKES_APEX_METHOD, String(m.qualifiedName), {
|
|
50
|
+
...edge.attributes,
|
|
51
|
+
resolvedBy: "flow-invocable-resolver",
|
|
52
|
+
ambiguous: candidates.length > 1,
|
|
53
|
+
});
|
|
54
|
+
newEdges.push({
|
|
55
|
+
...e,
|
|
56
|
+
orgId: edge.orgId,
|
|
57
|
+
firstSeenAt: edge.firstSeenAt,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (newEdges.length > 0)
|
|
62
|
+
store.mergeEdges(newEdges);
|
|
63
|
+
// Suppress an unused-import warning when asQualifiedName isn't directly used.
|
|
64
|
+
void asQualifiedName;
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=invocable-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invocable-resolver.js","sourceRoot":"","sources":["../../../src/parsers/flow/invocable-resolver.ts"],"names":[],"mappings":"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"}
|
package/dist/parsers/index.d.ts
CHANGED
|
@@ -16,6 +16,8 @@ import "./generic/index.js";
|
|
|
16
16
|
export * from "./contract.js";
|
|
17
17
|
export { parserRegistry, resetRegistryForTests } from "./registry.js";
|
|
18
18
|
export { resolveCrossFlavor, normalizeKey } from "./cross-flavor-resolver.js";
|
|
19
|
+
export { resolveApexMethodArity, type ArityResolveOpts, type ArityResolveResult, } from "./apex/arity-resolver.js";
|
|
20
|
+
export { resolveFlowApexMethods, type FlowInvocableResolveResult, } from "./flow/invocable-resolver.js";
|
|
19
21
|
export { ParserWorkerPool } from "./worker-pool.js";
|
|
20
22
|
export { loadAllRules } from "./rules/_loader.js";
|
|
21
23
|
export { RuleBasedParser } from "./rules/_engine.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/parsers/index.ts"],"names":[],"mappings":"AACA,OAAO,iBAAiB,CAAC;AACzB,OAAO,gBAAgB,CAAC;AACxB,OAAO,iBAAiB,CAAC;AACzB,OAAO,mBAAmB,CAAC;AAC3B,OAAO,qBAAqB,CAAC;AAC7B,OAAO,wBAAwB,CAAC;AAChC,OAAO,oBAAoB,CAAC;AAC5B,OAAO,uBAAuB,CAAC;AAC/B,OAAO,eAAe,CAAC;AACvB,OAAO,yBAAyB,CAAC;AACjC,OAAO,sBAAsB,CAAC;AAC9B,OAAO,kBAAkB,CAAC;AAC1B,OAAO,uBAAuB,CAAC;AAC/B,OAAO,oBAAoB,CAAC;AAC5B,OAAO,oBAAoB,CAAC;AAE5B,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/parsers/index.ts"],"names":[],"mappings":"AACA,OAAO,iBAAiB,CAAC;AACzB,OAAO,gBAAgB,CAAC;AACxB,OAAO,iBAAiB,CAAC;AACzB,OAAO,mBAAmB,CAAC;AAC3B,OAAO,qBAAqB,CAAC;AAC7B,OAAO,wBAAwB,CAAC;AAChC,OAAO,oBAAoB,CAAC;AAC5B,OAAO,uBAAuB,CAAC;AAC/B,OAAO,eAAe,CAAC;AACvB,OAAO,yBAAyB,CAAC;AACjC,OAAO,sBAAsB,CAAC;AAC9B,OAAO,kBAAkB,CAAC;AAC1B,OAAO,uBAAuB,CAAC;AAC/B,OAAO,oBAAoB,CAAC;AAC5B,OAAO,oBAAoB,CAAC;AAE5B,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EACL,sBAAsB,EACtB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,sBAAsB,EACtB,KAAK,0BAA0B,GAChC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/parsers/index.js
CHANGED
|
@@ -17,6 +17,8 @@ import "./generic/index.js";
|
|
|
17
17
|
export * from "./contract.js";
|
|
18
18
|
export { parserRegistry, resetRegistryForTests } from "./registry.js";
|
|
19
19
|
export { resolveCrossFlavor, normalizeKey } from "./cross-flavor-resolver.js";
|
|
20
|
+
export { resolveApexMethodArity, } from "./apex/arity-resolver.js";
|
|
21
|
+
export { resolveFlowApexMethods, } from "./flow/invocable-resolver.js";
|
|
20
22
|
export { ParserWorkerPool } from "./worker-pool.js";
|
|
21
23
|
export { loadAllRules } from "./rules/_loader.js";
|
|
22
24
|
export { RuleBasedParser } from "./rules/_engine.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/parsers/index.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,OAAO,iBAAiB,CAAC;AACzB,OAAO,gBAAgB,CAAC;AACxB,OAAO,iBAAiB,CAAC;AACzB,OAAO,mBAAmB,CAAC;AAC3B,OAAO,qBAAqB,CAAC;AAC7B,OAAO,wBAAwB,CAAC;AAChC,OAAO,oBAAoB,CAAC;AAC5B,OAAO,uBAAuB,CAAC;AAC/B,OAAO,eAAe,CAAC;AACvB,OAAO,yBAAyB,CAAC;AACjC,OAAO,sBAAsB,CAAC;AAC9B,OAAO,kBAAkB,CAAC;AAC1B,OAAO,uBAAuB,CAAC;AAC/B,OAAO,oBAAoB,CAAC;AAC5B,OAAO,oBAAoB,CAAC;AAE5B,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/parsers/index.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,OAAO,iBAAiB,CAAC;AACzB,OAAO,gBAAgB,CAAC;AACxB,OAAO,iBAAiB,CAAC;AACzB,OAAO,mBAAmB,CAAC;AAC3B,OAAO,qBAAqB,CAAC;AAC7B,OAAO,wBAAwB,CAAC;AAChC,OAAO,oBAAoB,CAAC;AAC5B,OAAO,uBAAuB,CAAC;AAC/B,OAAO,eAAe,CAAC;AACvB,OAAO,yBAAyB,CAAC;AACjC,OAAO,sBAAsB,CAAC;AAC9B,OAAO,kBAAkB,CAAC;AAC1B,OAAO,uBAAuB,CAAC;AAC/B,OAAO,oBAAoB,CAAC;AAC5B,OAAO,oBAAoB,CAAC;AAE5B,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EACL,sBAAsB,GAGvB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,sBAAsB,GAEvB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../../../src/parsers/lwc/bundle.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAKxE,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAkBD,qBAAa,eAAgB,YAAW,MAAM,CAAC,cAAc,CAAC;IAC5D,QAAQ,CAAC,QAAQ,6BAAyB;IAC1C,QAAQ,CAAC,IAAI,8BAA8B;IAErC,KAAK,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../../../src/parsers/lwc/bundle.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAKxE,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAkBD,qBAAa,eAAgB,YAAW,MAAM,CAAC,cAAc,CAAC;IAC5D,QAAQ,CAAC,QAAQ,6BAAyB;IAC1C,QAAQ,CAAC,IAAI,8BAA8B;IAErC,KAAK,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC;CAoD5E"}
|
|
@@ -34,15 +34,30 @@ export class LwcBundleParser {
|
|
|
34
34
|
nodes.push(makeNode(ctx, "LWC", lwcQname, { name: bundleName, ...meta }, hash));
|
|
35
35
|
nodes.push(makeNode(ctx, "LWCBundle", bundleQname, { name: bundleName, files: Object.keys(input.files) }, hash));
|
|
36
36
|
edges.push(makeEdge(ctx, bundleQname, REL_TYPES.CONTAINS_COMPONENT, lwcQname));
|
|
37
|
-
// JS
|
|
37
|
+
// Pass 1: walk the JS first so we know which template variables are
|
|
38
|
+
// wired to which sObjects before walking the HTML.
|
|
39
|
+
const mergedBindings = {
|
|
40
|
+
wireToSObject: new Map(),
|
|
41
|
+
schemaFieldImports: new Map(),
|
|
42
|
+
schemaObjectImports: new Map(),
|
|
43
|
+
};
|
|
38
44
|
for (const [fname, source] of Object.entries(input.files)) {
|
|
39
45
|
if (fname.endsWith(".js")) {
|
|
40
|
-
const { extraEdges, extraNodes } = extractJsEdges(source, lwcQname, ctx);
|
|
46
|
+
const { extraEdges, extraNodes, bindings: b } = extractJsEdges(source, lwcQname, ctx);
|
|
41
47
|
edges.push(...extraEdges);
|
|
42
48
|
nodes.push(...extraNodes);
|
|
49
|
+
for (const [k, v] of b.wireToSObject)
|
|
50
|
+
mergedBindings.wireToSObject.set(k, v);
|
|
51
|
+
for (const [k, v] of b.schemaFieldImports)
|
|
52
|
+
mergedBindings.schemaFieldImports.set(k, v);
|
|
53
|
+
for (const [k, v] of b.schemaObjectImports)
|
|
54
|
+
mergedBindings.schemaObjectImports.set(k, v);
|
|
43
55
|
}
|
|
44
|
-
|
|
45
|
-
|
|
56
|
+
}
|
|
57
|
+
// Pass 2: HTML, with the merged bindings available.
|
|
58
|
+
for (const [fname, source] of Object.entries(input.files)) {
|
|
59
|
+
if (fname.endsWith(".html")) {
|
|
60
|
+
const { extraEdges, extraNodes } = extractHtmlEdges(source, lwcQname, ctx, mergedBindings);
|
|
46
61
|
edges.push(...extraEdges);
|
|
47
62
|
nodes.push(...extraNodes);
|
|
48
63
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle.js","sourceRoot":"","sources":["../../../src/parsers/lwc/bundle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAiB,iBAAiB,EAAiB,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACnG,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,
|
|
1
|
+
{"version":3,"file":"bundle.js","sourceRoot":"","sources":["../../../src/parsers/lwc/bundle.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { EdgeFact, NodeFact } from "../../domain/index.js";
|
|
2
2
|
import type { ParseContext } from "../contract.js";
|
|
3
|
+
import type { LwcBindings } from "./js-visitor.js";
|
|
3
4
|
export interface HtmlExtractResult {
|
|
4
5
|
extraEdges: EdgeFact[];
|
|
5
6
|
extraNodes: NodeFact[];
|
|
6
7
|
}
|
|
7
|
-
export declare function extractHtmlEdges(source: string, lwcQname: string, ctx: ParseContext): HtmlExtractResult;
|
|
8
|
+
export declare function extractHtmlEdges(source: string, lwcQname: string, ctx: ParseContext, bindings?: LwcBindings): HtmlExtractResult;
|
|
8
9
|
//# sourceMappingURL=html-visitor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html-visitor.d.ts","sourceRoot":"","sources":["../../../src/parsers/lwc/html-visitor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGhE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,QAAQ,EAAE,CAAC;IACvB,UAAU,EAAE,QAAQ,EAAE,CAAC;CACxB;
|
|
1
|
+
{"version":3,"file":"html-visitor.d.ts","sourceRoot":"","sources":["../../../src/parsers/lwc/html-visitor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGhE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,QAAQ,EAAE,CAAC;IACvB,UAAU,EAAE,QAAQ,EAAE,CAAC;CACxB;AAaD,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,YAAY,EACjB,QAAQ,CAAC,EAAE,WAAW,GACrB,iBAAiB,CA2GnB"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { parse as parseHtml } from "parse5";
|
|
2
2
|
import { REL_TYPES } from "../../domain/index.js";
|
|
3
|
-
import { makeEdge } from "../common.js";
|
|
3
|
+
import { makeEdge, stripNs } from "../common.js";
|
|
4
4
|
function walk(node, visit) {
|
|
5
5
|
visit(node);
|
|
6
6
|
// <template> elements wrap their children in a `content` DocumentFragment per HTML spec.
|
|
@@ -11,13 +11,73 @@ function walk(node, visit) {
|
|
|
11
11
|
for (const c of kids)
|
|
12
12
|
walk(c, visit);
|
|
13
13
|
}
|
|
14
|
-
|
|
14
|
+
const BINDING_RE = /\{([A-Za-z_][\w]*(?:\.[A-Za-z_][\w]*)*)\}/g;
|
|
15
|
+
export function extractHtmlEdges(source, lwcQname, ctx, bindings) {
|
|
15
16
|
const extraEdges = [];
|
|
16
17
|
const extraNodes = [];
|
|
17
18
|
const tree = parseHtml(source);
|
|
18
19
|
const seenLwc = new Set();
|
|
19
20
|
const seenEvt = new Set();
|
|
21
|
+
const seenField = new Set();
|
|
22
|
+
const seenProperty = new Set();
|
|
23
|
+
const emitBinding = (expr) => {
|
|
24
|
+
// `record.Name` → split into [record, Name]; `record.fields.Account.Name`
|
|
25
|
+
// is unusual in templates and falls through to property.
|
|
26
|
+
const parts = expr.split(".");
|
|
27
|
+
const head = parts[0] ?? "";
|
|
28
|
+
if (bindings && parts.length >= 2) {
|
|
29
|
+
const sObject = bindings.wireToSObject.get(head);
|
|
30
|
+
if (sObject) {
|
|
31
|
+
// Extract the field name from the binding, handling both adapter shapes:
|
|
32
|
+
// `record.Name` → tail = [Name] → field = Name
|
|
33
|
+
// `record.fields.Name` → tail = [fields, Name] → field = Name
|
|
34
|
+
// `record.fields.Name.value` → tail = [fields, Name, value] → field = Name
|
|
35
|
+
// (v53+ getRecord returns FieldValueRepresentation; templates
|
|
36
|
+
// access `.value` on the field proxy.)
|
|
37
|
+
// We deliberately *don't* concatenate intermediate segments — they're
|
|
38
|
+
// either the `fields` accessor or the proxy `.value`/`.displayValue`,
|
|
39
|
+
// neither of which are part of the CustomField qname.
|
|
40
|
+
const tail = parts.slice(1);
|
|
41
|
+
let field = "";
|
|
42
|
+
if (tail[0] === "fields") {
|
|
43
|
+
field = tail[1] ?? "";
|
|
44
|
+
}
|
|
45
|
+
else if (tail.length === 1) {
|
|
46
|
+
field = tail[0] ?? "";
|
|
47
|
+
}
|
|
48
|
+
if (field) {
|
|
49
|
+
const dst = `CustomField:${stripNs(sObject, ctx.namespace)}.${stripNs(field, ctx.namespace)}`;
|
|
50
|
+
if (!seenField.has(dst)) {
|
|
51
|
+
seenField.add(dst);
|
|
52
|
+
extraEdges.push(makeEdge(ctx, lwcQname, REL_TYPES.LWC_BINDS_FIELD, dst, { binding: expr }));
|
|
53
|
+
}
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const dst = `LWCProperty:${expr}`;
|
|
59
|
+
if (!seenProperty.has(dst)) {
|
|
60
|
+
seenProperty.add(dst);
|
|
61
|
+
extraEdges.push(makeEdge(ctx, lwcQname, REL_TYPES.LWC_BINDS_PROPERTY, dst, { binding: expr }));
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const scanText = (text) => {
|
|
65
|
+
if (!text || text.indexOf("{") < 0)
|
|
66
|
+
return;
|
|
67
|
+
BINDING_RE.lastIndex = 0;
|
|
68
|
+
let m = BINDING_RE.exec(text);
|
|
69
|
+
while (m !== null) {
|
|
70
|
+
const expr = m[1];
|
|
71
|
+
if (expr)
|
|
72
|
+
emitBinding(expr);
|
|
73
|
+
m = BINDING_RE.exec(text);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
20
76
|
walk(tree, (n) => {
|
|
77
|
+
// Text nodes carry mustache bindings — `{record.Name}`, `{count}`, etc.
|
|
78
|
+
if (n?.nodeName === "#text" && typeof n?.value === "string") {
|
|
79
|
+
scanText(n.value);
|
|
80
|
+
}
|
|
21
81
|
const tag = n?.tagName;
|
|
22
82
|
if (!tag)
|
|
23
83
|
return;
|
|
@@ -40,7 +100,8 @@ export function extractHtmlEdges(source, lwcQname, ctx) {
|
|
|
40
100
|
extraEdges.push(makeEdge(ctx, lwcQname, REL_TYPES.EMBEDS_AURA, dst, { tag }));
|
|
41
101
|
}
|
|
42
102
|
}
|
|
43
|
-
// Attributes that look like onevent listeners
|
|
103
|
+
// Attributes that look like onevent listeners + mustache bindings in
|
|
104
|
+
// attribute values (e.g. `<lightning-input value={record.Name}>`).
|
|
44
105
|
for (const a of n?.attrs ?? []) {
|
|
45
106
|
const name = a?.name ?? "";
|
|
46
107
|
if (name.startsWith("on") && name.length > 2) {
|
|
@@ -50,6 +111,9 @@ export function extractHtmlEdges(source, lwcQname, ctx) {
|
|
|
50
111
|
extraEdges.push(makeEdge(ctx, lwcQname, REL_TYPES.LISTENS_TO_EVENT, `LWCEvent:${evtName}`));
|
|
51
112
|
}
|
|
52
113
|
}
|
|
114
|
+
const val = a?.value;
|
|
115
|
+
if (typeof val === "string")
|
|
116
|
+
scanText(val);
|
|
53
117
|
}
|
|
54
118
|
});
|
|
55
119
|
return { extraEdges, extraNodes };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html-visitor.js","sourceRoot":"","sources":["../../../src/parsers/lwc/html-visitor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,QAAQ,CAAC;AAE5C,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"html-visitor.js","sourceRoot":"","sources":["../../../src/parsers/lwc/html-visitor.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,28 @@ import type { ParseContext } from "../contract.js";
|
|
|
3
3
|
export interface JsExtractResult {
|
|
4
4
|
extraEdges: EdgeFact[];
|
|
5
5
|
extraNodes: NodeFact[];
|
|
6
|
+
/** Phase 5: bindings the HTML visitor needs to resolve `{var.field}`. */
|
|
7
|
+
bindings: LwcBindings;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* LWC template bindings discovered through JS analysis. Consumed by
|
|
11
|
+
* html-visitor to resolve `{var.field}` expressions in markup to real
|
|
12
|
+
* CustomField qnames when possible.
|
|
13
|
+
*/
|
|
14
|
+
export interface LwcBindings {
|
|
15
|
+
/** Map of wired property name → sObject API name. Populated from
|
|
16
|
+
* `@wire(getRecord, { fields: [...] })` and similar wire adapters whose
|
|
17
|
+
* result is bound to an instance property. */
|
|
18
|
+
wireToSObject: Map<string, string>;
|
|
19
|
+
/** Map of imported local name → CustomField qname. From
|
|
20
|
+
* `import NAME_FIELD from '@salesforce/schema/Account.Name'`. */
|
|
21
|
+
schemaFieldImports: Map<string, {
|
|
22
|
+
sObject: string;
|
|
23
|
+
field: string;
|
|
24
|
+
}>;
|
|
25
|
+
/** Map of imported local name → sObject API name. From
|
|
26
|
+
* `import ACCOUNT_OBJECT from '@salesforce/schema/Account'`. */
|
|
27
|
+
schemaObjectImports: Map<string, string>;
|
|
6
28
|
}
|
|
7
29
|
export declare function extractJsEdges(source: string, lwcQname: string, ctx: ParseContext): JsExtractResult;
|
|
8
30
|
//# sourceMappingURL=js-visitor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"js-visitor.d.ts","sourceRoot":"","sources":["../../../src/parsers/lwc/js-visitor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGhE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAKnD,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,QAAQ,EAAE,CAAC;IACvB,UAAU,EAAE,QAAQ,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"js-visitor.d.ts","sourceRoot":"","sources":["../../../src/parsers/lwc/js-visitor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGhE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAKnD,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,QAAQ,EAAE,CAAC;IACvB,UAAU,EAAE,QAAQ,EAAE,CAAC;IACvB,yEAAyE;IACzE,QAAQ,EAAE,WAAW,CAAC;CACvB;AAQD;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B;;mDAE+C;IAC/C,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC;sEACkE;IAClE,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpE;qEACiE;IACjE,mBAAmB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C;AAED,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,YAAY,GAChB,eAAe,CAmOjB"}
|
|
@@ -7,11 +7,18 @@ const traverse = _traverse.default ?? _traverse;
|
|
|
7
7
|
const APEX_IMPORT_RE = /^@salesforce\/apex\/([A-Za-z_]\w*)\.([A-Za-z_]\w*)$/;
|
|
8
8
|
const SR_IMPORT_RE = /^@salesforce\/resourceUrl\/([A-Za-z_]\w*)$/;
|
|
9
9
|
const LABEL_IMPORT_RE = /^@salesforce\/label\/c\.([A-Za-z_]\w*)$/;
|
|
10
|
+
const SCHEMA_OBJ_RE = /^@salesforce\/schema\/([A-Za-z_][\w]*)$/;
|
|
11
|
+
const SCHEMA_FIELD_RE = /^@salesforce\/schema\/([A-Za-z_][\w]*)\.([A-Za-z_][\w]*)$/;
|
|
10
12
|
export function extractJsEdges(source, lwcQname, ctx) {
|
|
11
13
|
const extraEdges = [];
|
|
12
14
|
const extraNodes = [];
|
|
13
15
|
// Map of localName -> { className, methodName } for apex imports.
|
|
14
16
|
const apexImports = {};
|
|
17
|
+
const bindings = {
|
|
18
|
+
wireToSObject: new Map(),
|
|
19
|
+
schemaFieldImports: new Map(),
|
|
20
|
+
schemaObjectImports: new Map(),
|
|
21
|
+
};
|
|
15
22
|
let ast;
|
|
16
23
|
try {
|
|
17
24
|
ast = parser.parse(source, {
|
|
@@ -23,7 +30,7 @@ export function extractJsEdges(source, lwcQname, ctx) {
|
|
|
23
30
|
extraNodes.push(makeNode(ctx, "ParseError", `ParseError:LWC:${lwcQname}`, {
|
|
24
31
|
message: err instanceof Error ? err.message : String(err),
|
|
25
32
|
}));
|
|
26
|
-
return { extraEdges, extraNodes };
|
|
33
|
+
return { extraEdges, extraNodes, bindings };
|
|
27
34
|
}
|
|
28
35
|
traverse(ast, {
|
|
29
36
|
ImportDeclaration(path) {
|
|
@@ -50,6 +57,29 @@ export function extractJsEdges(source, lwcQname, ctx) {
|
|
|
50
57
|
m = LABEL_IMPORT_RE.exec(src);
|
|
51
58
|
if (m) {
|
|
52
59
|
extraEdges.push(makeEdge(ctx, lwcQname, REL_TYPES.IMPORTS_CUSTOM_LABEL, `CustomLabel:${stripNs(m[1] ?? "", ctx.namespace)}`));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
// Schema field import: `import NAME_FIELD from '@salesforce/schema/Account.Name'`.
|
|
63
|
+
m = SCHEMA_FIELD_RE.exec(src);
|
|
64
|
+
if (m) {
|
|
65
|
+
const sObject = stripNs(m[1] ?? "", ctx.namespace);
|
|
66
|
+
const field = m[2] ?? "";
|
|
67
|
+
const localDefault = path.node.specifiers?.find((s) => s.type === "ImportDefaultSpecifier");
|
|
68
|
+
if (localDefault?.local?.name) {
|
|
69
|
+
bindings.schemaFieldImports.set(String(localDefault.local.name), { sObject, field });
|
|
70
|
+
}
|
|
71
|
+
extraEdges.push(makeEdge(ctx, lwcQname, REL_TYPES.READS_FIELD, `CustomField:${sObject}.${field}`, { via: "schema-import" }));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// Schema object import: `import ACCOUNT_OBJECT from '@salesforce/schema/Account'`.
|
|
75
|
+
m = SCHEMA_OBJ_RE.exec(src);
|
|
76
|
+
if (m) {
|
|
77
|
+
const sObject = stripNs(m[1] ?? "", ctx.namespace);
|
|
78
|
+
const localDefault = path.node.specifiers?.find((s) => s.type === "ImportDefaultSpecifier");
|
|
79
|
+
if (localDefault?.local?.name) {
|
|
80
|
+
bindings.schemaObjectImports.set(String(localDefault.local.name), sObject);
|
|
81
|
+
}
|
|
82
|
+
extraEdges.push(makeEdge(ctx, lwcQname, REL_TYPES.REFERENCES_OBJECT, `CustomObject:${sObject}`, { via: "schema-import" }));
|
|
53
83
|
}
|
|
54
84
|
},
|
|
55
85
|
Decorator(path) {
|
|
@@ -62,6 +92,60 @@ export function extractJsEdges(source, lwcQname, ctx) {
|
|
|
62
92
|
extraEdges.push(makeEdge(ctx, lwcQname, REL_TYPES.USES_WIRE, `ApexMethod:${target.className}.${target.methodName}(?)`, { kind: "apex" }));
|
|
63
93
|
}
|
|
64
94
|
}
|
|
95
|
+
// For wire adapters that return a record/records of a known sObject
|
|
96
|
+
// (getRecord / getRecords / getRelatedListRecords), bind the wired
|
|
97
|
+
// property name to that sObject so the template visitor can resolve
|
|
98
|
+
// `{record.Name}` → CustomField:Account.Name.
|
|
99
|
+
const wireAdapter = arg0?.type === "Identifier" ? String(arg0.name) : "";
|
|
100
|
+
const RECORD_ADAPTERS = new Set([
|
|
101
|
+
"getRecord",
|
|
102
|
+
"getRecords",
|
|
103
|
+
"getRecordCreateDefaults",
|
|
104
|
+
"getRecordUi",
|
|
105
|
+
"getRelatedListRecords",
|
|
106
|
+
]);
|
|
107
|
+
if (RECORD_ADAPTERS.has(wireAdapter)) {
|
|
108
|
+
// Pull `fields: [X, Y]` from the second arg if present, infer
|
|
109
|
+
// sObject from the first imported schema field.
|
|
110
|
+
const optsArg = expr.arguments?.[1];
|
|
111
|
+
let sObject = null;
|
|
112
|
+
if (optsArg?.type === "ObjectExpression") {
|
|
113
|
+
for (const prop of optsArg.properties) {
|
|
114
|
+
const keyName = prop?.key?.name ?? prop?.key?.value;
|
|
115
|
+
if (keyName === "fields" || keyName === "optionalFields") {
|
|
116
|
+
const elements = prop.value?.elements ?? [];
|
|
117
|
+
for (const el of elements) {
|
|
118
|
+
if (el?.type === "Identifier") {
|
|
119
|
+
const ref = bindings.schemaFieldImports.get(String(el.name));
|
|
120
|
+
if (ref) {
|
|
121
|
+
sObject = ref.sObject;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else if (keyName === "objectApiName") {
|
|
128
|
+
const v = prop.value;
|
|
129
|
+
if (v?.type === "Identifier") {
|
|
130
|
+
const ref = bindings.schemaObjectImports.get(String(v.name));
|
|
131
|
+
if (ref)
|
|
132
|
+
sObject = ref;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (sObject)
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// The decorated node is a ClassProperty / MethodDefinition; pull its name.
|
|
140
|
+
const decoratedName = (() => {
|
|
141
|
+
const parent = path.parentPath?.node;
|
|
142
|
+
const key = parent?.key;
|
|
143
|
+
return key?.name ?? key?.value;
|
|
144
|
+
})();
|
|
145
|
+
if (sObject && typeof decoratedName === "string") {
|
|
146
|
+
bindings.wireToSObject.set(decoratedName, sObject);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
65
149
|
}
|
|
66
150
|
},
|
|
67
151
|
CallExpression(path) {
|
|
@@ -85,7 +169,7 @@ export function extractJsEdges(source, lwcQname, ctx) {
|
|
|
85
169
|
}
|
|
86
170
|
},
|
|
87
171
|
});
|
|
88
|
-
return { extraEdges, extraNodes };
|
|
172
|
+
return { extraEdges, extraNodes, bindings };
|
|
89
173
|
}
|
|
90
174
|
function lwcCtx(ctx) {
|
|
91
175
|
return ctx;
|