Skip to content

migrate to tsdown - #517

Open
makrsmark wants to merge 3 commits into
airframesio:masterfrom
makrsmark:feature/tsdown
Open

migrate to tsdown#517
makrsmark wants to merge 3 commits into
airframesio:masterfrom
makrsmark:feature/tsdown

Conversation

@makrsmark

@makrsmark makrsmark commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • Chores
    • Updated the package to use modern ES module configuration.
    • Migrated the build process from tsup to tsdown while preserving existing output settings.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@makrsmark, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 44 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6f38872a-0887-46a1-9dba-17c362c5ea91

📥 Commits

Reviewing files that changed from the base of the PR and between af8646b and 0adf835.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (1)
  • package.json

Walkthrough

The package now uses ES module mode and migrates its build script, development dependency, and configuration provider from tsup to tsdown. The splitting: false option was removed. Other build settings remain unchanged.

Changes

Build Tool Migration

Layer / File(s) Summary
Configure tsdown build execution
package.json, tsdown.config.ts
The package declares ES module mode, runs tsdown, replaces the tsup dependency with tsdown, and updates the build configuration import. The splitting: false option was removed.

Estimated code review effort: 2 (Simple) | ~10 minutes

Poem

A rabbit checks the build at night,
“tsdown” makes the scripts align.
ES modules join the run,
Old tsup hops away from fun.
Clean config, ready to go—
Carrots await the green-light glow.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the migration from tsup to tsdown, which is the main change.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@package.json`:
- Line 59: Align the build workflow with the Node.js requirements of the tsdown
dependency declared in package.json. Keep Node.js 20 in the overall test matrix,
but skip npm run build for that runtime, or pin tsdown to a version supporting
Node.js 20; preserve builds for supported Node.js versions.
- Line 5: Complete the ESM migration before retaining "type": "module": rename
babel.config.js to babel.config.cjs or convert it to an ESM default export so
Jest can load it, then update package entrypoints to the tsdown-generated CJS
and ESM filenames instead of .mjs and define matching import/require conditional
exports.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f5f46fd8-be64-434c-ae68-db776764d810

📥 Commits

Reviewing files that changed from the base of the PR and between 90ad3b2 and af8646b.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • package.json
  • tsdown.config.ts

Comment thread package.json
"name": "@airframes/acars-decoder",
"version": "1.9.1",
"description": "ACARS Message Decoder for TypeScript/JavaScript",
"type": "module",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== package.json excerpt =="
if [ -f package.json ]; then
  cat -n package.json | sed -n '1,120p'
else
  echo "package.json not found"
fi

echo
echo "== babel config files =="
fd -a 'babel\.config\.(js|mjs|cjs)$' . || true

echo
echo "== package metadata type/main/module/exports/tests/scripts =="
python3 - <<'PY'
import json, pathlib
p = pathlib.Path("package.json")
if p.exists():
    data = json.loads(p.read_text())
    for key in ["type","main","module","exports","scripts","devDependencies"]:
        print(f"{key}: {data.get(key)!r}")
PY

echo
echo "== tsdown config files =="
fd -a 'tsdown\.config\.(js|mjs|ts|cjs)$' . || true

Repository: airframesio/acars-decoder-typescript

Length of output: 3525


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== babel.config.js =="
sed -n '1,220p' babel.config.js | cat -n

echo
echo "== tsdown.config.ts =="
sed -n '1,180p' tsdown.config.ts | cat -n

echo
echo "== lockfile tsdown/@babel/core versions if present =="
if [ -f package-lock.json ]; then
  python3 - <<'PY'
import json
from pathlib import Path
d=json.loads(Path("package-lock.json").read_text())
def print_pkg(name):
    root=d.get("packages",{}).get("",{}).get("dependencies",{}).get(name)
    deps=d.get("packages",{}).get(name)
    print(name, "root_version=", root.get("version"), "lock_version=", deps.get("version") if deps else None)
for name in ["tsdown","`@babel/core`","babel-jest","`@types/jest`","jest"]:
    print_pkg(name)
PY
fi
if [ -f package-lock.json ]; then
  rg -n '"tsdown"|\\n|"`@babel/core`"|\"babel-jest"|\"jest\"' package-lock.json | head -40
fi

Repository: airframesio/acars-decoder-typescript

Length of output: 879


🌐 Web query:

Babel config files babel.config.js Node ESM package.json type module module.exports

💡 Result:

When your package.json contains "type": "module", Node.js treats all.js files in that directory as ECMAScript Modules (ESM) [1][2]. Consequently, if you have a babel.config.js file in this environment, it must follow ESM syntax (using export default) rather than CommonJS syntax (using module.exports) [3]. If you attempt to use module.exports inside a babel.config.js file within an ESM package, you will likely encounter an error such as "ES Modules may not assign module.exports" [4]. To resolve or manage this configuration, you have several options: 1. Use ESM Syntax: Change your babel.config.js to use export default: export default { presets: [...], plugins: [...] }; Note that Babel config files are generally required to be synchronous [5]. If your configuration logic requires asynchronous operations, be aware that native ESM config files (.mjs or.js in an ESM package) may throw errors if Babel is called synchronously [5][6]. 2. Use.cjs Extension: Rename your file to babel.config.cjs. Files with the.cjs extension are always treated as CommonJS by Node.js, regardless of the "type": "module" setting in your package.json [1][2][5]. This allows you to safely continue using module.exports [5]. 3. Use.json Extension: If your configuration is static and does not require dynamic JavaScript logic, you can use babel.config.json [7][5]. This avoids the module format issue entirely [3]. In summary, Babel dynamically interprets the format of.js config files based on the nearest package.json "type" field [1][5]. For ESM projects, either use ESM syntax in your.js file or explicitly use the.cjs extension for CommonJS compatibility [1][5].

Citations:


🌐 Web query:

tsdown output format cjs esm file extension index.cjs index.js package.json module file

💡 Result:

In tsdown, output formats and file extensions are configurable to support modern (ESM) and legacy (CommonJS) environments [1][2]. Output Formats You can specify the output format using the format option in your configuration file or CLI [1][3]. For dual-format builds, pass an array: format: ['esm', 'cjs'] [2][4]. File Extensions and Defaults By default, tsdown uses the following extensions [5][6]: - ESM:.mjs - CJS:.cjs If "type": "module" is present in your package.json, tsdown defaults to.js for ESM output [5][4]. You can customize extensions using the outExtensions option [2][5]: outExtensions({ format }) { return { js: format === 'esm'? '.js': '.cjs', } } Package.json Exports tsdown can automatically generate the exports field in your package.json, which is recommended for modern projects to ensure proper module resolution [7][4]. You can enable this by setting exports: true in your configuration [7]. Example Configuration // tsdown.config.ts import { defineConfig } from 'tsdown'; export default defineConfig({ entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: true, exports: true, // Automatically generates package.json exports outExtensions({ format }) { return { js: format === 'esm'? '.js': '.cjs', }; }, }); This configuration will produce outputs such as index.js (ESM) and index.cjs (CJS), and automatically map them in your package.json under the exports field [7][4]. For legacy support, you can enable exports.legacy to generate the main, module, and types fields [7][4].

Citations:


🌐 Web query:

tsdown exports outExtensions babel.config.js package.json type module

💡 Result:

In tsdown, the configurations related to your query function as follows: Exports and Extensions The exports field in package.json can be automatically generated by setting exports: true in your tsdown.config.ts [1][2]. When enabled, tsdown infers your exports based on your build outputs [1][3]. For subpath exports, you can use the extensions option within the exports configuration to append a.js extension to keys (e.g., "./utils.js" instead of "./utils"), which follows Node.js recommendations [4][5][6]. Output Extensions To control output file naming, you can use the outExtensions option in your configuration [7]. This allows for custom extensions and overrides the default fixedExtension behavior, which typically defaults to.mjs or.cjs when the platform is set to node [7][2]. Package Type If your package.json specifies "type": "module", tsdown automatically detects this and will use the.js extension for ESM output instead of.mjs [2][8]. Babel Configuration tsdown does not use babel.config.js by default, but you can integrate Babel via plugins [9]. To use Babel, you can install @rolldown/plugin-babel and add it to the plugins array in your tsdown.config.ts [9][10]. This allows you to apply Babel transformations—such as the React Compiler—during the bundling process [9].

Citations:


Complete the ESM migration before enabling `"type": "module"$.

