Skip to content

Ship one copy of the DuckDB native library instead of three - #16480

Closed
juliasilge wants to merge 2 commits into
mainfrom
fix/dedupe-duckdb-14265
Closed

juliasilge wants to merge 2 commits into
mainfrom
fix/dedupe-duckdb-14265

Conversation

@juliasilge

Copy link
Copy Markdown
Member

Important

Keep this in draft until after we branch for 2026.10.

Before this PR, Positron shipped three copies of the DuckDB native library, one in each extension that uses DuckDB: positron-duckdb (the Data Explorer for files), positron-data-driver-duckdb, and positron-data-driver-pins. Each copy is about 112 MB.

This PR moves @duckdb/node-api to extensions/package.json. The build already packages the production dependencies of that file into the shared extensions/node_modules folder, and this is how typescript ships today. Each extension loads DuckDB only from a forked worker in its own dist/ folder. Node module resolution searches the parent folders, so all three workers can find the one shared copy. There are no changes to runtime code.

Changes:

  • Add @duckdb/node-api 1.5.5-r.3 to extensions/package.json with an exact pin. Remove it from the three extensions and from the build-time library positron-data-explorer-duckdb. All five lockfiles change.
  • Remove positron-data-driver-duckdb from extensionsWithNpmDeps. This extension has no runtime dependencies now.
  • Update the file-count budgets in build/lib/positron-path-budget.ts. The shared node_modules folder increases from 129 to about 312 files. The three extensions decrease to 21, 7, and 50 files. These counts are below the default budget, so their entries are removed. The total budget decreases from 17,500 to 17,100.
  • Update the esbuild comments that tell where DuckDB comes from.

On Linux, @duckdb/node-bindings also needs detect-libc at runtime. The dependency walk of the build adds it to the shared folder automatically.

After you pull this change, run npm install as usual. npm removes the old copies of DuckDB from the extension folders.

Release Notes

New Features

  • N/A

Bug Fixes

Validation Steps

@:data-explorer @:duck-db @:connections @:win @:web

The E2E tests run from source so they can test the module resolution, but not the packaged layout. In release builds, checkPackagedTreeTask checks the file counts of the packaged layout.

I did these checks on a local darwin-arm64 release build:

  • The app has one libduckdb.dylib, at Contents/Resources/app/extensions/node_modules/@duckdb/node-bindings-darwin-arm64/. No extension has its own @duckdb folder.
  • The file counts agree with the new budgets.
  • A CSV file opens in the Data Explorer with summary statistics. The preview of a DuckDB connection works. The preview of a pin works.

Note

The first CSV file that I opened after the build hit the 10-second timeout for column profiles one time. The problem did not occur again after I reopened the file or restarted the app. I would be very interested in what other folks see when they build this!

To test a release build:

  1. Open a CSV or Parquet file in the Data Explorer. Make sure that the summary panel shows statistics.
  2. In the Connections pane, connect to a DuckDB database. Preview a table.
  3. In the Connections pane, preview a pin.
  4. Make sure that the app has only one copy of the DuckDB library:
find Positron.app -name 'libduckdb*'

@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown

E2E Tests 🚀
This PR will run tests tagged with: @:critical @:data-explorer @:duck-db @:connections @:win @:web @:connect

Why these tags?
Tag Source
@:critical Always runs (required)
@:data-explorer PR description
@:duck-db PR description
@:connections PR description
@:win PR description
@:web PR description
@:connect Changed files

More on automatic tags from changed files.

readme  valid tags

@juliasilge

Copy link
Copy Markdown
Member Author

@juliasilge

Copy link
Copy Markdown
Member Author

Ah, this will not work after all! This approach moves @duckdb/node-api into extensions/package.json, and it turns out that conflicts with how we install dependencies. I'll open a new PR with a different approach.

Turns out there are two problems:

  1. The Excel extension check fails. The postinstall script of positron-duckdb (scripts/install-excel-extension.ts) reads node_modules/@duckdb/node-api/package.json in the extension folder. This PR removes that copy. In CI, the script stops the build (failed Linux build). On a dev machine, the script only shows a warning, and the install continues without .xlsx support.
  2. The macOS x64 build breaks. We build Intel Macs on arm64 runners with npm_config_arch=x64. Since Fix DuckDB on Intel Macs: Copy npm_config_arch => npm_config_cpu #15086, build/npm/postinstall.ts installs the target-arch optional dependencies only for folders that list @duckdb/node-api directly. With this PR, that folder is extensions/. This folder also has esbuild, which compiles all the extensions. With --cpu=x64, npm installs only the x64 esbuild binary. Node on the build machine runs as arm64, so esbuild stops with an error. The Linux and Windows builds do not cross-compile, so they do not have this problem.

For the next PR, I'll try removing the duplicate copies at packaging time, not at install time. Each extension keeps its own @duckdb/node-api dependency, with the same exact version in all of them. Thus the dev setup, the Excel check, and the fix for Intel Macs stay the same as on main. The build will leave @duckdb out of each extension and put one copy into the shared extensions/node_modules folder. The shipped layout would be the same as in this PR, so the size decrease of about 224 MB stays the same.

@juliasilge juliasilge closed this Oct 7, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 7, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Duplicate copies of DuckDB add > 100mb to installed app size

1 participant