Skip to content

feat(nx-plugin): migrate to ESM (Nx 23 native strip-types)#853

Merged
cogwirrel merged 5 commits into
mainfrom
feat/nx-plugin-esm-migration
Jun 29, 2026
Merged

feat(nx-plugin): migrate to ESM (Nx 23 native strip-types)#853
cogwirrel merged 5 commits into
mainfrom
feat/nx-plugin-esm-migration

Conversation

@nx-plugin-for-aws

@nx-plugin-for-aws nx-plugin-for-aws commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Reason for this change

Nx 23 loads a workspace's local TypeScript generators via Node's native type stripping (require()/import() of .ts), treating them as ESM or CJS per the nearest package.json type, and only falls back to swc/ts-node if that load throws. Generated workspaces are "type": "module", but @aws/nx-plugin was published as CommonJS and forced vended ts#nx-plugin projects to module: commonjs, relying on the swc/ts-node fallback to keep __dirname-style generators working.

Migrating the plugin to ESM lets vended generators run under native strip-types directly (no fallback) and lets the plugin and vended templates use import.meta.

Closes #814.

Description of changes

@aws/nx-plugin is now ESM-only, built by the stock @nx/js:swc executor (no custom build scripts).

  • Flip the package to "type": "module". The compile target uses @nx/js:swc, which runs swc (JS), tsc (declarations) and asset/package.json copying itself. exports are ESM-only (types + default); engines.node is set to >=20.19.0.
  • Why no CommonJS build: Node unflagged require() of synchronous ESM in 20.19.0 / 22.12.0 (nodejs/node#55085), so CommonJS consumers can require('@aws/nx-plugin/...') against the ESM build directly — the SDK has no top-level await. Nx loads generators as ESM via native strip. Neither path needs a CJS build, so the dual-format output (and the cjs/ dir, type: commonjs marker, and custom finalize-package.mjs) is gone.
  • sdk/ and bin/ moved under src/ so the project has a single source root the @nx/js:swc executor compiles in one pass (it can't take multiple roots, and additionalEntryPoints is unsupported under the TS-solution setup). exports/bin now point at ./src/sdk/* and ./src/bin/*.
  • Replace __dirname/__filename with import.meta.dirname/import.meta.filename (~90 files). Add with { type: 'json' } to JSON imports, convert module-scope require(...) to static imports / createRequire(import.meta.url), fix CJS-interop (fast-glob default import), and make type-only imports/exports explicit (verbatimModuleSyntax on the lib build). swc's resolveFully adds .js extensions automatically; externalHelpers: false inlines helpers (no @swc/helpers runtime dep).

Vended generators (ts#nx-plugin / ts#nx-generator)

  • Stop forcing module: commonjs and drop the @swc-node/register + @swc/core workaround. Generated plugins are marked "type": "module".
  • Vended generator and mcp-server templates use import.meta.dirname, import type { X } from './schema.js' and .js-suffixed relative imports (NodeNext requires explicit extensions; Nx's resolver falls back .js -> .ts under native strip), and type-only @nx/devkit imports. The export * that ts#nx-generator adds to a local plugin's index.ts is .js-suffixed too.

Docs: updated the nx-generator guide and contribute-generator tutorial to the ESM idiom.

Description of how you validated changes

  • Updated/added unit tests for the generator changes; full @aws/nx-plugin suite passes (2395 tests), snapshots regenerated, lint clean.
  • End-to-end via a local verdaccio registry: published the ESM-only @aws/nx-plugin, installed it into a type: module Nx 23 workspace, generated ts#nx-plugin + ts#nx-generator, ran the vended .ts generator (loads under native strip-types with no ts-node/swc fallback), and built the generated plugin's package target (tsc/nodenext) successfully. Confirmed both import (ESM) and require() (CJS via require(esm)) of every SDK subpath (sdk/license, sdk/ts, sdk/py, ...) resolve and load, and that the published package contains no cjs/ directory.

Issue # (if applicable)

Closes #814.

Checklist


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

@github-actions

Copy link
Copy Markdown
Contributor

📚 Documentation translations have been updated and committed (6ba5429) to this PR.

docs/src/content/docs/es/get_started/tutorials/contribute-generator.mdx
docs/src/content/docs/es/guides/nx-generator.mdx
docs/src/content/docs/fr/get_started/tutorials/contribute-generator.mdx
docs/src/content/docs/fr/guides/nx-generator.mdx
docs/src/content/docs/it/get_started/tutorials/contribute-generator.mdx
docs/src/content/docs/it/guides/nx-generator.mdx
docs/src/content/docs/jp/get_started/tutorials/contribute-generator.mdx
docs/src/content/docs/jp/guides/nx-generator.mdx
docs/src/content/docs/ko/get_started/tutorials/contribute-generator.mdx
docs/src/content/docs/ko/guides/nx-generator.mdx
docs/src/content/docs/pt/get_started/tutorials/contribute-generator.mdx
docs/src/content/docs/pt/guides/nx-generator.mdx
docs/src/content/docs/vi/get_started/tutorials/contribute-generator.mdx
docs/src/content/docs/vi/guides/nx-generator.mdx
docs/src/content/docs/zh/get_started/tutorials/contribute-generator.mdx
docs/src/content/docs/zh/guides/nx-generator.mdx

@codecov-commenter

codecov-commenter commented Jun 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.03448% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.29%. Comparing base (3f2c8a0) to head (334edea).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
.../src/agentcore-gateway/mcp-connection/generator.ts 50.00% 0 Missing and 1 partial ⚠️
packages/nx-plugin/src/license/generator.ts 0.00% 1 Missing ⚠️
packages/nx-plugin/src/license/sync/generator.ts 0.00% 1 Missing ⚠️
...ackages/nx-plugin/src/mcp-server/generator-info.ts 50.00% 1 Missing ⚠️
packages/nx-plugin/src/py/agent/generator.ts 0.00% 1 Missing ⚠️
packages/nx-plugin/src/py/mcp-server/generator.ts 0.00% 1 Missing ⚠️
packages/nx-plugin/src/py/project/generator.ts 0.00% 1 Missing ⚠️
packages/nx-plugin/src/ts/lib/generator.ts 0.00% 1 Missing ⚠️
packages/nx-plugin/src/ts/mcp-server/generator.ts 0.00% 1 Missing ⚠️
packages/nx-plugin/src/utils/config/utils.ts 66.66% 1 Missing ⚠️
... and 1 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #853      +/-   ##
==========================================
+ Coverage   84.24%   84.29%   +0.05%     
==========================================
  Files         154      154              
  Lines        5458     5457       -1     
  Branches     1268     1267       -1     
==========================================
+ Hits         4598     4600       +2     
  Misses        533      533              
+ Partials      327      324       -3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 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.

@nx-plugin-for-aws nx-plugin-for-aws changed the title feat(nx-plugin): migrate to ESM with dual CJS build feat(nx-plugin): migrate to ESM (Nx 23 native strip-types) Jun 29, 2026
cogwirrel
cogwirrel previously approved these changes Jun 29, 2026
nx-plugin-for-aws and others added 4 commits June 29, 2026 06:44
Nx 23 loads workspace `.ts` generators via Node's native type stripping,
treating them as ESM/CJS per the nearest package.json `type`. Generated
workspaces are `type: module`, but `@aws/nx-plugin` was published as
CommonJS and forced vended plugins to `module: commonjs`, relying on the
swc/ts-node fallback. This migrates the plugin to ESM end to end.

@aws/nx-plugin:
- Flip the package to `"type": "module"` and emit a dual ESM+CJS build
  via swc (ESM -> `src`, CJS -> `cjs`) plus tsc for declarations. A
  conditional `exports` map routes `import` -> ESM and `require` -> CJS so
  SDK consumers can use either format. A minimal `cjs/package.json`
  (`type: commonjs`) marks the CommonJS output.
- Replace `__dirname`/`__filename` with `import.meta.dirname`/`filename`
  (swc rewrites these back to CJS globals for the CJS build; tsc cannot,
  which is why swc drives the JS emit).
- Add `with { type: 'json' }` to JSON imports, convert module-scope
  `require(...)` to static imports / `createRequire`, and fix CJS interop
  (`fast-glob` default import) and type-only imports/exports
  (`verbatimModuleSyntax`).

Vended generators (ts#nx-plugin / ts#nx-generator):
- Stop forcing `module: commonjs` and drop the `@swc-node/register`
  workaround. Generated plugins are marked `"type": "module"`.
- Vended generator templates use `import.meta.dirname`,
  `import type { X } from './schema.js'` (NodeNext `.js` specifier; Nx's
  resolver falls back `.js` -> `.ts`) and type-only `@nx/devkit` imports.

Verified end to end via a local verdaccio registry: the vended `.ts`
generator loads under native strip-types with no transpiler fallback, and
both `import` and `require` of every SDK subpath work.

Closes #814
…orts

The generated `ts#nx-plugin` project is `"type": "module"`, so its `package`
target compiles with `tsc` under `moduleResolution: nodenext`, which requires
explicit `.js` extensions on relative imports (TS2835). Add `.js` to the
vended mcp-server template imports and to the `export *` that `ts#nx-generator`
adds to the plugin's `index.ts`. Fixing the broken `../utils` import also
resolves the cascading TS7006 (implicit `any`) errors.

The targeted nx-plugin smoke test only *runs* the generator (native strip), so
it passed; the full package-manager smoke matrix runs `build --all`, which
builds the plugin's `package` target and surfaced these errors.
Drop the CommonJS build entirely and produce a single ESM build using the
standard `@nx/js:swc` executor instead of hand-rolled swc CLI commands and a
custom finalize-package.mjs.

Rationale:
- Node unflagged `require(esm)` for synchronous ESM in 20.19.0 / 22.12.0, so
  CommonJS consumers can `require('@aws/nx-plugin/...')` against the ESM build
  directly (the SDK has no top-level await). Nx 23 loads generators as ESM via
  native type stripping. Neither path needs a CJS build.
- The only reason the dual build used custom swc commands was converting
  `import.meta.dirname` -> `__dirname` for CJS output. With CJS gone, the stock
  executor (swc + tsc declarations + asset/package.json copy) does everything.

Changes:
- Move `sdk/` and `bin/` under `src/` so the project has a single source root
  the `@nx/js:swc` executor compiles in one pass (it can't take multiple roots,
  and `additionalEntryPoints` is unsupported under the TS-solution setup).
  `exports`/`bin` now point at `./src/sdk/*` / `./src/bin/*`; internal
  `../src/...` imports become `../...`.
- `package.json`: ESM-only `exports` (`types`+`default`, no `require`/`cjs`),
  add `engines.node >= 20.19.0`.
- Delete `.swcrc.cjs` and `scripts/finalize-package.mjs`; no more `cjs/` dir or
  `type: commonjs` marker.
- `ts#nx-generator` `addSdkExport` writes to `src/sdk/*` with `../<dir>` module
  specifiers.

Verified via verdaccio: ESM `import` and CJS `require()` of every SDK subpath
work; the vended `.ts` generator loads under native strip and its plugin
`package` target builds.
…plugin SDK

Add an e2e case that overwrites a generated generator to import
`tsProjectGenerator` from `@aws/nx-plugin/sdk/ts` and delegate to it, then
compiles the plugin (tsc/nodenext against the installed SDK types) and runs
the generator (Nx native strip-types loads it as ESM, it imports the published
SDK's ESM generator and scaffolds a project). Regresses if SDK ESM interop or
the vended generator's ESM shape breaks.
@cogwirrel
cogwirrel merged commit 00e0c94 into main Jun 29, 2026
32 checks passed
@cogwirrel
cogwirrel deleted the feat/nx-plugin-esm-migration branch June 29, 2026 09:17
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.

Migrate @aws/nx-plugin generators to ESM (Nx 23 native strip-types)

3 participants