s scripts, jest also reads babel.config.js. With "type": "module", that file is parsed as ESM, but it still uses CommonJS module.exports, so npm test cannot load the Babel config. Rename it to babel.config.cjs or convert it to export default.

The tsdown config already has format: ['cjs', 'esm'], so package.json entrypoints should not use .mjs. Update main/module to the generated ESM/CJS files produced with "type": "module" and add conditional exports for import and require, or let tsdown generate them.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` at line 5, Complete the ESM migration before retaining "type":
"module": rename babel.config.js to babel.config.cjs or convert it to an ESM
default export so Jest can load it, then update package entrypoints to the
tsdown-generated CJS and ESM filenames instead of .mjs and define matching
import/require conditional exports.

Comment thread package.json
"ts-jest": "^29.2.6",
"ts-node": "^10.9.2",
"tsup": "^8.4.0",
"tsdown": "^0.22.14",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

npm view tsdown@0.22.14 engines --json
rg -n 'node-version:|npm run build' .github/workflows

Repository: airframesio/acars-decoder-typescript

Length of output: 880


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "package.json:"
cat -n package.json | sed -n '1,90p'

echo
echo "workflow files:"
fd -a '.*\.ya?ml$' .github/workflows -x sh -c 'echo "--- $1"; cat -n "$1"' sh {}

Repository: airframesio/acars-decoder-typescript

Length of output: 5244


Align the build job with tsdown 0.22.14.

tsdown@0.22.14 requires Node.js ^22.18.0 || >=24.11.0, but the test workflow runs npm run build on Node.js 20.x too. Keep the supported runtime matrix, but exclude Node.js 20 from the tsdown build step, or use a tsdown version compatible with Node.js 20.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` at line 59, Align the build workflow with the Node.js
requirements of the tsdown dependency declared in package.json. Keep Node.js 20
in the overall test matrix, but skip npm run build for that runtime, or pin
tsdown to a version supporting Node.js 20; preserve builds for supported Node.js
versions.

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