feat(nx-plugin): migrate to ESM (Nx 23 native strip-types)#853
Merged
Conversation
Contributor
|
📚 Documentation translations have been updated and committed (6ba5429) to this PR. |
Codecov Report❌ Patch coverage is 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. 🚀 New features to boost your workflow:
|
cogwirrel
previously approved these changes
Jun 29, 2026
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.
nx-plugin-for-aws
force-pushed
the
feat/nx-plugin-esm-migration
branch
from
June 29, 2026 06:46
50c014d to
11479f2
Compare
…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
approved these changes
Jun 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 nearestpackage.jsontype, and only falls back to swc/ts-node if that load throws. Generated workspaces are"type": "module", but@aws/nx-pluginwas published as CommonJS and forced vendedts#nx-pluginprojects tomodule: 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-pluginis now ESM-only, built by the stock@nx/js:swcexecutor (no custom build scripts)."type": "module". Thecompiletarget uses@nx/js:swc, which runs swc (JS),tsc(declarations) and asset/package.jsoncopying itself.exportsare ESM-only (types+default);engines.nodeis set to>=20.19.0.require()of synchronous ESM in 20.19.0 / 22.12.0 (nodejs/node#55085), so CommonJS consumers canrequire('@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 thecjs/dir,type: commonjsmarker, and customfinalize-package.mjs) is gone.sdk/andbin/moved undersrc/so the project has a single source root the@nx/js:swcexecutor compiles in one pass (it can't take multiple roots, andadditionalEntryPointsis unsupported under the TS-solution setup).exports/binnow point at./src/sdk/*and./src/bin/*.__dirname/__filenamewithimport.meta.dirname/import.meta.filename(~90 files). Addwith { type: 'json' }to JSON imports, convert module-scoperequire(...)to static imports /createRequire(import.meta.url), fix CJS-interop (fast-globdefault import), and make type-only imports/exports explicit (verbatimModuleSyntaxon the lib build). swc'sresolveFullyadds.jsextensions automatically;externalHelpers: falseinlines helpers (no@swc/helpersruntime dep).Vended generators (
ts#nx-plugin/ts#nx-generator)module: commonjsand drop the@swc-node/register+@swc/coreworkaround. Generated plugins are marked"type": "module".import.meta.dirname,import type { X } from './schema.js'and.js-suffixed relative imports (NodeNext requires explicit extensions; Nx's resolver falls back.js->.tsunder native strip), and type-only@nx/devkitimports. Theexport *thatts#nx-generatoradds to a local plugin'sindex.tsis.js-suffixed too.Docs: updated the
nx-generatorguide andcontribute-generatortutorial to the ESM idiom.Description of how you validated changes
@aws/nx-pluginsuite passes (2395 tests), snapshots regenerated, lint clean.@aws/nx-plugin, installed it into atype: moduleNx 23 workspace, generatedts#nx-plugin+ts#nx-generator, ran the vended.tsgenerator (loads under native strip-types with no ts-node/swc fallback), and built the generated plugin'spackagetarget (tsc/nodenext) successfully. Confirmed bothimport(ESM) andrequire()(CJS viarequire(esm)) of every SDK subpath (sdk/license,sdk/ts,sdk/py, ...) resolve and load, and that the published package contains nocjs/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