@typespec/mutator-framework 0.12.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 +21 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/mutation/index.d.ts +13 -0
- package/dist/src/mutation/index.d.ts.map +1 -0
- package/dist/src/mutation/index.js +13 -0
- package/dist/src/mutation/index.js.map +1 -0
- package/dist/src/mutation/interface.d.ts +11 -0
- package/dist/src/mutation/interface.d.ts.map +1 -0
- package/dist/src/mutation/interface.js +17 -0
- package/dist/src/mutation/interface.js.map +1 -0
- package/dist/src/mutation/intrinsic.d.ts +9 -0
- package/dist/src/mutation/intrinsic.d.ts.map +1 -0
- package/dist/src/mutation/intrinsic.js +11 -0
- package/dist/src/mutation/intrinsic.js.map +1 -0
- package/dist/src/mutation/literal.d.ts +9 -0
- package/dist/src/mutation/literal.d.ts.map +1 -0
- package/dist/src/mutation/literal.js +11 -0
- package/dist/src/mutation/literal.js.map +1 -0
- package/dist/src/mutation/model-property.d.ts +12 -0
- package/dist/src/mutation/model-property.d.ts.map +1 -0
- package/dist/src/mutation/model-property.js +18 -0
- package/dist/src/mutation/model-property.js.map +1 -0
- package/dist/src/mutation/model.d.ts +18 -0
- package/dist/src/mutation/model.d.ts.map +1 -0
- package/dist/src/mutation/model.js +34 -0
- package/dist/src/mutation/model.js.map +1 -0
- package/dist/src/mutation/mutation-engine.d.ts +80 -0
- package/dist/src/mutation/mutation-engine.d.ts.map +1 -0
- package/dist/src/mutation/mutation-engine.js +165 -0
- package/dist/src/mutation/mutation-engine.js.map +1 -0
- package/dist/src/mutation/mutation-engine.test.d.ts +2 -0
- package/dist/src/mutation/mutation-engine.test.d.ts.map +1 -0
- package/dist/src/mutation/mutation-engine.test.js +145 -0
- package/dist/src/mutation/mutation-engine.test.js.map +1 -0
- package/dist/src/mutation/mutation.d.ts +45 -0
- package/dist/src/mutation/mutation.d.ts.map +1 -0
- package/dist/src/mutation/mutation.js +29 -0
- package/dist/src/mutation/mutation.js.map +1 -0
- package/dist/src/mutation/operation.d.ts +13 -0
- package/dist/src/mutation/operation.d.ts.map +1 -0
- package/dist/src/mutation/operation.js +20 -0
- package/dist/src/mutation/operation.js.map +1 -0
- package/dist/src/mutation/scalar.d.ts +11 -0
- package/dist/src/mutation/scalar.d.ts.map +1 -0
- package/dist/src/mutation/scalar.js +17 -0
- package/dist/src/mutation/scalar.js.map +1 -0
- package/dist/src/mutation/simple-mutation-engine.d.ts +8 -0
- package/dist/src/mutation/simple-mutation-engine.d.ts.map +1 -0
- package/dist/src/mutation/simple-mutation-engine.js +11 -0
- package/dist/src/mutation/simple-mutation-engine.js.map +1 -0
- package/dist/src/mutation/union-variant.d.ts +10 -0
- package/dist/src/mutation/union-variant.d.ts.map +1 -0
- package/dist/src/mutation/union-variant.js +12 -0
- package/dist/src/mutation/union-variant.js.map +1 -0
- package/dist/src/mutation/union.d.ts +11 -0
- package/dist/src/mutation/union.d.ts.map +1 -0
- package/dist/src/mutation/union.js +18 -0
- package/dist/src/mutation/union.js.map +1 -0
- package/dist/src/mutation-node/enum-member.d.ts +7 -0
- package/dist/src/mutation-node/enum-member.d.ts.map +1 -0
- package/dist/src/mutation-node/enum-member.js +6 -0
- package/dist/src/mutation-node/enum-member.js.map +1 -0
- package/dist/src/mutation-node/enum-member.test.d.ts +2 -0
- package/dist/src/mutation-node/enum-member.test.d.ts.map +1 -0
- package/dist/src/mutation-node/enum-member.test.js +25 -0
- package/dist/src/mutation-node/enum-member.test.js.map +1 -0
- package/dist/src/mutation-node/enum.d.ts +8 -0
- package/dist/src/mutation-node/enum.d.ts.map +1 -0
- package/dist/src/mutation-node/enum.js +30 -0
- package/dist/src/mutation-node/enum.js.map +1 -0
- package/dist/src/mutation-node/enum.test.d.ts +2 -0
- package/dist/src/mutation-node/enum.test.d.ts.map +1 -0
- package/dist/src/mutation-node/enum.test.js +25 -0
- package/dist/src/mutation-node/enum.test.js.map +1 -0
- package/dist/src/mutation-node/factory.d.ts +17 -0
- package/dist/src/mutation-node/factory.d.ts.map +1 -0
- package/dist/src/mutation-node/factory.js +45 -0
- package/dist/src/mutation-node/factory.js.map +1 -0
- package/dist/src/mutation-node/index.d.ts +15 -0
- package/dist/src/mutation-node/index.d.ts.map +1 -0
- package/dist/src/mutation-node/index.js +16 -0
- package/dist/src/mutation-node/index.js.map +1 -0
- package/dist/src/mutation-node/interface.d.ts +8 -0
- package/dist/src/mutation-node/interface.d.ts.map +1 -0
- package/dist/src/mutation-node/interface.js +30 -0
- package/dist/src/mutation-node/interface.js.map +1 -0
- package/dist/src/mutation-node/intrinsic.d.ts +7 -0
- package/dist/src/mutation-node/intrinsic.d.ts.map +1 -0
- package/dist/src/mutation-node/intrinsic.js +6 -0
- package/dist/src/mutation-node/intrinsic.js.map +1 -0
- package/dist/src/mutation-node/literal.d.ts +7 -0
- package/dist/src/mutation-node/literal.d.ts.map +1 -0
- package/dist/src/mutation-node/literal.js +6 -0
- package/dist/src/mutation-node/literal.js.map +1 -0
- package/dist/src/mutation-node/model-property.d.ts +10 -0
- package/dist/src/mutation-node/model-property.d.ts.map +1 -0
- package/dist/src/mutation-node/model-property.js +48 -0
- package/dist/src/mutation-node/model-property.js.map +1 -0
- package/dist/src/mutation-node/model-property.test.d.ts +2 -0
- package/dist/src/mutation-node/model-property.test.d.ts.map +1 -0
- package/dist/src/mutation-node/model-property.test.js +108 -0
- package/dist/src/mutation-node/model-property.test.js.map +1 -0
- package/dist/src/mutation-node/model.d.ts +10 -0
- package/dist/src/mutation-node/model.d.ts.map +1 -0
- package/dist/src/mutation-node/model.js +82 -0
- package/dist/src/mutation-node/model.js.map +1 -0
- package/dist/src/mutation-node/model.test.d.ts +2 -0
- package/dist/src/mutation-node/model.test.d.ts.map +1 -0
- package/dist/src/mutation-node/model.test.js +133 -0
- package/dist/src/mutation-node/model.test.js.map +1 -0
- package/dist/src/mutation-node/mutation-edge.d.ts +18 -0
- package/dist/src/mutation-node/mutation-edge.d.ts.map +1 -0
- package/dist/src/mutation-node/mutation-edge.js +31 -0
- package/dist/src/mutation-node/mutation-edge.js.map +1 -0
- package/dist/src/mutation-node/mutation-node.d.ts +29 -0
- package/dist/src/mutation-node/mutation-node.d.ts.map +1 -0
- package/dist/src/mutation-node/mutation-node.js +82 -0
- package/dist/src/mutation-node/mutation-node.js.map +1 -0
- package/dist/src/mutation-node/mutation-node.test.d.ts +2 -0
- package/dist/src/mutation-node/mutation-node.test.d.ts.map +1 -0
- package/dist/src/mutation-node/mutation-node.test.js +88 -0
- package/dist/src/mutation-node/mutation-node.test.js.map +1 -0
- package/dist/src/mutation-node/mutation-subgraph.d.ts +17 -0
- package/dist/src/mutation-node/mutation-subgraph.d.ts.map +1 -0
- package/dist/src/mutation-node/mutation-subgraph.js +45 -0
- package/dist/src/mutation-node/mutation-subgraph.js.map +1 -0
- package/dist/src/mutation-node/operation.d.ts +9 -0
- package/dist/src/mutation-node/operation.d.ts.map +1 -0
- package/dist/src/mutation-node/operation.js +44 -0
- package/dist/src/mutation-node/operation.js.map +1 -0
- package/dist/src/mutation-node/scalar.d.ts +8 -0
- package/dist/src/mutation-node/scalar.d.ts.map +1 -0
- package/dist/src/mutation-node/scalar.js +28 -0
- package/dist/src/mutation-node/scalar.js.map +1 -0
- package/dist/src/mutation-node/scalar.test.d.ts +2 -0
- package/dist/src/mutation-node/scalar.test.d.ts.map +1 -0
- package/dist/src/mutation-node/scalar.test.js +40 -0
- package/dist/src/mutation-node/scalar.test.js.map +1 -0
- package/dist/src/mutation-node/tuple.d.ts +9 -0
- package/dist/src/mutation-node/tuple.d.ts.map +1 -0
- package/dist/src/mutation-node/tuple.js +32 -0
- package/dist/src/mutation-node/tuple.js.map +1 -0
- package/dist/src/mutation-node/tuple.test.d.ts +2 -0
- package/dist/src/mutation-node/tuple.test.d.ts.map +1 -0
- package/dist/src/mutation-node/tuple.test.js +29 -0
- package/dist/src/mutation-node/tuple.test.js.map +1 -0
- package/dist/src/mutation-node/union-variant.d.ts +8 -0
- package/dist/src/mutation-node/union-variant.d.ts.map +1 -0
- package/dist/src/mutation-node/union-variant.js +23 -0
- package/dist/src/mutation-node/union-variant.js.map +1 -0
- package/dist/src/mutation-node/union-variant.test.d.ts +2 -0
- package/dist/src/mutation-node/union-variant.test.d.ts.map +1 -0
- package/dist/src/mutation-node/union-variant.test.js +27 -0
- package/dist/src/mutation-node/union-variant.test.js.map +1 -0
- package/dist/src/mutation-node/union.d.ts +8 -0
- package/dist/src/mutation-node/union.d.ts.map +1 -0
- package/dist/src/mutation-node/union.js +30 -0
- package/dist/src/mutation-node/union.js.map +1 -0
- package/dist/src/mutation-node/union.test.d.ts +2 -0
- package/dist/src/mutation-node/union.test.d.ts.map +1 -0
- package/dist/src/mutation-node/union.test.js +25 -0
- package/dist/src/mutation-node/union.test.js.map +1 -0
- package/dist/test/test-host.d.ts +2 -0
- package/dist/test/test-host.d.ts.map +1 -0
- package/dist/test/test-host.js +6 -0
- package/dist/test/test-host.js.map +1 -0
- package/dist/test/utils.d.ts +4 -0
- package/dist/test/utils.d.ts.map +1 -0
- package/dist/test/utils.js +8 -0
- package/dist/test/utils.js.map +1 -0
- package/package.json +40 -0
- package/package.json.bak +42 -0
- package/readme.md +339 -0
- package/src/index.ts +4 -0
- package/src/mutation/index.ts +12 -0
- package/src/mutation/interface.ts +38 -0
- package/src/mutation/intrinsic.ts +23 -0
- package/src/mutation/literal.ts +29 -0
- package/src/mutation/model-property.ts +35 -0
- package/src/mutation/model.ts +58 -0
- package/src/mutation/mutation-engine.test.ts +202 -0
- package/src/mutation/mutation-engine.ts +288 -0
- package/src/mutation/mutation.ts +90 -0
- package/src/mutation/operation.ts +40 -0
- package/src/mutation/scalar.ts +36 -0
- package/src/mutation/simple-mutation-engine.ts +21 -0
- package/src/mutation/union-variant.ts +30 -0
- package/src/mutation/union.ts +39 -0
- package/src/mutation-node/enum-member.test.ts +26 -0
- package/src/mutation-node/enum-member.ts +8 -0
- package/src/mutation-node/enum.test.ts +26 -0
- package/src/mutation-node/enum.ts +33 -0
- package/src/mutation-node/factory.ts +95 -0
- package/src/mutation-node/index.ts +16 -0
- package/src/mutation-node/interface.ts +33 -0
- package/src/mutation-node/intrinsic.ts +8 -0
- package/src/mutation-node/literal.ts +10 -0
- package/src/mutation-node/model-property.test.ts +136 -0
- package/src/mutation-node/model-property.ts +53 -0
- package/src/mutation-node/model.test.ts +151 -0
- package/src/mutation-node/model.ts +89 -0
- package/src/mutation-node/mutation-edge.ts +43 -0
- package/src/mutation-node/mutation-node.test.ts +94 -0
- package/src/mutation-node/mutation-node.ts +110 -0
- package/src/mutation-node/mutation-subgraph.ts +59 -0
- package/src/mutation-node/operation.ts +49 -0
- package/src/mutation-node/scalar.test.ts +44 -0
- package/src/mutation-node/scalar.ts +32 -0
- package/src/mutation-node/tuple.test.ts +30 -0
- package/src/mutation-node/tuple.ts +35 -0
- package/src/mutation-node/union-variant.test.ts +28 -0
- package/src/mutation-node/union-variant.ts +26 -0
- package/src/mutation-node/union.test.ts +26 -0
- package/src/mutation-node/union.ts +33 -0
- package/test/test-host.ts +6 -0
- package/test/utils.ts +9 -0
- package/tsconfig.json +19 -0
- package/vitest.config.ts +4 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { MemberType, Type, UnionVariant } from "@typespec/compiler";
|
|
2
|
+
import type {
|
|
3
|
+
CustomMutationClasses,
|
|
4
|
+
MutationEngine,
|
|
5
|
+
MutationFor,
|
|
6
|
+
MutationOptions,
|
|
7
|
+
} from "./mutation-engine.js";
|
|
8
|
+
import { Mutation } from "./mutation.js";
|
|
9
|
+
|
|
10
|
+
export class UnionVariantMutation<
|
|
11
|
+
TOptions extends MutationOptions,
|
|
12
|
+
TCustomMutations extends CustomMutationClasses,
|
|
13
|
+
TEngine extends MutationEngine<TCustomMutations> = MutationEngine<TCustomMutations>,
|
|
14
|
+
> extends Mutation<UnionVariant, TCustomMutations, TOptions, TEngine> {
|
|
15
|
+
readonly kind = "UnionVariant";
|
|
16
|
+
type!: MutationFor<TCustomMutations, Type["kind"]>;
|
|
17
|
+
|
|
18
|
+
constructor(
|
|
19
|
+
engine: TEngine,
|
|
20
|
+
sourceType: UnionVariant,
|
|
21
|
+
referenceTypes: MemberType[] = [],
|
|
22
|
+
options: TOptions,
|
|
23
|
+
) {
|
|
24
|
+
super(engine, sourceType, referenceTypes, options);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
mutate(): void {
|
|
28
|
+
this.type = this.engine.mutate(this.sourceType.type, this.options);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { MemberType, Union } from "@typespec/compiler";
|
|
2
|
+
import type {
|
|
3
|
+
CustomMutationClasses,
|
|
4
|
+
MutationEngine,
|
|
5
|
+
MutationFor,
|
|
6
|
+
MutationOptions,
|
|
7
|
+
} from "./mutation-engine.js";
|
|
8
|
+
import { Mutation } from "./mutation.js";
|
|
9
|
+
|
|
10
|
+
export class UnionMutation<
|
|
11
|
+
TOptions extends MutationOptions,
|
|
12
|
+
TCustomMutations extends CustomMutationClasses,
|
|
13
|
+
TEngine extends MutationEngine<TCustomMutations> = MutationEngine<TCustomMutations>,
|
|
14
|
+
> extends Mutation<Union, TCustomMutations, TOptions, TEngine> {
|
|
15
|
+
readonly kind = "Union";
|
|
16
|
+
variants: Map<string | symbol, MutationFor<TCustomMutations, "UnionVariant">> = new Map();
|
|
17
|
+
|
|
18
|
+
constructor(
|
|
19
|
+
engine: TEngine,
|
|
20
|
+
sourceType: Union,
|
|
21
|
+
referenceTypes: MemberType[] = [],
|
|
22
|
+
options: TOptions,
|
|
23
|
+
) {
|
|
24
|
+
super(engine, sourceType, referenceTypes, options);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
protected mutateVariants() {
|
|
28
|
+
this.variants = new Map(
|
|
29
|
+
[...this.sourceType.variants].map(([name, variant]) => [
|
|
30
|
+
name,
|
|
31
|
+
this.engine.mutate(variant, this.options),
|
|
32
|
+
]),
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
mutate() {
|
|
37
|
+
this.mutateVariants();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { t, type TesterInstance } from "@typespec/compiler/testing";
|
|
2
|
+
import { beforeEach, expect, it } from "vitest";
|
|
3
|
+
import { Tester } from "../../test/test-host.js";
|
|
4
|
+
import { getSubgraph } from "../../test/utils.js";
|
|
5
|
+
let runner: TesterInstance;
|
|
6
|
+
beforeEach(async () => {
|
|
7
|
+
runner = await Tester.createInstance();
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it("handles mutation of member values", async () => {
|
|
11
|
+
const { program, Foo, a } = await runner.compile(t.code`
|
|
12
|
+
enum ${t.enum("Foo")} {
|
|
13
|
+
${t.enumMember("a")}: "valueA";
|
|
14
|
+
b: "valueB";
|
|
15
|
+
}
|
|
16
|
+
`);
|
|
17
|
+
|
|
18
|
+
const subgraph = getSubgraph(program);
|
|
19
|
+
const fooNode = subgraph.getNode(Foo);
|
|
20
|
+
const aNode = subgraph.getNode(a);
|
|
21
|
+
aNode.mutate((clone) => (clone.value = "valueARenamed"));
|
|
22
|
+
expect(aNode.isMutated).toBe(true);
|
|
23
|
+
expect(fooNode.isMutated).toBe(true);
|
|
24
|
+
expect(fooNode.mutatedType.members.get("a") === aNode.mutatedType).toBe(true);
|
|
25
|
+
expect(aNode.mutatedType.value).toBe("valueARenamed");
|
|
26
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { t, type TesterInstance } from "@typespec/compiler/testing";
|
|
2
|
+
import { beforeEach, expect, it } from "vitest";
|
|
3
|
+
import { Tester } from "../../test/test-host.js";
|
|
4
|
+
import { getSubgraph } from "../../test/utils.js";
|
|
5
|
+
let runner: TesterInstance;
|
|
6
|
+
beforeEach(async () => {
|
|
7
|
+
runner = await Tester.createInstance();
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it("handles mutation of members", async () => {
|
|
11
|
+
const { program, Foo, a } = await runner.compile(t.code`
|
|
12
|
+
enum ${t.enum("Foo")} {
|
|
13
|
+
${t.enumMember("a")};
|
|
14
|
+
b;
|
|
15
|
+
}
|
|
16
|
+
`);
|
|
17
|
+
|
|
18
|
+
const subgraph = getSubgraph(program);
|
|
19
|
+
const fooNode = subgraph.getNode(Foo);
|
|
20
|
+
const aNode = subgraph.getNode(a);
|
|
21
|
+
aNode.mutate((clone) => (clone.name = "aRenamed"));
|
|
22
|
+
expect(aNode.isMutated).toBe(true);
|
|
23
|
+
expect(fooNode.isMutated).toBe(true);
|
|
24
|
+
expect(fooNode.mutatedType.members.get("a") === undefined).toBeTruthy();
|
|
25
|
+
expect(fooNode.mutatedType.members.get("aRenamed") === aNode.mutatedType).toBeTruthy();
|
|
26
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Enum, EnumMember } from "@typespec/compiler";
|
|
2
|
+
import { MutationEdge } from "./mutation-edge.js";
|
|
3
|
+
import { MutationNode } from "./mutation-node.js";
|
|
4
|
+
|
|
5
|
+
export class EnumMutationNode extends MutationNode<Enum> {
|
|
6
|
+
readonly kind = "Enum";
|
|
7
|
+
|
|
8
|
+
traverse() {
|
|
9
|
+
for (const member of this.sourceType.members.values()) {
|
|
10
|
+
const memberNode = this.subgraph.getNode(member);
|
|
11
|
+
this.connectMember(memberNode, member.name);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
connectMember(memberNode: MutationNode<EnumMember>, sourcePropName: string) {
|
|
16
|
+
MutationEdge.create(this, memberNode, {
|
|
17
|
+
onTailMutation: () => {
|
|
18
|
+
this.mutatedType.members.delete(sourcePropName);
|
|
19
|
+
this.mutatedType.members.set(memberNode.mutatedType.name, memberNode.mutatedType);
|
|
20
|
+
},
|
|
21
|
+
onTailDeletion: () => {
|
|
22
|
+
this.mutatedType.members.delete(sourcePropName);
|
|
23
|
+
},
|
|
24
|
+
onTailReplaced: (newTail) => {
|
|
25
|
+
if (newTail.mutatedType.kind !== "EnumMember") {
|
|
26
|
+
throw new Error("Cannot replace enum member with non-enum member type");
|
|
27
|
+
}
|
|
28
|
+
this.mutatedType.members.delete(sourcePropName);
|
|
29
|
+
this.mutatedType.members.set(newTail.mutatedType.name, newTail.mutatedType);
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BooleanLiteral,
|
|
3
|
+
Enum,
|
|
4
|
+
EnumMember,
|
|
5
|
+
Interface,
|
|
6
|
+
IntrinsicType,
|
|
7
|
+
Model,
|
|
8
|
+
ModelProperty,
|
|
9
|
+
NumericLiteral,
|
|
10
|
+
Operation,
|
|
11
|
+
Scalar,
|
|
12
|
+
StringLiteral,
|
|
13
|
+
Tuple,
|
|
14
|
+
Type,
|
|
15
|
+
Union,
|
|
16
|
+
UnionVariant,
|
|
17
|
+
} from "@typespec/compiler";
|
|
18
|
+
import { EnumMemberMutationNode } from "./enum-member.js";
|
|
19
|
+
import { EnumMutationNode } from "./enum.js";
|
|
20
|
+
import { InterfaceMutationNode } from "./interface.js";
|
|
21
|
+
import { IntrinsicMutationNode } from "./intrinsic.js";
|
|
22
|
+
import { LiteralMutationNode } from "./literal.js";
|
|
23
|
+
import { ModelPropertyMutationNode } from "./model-property.js";
|
|
24
|
+
import { ModelMutationNode } from "./model.js";
|
|
25
|
+
import type { MutationSubgraph } from "./mutation-subgraph.js";
|
|
26
|
+
import { OperationMutationNode } from "./operation.js";
|
|
27
|
+
import { ScalarMutationNode } from "./scalar.js";
|
|
28
|
+
import { TupleMutationNode } from "./tuple.js";
|
|
29
|
+
import { UnionVariantMutationNode } from "./union-variant.js";
|
|
30
|
+
import { UnionMutationNode } from "./union.js";
|
|
31
|
+
|
|
32
|
+
export function mutationNodeFor<T extends Type>(
|
|
33
|
+
subgraph: MutationSubgraph,
|
|
34
|
+
sourceType: T,
|
|
35
|
+
): MutationNodeForType<T> {
|
|
36
|
+
switch (sourceType.kind) {
|
|
37
|
+
case "Operation":
|
|
38
|
+
return new OperationMutationNode(subgraph, sourceType) as MutationNodeForType<T>;
|
|
39
|
+
case "Interface":
|
|
40
|
+
return new InterfaceMutationNode(subgraph, sourceType) as MutationNodeForType<T>;
|
|
41
|
+
case "Model":
|
|
42
|
+
return new ModelMutationNode(subgraph, sourceType) as MutationNodeForType<T>;
|
|
43
|
+
case "ModelProperty":
|
|
44
|
+
return new ModelPropertyMutationNode(subgraph, sourceType) as MutationNodeForType<T>;
|
|
45
|
+
case "Scalar":
|
|
46
|
+
return new ScalarMutationNode(subgraph, sourceType) as MutationNodeForType<T>;
|
|
47
|
+
case "Tuple":
|
|
48
|
+
return new TupleMutationNode(subgraph, sourceType) as MutationNodeForType<T>;
|
|
49
|
+
case "Union":
|
|
50
|
+
return new UnionMutationNode(subgraph, sourceType) as MutationNodeForType<T>;
|
|
51
|
+
case "UnionVariant":
|
|
52
|
+
return new UnionVariantMutationNode(subgraph, sourceType) as MutationNodeForType<T>;
|
|
53
|
+
case "Enum":
|
|
54
|
+
return new EnumMutationNode(subgraph, sourceType) as MutationNodeForType<T>;
|
|
55
|
+
case "EnumMember":
|
|
56
|
+
return new EnumMemberMutationNode(subgraph, sourceType) as MutationNodeForType<T>;
|
|
57
|
+
case "String":
|
|
58
|
+
case "Number":
|
|
59
|
+
case "Boolean":
|
|
60
|
+
return new LiteralMutationNode(
|
|
61
|
+
subgraph,
|
|
62
|
+
sourceType as StringLiteral | NumericLiteral | BooleanLiteral,
|
|
63
|
+
) as MutationNodeForType<T>;
|
|
64
|
+
case "Intrinsic":
|
|
65
|
+
return new IntrinsicMutationNode(subgraph, sourceType) as MutationNodeForType<T>;
|
|
66
|
+
default:
|
|
67
|
+
throw new Error("Unsupported type kind: " + sourceType.kind);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type MutationNodeForType<T extends Type> = T extends Model
|
|
72
|
+
? ModelMutationNode
|
|
73
|
+
: T extends Interface
|
|
74
|
+
? InterfaceMutationNode
|
|
75
|
+
: T extends Operation
|
|
76
|
+
? OperationMutationNode
|
|
77
|
+
: T extends ModelProperty
|
|
78
|
+
? ModelPropertyMutationNode
|
|
79
|
+
: T extends Scalar
|
|
80
|
+
? ScalarMutationNode
|
|
81
|
+
: T extends Tuple
|
|
82
|
+
? TupleMutationNode
|
|
83
|
+
: T extends Union
|
|
84
|
+
? UnionMutationNode
|
|
85
|
+
: T extends UnionVariant
|
|
86
|
+
? UnionVariantMutationNode
|
|
87
|
+
: T extends Enum
|
|
88
|
+
? EnumMutationNode
|
|
89
|
+
: T extends EnumMember
|
|
90
|
+
? EnumMemberMutationNode
|
|
91
|
+
: T extends StringLiteral | NumericLiteral | BooleanLiteral
|
|
92
|
+
? LiteralMutationNode
|
|
93
|
+
: T extends IntrinsicType
|
|
94
|
+
? IntrinsicMutationNode
|
|
95
|
+
: never;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export * from "./mutation-node.js";
|
|
2
|
+
|
|
3
|
+
export * from "./enum.js";
|
|
4
|
+
export * from "./interface.js";
|
|
5
|
+
export * from "./intrinsic.js";
|
|
6
|
+
export * from "./literal.js";
|
|
7
|
+
export * from "./model-property.js";
|
|
8
|
+
export * from "./model.js";
|
|
9
|
+
export * from "./mutation-edge.js";
|
|
10
|
+
export * from "./mutation-subgraph.js";
|
|
11
|
+
export * from "./operation.js";
|
|
12
|
+
export * from "./scalar.js";
|
|
13
|
+
export * from "./tuple.js";
|
|
14
|
+
export * from "./union-variant.js";
|
|
15
|
+
export * from "./union.js";
|
|
16
|
+
//export * from "./enum-member.js";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Interface, Operation } from "@typespec/compiler";
|
|
2
|
+
import { MutationEdge } from "./mutation-edge.js";
|
|
3
|
+
import { MutationNode } from "./mutation-node.js";
|
|
4
|
+
|
|
5
|
+
export class InterfaceMutationNode extends MutationNode<Interface> {
|
|
6
|
+
readonly kind = "Interface";
|
|
7
|
+
|
|
8
|
+
traverse() {
|
|
9
|
+
for (const [opName, op] of this.sourceType.operations) {
|
|
10
|
+
const opNode = this.subgraph.getNode(op);
|
|
11
|
+
this.connectOperation(opNode, opName);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
connectOperation(opNode: MutationNode<Operation>, opName: string) {
|
|
16
|
+
MutationEdge.create(this, opNode, {
|
|
17
|
+
onTailMutation: () => {
|
|
18
|
+
this.mutatedType.operations.delete(opName);
|
|
19
|
+
this.mutatedType.operations.set(opNode.mutatedType.name, opNode.mutatedType);
|
|
20
|
+
},
|
|
21
|
+
onTailDeletion: () => {
|
|
22
|
+
this.mutatedType.operations.delete(opName);
|
|
23
|
+
},
|
|
24
|
+
onTailReplaced: (newTail) => {
|
|
25
|
+
if (newTail.mutatedType.kind !== "Operation") {
|
|
26
|
+
throw new Error("Cannot replace operation with non-operation type");
|
|
27
|
+
}
|
|
28
|
+
this.mutatedType.operations.delete(opName);
|
|
29
|
+
this.mutatedType.operations.set(newTail.mutatedType.name, newTail.mutatedType);
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { BooleanLiteral, NumericLiteral, StringLiteral } from "@typespec/compiler";
|
|
2
|
+
import { MutationNode } from "./mutation-node.js";
|
|
3
|
+
|
|
4
|
+
export class LiteralMutationNode extends MutationNode<
|
|
5
|
+
StringLiteral | NumericLiteral | BooleanLiteral
|
|
6
|
+
> {
|
|
7
|
+
readonly kind = "Literal";
|
|
8
|
+
|
|
9
|
+
traverse() {}
|
|
10
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { t, type TesterInstance } from "@typespec/compiler/testing";
|
|
2
|
+
import { $ } from "@typespec/compiler/typekit";
|
|
3
|
+
import { beforeEach, expect, it } from "vitest";
|
|
4
|
+
import { Tester } from "../../test/test-host.js";
|
|
5
|
+
import { getSubgraph } from "../../test/utils.js";
|
|
6
|
+
|
|
7
|
+
let runner: TesterInstance;
|
|
8
|
+
beforeEach(async () => {
|
|
9
|
+
runner = await Tester.createInstance();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("handles mutation of property types", async () => {
|
|
13
|
+
const { prop, program } = await runner.compile(t.code`
|
|
14
|
+
model Foo {
|
|
15
|
+
${t.modelProperty("prop")}: string;
|
|
16
|
+
}
|
|
17
|
+
`);
|
|
18
|
+
const subgraph = getSubgraph(program);
|
|
19
|
+
const propNode = subgraph.getNode(prop);
|
|
20
|
+
const stringNode = subgraph.getNode($(program).builtin.string);
|
|
21
|
+
stringNode.mutate();
|
|
22
|
+
expect(propNode.isMutated).toBe(true);
|
|
23
|
+
expect(propNode.mutatedType.type === stringNode.mutatedType).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("handles mutating a reference", async () => {
|
|
27
|
+
const { Foo, Bar, prop, program } = await runner.compile(t.code`
|
|
28
|
+
model ${t.model("Foo")} {
|
|
29
|
+
${t.modelProperty("prop")}: Bar;
|
|
30
|
+
};
|
|
31
|
+
model ${t.model("Bar")} {}
|
|
32
|
+
`);
|
|
33
|
+
|
|
34
|
+
const subgraph = getSubgraph(program);
|
|
35
|
+
const fooNode = subgraph.getNode(Foo);
|
|
36
|
+
const propNode = subgraph.getNode(prop);
|
|
37
|
+
const barPrime = subgraph.getReferenceNode(prop);
|
|
38
|
+
|
|
39
|
+
// initially the source type is just Bar.
|
|
40
|
+
expect(barPrime.sourceType === Bar).toBe(true);
|
|
41
|
+
|
|
42
|
+
barPrime.mutate();
|
|
43
|
+
expect(fooNode.isMutated).toBe(true);
|
|
44
|
+
expect(propNode.isMutated).toBe(true);
|
|
45
|
+
expect(barPrime.isMutated).toBe(true);
|
|
46
|
+
expect(fooNode.mutatedType.properties.get("prop")!.type === barPrime.mutatedType).toBeTruthy();
|
|
47
|
+
|
|
48
|
+
const barNode = subgraph.getNode(Bar);
|
|
49
|
+
barNode.mutate();
|
|
50
|
+
expect(barNode.isMutated).toBe(true);
|
|
51
|
+
expect(barPrime.isMutated).toBe(true);
|
|
52
|
+
// the mutated type doesn't change here.
|
|
53
|
+
expect(fooNode.mutatedType.properties.get("prop")!.type === barPrime.mutatedType).toBeTruthy();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("handles replacing the model reference", async () => {
|
|
57
|
+
const { Foo, Bar, prop, program } = await runner.compile(t.code`
|
|
58
|
+
model ${t.model("Foo")} {
|
|
59
|
+
${t.modelProperty("prop")}: Bar;
|
|
60
|
+
};
|
|
61
|
+
model ${t.model("Bar")} {}
|
|
62
|
+
`);
|
|
63
|
+
const tk = $(program);
|
|
64
|
+
const subgraph = getSubgraph(program);
|
|
65
|
+
const fooNode = subgraph.getNode(Foo);
|
|
66
|
+
const propNode = subgraph.getNode(prop);
|
|
67
|
+
const barPrime = subgraph.getReferenceNode(prop);
|
|
68
|
+
const unionType = tk.union.create({
|
|
69
|
+
variants: [
|
|
70
|
+
tk.unionVariant.create({ type: tk.builtin.string }),
|
|
71
|
+
tk.unionVariant.create({ type: Bar }),
|
|
72
|
+
],
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const replacedBarPrime = barPrime.replace(unionType);
|
|
76
|
+
|
|
77
|
+
// the subgraph now returns the new reference node
|
|
78
|
+
expect(subgraph.getReferenceNode(prop) === replacedBarPrime).toBe(true);
|
|
79
|
+
|
|
80
|
+
// foo and prop are marked mutated, barPrime is replaced
|
|
81
|
+
expect(fooNode.isMutated).toBe(true);
|
|
82
|
+
expect(propNode.isMutated).toBe(true);
|
|
83
|
+
expect(barPrime.isReplaced).toBe(true);
|
|
84
|
+
|
|
85
|
+
// prop's type is the replaced type
|
|
86
|
+
expect(tk.union.is(propNode.mutatedType.type)).toBe(true);
|
|
87
|
+
expect(
|
|
88
|
+
fooNode.mutatedType!.properties.get("prop")!.type === replacedBarPrime.mutatedType,
|
|
89
|
+
).toBeTruthy();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("handles mutating a reference to a reference", async () => {
|
|
93
|
+
const { myString, Foo, fprop, Bar, program } = await runner.compile(t.code`
|
|
94
|
+
scalar ${t.scalar("myString")} extends string;
|
|
95
|
+
model ${t.model("Foo")} {
|
|
96
|
+
${t.modelProperty("fprop")}: myString;
|
|
97
|
+
};
|
|
98
|
+
model ${t.model("Bar")} {
|
|
99
|
+
bprop: Foo.fprop;
|
|
100
|
+
}
|
|
101
|
+
`);
|
|
102
|
+
|
|
103
|
+
const subgraph = getSubgraph(program);
|
|
104
|
+
const fooNode = subgraph.getNode(Foo);
|
|
105
|
+
const barNode = subgraph.getNode(Bar);
|
|
106
|
+
const myStringNode = subgraph.getNode(myString);
|
|
107
|
+
|
|
108
|
+
myStringNode.mutate();
|
|
109
|
+
expect(myStringNode.isMutated).toBe(true);
|
|
110
|
+
expect(fooNode.isMutated).toBe(true);
|
|
111
|
+
expect(barNode.isMutated).toBe(true);
|
|
112
|
+
|
|
113
|
+
// Foo.prop's type is the mutated myString
|
|
114
|
+
expect(
|
|
115
|
+
fooNode.mutatedType.properties.get("fprop")!.type === myStringNode.mutatedType,
|
|
116
|
+
).toBeTruthy();
|
|
117
|
+
|
|
118
|
+
// Bar.prop's type is the mutated Foo.prop
|
|
119
|
+
expect(
|
|
120
|
+
barNode.mutatedType.properties.get("bprop")!.type ===
|
|
121
|
+
fooNode.mutatedType.properties.get("fprop")!,
|
|
122
|
+
).toBeTruthy();
|
|
123
|
+
|
|
124
|
+
const fpropRefNode = subgraph.getReferenceNode(fprop);
|
|
125
|
+
fpropRefNode.mutate();
|
|
126
|
+
expect(fpropRefNode.isMutated).toBe(true);
|
|
127
|
+
expect(
|
|
128
|
+
fooNode.mutatedType.properties.get("fprop")!.type === fpropRefNode.mutatedType,
|
|
129
|
+
).toBeTruthy();
|
|
130
|
+
|
|
131
|
+
// Bar.bprop references the mutated type (though is the same reference since fprop was already mutated)
|
|
132
|
+
expect(
|
|
133
|
+
barNode.mutatedType.properties.get("bprop")!.type ===
|
|
134
|
+
fooNode.mutatedType.properties.get("fprop")!,
|
|
135
|
+
).toBeTruthy();
|
|
136
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { ModelProperty, Type } from "@typespec/compiler";
|
|
2
|
+
import { MutationEdge } from "./mutation-edge.js";
|
|
3
|
+
import { MutationNode } from "./mutation-node.js";
|
|
4
|
+
|
|
5
|
+
export class ModelPropertyMutationNode extends MutationNode<ModelProperty> {
|
|
6
|
+
readonly kind = "ModelProperty";
|
|
7
|
+
#referenceMutated = false;
|
|
8
|
+
|
|
9
|
+
traverse() {
|
|
10
|
+
const typeNode = this.subgraph.getNode(this.sourceType.type);
|
|
11
|
+
const referenceNode = this.subgraph.getReferenceNode(this.sourceType);
|
|
12
|
+
|
|
13
|
+
this.connectType(typeNode);
|
|
14
|
+
this.connectReference(referenceNode);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
connectReference(referenceNode: MutationNode<Type>) {
|
|
18
|
+
MutationEdge.create(this, referenceNode, {
|
|
19
|
+
onTailMutation: () => {
|
|
20
|
+
this.#referenceMutated = true;
|
|
21
|
+
this.mutatedType.type = referenceNode.mutatedType;
|
|
22
|
+
},
|
|
23
|
+
onTailDeletion: () => {
|
|
24
|
+
this.#referenceMutated = true;
|
|
25
|
+
this.mutatedType.type = this.$.intrinsic.any;
|
|
26
|
+
},
|
|
27
|
+
onTailReplaced: (newTail) => {
|
|
28
|
+
this.#referenceMutated = true;
|
|
29
|
+
this.mutatedType.type = newTail.mutatedType;
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
connectType(typeNode: MutationNode<Type>) {
|
|
35
|
+
MutationEdge.create(this, typeNode, {
|
|
36
|
+
onTailMutation: () => {
|
|
37
|
+
if (this.#referenceMutated) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
this.mutatedType.type = typeNode.mutatedType;
|
|
41
|
+
},
|
|
42
|
+
onTailDeletion: () => {
|
|
43
|
+
if (this.#referenceMutated) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
this.mutatedType.type = this.$.intrinsic.any;
|
|
47
|
+
},
|
|
48
|
+
onTailReplaced: (newTail) => {
|
|
49
|
+
this.mutatedType.type = newTail.mutatedType;
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import type { Model, Type } from "@typespec/compiler";
|
|
2
|
+
import { t, type TesterInstance } from "@typespec/compiler/testing";
|
|
3
|
+
import { beforeEach, expect, it } from "vitest";
|
|
4
|
+
import { Tester } from "../../test/test-host.js";
|
|
5
|
+
import { getSubgraph } from "../../test/utils.js";
|
|
6
|
+
|
|
7
|
+
let runner: TesterInstance;
|
|
8
|
+
beforeEach(async () => {
|
|
9
|
+
runner = await Tester.createInstance();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("handles mutation of properties", async () => {
|
|
13
|
+
const { Foo, program } = await runner.compile(t.code`
|
|
14
|
+
model ${t.model("Foo")} {
|
|
15
|
+
prop: string;
|
|
16
|
+
}
|
|
17
|
+
`);
|
|
18
|
+
const subgraph = getSubgraph(program);
|
|
19
|
+
const fooNode = subgraph.getNode(Foo);
|
|
20
|
+
const propNode = subgraph.getNode(Foo.properties.get("prop")!);
|
|
21
|
+
propNode.mutate();
|
|
22
|
+
expect(fooNode.isMutated).toBe(true);
|
|
23
|
+
expect(fooNode.mutatedType.properties.get("prop") === propNode.mutatedType).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("handles deletion of properties", async () => {
|
|
27
|
+
const { Foo, program } = await runner.compile(t.code`
|
|
28
|
+
model ${t.model("Foo")} {
|
|
29
|
+
prop: string;
|
|
30
|
+
}
|
|
31
|
+
`);
|
|
32
|
+
const subgraph = getSubgraph(program);
|
|
33
|
+
const fooNode = subgraph.getNode(Foo);
|
|
34
|
+
const propNode = subgraph.getNode(Foo.properties.get("prop")!);
|
|
35
|
+
propNode.delete();
|
|
36
|
+
expect(fooNode.isMutated).toBe(true);
|
|
37
|
+
expect(fooNode.mutatedType.properties.get("prop")).toBeUndefined();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("handles mutation of properties with name change", async () => {
|
|
41
|
+
const { Foo, program } = await runner.compile(t.code`
|
|
42
|
+
model ${t.model("Foo")} {
|
|
43
|
+
prop: string;
|
|
44
|
+
}
|
|
45
|
+
`);
|
|
46
|
+
const subgraph = getSubgraph(program);
|
|
47
|
+
const fooNode = subgraph.getNode(Foo);
|
|
48
|
+
const propNode = subgraph.getNode(Foo.properties.get("prop")!);
|
|
49
|
+
propNode.mutate((clone) => (clone.name = "propRenamed"));
|
|
50
|
+
expect(fooNode.isMutated).toBe(true);
|
|
51
|
+
expect(fooNode.mutatedType.properties.get("prop") === undefined).toBe(true);
|
|
52
|
+
expect(fooNode.mutatedType.properties.get("propRenamed") === propNode.mutatedType).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("handles mutation of base models", async () => {
|
|
56
|
+
const { Foo, Bar, program } = await runner.compile(t.code`
|
|
57
|
+
model ${t.model("Foo")} extends Bar {
|
|
58
|
+
barProp: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
model ${t.model("Bar")} {
|
|
62
|
+
bazProp: string;
|
|
63
|
+
}
|
|
64
|
+
`);
|
|
65
|
+
const subgraph = getSubgraph(program);
|
|
66
|
+
const fooNode = subgraph.getNode(Foo);
|
|
67
|
+
const barNode = subgraph.getNode(Bar);
|
|
68
|
+
|
|
69
|
+
barNode.mutate();
|
|
70
|
+
expect(barNode.isMutated).toBe(true);
|
|
71
|
+
expect(fooNode.isMutated).toBe(true);
|
|
72
|
+
expect(fooNode.mutatedType.baseModel === barNode.mutatedType).toBeTruthy();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("handles deletion of base models", async () => {
|
|
76
|
+
const { Foo, Bar, program } = await runner.compile(t.code`
|
|
77
|
+
model ${t.model("Foo")} extends Bar {
|
|
78
|
+
barProp: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
model ${t.model("Bar")} {
|
|
82
|
+
bazProp: string;
|
|
83
|
+
}
|
|
84
|
+
`);
|
|
85
|
+
const subgraph = getSubgraph(program);
|
|
86
|
+
const fooNode = subgraph.getNode(Foo);
|
|
87
|
+
const barNode = subgraph.getNode(Bar);
|
|
88
|
+
|
|
89
|
+
barNode.delete();
|
|
90
|
+
expect(barNode.isDeleted).toBe(true);
|
|
91
|
+
expect(fooNode.isMutated).toBe(true);
|
|
92
|
+
expect(fooNode.mutatedType.baseModel).toBeUndefined();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("handles mutation of indexers", async () => {
|
|
96
|
+
const { Foo, Bar, program } = await runner.compile(t.code`
|
|
97
|
+
model ${t.model("Foo")} is Record<Bar> {};
|
|
98
|
+
model ${t.model("Bar")} {
|
|
99
|
+
bazProp: string;
|
|
100
|
+
}
|
|
101
|
+
`);
|
|
102
|
+
const subgraph = getSubgraph(program);
|
|
103
|
+
const fooNode = subgraph.getNode(Foo);
|
|
104
|
+
const barNode = subgraph.getNode(Bar);
|
|
105
|
+
|
|
106
|
+
barNode.mutate();
|
|
107
|
+
expect(barNode.isMutated).toBe(true);
|
|
108
|
+
expect(fooNode.isMutated).toBe(true);
|
|
109
|
+
expect((fooNode.mutatedType.indexer?.value as Type) === barNode.mutatedType).toBeTruthy();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("handles mutation of arrays", async () => {
|
|
113
|
+
const { Foo, Bar, bazProp, program } = await runner.compile(t.code`
|
|
114
|
+
model ${t.model("Foo")} {};
|
|
115
|
+
model ${t.model("Bar")} {
|
|
116
|
+
${t.modelProperty("bazProp")}: Foo[];
|
|
117
|
+
}
|
|
118
|
+
`);
|
|
119
|
+
|
|
120
|
+
const subgraph = getSubgraph(program);
|
|
121
|
+
const fooNode = subgraph.getNode(Foo);
|
|
122
|
+
const barNode = subgraph.getNode(Bar);
|
|
123
|
+
const bazPropNode = subgraph.getNode(bazProp);
|
|
124
|
+
|
|
125
|
+
fooNode.mutate();
|
|
126
|
+
expect(fooNode.isMutated).toBe(true);
|
|
127
|
+
expect(barNode.isMutated).toBe(true);
|
|
128
|
+
expect(bazPropNode.isMutated).toBe(true);
|
|
129
|
+
expect(
|
|
130
|
+
(bazPropNode.mutatedType.type as Model).indexer!.value === fooNode.mutatedType,
|
|
131
|
+
).toBeTruthy();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("handles circular models", async () => {
|
|
135
|
+
const { Foo, Bar, program } = await runner.compile(t.code`
|
|
136
|
+
model ${t.model("Foo")} {
|
|
137
|
+
bar: Bar;
|
|
138
|
+
};
|
|
139
|
+
model ${t.model("Bar")} {
|
|
140
|
+
foo: Foo;
|
|
141
|
+
}
|
|
142
|
+
`);
|
|
143
|
+
|
|
144
|
+
const subgraph = getSubgraph(program);
|
|
145
|
+
const fooNode = subgraph.getNode(Foo);
|
|
146
|
+
const barNode = subgraph.getNode(Bar);
|
|
147
|
+
|
|
148
|
+
fooNode.mutate();
|
|
149
|
+
expect(fooNode.isMutated).toBe(true);
|
|
150
|
+
expect(barNode.isMutated).toBe(true);
|
|
151
|
+
});
|