diff --git a/.chronus/changes/copilot-update-init-templates-testing-framework-2026-1-5-20-11-37.md b/.chronus/changes/copilot-update-init-templates-testing-framework-2026-1-5-20-11-37.md new file mode 100644 index 00000000000..cf2f9c23e28 --- /dev/null +++ b/.chronus/changes/copilot-update-init-templates-testing-framework-2026-1-5-20-11-37.md @@ -0,0 +1,7 @@ +--- +changeKind: internal +packages: + - "@typespec/compiler" +--- + +Update init templates to use createTester API and modern ESLint configuration \ No newline at end of file diff --git a/packages/compiler/templates/__snapshots__/emitter-ts/eslint.config.js b/packages/compiler/templates/__snapshots__/emitter-ts/eslint.config.js index 75cb4f7ce3e..430df9c59ea 100644 --- a/packages/compiler/templates/__snapshots__/emitter-ts/eslint.config.js +++ b/packages/compiler/templates/__snapshots__/emitter-ts/eslint.config.js @@ -1,11 +1,20 @@ // @ts-check import eslint from "@eslint/js"; +import { defineConfig } from "eslint/config"; import tsEslint from "typescript-eslint"; -export default tsEslint.config( +export default defineConfig( { ignores: ["**/dist/**/*", "**/.temp/**/*"], }, eslint.configs.recommended, ...tsEslint.configs.recommended, + { + rules: { + "@typescript-eslint/no-unused-vars": [ + "warn", + { varsIgnorePattern: "^_", argsIgnorePattern: "^_" }, + ], + }, + }, ); diff --git a/packages/compiler/templates/__snapshots__/emitter-ts/package.json b/packages/compiler/templates/__snapshots__/emitter-ts/package.json index f9f12501c17..8231ab14f7b 100644 --- a/packages/compiler/templates/__snapshots__/emitter-ts/package.json +++ b/packages/compiler/templates/__snapshots__/emitter-ts/package.json @@ -18,8 +18,7 @@ }, "devDependencies": { "@types/node": "latest", - "@typescript-eslint/eslint-plugin": "^8.15.0", - "@typescript-eslint/parser": "^8.15.0", + "typescript-eslint": "^8.49.0", "eslint": "^9.15.0", "@typespec/compiler": "latest", "typescript": "^5.3.3", diff --git a/packages/compiler/templates/__snapshots__/emitter-ts/test/test-host.ts b/packages/compiler/templates/__snapshots__/emitter-ts/test/test-host.ts index 14b78cf71f5..8e62dac4d01 100644 --- a/packages/compiler/templates/__snapshots__/emitter-ts/test/test-host.ts +++ b/packages/compiler/templates/__snapshots__/emitter-ts/test/test-host.ts @@ -1,43 +1,16 @@ import { Diagnostic, resolvePath } from "@typespec/compiler"; -import { - createTestHost, - createTestWrapper, - expectDiagnosticEmpty, -} from "@typespec/compiler/testing"; -import { EmitterTsTestLibrary } from "../src/testing/index.js"; +import { expectDiagnosticEmpty } from "@typespec/compiler/testing"; +import { createTester } from "@typespec/compiler/testing"; -export async function createEmitterTsTestHost() { - return createTestHost({ - libraries: [EmitterTsTestLibrary], - }); -} - -export async function createEmitterTsTestRunner() { - const host = await createEmitterTsTestHost(); - - return createTestWrapper(host, { - compilerOptions: { - noEmit: false, - emit: ["emitter-ts"], - }, - }); -} +export const Tester = createTester(resolvePath(import.meta.dirname, "../.."), { + libraries: ["emitter-ts"], +}).emit("emitter-ts"); export async function emitWithDiagnostics( code: string ): Promise<[Record, readonly Diagnostic[]]> { - const runner = await createEmitterTsTestRunner(); - await runner.compileAndDiagnose(code, { - outputDir: "tsp-output", - }); - const emitterOutputDir = "./tsp-output/emitter-ts"; - const files = await runner.program.host.readDir(emitterOutputDir); - - const result: Record = {}; - for (const file of files) { - result[file] = (await runner.program.host.readFile(resolvePath(emitterOutputDir, file))).text; - } - return [result, runner.program.diagnostics]; + const [{ outputs }, diagnostics] = await Tester.compileAndDiagnose(code); + return [outputs, diagnostics]; } export async function emit(code: string): Promise> { diff --git a/packages/compiler/templates/__snapshots__/library-ts/eslint.config.js b/packages/compiler/templates/__snapshots__/library-ts/eslint.config.js index 75cb4f7ce3e..430df9c59ea 100644 --- a/packages/compiler/templates/__snapshots__/library-ts/eslint.config.js +++ b/packages/compiler/templates/__snapshots__/library-ts/eslint.config.js @@ -1,11 +1,20 @@ // @ts-check import eslint from "@eslint/js"; +import { defineConfig } from "eslint/config"; import tsEslint from "typescript-eslint"; -export default tsEslint.config( +export default defineConfig( { ignores: ["**/dist/**/*", "**/.temp/**/*"], }, eslint.configs.recommended, ...tsEslint.configs.recommended, + { + rules: { + "@typescript-eslint/no-unused-vars": [ + "warn", + { varsIgnorePattern: "^_", argsIgnorePattern: "^_" }, + ], + }, + }, ); diff --git a/packages/compiler/templates/__snapshots__/library-ts/package.json b/packages/compiler/templates/__snapshots__/library-ts/package.json index 408d5aac9ae..5e2377b221b 100644 --- a/packages/compiler/templates/__snapshots__/library-ts/package.json +++ b/packages/compiler/templates/__snapshots__/library-ts/package.json @@ -19,8 +19,7 @@ }, "devDependencies": { "@types/node": "latest", - "@typescript-eslint/eslint-plugin": "^8.15.0", - "@typescript-eslint/parser": "^8.15.0", + "typescript-eslint": "^8.49.0", "@typespec/compiler": "latest", "@typespec/library-linter": "latest", "eslint": "^9.15.0", diff --git a/packages/compiler/templates/__snapshots__/library-ts/test/decorators.test.ts b/packages/compiler/templates/__snapshots__/library-ts/test/decorators.test.ts index b089e56bcd8..51ee4d92418 100644 --- a/packages/compiler/templates/__snapshots__/library-ts/test/decorators.test.ts +++ b/packages/compiler/templates/__snapshots__/library-ts/test/decorators.test.ts @@ -1,29 +1,25 @@ import { strictEqual } from "node:assert"; -import { describe, it, beforeEach } from "node:test"; -import type { Operation } from "@typespec/compiler"; -import { BasicTestRunner, expectDiagnostics, extractCursor } from "@typespec/compiler/testing"; +import { describe, it } from "node:test"; +import type { Operation, Program } from "@typespec/compiler"; +import { expectDiagnostics } from "@typespec/compiler/testing"; import { getAlternateName } from "../src/decorators.js"; -import { createLibraryTsTestRunner } from "./test-host.js"; +import { Tester } from "./test-host.js"; describe("decorators", () => { - let runner: BasicTestRunner; - - beforeEach(async () => { - runner = await createLibraryTsTestRunner(); - }) - describe("@alternateName", () => { it("set alternate name on operation", async () => { - const { test } = (await runner.compile( - `@alternateName("bar") @test op test(): void;` - )) as { test: Operation }; - strictEqual(getAlternateName(runner.program, test), "bar"); + const { test, program } = (await Tester.compile(` + using LibraryTs; + @alternateName("bar") @test op test(): void; + `)) as unknown as { test: Operation; program: Program }; + strictEqual(getAlternateName(program, test), "bar"); }); it("emit diagnostic if not used on an operation", async () => { - const diagnostics = await runner.diagnose( - `@alternateName("bar") model Test {}` - ); + const diagnostics = await Tester.diagnose(` + using LibraryTs; + @alternateName("bar") model Test {} + `); expectDiagnostics(diagnostics, { severity: "error", code: "decorator-wrong-target", @@ -33,15 +29,14 @@ describe("decorators", () => { it("emit diagnostic if using banned name", async () => { - const {pos, source} = extractCursor(`@alternateName(┆"banned") op test(): void;`) - const diagnostics = await runner.diagnose( - source - ); + const diagnostics = await Tester.diagnose(` + using LibraryTs; + @alternateName("banned") op test(): void; + `); expectDiagnostics(diagnostics, { severity: "error", code: "library-ts/banned-alternate-name", - message: `Banned alternate name "banned".`, - pos: pos + runner.autoCodeOffset + message: `Banned alternate name "banned".` }) }); }); diff --git a/packages/compiler/templates/__snapshots__/library-ts/test/test-host.ts b/packages/compiler/templates/__snapshots__/library-ts/test/test-host.ts index e973984e84b..588a223c42b 100644 --- a/packages/compiler/templates/__snapshots__/library-ts/test/test-host.ts +++ b/packages/compiler/templates/__snapshots__/library-ts/test/test-host.ts @@ -1,17 +1,7 @@ -import { createTestHost, createTestWrapper } from "@typespec/compiler/testing"; -import { LibraryTsTestLibrary } from "../src/testing/index.js"; +import { resolvePath } from "@typespec/compiler"; +import { createTester } from "@typespec/compiler/testing"; -export async function createLibraryTsTestHost() { - return createTestHost({ - libraries: [LibraryTsTestLibrary], - }); -} - -export async function createLibraryTsTestRunner() { - const host = await createLibraryTsTestHost(); - - return createTestWrapper(host, { - autoUsings: ["LibraryTs"] - }); -} +export const Tester = createTester(resolvePath(import.meta.dirname, "../.."), { + libraries: ["library-ts"], +}).import("library-ts"); diff --git a/packages/compiler/templates/emitter-ts/eslint.config.js b/packages/compiler/templates/emitter-ts/eslint.config.js index 75cb4f7ce3e..430df9c59ea 100644 --- a/packages/compiler/templates/emitter-ts/eslint.config.js +++ b/packages/compiler/templates/emitter-ts/eslint.config.js @@ -1,11 +1,20 @@ // @ts-check import eslint from "@eslint/js"; +import { defineConfig } from "eslint/config"; import tsEslint from "typescript-eslint"; -export default tsEslint.config( +export default defineConfig( { ignores: ["**/dist/**/*", "**/.temp/**/*"], }, eslint.configs.recommended, ...tsEslint.configs.recommended, + { + rules: { + "@typescript-eslint/no-unused-vars": [ + "warn", + { varsIgnorePattern: "^_", argsIgnorePattern: "^_" }, + ], + }, + }, ); diff --git a/packages/compiler/templates/emitter-ts/package.json b/packages/compiler/templates/emitter-ts/package.json index 96ca29f0f73..1c9baaf7d2c 100644 --- a/packages/compiler/templates/emitter-ts/package.json +++ b/packages/compiler/templates/emitter-ts/package.json @@ -18,8 +18,7 @@ }, "devDependencies": { "@types/node": "latest", - "@typescript-eslint/eslint-plugin": "^8.15.0", - "@typescript-eslint/parser": "^8.15.0", + "typescript-eslint": "^8.49.0", "eslint": "^9.15.0", "@typespec/compiler": "latest", "typescript": "^5.3.3", diff --git a/packages/compiler/templates/emitter-ts/test/test-host.ts.mu b/packages/compiler/templates/emitter-ts/test/test-host.ts.mu index 5f1f869861a..950a699f0fa 100644 --- a/packages/compiler/templates/emitter-ts/test/test-host.ts.mu +++ b/packages/compiler/templates/emitter-ts/test/test-host.ts.mu @@ -1,43 +1,16 @@ import { Diagnostic, resolvePath } from "@typespec/compiler"; -import { - createTestHost, - createTestWrapper, - expectDiagnosticEmpty, -} from "@typespec/compiler/testing"; -import { {{#casing.pascalCase}}{{name}}{{/casing.pascalCase}}TestLibrary } from "../src/testing/index.js"; +import { expectDiagnosticEmpty } from "@typespec/compiler/testing"; +import { createTester } from "@typespec/compiler/testing"; -export async function create{{#casing.pascalCase}}{{name}}{{/casing.pascalCase}}TestHost() { - return createTestHost({ - libraries: [{{#casing.pascalCase}}{{name}}{{/casing.pascalCase}}TestLibrary], - }); -} - -export async function create{{#casing.pascalCase}}{{name}}{{/casing.pascalCase}}TestRunner() { - const host = await create{{#casing.pascalCase}}{{name}}{{/casing.pascalCase}}TestHost(); - - return createTestWrapper(host, { - compilerOptions: { - noEmit: false, - emit: ["{{name}}"], - }, - }); -} +export const Tester = createTester(resolvePath(import.meta.dirname, "../.."), { + libraries: ["{{name}}"], +}).emit("{{name}}"); export async function emitWithDiagnostics( code: string ): Promise<[Record, readonly Diagnostic[]]> { - const runner = await create{{#casing.pascalCase}}{{name}}{{/casing.pascalCase}}TestRunner(); - await runner.compileAndDiagnose(code, { - outputDir: "tsp-output", - }); - const emitterOutputDir = "./tsp-output/{{name}}"; - const files = await runner.program.host.readDir(emitterOutputDir); - - const result: Record = {}; - for (const file of files) { - result[file] = (await runner.program.host.readFile(resolvePath(emitterOutputDir, file))).text; - } - return [result, runner.program.diagnostics]; + const [{ outputs }, diagnostics] = await Tester.compileAndDiagnose(code); + return [outputs, diagnostics]; } export async function emit(code: string): Promise> { diff --git a/packages/compiler/templates/library-ts/eslint.config.js b/packages/compiler/templates/library-ts/eslint.config.js index 75cb4f7ce3e..430df9c59ea 100644 --- a/packages/compiler/templates/library-ts/eslint.config.js +++ b/packages/compiler/templates/library-ts/eslint.config.js @@ -1,11 +1,20 @@ // @ts-check import eslint from "@eslint/js"; +import { defineConfig } from "eslint/config"; import tsEslint from "typescript-eslint"; -export default tsEslint.config( +export default defineConfig( { ignores: ["**/dist/**/*", "**/.temp/**/*"], }, eslint.configs.recommended, ...tsEslint.configs.recommended, + { + rules: { + "@typescript-eslint/no-unused-vars": [ + "warn", + { varsIgnorePattern: "^_", argsIgnorePattern: "^_" }, + ], + }, + }, ); diff --git a/packages/compiler/templates/library-ts/package.json b/packages/compiler/templates/library-ts/package.json index 2a104d5c60b..456e579ed0a 100644 --- a/packages/compiler/templates/library-ts/package.json +++ b/packages/compiler/templates/library-ts/package.json @@ -19,8 +19,7 @@ }, "devDependencies": { "@types/node": "latest", - "@typescript-eslint/eslint-plugin": "^8.15.0", - "@typescript-eslint/parser": "^8.15.0", + "typescript-eslint": "^8.49.0", "@typespec/compiler": "latest", "@typespec/library-linter": "latest", "eslint": "^9.15.0", diff --git a/packages/compiler/templates/library-ts/test/decorators.test.ts.mu b/packages/compiler/templates/library-ts/test/decorators.test.ts.mu index 18b154793df..f79480f86f2 100644 --- a/packages/compiler/templates/library-ts/test/decorators.test.ts.mu +++ b/packages/compiler/templates/library-ts/test/decorators.test.ts.mu @@ -1,29 +1,25 @@ import { strictEqual } from "node:assert"; -import { describe, it, beforeEach } from "node:test"; -import type { Operation } from "@typespec/compiler"; -import { BasicTestRunner, expectDiagnostics, extractCursor } from "@typespec/compiler/testing"; +import { describe, it } from "node:test"; +import type { Operation, Program } from "@typespec/compiler"; +import { expectDiagnostics } from "@typespec/compiler/testing"; import { getAlternateName } from "../src/decorators.js"; -import { create{{#casing.pascalCase}}{{name}}{{/casing.pascalCase}}TestRunner } from "./test-host.js"; +import { Tester } from "./test-host.js"; describe("decorators", () => { - let runner: BasicTestRunner; - - beforeEach(async () => { - runner = await create{{#casing.pascalCase}}{{name}}{{/casing.pascalCase}}TestRunner(); - }) - describe("@alternateName", () => { it("set alternate name on operation", async () => { - const { test } = (await runner.compile( - `@alternateName("bar") @test op test(): void;` - )) as { test: Operation }; - strictEqual(getAlternateName(runner.program, test), "bar"); + const { test, program } = (await Tester.compile(` + using {{#casing.pascalCase}}{{name}}{{/casing.pascalCase}}; + @alternateName("bar") @test op test(): void; + `)) as unknown as { test: Operation; program: Program }; + strictEqual(getAlternateName(program, test), "bar"); }); it("emit diagnostic if not used on an operation", async () => { - const diagnostics = await runner.diagnose( - `@alternateName("bar") model Test {}` - ); + const diagnostics = await Tester.diagnose(` + using {{#casing.pascalCase}}{{name}}{{/casing.pascalCase}}; + @alternateName("bar") model Test {} + `); expectDiagnostics(diagnostics, { severity: "error", code: "decorator-wrong-target", @@ -33,15 +29,14 @@ describe("decorators", () => { it("emit diagnostic if using banned name", async () => { - const {pos, source} = extractCursor(`@alternateName(┆"banned") op test(): void;`) - const diagnostics = await runner.diagnose( - source - ); + const diagnostics = await Tester.diagnose(` + using {{#casing.pascalCase}}{{name}}{{/casing.pascalCase}}; + @alternateName("banned") op test(): void; + `); expectDiagnostics(diagnostics, { severity: "error", code: "{{name}}/banned-alternate-name", - message: `Banned alternate name "banned".`, - pos: pos + runner.autoCodeOffset + message: `Banned alternate name "banned".` }) }); }); diff --git a/packages/compiler/templates/library-ts/test/test-host.ts.mu b/packages/compiler/templates/library-ts/test/test-host.ts.mu index b75e5c1045d..e87ec478d83 100644 --- a/packages/compiler/templates/library-ts/test/test-host.ts.mu +++ b/packages/compiler/templates/library-ts/test/test-host.ts.mu @@ -1,17 +1,7 @@ -import { createTestHost, createTestWrapper } from "@typespec/compiler/testing"; -import { {{#casing.pascalCase}}{{name}}{{/casing.pascalCase}}TestLibrary } from "../src/testing/index.js"; +import { resolvePath } from "@typespec/compiler"; +import { createTester } from "@typespec/compiler/testing"; -export async function create{{#casing.pascalCase}}{{name}}{{/casing.pascalCase}}TestHost() { - return createTestHost({ - libraries: [{{#casing.pascalCase}}{{name}}{{/casing.pascalCase}}TestLibrary], - }); -} - -export async function create{{#casing.pascalCase}}{{name}}{{/casing.pascalCase}}TestRunner() { - const host = await create{{#casing.pascalCase}}{{name}}{{/casing.pascalCase}}TestHost(); - - return createTestWrapper(host, { - autoUsings: ["{{#casing.pascalCase}}{{name}}{{/casing.pascalCase}}"] - }); -} +export const Tester = createTester(resolvePath(import.meta.dirname, "../.."), { + libraries: ["{{name}}"], +}).import("{{name}}");