From ffa3726af5e94f34469f6555063de23c86447bd8 Mon Sep 17 00:00:00 2001 From: burgerni10 Date: Mon, 28 Sep 2026 14:33:28 +0200 Subject: [PATCH] fix: treat private class methods as function boundaries in TLA detection `detectESMFeatures` did not stop at `ClassPrivateMethod` when climbing from an `await` / `for await`, so `await` inside `async #method()` was reported as top-level await. Combined with exports, the module was left untransformed, bytecode generation failed and the file was skipped from the executable (e.g. node-opcua >= 2.184). Use Babel's `isFunction()` alias, which covers every function-like node. Co-Authored-By: Claude Opus 5.5 --- lib/esm-transformer.ts | 16 ++-------------- test/unit/esm-transformer.test.ts | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/lib/esm-transformer.ts b/lib/esm-transformer.ts index fa331ad3..2939efa6 100644 --- a/lib/esm-transformer.ts +++ b/lib/esm-transformer.ts @@ -103,13 +103,7 @@ function detectESMFeatures( let isTopLevel = true; while (parent) { - if ( - parent.isFunctionDeclaration() || - parent.isFunctionExpression() || - parent.isArrowFunctionExpression() || - parent.isObjectMethod() || - parent.isClassMethod() - ) { + if (parent.isFunction()) { isTopLevel = false; break; } @@ -132,13 +126,7 @@ function detectESMFeatures( let isTopLevel = true; while (parent) { - if ( - parent.isFunctionDeclaration() || - parent.isFunctionExpression() || - parent.isArrowFunctionExpression() || - parent.isObjectMethod() || - parent.isClassMethod() - ) { + if (parent.isFunction()) { isTopLevel = false; break; } diff --git a/test/unit/esm-transformer.test.ts b/test/unit/esm-transformer.test.ts index 2325f916..46141b6b 100644 --- a/test/unit/esm-transformer.test.ts +++ b/test/unit/esm-transformer.test.ts @@ -128,6 +128,21 @@ describe('transformESMtoCJS', () => { assert.doesNotMatch(res.code, /async\s*\(\s*\)\s*=>/); }); + it('await inside a private class method does NOT trigger IIFE wrap', () => { + // `async #m()` is a ClassPrivateMethod, not a ClassMethod — the climber + // must treat it as a function boundary too, otherwise any module using + // private async methods is reported as TLA + exports and left untransformed. + const src = [ + 'export class C {', + ' async #m() { await 1; }', + ' async #n() { for await (const x of []) {} }', + '}', + ].join('\n'); + const res = transformESMtoCJS(src, 'private-method.mjs'); + assert.equal(res.isTransformed, true); + assert.doesNotMatch(res.code, /async\s*\(\s*\)\s*=>/); + }); + it('import.meta: esbuild emits shim, we inject the real implementation', () => { const res = transformESMtoCJS( 'export const here = import.meta.url;\n',