Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions packages/typescript/src/api/async/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,19 +317,19 @@ export class API<FromLSP extends boolean = false> implements FormatDiagnosticsHo
return this.client.apiRequest("transpileModule", { input, options });
}

async transpileModuleFromFile(fileName: string, options: TranspileOptions = {}): Promise<TranspileOutput> {
async transpileModuleFromFile(file: DocumentIdentifier, options: TranspileOptions = {}): Promise<TranspileOutput> {
await this.ensureInitialized();
return this.client.apiRequest("transpileModuleFromFile", { fileName, options });
return this.client.apiRequest("transpileModuleFromFile", { fileName: resolveFileName(file), options });
}

async transpileDeclaration(input: string, options: TranspileOptions = {}): Promise<TranspileOutput> {
await this.ensureInitialized();
return this.client.apiRequest("transpileDeclaration", { input, options });
}

async transpileDeclarationFromFile(fileName: string, options: TranspileOptions = {}): Promise<TranspileOutput> {
async transpileDeclarationFromFile(file: DocumentIdentifier, options: TranspileOptions = {}): Promise<TranspileOutput> {
await this.ensureInitialized();
return this.client.apiRequest("transpileDeclarationFromFile", { fileName, options });
return this.client.apiRequest("transpileDeclarationFromFile", { fileName: resolveFileName(file), options });
}

async updateSnapshot(params?: FromLSP extends true ? LSPUpdateSnapshotParams : UpdateSnapshotParams): Promise<Snapshot> {
Expand Down Expand Up @@ -1086,8 +1086,8 @@ export class Program implements FormatDiagnosticsHost {
* is not part of the program. Metadata is fetched lazily per file and cached on this
* `Program` instance.
*/
getSourceFileMetadata(fileName: string): Promise<SourceFileMetadata | undefined> {
return this.getSourceFileMetadataByPath(this.toPath(fileName));
getSourceFileMetadata(file: DocumentIdentifier): Promise<SourceFileMetadata | undefined> {
return this.getSourceFileMetadataByPath(this.toPath(resolveFileName(file)));
}

/**
Expand Down
6 changes: 6 additions & 0 deletions packages/typescript/src/api/proto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ export function resolveFileName(identifier: DocumentIdentifier): string {
if (typeof identifier === "string") {
return identifier;
}
if (typeof identifier !== "object" || identifier === null || typeof identifier.uri !== "string") {
const received = typeof identifier === "object" && identifier !== null
? `an object with keys: ${Object.keys(identifier).join(", ")}`
: String(identifier);
throw new TypeError(`Expected a string or { uri } for the document, received ${received}`);
}
return documentURIToFileName(identifier.uri);
}

Expand Down
36 changes: 18 additions & 18 deletions packages/typescript/src/api/sync/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -451,20 +451,20 @@ export class API<FromLSP extends boolean = false> implements FormatDiagnosticsHo
}

get transpileModuleFromFile(): {
(fileName: string, options?: TranspileOptions): TranspileOutput;
gen(fileName: string, options?: TranspileOptions): Generator<ProtocolRequest, TranspileOutput, ProtocolResponse["result"]>;
(file: DocumentIdentifier, options?: TranspileOptions): TranspileOutput;
gen(file: DocumentIdentifier, options?: TranspileOptions): Generator<ProtocolRequest, TranspileOutput, ProtocolResponse["result"]>;
} {
const owner = this;
return cacheGeneratorMethod(
owner,
"transpileModuleFromFile",
function (fileName: string, options: TranspileOptions = {}): TranspileOutput {
function (file: DocumentIdentifier, options: TranspileOptions = {}): TranspileOutput {
owner.ensureInitialized();
return owner.client.apiRequest("transpileModuleFromFile", { fileName, options });
return owner.client.apiRequest("transpileModuleFromFile", { fileName: resolveFileName(file), options });
},
function* (fileName: string, options: TranspileOptions = {}): Generator<ProtocolRequest, TranspileOutput, ProtocolResponse["result"]> {
function* (file: DocumentIdentifier, options: TranspileOptions = {}): Generator<ProtocolRequest, TranspileOutput, ProtocolResponse["result"]> {
yield* owner.ensureInitialized.gen();
return yield* apiRequest("transpileModuleFromFile", { fileName, options });
return yield* apiRequest("transpileModuleFromFile", { fileName: resolveFileName(file), options });
},
);
}
Expand All @@ -489,20 +489,20 @@ export class API<FromLSP extends boolean = false> implements FormatDiagnosticsHo
}

get transpileDeclarationFromFile(): {
(fileName: string, options?: TranspileOptions): TranspileOutput;
gen(fileName: string, options?: TranspileOptions): Generator<ProtocolRequest, TranspileOutput, ProtocolResponse["result"]>;
(file: DocumentIdentifier, options?: TranspileOptions): TranspileOutput;
gen(file: DocumentIdentifier, options?: TranspileOptions): Generator<ProtocolRequest, TranspileOutput, ProtocolResponse["result"]>;
} {
const owner = this;
return cacheGeneratorMethod(
owner,
"transpileDeclarationFromFile",
function (fileName: string, options: TranspileOptions = {}): TranspileOutput {
function (file: DocumentIdentifier, options: TranspileOptions = {}): TranspileOutput {
owner.ensureInitialized();
return owner.client.apiRequest("transpileDeclarationFromFile", { fileName, options });
return owner.client.apiRequest("transpileDeclarationFromFile", { fileName: resolveFileName(file), options });
},
function* (fileName: string, options: TranspileOptions = {}): Generator<ProtocolRequest, TranspileOutput, ProtocolResponse["result"]> {
function* (file: DocumentIdentifier, options: TranspileOptions = {}): Generator<ProtocolRequest, TranspileOutput, ProtocolResponse["result"]> {
yield* owner.ensureInitialized.gen();
return yield* apiRequest("transpileDeclarationFromFile", { fileName, options });
return yield* apiRequest("transpileDeclarationFromFile", { fileName: resolveFileName(file), options });
},
);
}
Expand Down Expand Up @@ -2008,18 +2008,18 @@ export class Program implements FormatDiagnosticsHost {
* `Program` instance.
*/
get getSourceFileMetadata(): {
(fileName: string): SourceFileMetadata | undefined;
gen(fileName: string): Generator<ProtocolRequest, SourceFileMetadata | undefined, ProtocolResponse["result"]>;
(file: DocumentIdentifier): SourceFileMetadata | undefined;
gen(file: DocumentIdentifier): Generator<ProtocolRequest, SourceFileMetadata | undefined, ProtocolResponse["result"]>;
} {
const owner = this;
return cacheGeneratorMethod(
owner,
"getSourceFileMetadata",
function (fileName: string): SourceFileMetadata | undefined {
return owner.getSourceFileMetadataByPath(owner.toPath(fileName));
function (file: DocumentIdentifier): SourceFileMetadata | undefined {
return owner.getSourceFileMetadataByPath(owner.toPath(resolveFileName(file)));
},
function* (fileName: string): Generator<ProtocolRequest, SourceFileMetadata | undefined, ProtocolResponse["result"]> {
return yield* owner.getSourceFileMetadataByPath.gen(owner.toPath(fileName));
function* (file: DocumentIdentifier): Generator<ProtocolRequest, SourceFileMetadata | undefined, ProtocolResponse["result"]> {
return yield* owner.getSourceFileMetadataByPath.gen(owner.toPath(resolveFileName(file)));
},
);
}
Expand Down
27 changes: 24 additions & 3 deletions packages/typescript/test/async/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import {
CheckFlags,
type ConditionalType,
DiagnosticCategory,
type DocumentIdentifier,
EmitOnly,
type FreshableType,
type ImportAdderAction,
Expand Down Expand Up @@ -287,7 +288,7 @@ describe("API", () => {
});
assert.equal(isolatedDeclarationModuleOutput.diagnostics?.length ?? 0, 0);

const moduleFileOutput = await api.transpileModuleFromFile("/input.ts", {
const moduleFileOutput = await api.transpileModuleFromFile({ uri: "file:///input.ts" }, {
compilerOptions: { module: ModuleKind.CommonJS },
});
assert.match(moduleFileOutput.outputText, /exports\.x = 1/);
Expand All @@ -300,7 +301,7 @@ describe("API", () => {
});
assert.equal(windowsDeclarationOutput.outputText, "export declare const x: number;\n");

const declarationFileOutput = await api.transpileDeclarationFromFile("/input.ts");
const declarationFileOutput = await api.transpileDeclarationFromFile({ uri: "file:///input.ts" });
assert.equal(declarationFileOutput.outputText, "export declare const x: number;\n");
}
finally {
Expand Down Expand Up @@ -1087,6 +1088,26 @@ describe("Checker - getMemberInModuleExports", () => {
});

describe("SourceFile", () => {
test("getSourceFile rejects invalid document identifiers", async () => {
const api = spawnAPI();
try {
const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" });
const program = snapshot.getProject("/tsconfig.json")!.program;
const document = { fileName: "/src/index.ts" } as unknown as DocumentIdentifier;

await assert.rejects( // @sync: assert.throws(
() => program.getSourceFile(document),
{
name: "TypeError",
message: "Expected a string or { uri } for the document, received an object with keys: fileName",
},
);
}
finally {
await api.close();
}
});

test("getSourceFileNames returns all program files, not just root files", async () => {
const api = spawnAPI({
"/tsconfig.json": JSON.stringify({
Expand Down Expand Up @@ -1173,7 +1194,7 @@ describe("SourceFile", () => {

const mts = await program.getSourceFile("/src/esm.mts");
assert.ok(mts);
assert.equal((await program.getSourceFileMetadata(mts.fileName))?.impliedNodeFormat, ModuleKind.ESNext);
assert.equal((await program.getSourceFileMetadata({ uri: "file:///src/esm.mts" }))?.impliedNodeFormat, ModuleKind.ESNext);
assert.equal((await program.getSourceFileMetadataByPath(mts.path))?.impliedNodeFormat, ModuleKind.ESNext);

const cts = await program.getSourceFile("/src/cjs.cts");
Expand Down
27 changes: 24 additions & 3 deletions packages/typescript/test/sync/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import {
CheckFlags,
type ConditionalType,
DiagnosticCategory,
type DocumentIdentifier,
EmitOnly,
type FreshableType,
type ImportAdderAction,
Expand Down Expand Up @@ -295,7 +296,7 @@ describe("API", () => {
});
assert.equal(isolatedDeclarationModuleOutput.diagnostics?.length ?? 0, 0);

const moduleFileOutput = api.transpileModuleFromFile("/input.ts", {
const moduleFileOutput = api.transpileModuleFromFile({ uri: "file:///input.ts" }, {
compilerOptions: { module: ModuleKind.CommonJS },
});
assert.match(moduleFileOutput.outputText, /exports\.x = 1/);
Expand All @@ -308,7 +309,7 @@ describe("API", () => {
});
assert.equal(windowsDeclarationOutput.outputText, "export declare const x: number;\n");

const declarationFileOutput = api.transpileDeclarationFromFile("/input.ts");
const declarationFileOutput = api.transpileDeclarationFromFile({ uri: "file:///input.ts" });
assert.equal(declarationFileOutput.outputText, "export declare const x: number;\n");
}
finally {
Expand Down Expand Up @@ -1003,6 +1004,26 @@ describe("Checker - getMemberInModuleExports", () => {
});

describe("SourceFile", () => {
test("getSourceFile rejects invalid document identifiers", () => {
const api = spawnAPI();
try {
const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" });
const program = snapshot.getProject("/tsconfig.json")!.program;
const document = { fileName: "/src/index.ts" } as unknown as DocumentIdentifier;

assert.throws(
() => program.getSourceFile(document),
{
name: "TypeError",
message: "Expected a string or { uri } for the document, received an object with keys: fileName",
},
);
}
finally {
api.close();
}
});

test("getSourceFileNames returns all program files, not just root files", () => {
const api = spawnAPI({
"/tsconfig.json": JSON.stringify({
Expand Down Expand Up @@ -1089,7 +1110,7 @@ describe("SourceFile", () => {

const mts = program.getSourceFile("/src/esm.mts");
assert.ok(mts);
assert.equal((program.getSourceFileMetadata(mts.fileName))?.impliedNodeFormat, ModuleKind.ESNext);
assert.equal((program.getSourceFileMetadata({ uri: "file:///src/esm.mts" }))?.impliedNodeFormat, ModuleKind.ESNext);
assert.equal((program.getSourceFileMetadataByPath(mts.path))?.impliedNodeFormat, ModuleKind.ESNext);

const cts = program.getSourceFile("/src/cjs.cts");
Expand Down