import type { Props } from "@alloy-js/core";
import {
code,
List,
memberRefkey,
namekey,
Output,
refkey,
render,
StatementList,
} from "@alloy-js/core";
import { describe, expect, it } from "vitest";
import { ClassField } from "../src/components/stc/index.js";
import {
ClassDeclaration,
FunctionDeclaration,
InterfaceDeclaration,
InterfaceMember,
SourceFile,
TypeDeclaration,
VarDeclaration,
} from "../src/index.js";
import type { ParameterDescriptor } from "../src/parameter-descriptor.js";
import { TestFile } from "./utils.js";
it("works", () => {
expect(
,
).toRenderTo(`
function foo() {}
`);
});
it("can be exported", () => {
expect(
,
).toRenderTo(`
export function foo() {}
`);
});
it("can be a default export", () => {
expect(
,
).toRenderTo(`
export default function foo() {}
`);
});
it("can be an async function", () => {
expect(
,
).toRenderTo(`
export async function foo() {}
`);
});
it("can be an async function with returnType", () => {
expect(
,
).toRenderTo(`
export async function foo(): Promise {}
`);
});
it("can be an async function with returnType element", () => {
function Foo(_props?: Props) {
return <>Foo>;
}
expect(
} />
,
).toRenderTo(`
export async function foo(): Promise {}
`);
});
it("supports parameters by element", () => {
const decl = (
return a + b;
a, b
);
expect({decl}).toRenderTo(`
function foo(a, b) {
return a + b;
}
`);
});
it("supports type parameters by descriptor object", () => {
const decl = (
);
expect({decl}).toRenderTo(`
function foo() {}
`);
});
it("supports type parameters with namekeys", () => {
const decl = (
);
expect({decl}).toRenderTo(`
function foo() {}
`);
});
it("supports type parameters by descriptor array", () => {
const decl = (
);
expect({decl}).toRenderTo(`
function foo() {}
`);
});
it("supports type parameters by element", () => {
const decl = (
a, b
);
expect({decl}).toRenderTo(`
function foo() {}
`);
});
it("resolves references to function-local symbols from deeply indented code blocks (#429)", () => {
const value = refkey();
expect(
{code`
if (value) {
if (!${value}) {
return ${value} as any;
}
}
return ${value};
`}
,
).toRenderTo(`
export function serialize(value: string | null): string {
if (value) {
if (!value) {
return value as any;
}
}
return value;
}
`);
});
it("do not add an extra comma when there is no parameters", () => {
expect(
,
).toRenderTo(`
function thisFunctionNameIsTooLongSoTheFormatterWillInsertLineBreakAndIfBreakNodes() {
}
`);
});
it("resolves references to function-local symbols from indented code blocks (#429)", () => {
const value = refkey();
expect(
{code`
if (!${value}) {
return ${value} as any;
}
return ${value};
`}
,
).toRenderTo(`
export function serialize(value: string | null): string {
if (!value) {
return value as any;
}
return value;
}
`);
});
describe("symbols", () => {
it("creates a nested scope", () => {
const innerRefkey = refkey();
const outerRefkey = refkey();
const decl = (
{innerRefkey}
1
2
{outerRefkey}
);
expect({decl}).toRenderTo(`
function foo() {
refme;
const refme = 1;
};
const refme = 2;
refme;
`);
});
it("throws an error when trying to access a symbol in a nested function scope", () => {
const innerRefkey = refkey();
const decl = (
<>
1
;{innerRefkey}
>
);
expect(() =>
render({decl}, { insertFinalNewLine: false }),
).toThrow(/Cannot reference a symbol/);
});
it("creates symbols for parameters", () => {
const rk = refkey();
const decl = (
{rk}
);
expect({decl}).toRenderTo(`
function foo(sym: any) {
function bar() {
sym
}
}
`);
});
it("creates symbols for parameters and addresses conflicts", () => {
const decl = (
1;
);
expect({decl}).toRenderTo(`
function foo(conflict: any) {
const conflict_2 = 1;
}
`);
});
it("create optional parameters", () => {
const paramDesc: ParameterDescriptor = {
name: "foo",
refkey: refkey(),
type: "any",
optional: true,
};
const decl = (
console.log(foo);
);
expect({decl}).toRenderTo(`
function foo(foo?: any) {
console.log(foo);
}
`);
});
it("create optional parameters", () => {
const paramDesc: ParameterDescriptor = {
name: "foo",
refkey: refkey(),
type: "any[]",
rest: true,
};
const decl = (
console.log(foo);
);
expect({decl}).toRenderTo(`
function foo(...foo: any[]) {
console.log(foo);
}
`);
});
it("creates parameters with default values", () => {
const paramDesc: ParameterDescriptor = {
name: "foo",
refkey: refkey(),
type: "string",
default: '"bar"',
};
const decl = (
console.log(foo);
);
expect({decl}).toRenderTo(`
function foo(foo: string = "bar") {
console.log(foo);
}
`);
});
it("correctly renders mixed parameters", () => {
const params: ParameterDescriptor[] = [
{ name: "a", refkey: refkey(), type: "string" },
{ name: "b", refkey: refkey(), type: "number", optional: true },
{ name: "c", refkey: refkey(), type: "boolean", default: "false" },
{ name: "d", refkey: refkey(), type: "any[]", rest: true },
];
const decl = (
console.log(a, b, c, d);
);
expect({decl}).toRenderTo(`
function foo(a: string, b?: number, c: boolean = false, ...d: any[]) {
console.log(a, b, c, d);
}
`);
});
it("adds symbols for members of parameters when a type is provided", () => {
const ifaceRk = refkey();
const ifaceMemberRk = refkey();
const clsRk = refkey();
const clsMemberRk = refkey();
const ifaceParamRk = refkey();
const clsParamRk = refkey();
const ifaceParam: ParameterDescriptor = {
name: "ifaceParam",
refkey: ifaceParamRk,
type: ifaceRk,
optional: true,
};
const clsParam: ParameterDescriptor = {
name: "classParam",
refkey: clsParamRk,
type: clsRk,
optional: true,
};
const decl = (
string
42
<>console.log({memberRefkey(ifaceParamRk, ifaceMemberRk)})>
<>console.log({memberRefkey(clsParamRk, clsMemberRk)});>
);
expect({decl}).toRenderTo(`
interface IFace {
ifaceProp: string
}
class Class {
classProp = 42
}
function fn(ifaceParam?: IFace, classParam?: Class) {
console.log(ifaceParam?.ifaceProp);
console.log(classParam?.classProp);;
}
`);
});
it("adds symbols for members of parameters when a type and default are provided", () => {
const ifaceRk = refkey();
const ifaceMemberRk = refkey();
const paramRk = refkey();
const param: ParameterDescriptor = {
name: "myParam",
refkey: paramRk,
type: ifaceRk,
default: "undefined",
};
const decl = (
string
{memberRefkey(paramRk, ifaceMemberRk)}
);
expect({decl}).toRenderTo(`
interface IFace {
myProp: string
}
function fn(myParam: IFace = undefined) {
myParam.myProp
}
`);
});
});
it("has parameters and return type in type ref context", () => {
const i1 = namekey("iface1");
const i2 = namekey("iface2");
const template = (
);
expect(template).toRenderTo({
"test1.ts": `
type iface1 = any;
type iface2 = any;
`,
"test2.ts": `
import type { iface1, iface2 } from "./test1.js";
function foo(a: iface1): iface2 {}
`,
});
});