[Feat] Improve deck.gl build speed and memory usage
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 14.6k
- Forks
- 2.3k
- Avg merge
- 2d 9h
- Merged PRs (30d)
- 42
Description
Target Use Case
Improve deck.gl's JavaScript/TypeScript build speed and memory profile for local development and CI, while preserving the current published package output.
The current full module build is slow enough to interrupt common development workflows. It also deletes TypeScript incremental state on every root build and compiles modules serially even where the dependency graph allows parallelism.
Current Build Shape
The root build script currently runs:
npm run clean && ocular-build && lerna run build
ocular-clean deletes every module dist directory and removes every tsconfig.tsbuildinfo file. ocular-build then walks modules in dependency order and runs roughly this in each package:
npx tspc --declaration --declarationMap --sourceMap --outDir dist --project tsconfig.json
After TypeScript emit, @vis.gl/dev-tools runs esbuild-based CJS conversion for package entry points. lerna run build then runs additional package scripts for packages such as @deck.gl/widgets, deck.gl, and @deck.gl/jupyter-widget.
The TypeScript build currently depends on ts-patch/tspc because deck.gl uses custom TypeScript transforms:
ts-transform-version-inlinets-transform-remove-glsl-commentsts-transform-inline-webgl-constantsts-transform-append-extension
Those transforms are part of the current output contract and are an important constraint for any compiler migration.
Local Baseline
Measured locally on May 27, 2026 with Node 24.10.0 and TypeScript 5.5.4:
| Command | Time |
|---|---|
npm run build |
32.0s |
cold per-module tspc pass only |
35.3s |
| esbuild CJS conversion for all modules | 1.9s |
Largest cold tspc slices:
| Module | Time |
|---|---|
@deck.gl/widgets |
5.2s |
@deck.gl/geo-layers |
3.1s |
@deck.gl/core |
3.0s |
@deck.gl/carto |
2.9s |
@deck.gl/aggregation-layers |
2.3s |
Extended TypeScript diagnostics on representative module checks showed each TypeScript process loading hundreds of files and using roughly 300MB of memory. The current serial build keeps peak memory bounded, but total CPU and memory churn is high.
Observed Problem: Warm Builds Are Not Reliable Today
The default root build deletes incremental state, so local builds never benefit from TypeScript incremental compilation.
Running ocular-build without a clean currently fails in modules/jupyter-widget because src/plugin.js imports ../dist/index, causing generated dist/*.d.ts files to become compiler inputs and then be overwritten.
Example failure:
error TS5055: Cannot write file 'modules/jupyter-widget/dist/index.d.ts' because it would overwrite input file.
This output/input boundary likely needs to be fixed before warm builds, affected builds, or task caching can be trusted.
Proposed Direction
I would like maintainer feedback on a staged modernization path instead of jumping straight to a single tool replacement.
-
Make warm builds valid
- Fix generated output being consumed as source input, starting with
modules/jupyter-widget. - Verify
ocular-buildcan run twice withoutocular-clean.
- Fix generated output being consumed as source input, starting with
-
Separate release and development builds
- Keep a clean release/publish build.
- Add a warm local command such as
build:dev = ocular-build && lerna run build. - Question: should
npm run buildremain clean-first, or should release workflows call a more explicitbuild:clean?
-
Add affected-module builds
- Read
modules/*/tsconfig.jsonreferences. - Build changed modules plus downstream dependents.
- Reuse existing
ocular-build module-a,module-bbehavior where possible.
- Read
-
Parallelize by dependency level
- Run independent modules in parallel after their dependencies complete.
- Keep a conservative worker limit because TypeScript processes use significant memory.
- Example levels from the current graph:
core
arcgis, extensions, google-maps, json, layers, mapbox, mesh-layers, test-utils, widgets
aggregation-layers, geo-layers, react
carto
jupyter-widget, main
-
Revisit TypeScript project build mode
- Prototype a root
tsconfig.build.jsonandtspc -b tsconfig.build.json. - Compare clean build time, warm build time, memory use, and emitted JS/declaration/source map diffs.
- If
tspc -bcannot preserve transformed output, a custom DAG runner around per-moduletspcmay still be useful.
- Prototype a root
-
Prepare for TypeScript 6
- TypeScript 6 is now latest on npm.
- Current config hits TS6 deprecation errors for
baseUrlandmoduleResolution: "node"/node10unless suppressed. - Treat TS6 as a compatibility migration, not the primary speed win.
-
Track TypeScript 7 /
tsgofor future checks- TypeScript 7 native compiler is promising for parallel project checking/building.
- Current config blocks
tsgobecause TS7 removes options still used by deck.gl. - Also, TS7 should not become the publishing compiler until custom transform and declaration-output needs are solved.
- A possible first step is an experimental
typecheck:tsgoonce config blockers are removed.
Modern Patterns Worth Evaluating
- TypeScript project references with valid warm incremental builds.
- Affected-package task selection based on the module dependency graph.
- Dependency-level parallel execution with a memory-aware worker limit.
- Optional local and CI task caching through Lerna/Nx or similar.
- TypeScript 7 native compiler checks once deck.gl's config is compatible.
- Keeping release-grade clean builds separate from fast local rebuilds.
Related History
- #6381 introduced the TypeScript monorepo setup and explicitly called out incremental builds as a benefit.
- #6802 enabled declaration file emit.
- #7546 switched standalone bundling from webpack to esbuild.
- #8040 cleaned TypeScript output before builds to avoid stale files being published.
- #8366 upgraded TypeScript from 4.x to 5.2.2 via ocular-devtools.
- #8494 clarified that TypeScript build output and static asset copying are separate responsibilities.
- #8531 and #8563 added/restored build transforms that affect generated output.
- #9832 showed that accidental
src/distimport boundaries can cause downstream TypeScript compile problems.
Maintainer Feedback Requested
- Should deck.gl keep
npm run buildas the clean release build and add a separate warm local build, or should the defaultbuildbecome warm? - Should affected builds live in deck.gl,
@vis.gl/dev-tools, or Lerna/Nx task graph configuration? - What worker count is safe for CI given TypeScript's memory use?
- Should the custom TypeScript transforms remain in the compiler path, or should we investigate moving them into post-processing to unblock future TypeScript 7 native builds?
- Should TypeScript 6 migration be handled before parallel/incremental build work, or can these proceed independently?
- How strict should output equivalence be for non-release local builds?
References
- TypeScript 6.0: https://devblogs.microsoft.com/typescript/announcing-typescript-6-0/
- TypeScript 7.0 beta: https://devblogs.microsoft.com/typescript/announcing-typescript-7-0-beta/
- TypeScript native preview: https://www.npmjs.com/package/@typescript/native-preview
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the root build scripts, ocular-clean, ocular-build, and modules/jupyter-widget/src/plugin.js, then inspect the modules/*/tsconfig.json references and existing ocular-build module selection. Reproduce the warm-build TS5055 failure and establish the maintainer-approved staged scope. Done should include a reliable warm-build path, measured clean and warm timings, memory comparisons, and confirmation that emitted output remains equivalent where required.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, node.js, typescript
- Domain
- build-system, developer-experience, performance
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100