@typespec/emitter-framework 0.11.0-dev.0 → 0.11.0-dev.2
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/src/core/components/index.d.ts +2 -0
- package/dist/src/core/components/index.d.ts.map +1 -1
- package/dist/src/core/components/index.js +3 -1
- package/dist/src/core/components/overrides/component-overrides.d.ts +64 -0
- package/dist/src/core/components/overrides/component-overrides.d.ts.map +1 -0
- package/dist/src/core/components/overrides/component-overrides.js +40 -0
- package/dist/src/core/components/overrides/config.d.ts +28 -0
- package/dist/src/core/components/overrides/config.d.ts.map +1 -0
- package/dist/src/core/components/overrides/config.js +54 -0
- package/dist/src/core/components/overrides/context.d.ts +11 -0
- package/dist/src/core/components/overrides/context.d.ts.map +1 -0
- package/dist/src/core/components/overrides/context.js +8 -0
- package/dist/src/csharp/components/class/declaration.d.ts +1 -1
- package/dist/src/csharp/components/class/declaration.d.ts.map +1 -1
- package/dist/src/csharp/components/class/declaration.js +4 -3
- package/dist/src/csharp/components/class/declaration.test.js +74 -1
- package/dist/src/csharp/components/type-expression.d.ts.map +1 -1
- package/dist/src/csharp/components/type-expression.js +54 -5
- package/dist/src/csharp/components/type-expression.test.js +60 -0
- package/dist/src/testing/scenario-test/harness.js +6 -1
- package/dist/src/typescript/components/interface-member.d.ts.map +1 -1
- package/dist/src/typescript/components/interface-member.js +11 -19
- package/dist/src/typescript/components/type-expression.d.ts +0 -1
- package/dist/src/typescript/components/type-expression.d.ts.map +1 -1
- package/dist/src/typescript/components/type-expression.js +11 -11
- package/dist/src/typescript/components/type-transform.d.ts.map +1 -1
- package/dist/src/typescript/components/type-transform.js +3 -3
- package/dist/test/typescript/components/component-override.test.d.ts +2 -0
- package/dist/test/typescript/components/component-override.test.d.ts.map +1 -0
- package/dist/test/typescript/components/component-override.test.js +77 -0
- package/dist/test/typescript/test-host.d.ts +2 -6
- package/dist/test/typescript/test-host.d.ts.map +1 -1
- package/dist/test/typescript/test-host.js +4 -15
- package/package.json +2 -6
- package/src/core/components/index.tsx +2 -0
- package/src/core/components/overrides/component-overrides.tsx +134 -0
- package/src/core/components/overrides/config.ts +85 -0
- package/src/core/components/overrides/context.ts +14 -0
- package/src/csharp/components/class/declaration.test.tsx +65 -1
- package/src/csharp/components/class/declaration.tsx +7 -3
- package/src/csharp/components/type-expression.test.tsx +64 -0
- package/src/csharp/components/type-expression.tsx +54 -5
- package/src/testing/scenario-test/harness.ts +6 -1
- package/src/typescript/components/interface-member.tsx +8 -14
- package/src/typescript/components/type-expression.tsx +7 -8
- package/src/typescript/components/type-transform.tsx +3 -7
- package/test/typescript/components/component-override.test.tsx +71 -0
- package/test/typescript/test-host.ts +4 -16
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"type-transform.d.ts","sourceRoot":"","sources":["../../../../src/typescript/components/type-transform.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAsB,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAEhF,OAAO,KAAK,EAEV,KAAK,EAIL,IAAI,EACJ,KAAK,EACN,MAAM,oBAAoB,CAAC;AAc5B,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,aAAa,GAAG,WAAW,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,KAAK,CAAC;IACZ,MAAM,EAAE,aAAa,GAAG,WAAW,CAAC;CACrC;AA+DD;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,kBAAkB,YAqEjE;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,GAAG,WAAW,UAEvF;AAED,MAAM,WAAW,6BAA6B;IAC5C,IAAI,EAAE,KAAK,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,aAAa,GAAG,WAAW,CAAC;IACpC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,6BAA6B,YAwE5E;AAyDD,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,IAAI,EAAE,IAAI,CAAC;IACX;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;OAEG;IACH,MAAM,EAAE,aAAa,GAAG,WAAW,CAAC;IACpC;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAMD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG,QAAQ,
|
|
1
|
+
{"version":3,"file":"type-transform.d.ts","sourceRoot":"","sources":["../../../../src/typescript/components/type-transform.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAsB,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAEhF,OAAO,KAAK,EAEV,KAAK,EAIL,IAAI,EACJ,KAAK,EACN,MAAM,oBAAoB,CAAC;AAc5B,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,aAAa,GAAG,WAAW,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,KAAK,CAAC;IACZ,MAAM,EAAE,aAAa,GAAG,WAAW,CAAC;CACrC;AA+DD;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,kBAAkB,YAqEjE;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,GAAG,WAAW,UAEvF;AAED,MAAM,WAAW,6BAA6B;IAC5C,IAAI,EAAE,KAAK,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,aAAa,GAAG,WAAW,CAAC;IACpC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,6BAA6B,YAwE5E;AAyDD,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,IAAI,EAAE,IAAI,CAAC;IACX;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;OAEG;IACH,MAAM,EAAE,aAAa,GAAG,WAAW,CAAC;IACpC;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAMD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG,QAAQ,CAmEzE"}
|
|
@@ -205,7 +205,7 @@ export function ModelTransformExpression(props) {
|
|
|
205
205
|
return _$createComponent(ts.ObjectExpression, {
|
|
206
206
|
get children() {
|
|
207
207
|
return [baseModelTransform, _$memo(() => mapJoin(() => modelProperties, (_, property) => {
|
|
208
|
-
const unpackedType =
|
|
208
|
+
const unpackedType = property.type;
|
|
209
209
|
let targetPropertyName = property.name;
|
|
210
210
|
let sourcePropertyName = namePolicy.getName(property.name, "interface-member");
|
|
211
211
|
if (props.target === "application") {
|
|
@@ -346,7 +346,7 @@ export function TypeTransformCall(props) {
|
|
|
346
346
|
}
|
|
347
347
|
const transformType = collapsedProperty?.type ?? props.type;
|
|
348
348
|
if ($.model.is(transformType) && $.array.is(transformType)) {
|
|
349
|
-
const unpackedElement = $.
|
|
349
|
+
const unpackedElement = $.array.getElementType(transformType);
|
|
350
350
|
return _$createComponent(ts.FunctionCallExpression, {
|
|
351
351
|
target: ArraySerializerRefkey,
|
|
352
352
|
get args() {
|
|
@@ -360,7 +360,7 @@ export function TypeTransformCall(props) {
|
|
|
360
360
|
});
|
|
361
361
|
}
|
|
362
362
|
if ($.model.is(transformType) && $.record.is(transformType)) {
|
|
363
|
-
const unpackedElement = $.
|
|
363
|
+
const unpackedElement = $.record.getElementType(transformType);
|
|
364
364
|
return _$createComponent(ts.FunctionCallExpression, {
|
|
365
365
|
target: RecordSerializerRefkey,
|
|
366
366
|
get args() {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-override.test.d.ts","sourceRoot":"","sources":["../../../../test/typescript/components/component-override.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { memo as _$memo, createComponent as _$createComponent, createIntrinsic as _$createIntrinsic } from "@alloy-js/core/jsx-runtime";
|
|
2
|
+
import { Experimental_ComponentOverrides, Experimental_ComponentOverridesConfig } from "#core/index.js";
|
|
3
|
+
import { FunctionDeclaration } from "#typescript/index.js";
|
|
4
|
+
import { For, List } from "@alloy-js/core";
|
|
5
|
+
import { d } from "@alloy-js/core/testing";
|
|
6
|
+
import { SourceFile } from "@alloy-js/typescript";
|
|
7
|
+
import { expect, it } from "vitest";
|
|
8
|
+
import { Output } from "../../../src/core/components/output.js";
|
|
9
|
+
import { InterfaceDeclaration } from "../../../src/typescript/components/interface-declaration.js";
|
|
10
|
+
import { Tester } from "../../test-host.js";
|
|
11
|
+
it("uses overridden component", async () => {
|
|
12
|
+
const {
|
|
13
|
+
program
|
|
14
|
+
} = await Tester.compile(`
|
|
15
|
+
namespace DemoService;
|
|
16
|
+
model Foo {
|
|
17
|
+
knownProp: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
op foo(): Foo;
|
|
21
|
+
|
|
22
|
+
`);
|
|
23
|
+
const [namespace] = program.resolveTypeReference("DemoService");
|
|
24
|
+
const models = Array.from(namespace.models.values());
|
|
25
|
+
const operations = Array.from(namespace.operations.values());
|
|
26
|
+
expect(_$createComponent(Output, {
|
|
27
|
+
program: program,
|
|
28
|
+
get children() {
|
|
29
|
+
return _$createComponent(TestClientOverrides, {
|
|
30
|
+
get children() {
|
|
31
|
+
return _$createComponent(SourceFile, {
|
|
32
|
+
path: "test.ts",
|
|
33
|
+
get children() {
|
|
34
|
+
return [_$createComponent(List, {
|
|
35
|
+
hardline: true,
|
|
36
|
+
get children() {
|
|
37
|
+
return models.map(model => _$createComponent(InterfaceDeclaration, {
|
|
38
|
+
"export": true,
|
|
39
|
+
type: model
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
}), _$createIntrinsic("hbr", {}), _$createComponent(For, {
|
|
43
|
+
each: operations,
|
|
44
|
+
children: operation => _$createComponent(FunctionDeclaration, {
|
|
45
|
+
"export": true,
|
|
46
|
+
type: operation
|
|
47
|
+
})
|
|
48
|
+
})];
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
})).toRenderTo(d`
|
|
55
|
+
export interface Foo {
|
|
56
|
+
knownProp: string;
|
|
57
|
+
}
|
|
58
|
+
export function foo(): unknown {}
|
|
59
|
+
`);
|
|
60
|
+
});
|
|
61
|
+
function TestClientOverrides(props) {
|
|
62
|
+
const overrides = Experimental_ComponentOverridesConfig().forTypeKind("Model", {
|
|
63
|
+
reference: props => {
|
|
64
|
+
if (props.type.name === "Foo") {
|
|
65
|
+
return "unknown";
|
|
66
|
+
} else {
|
|
67
|
+
return props.default;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
return _$createComponent(Experimental_ComponentOverrides, {
|
|
72
|
+
overrides: overrides,
|
|
73
|
+
get children() {
|
|
74
|
+
return props.children;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import type { Program } from "@typespec/compiler";
|
|
2
|
-
export declare function createTypespecCliTestHost(
|
|
3
|
-
libraries: "Http"[];
|
|
4
|
-
}): Promise<import("@typespec/compiler/testing").TestHost>;
|
|
2
|
+
export declare function createTypespecCliTestHost(): Promise<import("@typespec/compiler/testing").TestHost>;
|
|
5
3
|
export declare function createEmitterFrameworkTestRunner(options?: {
|
|
6
4
|
autoUsings?: string[];
|
|
7
5
|
}): Promise<import("@typespec/compiler/testing").BasicTestRunner>;
|
|
8
|
-
export declare function getProgram(code: string
|
|
9
|
-
libraries: "Http"[];
|
|
10
|
-
}): Promise<Program>;
|
|
6
|
+
export declare function getProgram(code: string): Promise<Program>;
|
|
11
7
|
/**
|
|
12
8
|
* Initializes an empty program in the compiler.
|
|
13
9
|
* This is useful when you want to initialize the default TypeKits without any code.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-host.d.ts","sourceRoot":"","sources":["../../../test/typescript/test-host.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"test-host.d.ts","sourceRoot":"","sources":["../../../test/typescript/test-host.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAOlD,wBAAsB,yBAAyB,2DAE9C;AAED,wBAAsB,gCAAgC,CAAC,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;CAAO,iEAK7F;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAU/D;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAEtD"}
|
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
import { createTestHost, createTestWrapper, expectDiagnosticEmpty } from "@typespec/compiler/testing";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
libraries: []
|
|
5
|
-
}) {
|
|
6
|
-
const libraries = [];
|
|
7
|
-
if (options.libraries.includes("Http")) {
|
|
8
|
-
libraries.push(HttpTestLibrary);
|
|
9
|
-
}
|
|
10
|
-
return createTestHost({
|
|
11
|
-
libraries
|
|
12
|
-
});
|
|
2
|
+
export async function createTypespecCliTestHost() {
|
|
3
|
+
return createTestHost({});
|
|
13
4
|
}
|
|
14
5
|
export async function createEmitterFrameworkTestRunner(options = {}) {
|
|
15
6
|
const host = await createTypespecCliTestHost();
|
|
@@ -17,10 +8,8 @@ export async function createEmitterFrameworkTestRunner(options = {}) {
|
|
|
17
8
|
autoUsings: options.autoUsings
|
|
18
9
|
});
|
|
19
10
|
}
|
|
20
|
-
export async function getProgram(code
|
|
21
|
-
|
|
22
|
-
}) {
|
|
23
|
-
const host = await createTypespecCliTestHost(options);
|
|
11
|
+
export async function getProgram(code) {
|
|
12
|
+
const host = await createTypespecCliTestHost();
|
|
24
13
|
const wrapper = createTestWrapper(host, {
|
|
25
14
|
compilerOptions: {
|
|
26
15
|
noEmit: true
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typespec/emitter-framework",
|
|
3
|
-
"version": "0.11.0-dev.
|
|
3
|
+
"version": "0.11.0-dev.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"repository": {
|
|
@@ -44,9 +44,7 @@
|
|
|
44
44
|
"@alloy-js/core": "^0.19.0",
|
|
45
45
|
"@alloy-js/csharp": "^0.19.0",
|
|
46
46
|
"@alloy-js/typescript": "^0.19.0",
|
|
47
|
-
"@typespec/compiler": "^1.3.0"
|
|
48
|
-
"@typespec/http": "^1.3.0",
|
|
49
|
-
"@typespec/rest": "^0.73.0 || >=0.74.0-dev <0.74.0"
|
|
47
|
+
"@typespec/compiler": "^1.3.0"
|
|
50
48
|
},
|
|
51
49
|
"devDependencies": {
|
|
52
50
|
"@alloy-js/cli": "^0.19.0",
|
|
@@ -54,8 +52,6 @@
|
|
|
54
52
|
"@alloy-js/rollup-plugin": "^0.1.0",
|
|
55
53
|
"@alloy-js/typescript": "^0.19.0",
|
|
56
54
|
"@typespec/compiler": "^1.3.0",
|
|
57
|
-
"@typespec/http": "^1.3.0",
|
|
58
|
-
"@typespec/rest": "^0.73.0 || >=0.74.0-dev <0.74.0",
|
|
59
55
|
"concurrently": "^9.1.2",
|
|
60
56
|
"pathe": "^2.0.3",
|
|
61
57
|
"prettier": "~3.6.2",
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { type Children, type ComponentDefinition } from "@alloy-js/core";
|
|
2
|
+
import type { ObjectPropertyProps, VarDeclarationProps } from "@alloy-js/typescript";
|
|
3
|
+
import type {
|
|
4
|
+
Enum,
|
|
5
|
+
EnumMember,
|
|
6
|
+
Model,
|
|
7
|
+
ModelProperty,
|
|
8
|
+
Scalar,
|
|
9
|
+
Type,
|
|
10
|
+
Union,
|
|
11
|
+
UnionVariant,
|
|
12
|
+
} from "@typespec/compiler";
|
|
13
|
+
import { useTsp } from "../../context/index.js";
|
|
14
|
+
import {
|
|
15
|
+
type Experimental_ComponentOverridesConfig,
|
|
16
|
+
getOverrideForType,
|
|
17
|
+
getOverridesForTypeKind,
|
|
18
|
+
} from "./config.js";
|
|
19
|
+
import { type ComponentOverridesContext, OverridesContext, useOverrides } from "./context.js";
|
|
20
|
+
|
|
21
|
+
export interface Experimental_OverrideEmitPropsBase<TCustomType extends Type> {
|
|
22
|
+
/**
|
|
23
|
+
* The TypeSpec type to render.
|
|
24
|
+
*/
|
|
25
|
+
type: TCustomType;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The default emitted output for this type.
|
|
29
|
+
*/
|
|
30
|
+
default: Children;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type Experimental_CustomTypeToProps<TCustomType extends Type> =
|
|
34
|
+
TCustomType extends ModelProperty
|
|
35
|
+
? ObjectPropertyProps
|
|
36
|
+
: TCustomType extends EnumMember
|
|
37
|
+
? {}
|
|
38
|
+
: TCustomType extends UnionVariant
|
|
39
|
+
? {}
|
|
40
|
+
: TCustomType extends Model | Scalar | Union | Enum
|
|
41
|
+
? VarDeclarationProps
|
|
42
|
+
: VarDeclarationProps | ObjectPropertyProps;
|
|
43
|
+
|
|
44
|
+
export interface Experimental_OverrideReferenceProps<TCustomType extends Type>
|
|
45
|
+
extends Experimental_OverrideEmitPropsBase<TCustomType> {
|
|
46
|
+
/**
|
|
47
|
+
* The member this type is referenced from, if any. This member may contain
|
|
48
|
+
* additional metadata that should be represented in the emitted output.
|
|
49
|
+
*/
|
|
50
|
+
member?: ModelProperty;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface Experimental_OverrideDeclareProps<TCustomType extends Type>
|
|
54
|
+
extends Experimental_OverrideEmitPropsBase<TCustomType> {
|
|
55
|
+
Declaration: ComponentDefinition<Experimental_CustomTypeToProps<TCustomType>>;
|
|
56
|
+
declarationProps: Experimental_CustomTypeToProps<TCustomType>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type Experimental_OverrideDeclarationComponent<TCustomType extends Type> =
|
|
60
|
+
ComponentDefinition<Experimental_OverrideDeclareProps<TCustomType>>;
|
|
61
|
+
|
|
62
|
+
export type Experimental_OverrideReferenceComponent<TCustomType extends Type> = ComponentDefinition<
|
|
63
|
+
Experimental_OverrideReferenceProps<TCustomType>
|
|
64
|
+
>;
|
|
65
|
+
|
|
66
|
+
export interface Experimental_ComponentOverridesConfigBase<TCustomType extends Type> {
|
|
67
|
+
/**
|
|
68
|
+
* Override when this type is referenced.
|
|
69
|
+
* e.g. When used in <TypeExpression type={type} />
|
|
70
|
+
*/
|
|
71
|
+
reference?: Experimental_OverrideReferenceComponent<TCustomType>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface Experimental_ComponentOverridesProps {
|
|
75
|
+
overrides: Experimental_ComponentOverridesConfig;
|
|
76
|
+
children?: Children;
|
|
77
|
+
}
|
|
78
|
+
export function Experimental_ComponentOverrides(props: Experimental_ComponentOverridesProps) {
|
|
79
|
+
const context: ComponentOverridesContext = {
|
|
80
|
+
overrides: props.overrides,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
return <OverridesContext.Provider value={context}>{props.children}</OverridesContext.Provider>;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface Experimental_OverrideTypeComponentCommonProps<T extends Type> {
|
|
87
|
+
/**
|
|
88
|
+
* The TypeSpec type to render.
|
|
89
|
+
*/
|
|
90
|
+
type: T;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The default rendering.
|
|
94
|
+
*/
|
|
95
|
+
children: Children;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface Experimental_OverridableComponentReferenceProps<T extends Type>
|
|
99
|
+
extends Experimental_OverrideTypeComponentCommonProps<T> {
|
|
100
|
+
/**
|
|
101
|
+
* Pass when rendering a reference to the provided type or type kind.
|
|
102
|
+
*/
|
|
103
|
+
reference: true;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* The member this type is referenced from, if any. This member may contain
|
|
107
|
+
* additional metadata that should be represented in the emitted output.
|
|
108
|
+
*/
|
|
109
|
+
member?: ModelProperty;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export type Experimental_OverridableComponentProps<T extends Type> =
|
|
113
|
+
Experimental_OverridableComponentReferenceProps<T>;
|
|
114
|
+
|
|
115
|
+
export function Experimental_OverridableComponent<T extends Type>(
|
|
116
|
+
props: Experimental_OverridableComponentProps<T>,
|
|
117
|
+
) {
|
|
118
|
+
const options = useOverrides();
|
|
119
|
+
const { $ } = useTsp();
|
|
120
|
+
const descriptor =
|
|
121
|
+
getOverrideForType($.program, props.type, options.overrides) ??
|
|
122
|
+
getOverridesForTypeKind($.program, props.type.kind, options.overrides);
|
|
123
|
+
|
|
124
|
+
if (!descriptor) {
|
|
125
|
+
return <>{props.children}</>;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if ("reference" in props && props.reference && descriptor.reference) {
|
|
129
|
+
const CustomComponent = descriptor.reference;
|
|
130
|
+
return <CustomComponent type={props.type} member={props.member} default={props.children} />;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return <>{props.children}</>;
|
|
134
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { Program, Scalar, Type } from "@typespec/compiler";
|
|
2
|
+
import { $ } from "@typespec/compiler/typekit";
|
|
3
|
+
import type { Experimental_ComponentOverridesConfigBase } from "./component-overrides.jsx";
|
|
4
|
+
|
|
5
|
+
const getOverrideForTypeSym: unique symbol = Symbol.for("ef-ts:getOverrideForType");
|
|
6
|
+
const getOverrideForTypeKindSym: unique symbol = Symbol.for("ef-ts:getOverrideForTypeKind");
|
|
7
|
+
|
|
8
|
+
export type Experimental_ComponentOverridesConfig = Experimental_ComponentOverridesClass;
|
|
9
|
+
export const Experimental_ComponentOverridesConfig = function () {
|
|
10
|
+
return new Experimental_ComponentOverridesClass();
|
|
11
|
+
} as {
|
|
12
|
+
new (): Experimental_ComponentOverridesClass;
|
|
13
|
+
(): Experimental_ComponentOverridesClass;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export class Experimental_ComponentOverridesClass {
|
|
17
|
+
#typeEmitOptions: Map<Type, Experimental_ComponentOverridesConfigBase<any>> = new Map();
|
|
18
|
+
#typeKindEmitOptions: Map<Type["kind"], Experimental_ComponentOverridesConfigBase<any>> =
|
|
19
|
+
new Map();
|
|
20
|
+
|
|
21
|
+
forType<const T extends Type>(type: T, options: Experimental_ComponentOverridesConfigBase<T>) {
|
|
22
|
+
this.#typeEmitOptions.set(type, options);
|
|
23
|
+
|
|
24
|
+
return this;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
forTypeKind<const TKind extends Type["kind"]>(
|
|
28
|
+
typeKind: TKind,
|
|
29
|
+
options: Experimental_ComponentOverridesConfigBase<Extract<Type, { kind: TKind }>>,
|
|
30
|
+
) {
|
|
31
|
+
this.#typeKindEmitOptions.set(typeKind, options);
|
|
32
|
+
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @internal
|
|
38
|
+
*/
|
|
39
|
+
[getOverrideForTypeSym](program: Program, type: Type) {
|
|
40
|
+
const options = this.#typeEmitOptions.get(type);
|
|
41
|
+
if (options || !$(program).scalar.is(type) /** || isBuiltIn(program, type) */) {
|
|
42
|
+
return options;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// have a scalar, it's not a built-in scalar, and didn't find options, so
|
|
46
|
+
// see if we have options for a base scalar.
|
|
47
|
+
let currentScalar: Scalar | undefined = type;
|
|
48
|
+
while (
|
|
49
|
+
currentScalar &&
|
|
50
|
+
// !isBuiltIn(program, currentScalar) &&
|
|
51
|
+
!this.#typeEmitOptions.has(currentScalar)
|
|
52
|
+
) {
|
|
53
|
+
currentScalar = currentScalar?.baseScalar;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!currentScalar) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return this.#typeEmitOptions.get(currentScalar);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @internal
|
|
65
|
+
*/
|
|
66
|
+
[getOverrideForTypeKindSym](program: Program, typeKind: Type["kind"]) {
|
|
67
|
+
return this.#typeKindEmitOptions.get(typeKind);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function getOverrideForType(
|
|
72
|
+
program: Program,
|
|
73
|
+
type: Type,
|
|
74
|
+
options?: Experimental_ComponentOverridesConfig,
|
|
75
|
+
) {
|
|
76
|
+
return options?.[getOverrideForTypeSym](program, type);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function getOverridesForTypeKind(
|
|
80
|
+
program: Program,
|
|
81
|
+
typeKind: Type["kind"],
|
|
82
|
+
options?: Experimental_ComponentOverridesConfig,
|
|
83
|
+
) {
|
|
84
|
+
return options?.[getOverrideForTypeKindSym](program, typeKind);
|
|
85
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createContext, useContext, type ComponentContext } from "@alloy-js/core";
|
|
2
|
+
import type { Experimental_ComponentOverridesConfig } from "./config.js";
|
|
3
|
+
|
|
4
|
+
export interface ComponentOverridesContext {
|
|
5
|
+
overrides?: Experimental_ComponentOverridesConfig;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Context for setting overrides for components
|
|
9
|
+
*/
|
|
10
|
+
export const OverridesContext: ComponentContext<ComponentOverridesContext> = createContext({});
|
|
11
|
+
|
|
12
|
+
export function useOverrides(): ComponentOverridesContext {
|
|
13
|
+
return useContext(OverridesContext)!;
|
|
14
|
+
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Experimental_ComponentOverrides,
|
|
3
|
+
Experimental_ComponentOverridesConfig,
|
|
4
|
+
} from "#core/index.js";
|
|
1
5
|
import { Tester } from "#test/test-host.js";
|
|
2
|
-
import { type Children } from "@alloy-js/core";
|
|
6
|
+
import { List, type Children } from "@alloy-js/core";
|
|
7
|
+
import { d } from "@alloy-js/core/testing";
|
|
3
8
|
import { createCSharpNamePolicy, Namespace, SourceFile } from "@alloy-js/csharp";
|
|
4
9
|
import { t, type TesterInstance } from "@typespec/compiler/testing";
|
|
5
10
|
import { beforeEach, describe, expect, it } from "vitest";
|
|
@@ -67,6 +72,65 @@ it("renders a class declaration with properties", async () => {
|
|
|
67
72
|
`);
|
|
68
73
|
});
|
|
69
74
|
|
|
75
|
+
it("renders a class declaration with properties using component override", async () => {
|
|
76
|
+
const { TestModel, Foo, Bar } = await runner.compile(t.code`
|
|
77
|
+
model ${t.model("Foo")} {}
|
|
78
|
+
model ${t.model("Bar")} {}
|
|
79
|
+
model ${t.model("TestModel")} {
|
|
80
|
+
Prop1: string;
|
|
81
|
+
Prop2: int32;
|
|
82
|
+
Prop3?: Foo;
|
|
83
|
+
}
|
|
84
|
+
`);
|
|
85
|
+
|
|
86
|
+
expect(
|
|
87
|
+
<Wrapper>
|
|
88
|
+
<TestClientOverrides>
|
|
89
|
+
<List hardline>
|
|
90
|
+
<ClassDeclaration type={Foo} />
|
|
91
|
+
<ClassDeclaration type={Bar} />
|
|
92
|
+
<ClassDeclaration type={TestModel} />
|
|
93
|
+
</List>
|
|
94
|
+
</TestClientOverrides>
|
|
95
|
+
</Wrapper>,
|
|
96
|
+
).toRenderTo(d`
|
|
97
|
+
namespace TestNamespace
|
|
98
|
+
{
|
|
99
|
+
class Foo
|
|
100
|
+
{
|
|
101
|
+
|
|
102
|
+
}
|
|
103
|
+
class Bar
|
|
104
|
+
{
|
|
105
|
+
|
|
106
|
+
}
|
|
107
|
+
class TestModel
|
|
108
|
+
{
|
|
109
|
+
public required string Prop1 { get; set; }
|
|
110
|
+
public required int Prop2 { get; set; }
|
|
111
|
+
public Bar Prop3 { get; set; }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
`);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
function TestClientOverrides(props: { children?: Children }) {
|
|
118
|
+
const overrides = Experimental_ComponentOverridesConfig().forTypeKind("Model", {
|
|
119
|
+
reference: (props) => {
|
|
120
|
+
if (props.type.name === "Foo") {
|
|
121
|
+
return "Bar";
|
|
122
|
+
} else {
|
|
123
|
+
return props.default;
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
return (
|
|
128
|
+
<Experimental_ComponentOverrides overrides={overrides}>
|
|
129
|
+
{props.children}
|
|
130
|
+
</Experimental_ComponentOverrides>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
70
134
|
it("can override class name", async () => {
|
|
71
135
|
const { TestModel } = await runner.compile(t.code`
|
|
72
136
|
model ${t.model("TestModel")} {}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { type Children
|
|
1
|
+
import { For, type Children } from "@alloy-js/core";
|
|
2
2
|
import * as cs from "@alloy-js/csharp";
|
|
3
3
|
import { Method } from "@alloy-js/csharp";
|
|
4
|
-
import type
|
|
4
|
+
import { isVoidType, type Interface, type Model } from "@typespec/compiler";
|
|
5
5
|
import { useTsp } from "../../../core/index.js";
|
|
6
6
|
import { Property } from "../property/property.jsx";
|
|
7
7
|
import { TypeExpression } from "../type-expression.jsx";
|
|
@@ -53,8 +53,12 @@ export function ClassDeclaration(props: ClassDeclarationProps): Children {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
function ClassProperties(props: ClassPropertiesProps): Children {
|
|
56
|
+
// Ignore 'void' type properties which is not valid in csharp
|
|
57
|
+
const properties = Array.from(props.type.properties.entries()).filter(
|
|
58
|
+
([_, p]) => !isVoidType(p.type),
|
|
59
|
+
);
|
|
56
60
|
return (
|
|
57
|
-
<For each={
|
|
61
|
+
<For each={properties} hardline>
|
|
58
62
|
{([name, property]) => <Property type={property} jsonAttributes={props.jsonAttributes} />}
|
|
59
63
|
</For>
|
|
60
64
|
);
|
|
@@ -131,3 +131,67 @@ describe("Record map to IDictionary", () => {
|
|
|
131
131
|
);
|
|
132
132
|
});
|
|
133
133
|
});
|
|
134
|
+
|
|
135
|
+
describe("Nullable union", () => {
|
|
136
|
+
it("nullable boolean", async () => {
|
|
137
|
+
const { Pet } = (await runner.compile(`
|
|
138
|
+
@test model Pet {
|
|
139
|
+
@test name: boolean | null;
|
|
140
|
+
}
|
|
141
|
+
`)) as { Pet: Model };
|
|
142
|
+
|
|
143
|
+
const res = render(
|
|
144
|
+
<Wrapper>
|
|
145
|
+
<ClassDeclaration type={Pet} />
|
|
146
|
+
</Wrapper>,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
assertFileContents(
|
|
150
|
+
res,
|
|
151
|
+
d`
|
|
152
|
+
namespace TestNamespace
|
|
153
|
+
{
|
|
154
|
+
class Pet
|
|
155
|
+
{
|
|
156
|
+
public required bool? name { get; set; }
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
`,
|
|
160
|
+
);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
describe("Literal types", () => {
|
|
165
|
+
it("literal types (string, int, double, bool)", async () => {
|
|
166
|
+
const { Pet } = (await runner.compile(`
|
|
167
|
+
@test model Pet {
|
|
168
|
+
@test boolName: true;
|
|
169
|
+
@test intName: 42;
|
|
170
|
+
@test doubleName: 3.14;
|
|
171
|
+
@test stringName: "Hello";
|
|
172
|
+
}
|
|
173
|
+
`)) as { Pet: Model };
|
|
174
|
+
|
|
175
|
+
const res = render(
|
|
176
|
+
<Wrapper>
|
|
177
|
+
<ClassDeclaration type={Pet} />
|
|
178
|
+
</Wrapper>,
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
assertFileContents(
|
|
182
|
+
res,
|
|
183
|
+
d`
|
|
184
|
+
namespace TestNamespace
|
|
185
|
+
{
|
|
186
|
+
class Pet
|
|
187
|
+
{
|
|
188
|
+
public required bool boolName { get; set; }
|
|
189
|
+
public required int intName { get; set; }
|
|
190
|
+
public required double doubleName { get; set; }
|
|
191
|
+
public required string stringName { get; set; }
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
`,
|
|
195
|
+
);
|
|
196
|
+
});
|
|
197
|
+
});
|