Skip to content

feat: add automatic module extensions for bundless builds - #869

Merged
fireairforce merged 3 commits into
masterfrom
zoomdong/esm-output-options
Sep 7, 2026
Merged

fireairforce merged 3 commits into
masterfrom
zoomdong/esm-output-options

Conversation

@fireairforce

@fireairforce fireairforce commented Sep 7, 2026 •

Copy link
Copy Markdown
Member

Father's bundless builds normally emit .js and .d.ts, leaving consumers to account for package types and extensionless module references. Add explicit options to produce matching runtime and declaration filenames for native Node ESM and CommonJS consumers.

esm.autoExtension and cjs.autoExtension use the package type to select filenames:

Package type ESM CJS
module .js / .d.ts .cjs / .d.cts
commonjs or absent .mjs / .d.mts .js / .d.ts

The filename policy and autoExtension name follow Rslib. Father matches declaration extensions at the same time. No package.json is generated or modified. Copied package scopes and relocated overrides are respected, and both formats can share an output directory when their extensions differ.

export default {
  esm: { platform: 'node', autoExtension: true },
  cjs: { autoExtension: true },
};

redirect.js.extension and redirect.dts.extension control relative module references independently, using Rslib's naming. They follow autoExtension by default and can be explicitly overridden. Rewriting covers imports, re-exports, literal dynamic imports, CJS require / require.resolve, and declaration references. Source map filenames, URLs and mappings follow the resulting output.

esm.resolveDepSubpath remains independent and defaults to false, following the behavior of tsdown's deps.resolveDepSubpath: only resolvable subpaths of packages without exports are completed. Public exports and package roots retain their specifiers.

Compatibility: all new behavior is opt-in. Neither exports.import nor root type metadata enables it. Without new options, existing Father 4 output remains unchanged. Users enabling automatic extensions must update their published runtime and type entry points. Chinese and English documentation includes both package-type policies and a dual-format publishing example.

Related: react-component/father-plugin#27

Validation:

  • pnpm run build and pnpm exec tsc --noEmit
  • Full Jest suite with isolated workers: pnpm exec jest --maxWorkers=1 --workerIdleMemoryLimit=1MB — 17 suites, 183 tests and 1 snapshot passed
  • Babel, esbuild and SWC each tested with module, commonjs and absent package types; actual Node import/require and TypeScript NodeNext ESM/CJS consumers.
  • Option independence, copied declaration maps, relocated package scopes, cache hits and package-type changes, shared output directories, parallel builds, and watch updates/deletions. Watch tests use polling on Windows to avoid the Node 24 native fs-event assertion observed in the previous CI run.
  • Compared unchanged configurations against Father 4.6.36 (2ca945c): all 9 transformer/metadata combinations produced byte-identical JS, declarations, maps, CJS and copied package metadata.
  • The existing dev file-change test now waits for both ESM and UMD output contents, avoiding a fixed-delay race under CI load; all 5 dev tests passed locally.
  • Prettier and git diff --check.

@fireairforce fireairforce changed the title feat: add opt-in ESM output compatibility options feat: add automatic module extensions for bundless builds Sep 7, 2026
@fireairforce
fireairforce merged commit 9d760dc into master Sep 7, 2026
7 checks passed
@fireairforce
fireairforce deleted the zoomdong/esm-output-options branch September 7, 2026 06:39
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.

1 participant