From c22bfb307f5ac11a11f29a4ff2bd9dd7e4a3798f Mon Sep 17 00:00:00 2001 From: Daniel Kuschny Date: Sat, 12 Oct 2024 17:09:11 +0200 Subject: [PATCH] fix: Avoid ESBuild destroying Proxy in __toESM (#164) --- src/discoverer/evaluate.ts | 35 ++++++++++++++++++++++-- src/test/integration/typescript.test.ts | 2 +- test-workspaces/typescript/hello.test.ts | 4 +++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/discoverer/evaluate.ts b/src/discoverer/evaluate.ts index 71ebf55..d0c36c5 100644 --- a/src/discoverer/evaluate.ts +++ b/src/discoverer/evaluate.ts @@ -62,12 +62,32 @@ export class EvaluationTestDiscoverer implements ITestDiscoverer { function placeholder(): unknown { return new Proxy(placeholder, { get: (obj, target) => { - const desc = Object.getOwnPropertyDescriptor(obj, target); - if (desc && !desc.writable && !desc.configurable) { - return desc.value; // avoid invariant volation https://stackoverflow.com/q/75148897 + try { + const desc = Object.getOwnPropertyDescriptor(obj, target); + if (desc && !desc.writable && !desc.configurable) { + return desc.value; // avoid invariant volation https://stackoverflow.com/q/75148897 + } + return placeholder(); + } catch (e) { + return placeholder(); } + }, + set: () => true, + apply: () => { return placeholder(); }, + }); + } + + function objectPlaceholder(originalObject: any): unknown { + return new Proxy(objectPlaceholder, { + get: (_, target) => { + if (target === 'create') { + return placeholder(); + } else { + return originalObject[target]; + } + }, set: () => true, }); } @@ -186,6 +206,15 @@ export class EvaluationTestDiscoverer implements ITestDiscoverer { } else if (prop in target) { return target[prop]; // top-level `var` defined get set on the contextObj } else if (prop in globalThis && !replacedGlobals.has(prop as string)) { + // Bug #153: ESBuild will wrap require() calls into __toESM which breaks quite some things + // we want to keep our Proxy placeholder object in all scenarios + // Due to that we provide a special proxy object which will create again placeholder proxies + // on Object.create + // https://github.com/evanw/esbuild/blob/d34e79e2a998c21bb71d57b92b0017ca11756912/internal/runtime/runtime.go#L231-L242 + if (prop === 'Object') { + return objectPlaceholder((globalThis as any)[prop]); + } + return (globalThis as any)[prop]; } else { return placeholder(); diff --git a/src/test/integration/typescript.test.ts b/src/test/integration/typescript.test.ts index 9ecd3d3..fbac2f1 100644 --- a/src/test/integration/typescript.test.ts +++ b/src/test/integration/typescript.test.ts @@ -84,7 +84,7 @@ describe('typescript', () => { expect(failed.message?.location).to.not.be.undefined; expect(failed.message?.location?.uri.toString()).to.include('hello.test.ts'); expect(path.isAbsolute(failed.message!.location!.uri.fsPath)).to.be.true; - expect(failed.message?.location?.range.start.line).to.equal(25); + expect(failed.message?.location?.range.start.line).to.equal(29); expect(failed.message?.location?.range.start.character).to.equal(5); }); }); diff --git a/test-workspaces/typescript/hello.test.ts b/test-workspaces/typescript/hello.test.ts index 7036e95..ad5d3e5 100644 --- a/test-workspaces/typescript/hello.test.ts +++ b/test-workspaces/typescript/hello.test.ts @@ -1,4 +1,8 @@ import { strictEqual } from 'node:assert'; +import path, { join } from 'node:path'; + +path.join('', ''); +join('', ''); // just some typescript code which would be valid directly in Node