Skip to content

fix: treat private class methods as function boundaries in TLA detection - #307

Merged
robertsLando merged 1 commit into
yao-pkg:mainfrom
burgerni10:fix/esm-private-method-tla
Sep 29, 2026
Merged

robertsLando merged 1 commit into
yao-pkg:mainfrom
burgerni10:fix/esm-private-method-tla

Conversation

@burgerni10

Copy link
Copy Markdown

Problem

detectESMFeatures climbs up from each AwaitExpression / for await and stops at function boundaries. The check lists FunctionDeclaration, FunctionExpression, ArrowFunctionExpression, ObjectMethod and ClassMethod, but not ClassPrivateMethod. So await inside async #method() is reported as top-level await.

If the module also has export statements, the transformer logs "has both top-level await and export statements" and returns the source untransformed. Bytecode generation then fails and the file is skipped from the executable. The binary crashes at runtime, e.g. ERR_MODULE_NOT_FOUND from a sibling import.

Real-world trigger: node-opcua ≥ 2.184 (node-opcua-chunkmanager, node-opcua-server, node-opcua-pki, node-opcua-certificate-manager, …) uses private async methods, so any app bundling it gets a broken executable with pkg 6.22.0.

Fix

Replace the list of checks with parent.isFunction(). Babel's Function alias covers all function-like nodes, including ClassPrivateMethod, so no future function kind can slip through.

Tests

  • New unit test: await / for await inside private methods of an exported class. It fails before the change and passes after.
  • yarn test:unit (258 pass) and yarn lint are clean.
  • Checked on a real app (OIBus, node-opcua 2.186.1, node26-macos-arm64): the warnings are gone and the executable starts.

🤖 Generated with Claude Code

@burgerni10

Copy link
Copy Markdown
Author

I only noticed now that PR #300 fix it another way. Sorry for this duplicate, you can close this PR if you think the other fix is better

@codecov

codecov Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.47%. Comparing base (36f18af) to head (ffa3726).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #307      +/-   ##
==========================================
- Coverage   87.23%   86.47%   -0.77%     
==========================================
  Files          23       23              
  Lines        7929     7917      -12     
  Branches     1214     1213       -1     
==========================================
- Hits         6917     6846      -71     
- Misses       1005     1063      +58     
- Partials        7        8       +1     
Files with missing lines Coverage Δ
lib/esm-transformer.ts 88.38% <100.00%> (+0.37%) ⬆️

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@robertsLando
robertsLando requested a balanced review from Copilot September 28, 2026 14:52
Comment on lines +116 to +119
// node-opcua >= 2.185 ships ESM and relies on `import.meta.dirname`,
// which esbuild empties when emitting CJS
'--define:import.meta.dirname=__dirname',
'--define:import.meta.filename=__filename',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

not needed

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

tests on ubuntu-latest 22.x and 24.x were failing without it : https://github.com/yao-pkg/pkg/actions/runs/36423145365

I've removed it anyway since you pinned node-opcua to 2.181.0 in tests.

I directly edited my commit

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟢 Approval recommended

The generalized Babel function-boundary check correctly addresses the regression and has focused unit coverage.

Review effort: Balanced
Findings: None

What changed in this PR

Fixes false top-level-await detection for private class methods.

Changes:

  • Uses Babel’s function alias for all function boundaries.
  • Adds regression coverage for private async methods.
  • Defines import.meta paths in the node-opcua bundle test.
File Description
lib/​esm-transformer.ts Uses isFunction() during await detection.
test/​unit/​esm-transformer.test.ts Tests private methods containing await expressions.
test/​test-80-compression-node-opcua/​main.js Supplies CJS-compatible import.meta definitions.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@burgerni10
burgerni10 force-pushed the fix/esm-private-method-tla branch from f96c783 to e7491b8 Compare September 28, 2026 15:03
`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 <noreply@anthropic.com>
@burgerni10
burgerni10 force-pushed the fix/esm-private-method-tla branch from e7491b8 to ffa3726 Compare September 28, 2026 15:04
@robertsLando
robertsLando merged commit d3e91a9 into yao-pkg:main Sep 29, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants