Commit 282824e
feat(javascript): add JS runtime + render harness + Chart.js, D3.js, ECharts (Phase 1) (#8244)
Closes #8241.
Adds **JavaScript** as anyplot's fourth language and ships the three
framework-agnostic Phase-1 libraries: **Chart.js**, **D3.js**, **Apache
ECharts**. This is the foundation of the JS rollout — the Highcharts
migration (#8242) and the MUI X React path (#8243) build on it. Matches
the one-pass `feat(makie)` (#7613) discipline.
## The genuinely new piece: anyplot's first browser-rendered runtime
R (`Rscript`) and Julia (`julia`) are CLI runtimes that write a PNG
directly. JS charting libraries render in a browser DOM — there is no
headless CLI that produces a PNG. So this introduces a **Node 22 +
Playwright headless-Chromium render harness**. The authored artifact is
an **idiomatic snippet** (`new Chart(...)`, `d3.select(...)`,
`echarts.init(...)`) drawing into `#container`; the harness wraps it in
HTML, loads the pinned bundle, awaits a render-complete signal,
screenshots the mount at **exact pixels**, and emits **both** the
gallery PNG and the self-contained interactive HTML — the same "HTML is
the deliverable, PNG is derived" model anyplot already uses for its
Python interactive libraries.
## Validated end-to-end (real Playwright + Chromium)
All three libraries, both themes, landscape + square:
- Exact **3200×1800** / **2400×2400** (verified straight from the PNG
IHDR)
- First categorical series renders in brand green `#009E73`
- Theme-adaptive chrome (paper `#FAF8F1` / ink `#1A1A17` backgrounds,
soft grid)
- Snippet errors fail the render with a non-zero exit (no silent blank
PNGs)
## What changed
**Registry (`core/constants.py`)**
- `SUPPORTED_LANGUAGES += javascript`; `LANGUAGES_METADATA` entry (Node
22).
- `SUPPORTED_LIBRARIES += chartjs, d3, echarts` (alpha-sorted); the
three in `INTERACTIVE_LIBRARIES`.
- **`framework`** field (`none|react|vue|svelte|angular`, default
`none`) on every library — lets one `javascript` entry later cover React
libs (#8243) without duplicating the registry (§6). Backed by a new
`libraries.framework` column + alembic migration.
- **Per-library `file_extension` override** mechanism +
`language_file_extensions()`, honored by `sync_to_postgres` so a future
`.tsx` React entry is a one-line change. Phase-1 libs are all `.js`.
*(This is the one real schema wrinkle JS adds — called out explicitly.)*
- DB seeds now project metadata dicts onto model columns, so
metadata-only keys never break the bulk insert.
**Runtime + harness**
- `package.json` + committed `package-lock.json` (chart.js, d3, echarts
+ playwright, esbuild) — the JS sibling of `Project.toml` / the Posit
snapshot.
- `.github/actions/setup-node` composite action: `npm ci` → Playwright
Chromium → render smoke test asserting exact 3200×1800.
- `automation/js-render/render.mjs` — the shared harness (theme tokens,
per-library bundle, `//# anyplot-orientation:` directive,
render-complete wait, exact-pixel screenshot, self-contained HTML).
**Workflows** — `javascript` arm in the `(LANGUAGE, EXT)` case of
`impl-generate` / `impl-repair` / `impl-review` / `impl-merge`;
`setup-node` step in generate + repair; Node + npm version extraction;
per-library canvas hints + a `//` header-rewrite branch (preserving
`//#` directives) in review; libs added to generate/bulk-generate
choices + `ALL_LIBRARIES`.
**Prompts** — new `prompts/library/{chartjs,d3,echarts}.md` (mount-node
contract, `ANYPLOT_THEME` tokens, canvas hard rule, no-workarounds,
forbidden patterns); JS render contract added to the generate + repair
workflow prompts.
**Frontend** — `javascript` in `LIBRARIES` / `LIB_ABBREV` /
`LIB_TO_LANG` / `LANG_DISPLAY` / `LANG_EXT`; Prism `javascript` grammar
registered; ext/runner ternaries extended in `PlotOfTheDay(+Terminal)`.
**Tests** — constants tests updated for 14 libraries / 4 languages, the
`framework` field, and the extension-override helpers.
## Test results
- `tests/unit` Python: constants (43), sync + workflows (337), DB
repositories with the new `framework` column (37) — all pass.
*(Pre-existing GCS-font image tests fail only for lack of GCS creds in
the sandbox; unrelated.)*
- Frontend: `tsc` clean, ESLint clean (4 pre-existing warnings), **524
vitest tests pass**.
- All edited/new YAML parses; alembic resolves to a single head
(`f7a2c9d4e8b1`).
## Notes / decisions
- **Plotly stays Python**, no `plotly.js` entry (most-used-variant rule,
§6). Same for `plotly (R)` / `highcharter (R)`.
- JS & TS are one language; snippets authored in plain JS.
React/Vue/etc. are a `framework` flag, not a language.
- `esbuild` is pinned and the harness's `framework=react` branch is
scaffolded but **not exercised** until #8243.
- Versions in metadata (`chart.js` 4.4, `d3` 7.9, `echarts` 5.5; Node
22) should be reconfirmed on the first CI run, per the issue.
https://claude.ai/code/session_019awsxNrPnEZStvLfwarTzQ
---
_Generated by [Claude
Code](https://claude.ai/code/session_019awsxNrPnEZStvLfwarTzQ)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 851b2f9 commit 282824e
26 files changed
Lines changed: 2488 additions & 49 deletions
File tree
- .github
- actions/setup-node
- workflows
- alembic/versions
- app/src
- components
- constants
- types
- automation
- js-render
- scripts
- core
- database
- docs/concepts
- prompts
- library
- workflow-prompts
- tests/unit
- automation/scripts
- core
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
33 | 36 | | |
34 | 37 | | |
35 | 38 | | |
| |||
54 | 57 | | |
55 | 58 | | |
56 | 59 | | |
57 | | - | |
| 60 | + | |
58 | 61 | | |
59 | 62 | | |
60 | 63 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
34 | 37 | | |
35 | 38 | | |
36 | 39 | | |
| |||
124 | 127 | | |
125 | 128 | | |
126 | 129 | | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
127 | 134 | | |
128 | 135 | | |
129 | 136 | | |
| |||
255 | 262 | | |
256 | 263 | | |
257 | 264 | | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
258 | 269 | | |
259 | 270 | | |
260 | 271 | | |
| |||
526 | 537 | | |
527 | 538 | | |
528 | 539 | | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
529 | 544 | | |
530 | 545 | | |
531 | 546 | | |
| |||
558 | 573 | | |
559 | 574 | | |
560 | 575 | | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
561 | 584 | | |
562 | 585 | | |
563 | 586 | | |
| |||
572 | 595 | | |
573 | 596 | | |
574 | 597 | | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
575 | 609 | | |
576 | 610 | | |
577 | 611 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
113 | 113 | | |
114 | 114 | | |
115 | 115 | | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
116 | 120 | | |
117 | 121 | | |
118 | 122 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
79 | 79 | | |
80 | 80 | | |
81 | 81 | | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
82 | 86 | | |
83 | 87 | | |
84 | 88 | | |
| |||
127 | 131 | | |
128 | 132 | | |
129 | 133 | | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
130 | 138 | | |
131 | 139 | | |
132 | 140 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
109 | 109 | | |
110 | 110 | | |
111 | 111 | | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
112 | 116 | | |
113 | 117 | | |
114 | 118 | | |
| |||
268 | 272 | | |
269 | 273 | | |
270 | 274 | | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
271 | 278 | | |
272 | 279 | | |
273 | 280 | | |
| |||
645 | 652 | | |
646 | 653 | | |
647 | 654 | | |
648 | | - | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
649 | 658 | | |
650 | 659 | | |
651 | 660 | | |
| |||
694 | 703 | | |
695 | 704 | | |
696 | 705 | | |
| 706 | + | |
| 707 | + | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
697 | 729 | | |
698 | 730 | | |
699 | 731 | | |
| |||
Lines changed: 35 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| 5 | + | |
5 | 6 | | |
6 | 7 | | |
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
| 11 | + | |
10 | 12 | | |
11 | 13 | | |
12 | 14 | | |
13 | 15 | | |
14 | 16 | | |
15 | 17 | | |
16 | 18 | | |
| 19 | + | |
17 | 20 | | |
18 | 21 | | |
19 | 22 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
103 | 103 | | |
104 | 104 | | |
105 | 105 | | |
106 | | - | |
107 | | - | |
108 | | - | |
109 | | - | |
110 | | - | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
111 | 111 | | |
112 | 112 | | |
113 | 113 | | |
| |||
265 | 265 | | |
266 | 266 | | |
267 | 267 | | |
268 | | - | |
| 268 | + | |
269 | 269 | | |
270 | 270 | | |
271 | 271 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
| 48 | + | |
48 | 49 | | |
49 | | - | |
50 | | - | |
| 50 | + | |
| 51 | + | |
51 | 52 | | |
52 | 53 | | |
53 | 54 | | |
| |||
0 commit comments