Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: feature
packages:
- "@typespec/compiler"
---

Support importing .ts decorator modules from TypeSpec source files.
2 changes: 1 addition & 1 deletion packages/compiler/src/core/source-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export function createSourceFile(text: string, path: string): SourceFile {

export function getSourceFileKindFromExt(path: string): SourceFileKind | undefined {
const ext = getAnyExtensionFromPath(path);
if (ext === ".js" || ext === ".mjs") {
if (ext === ".js" || ext === ".mjs" || ext === ".ts") {
return "js";
} else if (ext === ".tsp") {
return "typespec";
Expand Down
23 changes: 23 additions & 0 deletions packages/compiler/test/checker/imports.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,29 @@ describe("compiler: imports", () => {
expectFileLoaded(program, { typespec: ["proj/main.tsp"], js: ["blue.js"] });
});

it("import relative TS file", async () => {
const { program } = await Tester.files({
"blue.ts": mockFile.js({ $blue() {} }),
}).compile(`
import "./blue.ts";
@blue
model A {}
`);
expectFileLoaded(program, { typespec: ["main.tsp"], js: ["blue.ts"] });
});

it("import relative TS file in parent folder", async () => {
const { program } = await Tester.files({
"blue.ts": mockFile.js({ $blue() {} }),
"proj/main.tsp": `
import "../blue.ts";
@blue
model A {}
`,
}).compile(`import "./proj/main.tsp";`);
expectFileLoaded(program, { typespec: ["proj/main.tsp"], js: ["blue.ts"] });
});

it("import directory with main.tsp", async () => {
const { program } = await Tester.files({
"test/main.tsp": `model C { }`,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export function $blue(context: unknown, target: unknown): void {
void context;
void target;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import "./decorators.ts";

@blue
model A {}
5 changes: 5 additions & 0 deletions packages/compiler/test/e2e/scenarios/scenarios.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@ describe("compiler: entrypoints", () => {
expectDiagnosticEmpty(program.diagnostics);
});

it("succeed with relative .ts module import", async () => {
const program = await compileScenario("import-relative-ts");
expectDiagnosticEmpty(program.diagnostics);
});

it("succeed if loading different install of the same library at the same version", async () => {
const program = await compileScenario("same-library-same-version", {
emit: ["@typespec/lib2"],
Expand Down
2 changes: 1 addition & 1 deletion website/src/content/docs/docs/language-basics/imports.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ llmstxt: true

Imports are used to include files or libraries into your TypeSpec program. When compiling a TypeSpec file, you specify the path to your root TypeSpec file, typically named "main.tsp". From this root file, any imported files are added to your program. If a directory is imported, TypeSpec will search for a `main.tsp` file within that directory.

The path specified in the import must either start with `"./"` or `"../"`, or be an absolute path. The path should either point to a directory, or have an extension of either ".tsp" or ".js". The examples below illustrate how to use imports to assemble a TypeSpec program from multiple files:
The path specified in the import must either start with `"./"` or `"../"`, or be an absolute path. The path should either point to a directory, or have an extension of either ".tsp", ".js", or ".ts". The examples below illustrate how to use imports to assemble a TypeSpec program from multiple files:

## Importing a TypeSpec file

Expand Down