Skip to content

perf(appkit-ui): enable tree-shaking with sideEffects flag and modular echarts imports#442

Open
MarioCadenas wants to merge 2 commits into
mainfrom
perf/appkit-ui-tree-shaking
Open

perf(appkit-ui): enable tree-shaking with sideEffects flag and modular echarts imports#442
MarioCadenas wants to merge 2 commits into
mainfrom
perf/appkit-ui-tree-shaking

Conversation

@MarioCadenas

Copy link
Copy Markdown
Collaborator

Problem (P-1, High)

@databricks/appkit-ui had no sideEffects declaration, so webpack/esbuild/Rollup consumers had to assume every module re-exported by the /react barrel (src/react/index.ts) is side-effectful and keep all of them. Importing just Button shipped the entire chart stack — including the full echarts bundle (~350KB gz), @tanstack/react-table, and marked + dompurify — because charts/base.tsx imported echarts-for-react's default entry, which imports the monolithic echarts package.

Changes

"sideEffects": ["**/*.css"] in packages/appkit-ui/package.json

This tells bundlers that only CSS files have import side effects, so any JS module whose exports are unused can be dropped from the graph. Reasoning for the array (vs false):

  • No JS module in the package relies on import-time side effects from another module (no polyfills, no cross-module registration) — verified by survey. The new ECharts registration lives in the same module as BaseChart, so it always executes when any chart component is used.
  • The package ships dist/styles.css (exported as ./styles.css), which consumers import purely for its side effect. Declaring false would let bundlers prune that import; ["**/*.css"] keeps it safe.
  • The dist publish tool (tools/dist-appkit.ts) spreads the whole package.json, so the field survives into the published package.

Modular ECharts imports in charts/base.tsx

Switched from echarts-for-react (full echarts entry) to echarts-for-react/lib/core + echarts/core, registering exactly what the option builders in options.ts use:

  • Series: LineChart (line + area), BarChart (vertical + horizontal), PieChart (pie + donut), ScatterChart, HeatmapChart, RadarChart
  • Components: TitleComponent (title), TooltipComponent, LegendComponent, GridComponent (grid/xAxis/yAxis), VisualMapComponent (heatmap color scale)
  • Features: LegacyGridContainLabel — the cartesian option builder sets grid.containLabel: true, which in ECharts 6 is a separately registered feature (the full bundle auto-includes it; the mount tests caught its absence as a runtime warning)
  • Renderer: CanvasRenderer (BaseChart always renders with renderer: "canvas")

Nothing in options.ts/normalize.ts uses dataZoom, toolbox, markLine, dataset, or other components, so they are intentionally not registered. The ECharts instance type stays a type-only import type { ECharts } from "echarts" (erased at compile time). Consumers passing custom options that need extra features can register them in their own app via echarts/core's use() — documented in a comment at the registration site.

New mount tests (charts/__tests__/base.test.tsx)

Existing chart tests only covered pure option/normalize functions and never exercised ECharts rendering. ECharts does not throw on a missing registration — it logs "Series X is used but not imported" and renders a blank chart. The new suite mounts BaseChart for every chart family (line, area, bar, horizontal bar, scatter, pie, donut, radar, heatmap with visualMap) under jsdom with a canvas stub and fails on any missing-registration error/warning.

Verification

  • pnpm build — dist preserves module structure (unbundle mode); built dist/react/charts/base.js imports only echarts/core subpaths, no bare echarts value import anywhere in dist
  • pnpm vitest run --project appkit-ui — 17 files, 320 tests passed (including 9 new mount tests)
  • pnpm -r typecheck, pnpm check:fix, pnpm docs:build — clean

This pull request and its description were written by Isaac.

…r echarts imports

Importing any single component from the @databricks/appkit-ui/react barrel
(e.g. Button) previously forced bundlers to retain every re-exported module,
including the full echarts bundle (~350KB gz), because the package declared
no sideEffects field and charts/base.tsx imported the monolithic echarts
entry via echarts-for-react's default export.

- Declare "sideEffects": ["**/*.css"] so bundlers can drop unused modules
  while keeping the dist/styles.css side-effect import alive. No JS module
  in the package relies on import-time side effects from another module;
  the ECharts registration lives in the same module as BaseChart, so it is
  always retained when charts are used.
- Switch base.tsx to echarts-for-react/lib/core + echarts/core, registering
  only what the option builders use: LineChart, BarChart, PieChart,
  ScatterChart, HeatmapChart, RadarChart; Title/Tooltip/Legend/Grid/
  VisualMap components; LegacyGridContainLabel (grid.containLabel) and
  CanvasRenderer. Keep ECharts instance type as a type-only import.
- Add mount tests for every chart family that fail on ECharts
  missing-registration errors (which are logged, not thrown).

Co-authored-by: Isaac
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
@MarioCadenas MarioCadenas requested a review from a team as a code owner June 11, 2026 16:32
@MarioCadenas MarioCadenas requested a review from pkosiec June 11, 2026 16:32
…tions

- Document on the `options` prop that only the built-in feature set is
  registered; extra ECharts features (dataZoom, toolbox, markLine, ...)
  must be registered by the consumer via `use` from "echarts/core",
  which requires resolving the same echarts module instance/version.
- Note in the registration block that tooltip-driven axisPointer ships
  with TooltipComponent, and that `use()` must stay co-located with
  BaseChart because package.json#sideEffects declares JS modules pure.
- Fix the type-only import comment: the ECharts type is used for the
  stored instance ref.
- Add tests: custom `options` with a registered component logs no
  registration errors; empty data renders the "No data" fallback.

Co-authored-by: Isaac
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
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