Skip to content

feat(typescript): add opt-in esmOnly flag for ESM-only SDK output - #17503

Merged
rishabh-fern merged 2 commits into
mainfrom
devin/1787321606-ts-sdk-esm-only
Sep 11, 2026
Merged

feat(typescript): add opt-in esmOnly flag for ESM-only SDK output#17503
rishabh-fern merged 2 commits into
mainfrom
devin/1787321606-ts-sdk-esm-only

Conversation

@thesandlord

@thesandlord thesandlord commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Description

Linear ticket: Refs (none — customer request, Auth0 Pylon #22973)

Adds an opt-in esmOnly custom config flag to the TypeScript SDK generator. When enabled, the generated package ships only the existing ESM build — decoupling it from the combined CJS package — to avoid the Dual Package Hazard. Nothing changes for the default (CJS + ESM) generation; esmOnly reuses the existing ESM pipeline (tsconfig.esm.json, rename-to-esm-files .mjs/.d.mts output) and simply drops the CJS half.

With esmOnly: true the generated package.json becomes:

{
    "type": "module",
    "main": "./dist/esm/index.mjs",
    "module": "./dist/esm/index.mjs",
    "types": "./dist/esm/index.d.mts",
    "exports": {
        ".": { "types": "./dist/esm/index.d.mts", "default": "./dist/esm/index.mjs" },
        "./package.json": "./package.json"
    },
    "scripts": { "build": "pnpm build:esm" }
}

tsconfig.cjs.json and build:cjs are not generated; root tsconfig.json extends tsconfig.esm.json. The bundled CJS rename script is emitted as scripts/rename-to-esm-files.cjs (instead of .js) because "type": "module" would otherwise make Node execute the CommonJS script as ESM.

Unsupported combinations fail fast with a clear error rather than silently ignoring the flag:

  • esmOnly + useLegacyExports
  • esmOnly + bundle

outputEsm build fix

This PR also fixes a pre-existing bug in outputEsm: true packages (reproduced against the Auth0 config): they are marked "type": "module", so build:esm's node scripts/rename-to-esm-files.js crashed with ReferenceError: require is not defined in ES module scope. The .cjs helper treatment now applies to any "type": "module" package (esmOnly || outputEsm):

// AsIsManager / SimpleTypescriptProject
esModulePackage: config.esmOnly || config.outputEsm
"scripts/rename-to-esm-files.js" -> esModulePackage ? ".cjs" : ".js"

outputEsm packages remain dual-published (CJS + ESM) — only the helper's extension changes (identical contents), so the compiled dist/esm output is unchanged (verified byte-identical on the Auth0 SDK: 1444 files, empty recursive diff between outputEsm and esmOnly builds).

Changes Made

  • Config plumbing: esmOnly added to TypescriptCustomConfigSchema, SdkCustomConfig, threaded through SdkGeneratorCliSdkGeneratorSimpleTypescriptProject / AsIsManager
  • SimpleTypescriptProject: ESM-only package.json (type, main/types, exports map without require/CJS conditions, incl. subpackage exports), skip tsconfig.cjs.json, root tsconfig extends ESM config, build runs only build:esm
  • AsIsManager / SimpleTypescriptProject: emit and invoke the rename script as .cjs for any "type": "module" package (esmOnly or outputEsm)
  • SdkGeneratorCli: throw on esmOnly + useLegacyExports and esmOnly + bundle
  • Seed: new simple-api/esm-only and simple-api/output-esm output variants (seed/ts-sdk/seed.yml) with generated fixtures
  • Changelogs: add-esm-only-flag.yml (feat) and fix-output-esm-rename-script.yml (fix) under generators/typescript/sdk/changes/unreleased/
  • Updated README.md generator (if applicable) — N/A

Testing

  • Unit tests added/updated — SimpleTypescriptProject.test.ts covering default (dual CJS+ESM, .js helper), outputEsm: true (still dual, .cjs helper), and esmOnly: true; all 33 tests in the package pass
  • Manual testing completed — generated the esm-only seed fixture, ran pnpm install && pnpm build (compiles, 126 files renamed to .mjs/.d.mts), verified Node import resolves root and /user subpath exports, and publint passes. Generated the new output-esm fixture and ran pnpm install && pnpm build: both build:cjs and build:esm now succeed (previously build:esm crashed on main). Verified default fixtures are byte-identical to before (no snapshot changes with the flags off). Seed's Docker validator image could not be pulled in this environment (network-restricted), so full seed validation should run in CI.

Written by Devin

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

SDK Generation Benchmark Results

Comparing PR branch against median of 5 nightly run(s) on main (latest: 2026-09-10T04:06:30Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
ts-sdk square 181s (n=5) 174s (n=5) 166s -15s (-8.3%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-09-10T04:06:30Z). Trigger benchmark-baseline to refresh.
Last updated: 2026-09-10 18:07 UTC

…m packages

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@rishabh-fern
rishabh-fern merged commit 6c6fa53 into main Sep 11, 2026
76 checks passed
@rishabh-fern
rishabh-fern deleted the devin/1787321606-ts-sdk-esm-only branch September 11, 2026 15:26
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