Skip to content

Commit 2be2c50

Browse files
committed
Add operation components, orchestrator, and emitter rendering
Brings the component-based GraphQL emitter online by wiring the data pipeline from the foundation skeleton into Alloy JSX rendering. New components: - components/operations/query-type.tsx, mutation-type.tsx, subscription-type.tsx: render the three GraphQL root operation types - components/operations/index.ts: barrel export - components/type-collections.tsx: orchestrator that dispatches each classified-type bucket (scalars, enums, unions, interfaces, objects, inputs) into the appropriate leaf-type component Emitter wiring: - Rename src/emitter.ts → src/emitter.tsx - Phase 5 now renders via Alloy's renderSchema, converts to SDL with graphql-js printSchema, and writes the output via emitFile - Adds output-file option handling with interpolatePath Testing: - test/e2e.test.ts: single happy-path smoke test proving the full TypeSpec → mutation → classification → rendering → SDL pipeline works - Update the existing test/emitter.test.ts data-pipeline test to now assert that SDL output is produced (Phase 5 is wired up) Broader integration coverage (nullability, input/output splitting, unions, enums, arrays, circular refs, deprecation, doc comments) lands in a follow-up PR focused on tests.
1 parent 6cd9324 commit 2be2c50

9 files changed

Lines changed: 401 additions & 87 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export { QueryType, type QueryTypeProps } from "./query-type.js";
2+
export { MutationType, type MutationTypeProps } from "./mutation-type.js";
3+
export { SubscriptionType, type SubscriptionTypeProps } from "./subscription-type.js";
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { type Operation } from "@typespec/compiler";
2+
import * as gql from "@alloy-js/graphql";
3+
import { OperationField } from "../fields/index.js";
4+
5+
export interface MutationTypeProps {
6+
/** Mutation operations to render as fields */
7+
operations: Operation[];
8+
}
9+
10+
/**
11+
* Renders the GraphQL Mutation root type using Alloy's Mutation component
12+
*
13+
* Only renders if operations exist (Mutation is optional in GraphQL)
14+
*/
15+
export function MutationType(props: MutationTypeProps) {
16+
if (props.operations.length === 0) {
17+
return null;
18+
}
19+
20+
return (
21+
<gql.Mutation>
22+
{props.operations.map((op) => (
23+
<OperationField operation={op} />
24+
))}
25+
</gql.Mutation>
26+
);
27+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { type Operation } from "@typespec/compiler";
2+
import * as gql from "@alloy-js/graphql";
3+
import { OperationField } from "../fields/index.js";
4+
5+
export interface QueryTypeProps {
6+
/** Query operations to render as fields */
7+
operations: Operation[];
8+
}
9+
10+
/**
11+
* Renders the GraphQL Query root type using Alloy's Query component.
12+
* Returns null if no query operations exist (the emitter will emit an
13+
* empty-schema diagnostic in that case).
14+
*/
15+
export function QueryType(props: QueryTypeProps) {
16+
if (props.operations.length === 0) {
17+
return null;
18+
}
19+
20+
return (
21+
<gql.Query>
22+
{props.operations.map((op) => (
23+
<OperationField operation={op} />
24+
))}
25+
</gql.Query>
26+
);
27+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { type Operation } from "@typespec/compiler";
2+
import * as gql from "@alloy-js/graphql";
3+
import { OperationField } from "../fields/index.js";
4+
5+
export interface SubscriptionTypeProps {
6+
/** Subscription operations to render as fields */
7+
operations: Operation[];
8+
}
9+
10+
/**
11+
* Renders the GraphQL Subscription root type using Alloy's Subscription component
12+
*
13+
* Only renders if operations exist (Subscription is optional in GraphQL)
14+
*/
15+
export function SubscriptionType(props: SubscriptionTypeProps) {
16+
if (props.operations.length === 0) {
17+
return null;
18+
}
19+
20+
return (
21+
<gql.Subscription>
22+
{props.operations.map((op) => (
23+
<OperationField operation={op} />
24+
))}
25+
</gql.Subscription>
26+
);
27+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { For } from "@alloy-js/core";
2+
import * as gql from "@alloy-js/graphql";
3+
import { useGraphQLSchema } from "../context/index.js";
4+
import {
5+
ScalarType,
6+
EnumType,
7+
UnionType,
8+
InterfaceType,
9+
ObjectType,
10+
InputType,
11+
} from "./types/index.js";
12+
13+
/**
14+
* Renders scalar variant types for encoded scalars (e.g., bytes + base64 -> Bytes)
15+
* AND custom user-defined scalars
16+
*/
17+
export function ScalarVariantTypes() {
18+
const { classifiedTypes } = useGraphQLSchema();
19+
20+
// Get set of variant names to avoid duplicates
21+
const variantNames = new Set(
22+
classifiedTypes.scalarVariants.map((v) => v.graphqlName),
23+
);
24+
25+
// Filter custom scalars to only include ones not already in variants
26+
const customScalars = classifiedTypes.scalars.filter(
27+
(s) => !variantNames.has(s.name),
28+
);
29+
30+
return (
31+
<>
32+
<For each={classifiedTypes.scalarVariants}>
33+
{(variant) => (
34+
<gql.ScalarType
35+
name={variant.graphqlName}
36+
specifiedByUrl={variant.specificationUrl}
37+
/>
38+
)}
39+
</For>
40+
<For each={customScalars}>
41+
{(scalar) => <ScalarType type={scalar} />}
42+
</For>
43+
</>
44+
);
45+
}
46+
47+
/**
48+
* Renders all enum types in the schema
49+
*/
50+
export function EnumTypes() {
51+
const { classifiedTypes } = useGraphQLSchema();
52+
return (
53+
<For each={classifiedTypes.enums}>
54+
{(enumType) => <EnumType type={enumType} />}
55+
</For>
56+
);
57+
}
58+
59+
/**
60+
* Renders all union types in the schema
61+
*/
62+
export function UnionTypes() {
63+
const { classifiedTypes } = useGraphQLSchema();
64+
return (
65+
<For each={classifiedTypes.unions}>
66+
{(union) => <UnionType type={union} />}
67+
</For>
68+
);
69+
}
70+
71+
/**
72+
* Renders all interface types in the schema
73+
*/
74+
export function InterfaceTypes() {
75+
const { classifiedTypes } = useGraphQLSchema();
76+
return (
77+
<For each={classifiedTypes.interfaces}>
78+
{(iface) => <InterfaceType type={iface} />}
79+
</For>
80+
);
81+
}
82+
83+
/**
84+
* Renders all object types in the schema
85+
*/
86+
export function ObjectTypes() {
87+
const { classifiedTypes } = useGraphQLSchema();
88+
return (
89+
<For each={classifiedTypes.outputModels}>
90+
{(model) => <ObjectType type={model} />}
91+
</For>
92+
);
93+
}
94+
95+
/**
96+
* Renders all input types in the schema
97+
*/
98+
export function InputTypes() {
99+
const { classifiedTypes } = useGraphQLSchema();
100+
return (
101+
<For each={classifiedTypes.inputModels}>
102+
{(model) => <InputType type={model} />}
103+
</For>
104+
);
105+
}

packages/graphql/src/emitter.ts

Lines changed: 0 additions & 83 deletions
This file was deleted.

0 commit comments

Comments
 (0)