Skip to content
Draft
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
50 changes: 50 additions & 0 deletions packages/compiler/src/server/mock-process.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Buffer } from "node:buffer";
import { Readable as ReadableStream, Writable as WritableStream } from "node:stream";

export function mockProcess(path: string): NodeJS.Process {
return {
...process,
exit: () => {
throw new Error(`Importing ${path} tried to call process.exit`);
},
stdin: readableNoopStream() as any,
stdout: writableNoopStream() as any,
on: (() => {}) as any,
};
}

function readableNoopStream({ size = 0 } = {}) {
let producedSize = 0;

return new ReadableStream({
read(readSize) {
let shouldEnd = false;

if (producedSize + readSize >= size) {
readSize = size - producedSize;
shouldEnd = true;
}

setImmediate(() => {
if (size === 0) {
this.push(null);
}

producedSize += readSize;
this.push(Buffer.alloc(readSize));

if (shouldEnd) {
this.push(null);
}
});
},
});
}

function writableNoopStream() {
return new WritableStream({
write(chunk, encding, callback) {
setImmediate(callback);
},
});
}
18 changes: 2 additions & 16 deletions packages/compiler/src/server/npm-package-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,22 +202,8 @@ export class NpmPackage {
return undefined;
}
const entrypoint = module.type === "file" ? module.path : module.mainFile;
const oldExit = process.exit;
try {
// override process.exit to prevent the process from exiting because of it's called in loaded js file
let result: any;
process.exit = (() => {
// for module that calls process.exit when being imported, create an empty object as it's exports to avoid load it again
result = {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's better for loadJsFile to return a mock result or throw so that the caller can do something for the "bad" lib. otherwise, cache won't work for these "bad" lib and we will keep loading the "bad" lib every time unnecessary.

throw new Error(
"process.exit is called unexpectedly when loading js file: " + entrypoint,
);
}) as any;
const [file] = await loadJsFile(host, entrypoint, NoTarget);
return result ?? file?.esmExports;
} finally {
process.exit = oldExit;
}
const [file] = await loadJsFile(host, entrypoint, NoTarget);
return file?.esmExports;
} catch (e) {
return undefined;
}
Expand Down
15 changes: 14 additions & 1 deletion packages/compiler/src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import { NodeHost } from "../core/node-host.js";
import { typespecVersion } from "../manifest.js";
import { createClientConfigProvider } from "./client-config-provider.js";
import { mockProcess } from "./mock-process.js";
import { createServer } from "./serverlib.js";
import { CustomRequestName, Server, ServerHost, ServerLog } from "./types.js";

Expand Down Expand Up @@ -48,7 +49,19 @@ function main() {
console.error = (data: any, ...args: any[]) => connection.console.error(format(data, ...args));

const host: ServerHost = {
compilerHost: NodeHost,
compilerHost: {
...NodeHost,
getJsImport: (path: string) => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only doing this in the server right now but feels like this probably should just be in the NodeHost because right now the repro will let tsp compile stuck

const oldProcess = globalThis.process;
try {
globalThis.process = mockProcess(path) as any;
const result = NodeHost.getJsImport(path);
return result;
} finally {
globalThis.process.stdin = oldProcess.stdin;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrong revert

}
},
},
sendDiagnostics(params: PublishDiagnosticsParams) {
void connection.sendDiagnostics(params);
},
Expand Down
Loading