From 506b3730fca82953b0c24b49dfed7887459b7220 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 8 Oct 2026 20:00:35 -0400 Subject: [PATCH 1/6] programs-react, pointers-react: support React 19 and shiki 3 --- packages/pointers-react/CHANGELOG.md | 5 +++++ packages/pointers-react/package.json | 4 ++-- packages/programs-react/CHANGELOG.md | 7 +++++++ packages/programs-react/package.json | 10 +++++----- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/pointers-react/CHANGELOG.md b/packages/pointers-react/CHANGELOG.md index 7802445533..a250cb0c03 100644 --- a/packages/pointers-react/CHANGELOG.md +++ b/packages/pointers-react/CHANGELOG.md @@ -7,6 +7,11 @@ root [`CHANGELOG.md`](../../CHANGELOG.md). ## Unreleased +### Changed + +- The `react` and `react-dom` peer ranges now include React 19 + (`^18.0.0 || ^19.0.0`). The package needs no code change for it. + ## 0.1.0-preview.2 — 2026-10-07 ### Breaking diff --git a/packages/pointers-react/package.json b/packages/pointers-react/package.json index dced6a7806..e971d13787 100644 --- a/packages/pointers-react/package.json +++ b/packages/pointers-react/package.json @@ -58,8 +58,8 @@ "vitest": "^3.2.4" }, "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" }, "publishConfig": { "access": "public" diff --git a/packages/programs-react/CHANGELOG.md b/packages/programs-react/CHANGELOG.md index 428bb61ab8..aefa6ccd69 100644 --- a/packages/programs-react/CHANGELOG.md +++ b/packages/programs-react/CHANGELOG.md @@ -7,6 +7,13 @@ root [`CHANGELOG.md`](../../CHANGELOG.md). ## Unreleased +### Changed + +- The `react` and `react-dom` peer ranges now include React 19 + (`^18.0.0 || ^19.0.0`), and the `shiki`, `@shikijs/langs` and + `@shikijs/themes` dependency ranges now include version 3 + (`^2.5.0 || ^3.0.0`). The package needs no code change for either. + ## 0.1.0-preview.2 — 2026-10-07 ### Changed diff --git a/packages/programs-react/package.json b/packages/programs-react/package.json index 36783d861b..61c0cfb630 100644 --- a/packages/programs-react/package.json +++ b/packages/programs-react/package.json @@ -52,9 +52,9 @@ "dependencies": { "@ethdebug/format": "^0.1.0-draft.2", "@ethdebug/pointers": "^0.1.0-preview.2", - "@shikijs/langs": "^2.5.0", - "@shikijs/themes": "^2.5.0", - "shiki": "^2.5.0" + "@shikijs/langs": "^2.5.0 || ^3.0.0", + "@shikijs/themes": "^2.5.0 || ^3.0.0", + "shiki": "^2.5.0 || ^3.0.0" }, "devDependencies": { "@testing-library/dom": "^10.0.0", @@ -70,8 +70,8 @@ "vitest": "^3.2.4" }, "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" }, "publishConfig": { "access": "public" From 61f8505415068b4509f622447b55f86f2489b5d6 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 8 Oct 2026 20:00:44 -0400 Subject: [PATCH 2/6] programs-react, pointers-react: update lockfile --- yarn.lock | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 2 deletions(-) diff --git a/yarn.lock b/yarn.lock index dd56e307cd..f28a56b09f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4830,6 +4830,16 @@ "@types/hast" "^3.0.4" hast-util-to-html "^9.0.4" +"@shikijs/core@3.23.0": + version "3.23.0" + resolved "https://registry.yarnpkg.com/@shikijs/core/-/core-3.23.0.tgz#79248ec4ad3de4fd5c12993f5c30cb071ec04812" + integrity sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA== + dependencies: + "@shikijs/types" "3.23.0" + "@shikijs/vscode-textmate" "^10.0.2" + "@types/hast" "^3.0.4" + hast-util-to-html "^9.0.5" + "@shikijs/engine-javascript@2.5.0": version "2.5.0" resolved "https://registry.yarnpkg.com/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz#e045c6ecfbda6c99137547b0a482e0b87f1053fc" @@ -4839,6 +4849,15 @@ "@shikijs/vscode-textmate" "^10.0.2" oniguruma-to-es "^3.1.0" +"@shikijs/engine-javascript@3.23.0": + version "3.23.0" + resolved "https://registry.yarnpkg.com/@shikijs/engine-javascript/-/engine-javascript-3.23.0.tgz#eae89a47913f486e5a05130d13b965c424c33b21" + integrity sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA== + dependencies: + "@shikijs/types" "3.23.0" + "@shikijs/vscode-textmate" "^10.0.2" + oniguruma-to-es "^4.3.4" + "@shikijs/engine-oniguruma@2.5.0": version "2.5.0" resolved "https://registry.yarnpkg.com/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz#230de5693cc1da6c9d59c7ad83593c2027274817" @@ -4847,13 +4866,28 @@ "@shikijs/types" "2.5.0" "@shikijs/vscode-textmate" "^10.0.2" -"@shikijs/langs@2.5.0", "@shikijs/langs@^2.5.0": +"@shikijs/engine-oniguruma@3.23.0": + version "3.23.0" + resolved "https://registry.yarnpkg.com/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz#789421048d66ac1b33613169d6d18b9cc6e340ed" + integrity sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g== + dependencies: + "@shikijs/types" "3.23.0" + "@shikijs/vscode-textmate" "^10.0.2" + +"@shikijs/langs@2.5.0": version "2.5.0" resolved "https://registry.yarnpkg.com/@shikijs/langs/-/langs-2.5.0.tgz#97ab50c495922cc1ca06e192985b28dc73de5d50" integrity sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w== dependencies: "@shikijs/types" "2.5.0" +"@shikijs/langs@3.23.0", "@shikijs/langs@^2.5.0 || ^3.0.0": + version "3.23.0" + resolved "https://registry.yarnpkg.com/@shikijs/langs/-/langs-3.23.0.tgz#00959d8b16c7f671221ae79b3ad8cde7e6a5c112" + integrity sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg== + dependencies: + "@shikijs/types" "3.23.0" + "@shikijs/themes@2.5.0", "@shikijs/themes@^2.5.0": version "2.5.0" resolved "https://registry.yarnpkg.com/@shikijs/themes/-/themes-2.5.0.tgz#8c6aecf73f5455681c8bec15797cf678162896cb" @@ -4861,6 +4895,13 @@ dependencies: "@shikijs/types" "2.5.0" +"@shikijs/themes@3.23.0", "@shikijs/themes@^2.5.0 || ^3.0.0": + version "3.23.0" + resolved "https://registry.yarnpkg.com/@shikijs/themes/-/themes-3.23.0.tgz#fd96ca5ad52639057995bc2093682884e1846f27" + integrity sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA== + dependencies: + "@shikijs/types" "3.23.0" + "@shikijs/types@2.5.0": version "2.5.0" resolved "https://registry.yarnpkg.com/@shikijs/types/-/types-2.5.0.tgz#e949c7384802703a48b9d6425dd41673c164df69" @@ -4869,6 +4910,14 @@ "@shikijs/vscode-textmate" "^10.0.2" "@types/hast" "^3.0.4" +"@shikijs/types@3.23.0": + version "3.23.0" + resolved "https://registry.yarnpkg.com/@shikijs/types/-/types-3.23.0.tgz#d441571a058641926018ae3de99866f39e5bbdf2" + integrity sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ== + dependencies: + "@shikijs/vscode-textmate" "^10.0.2" + "@types/hast" "^3.0.4" + "@shikijs/vscode-textmate@^10.0.2": version "10.0.2" resolved "https://registry.yarnpkg.com/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz#a90ab31d0cc1dfb54c66a69e515bf624fa7b2224" @@ -10276,6 +10325,23 @@ hast-util-to-html@^9.0.4: stringify-entities "^4.0.0" zwitch "^2.0.4" +hast-util-to-html@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz#ccc673a55bb8e85775b08ac28380f72d47167005" + integrity sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw== + dependencies: + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + ccount "^2.0.0" + comma-separated-tokens "^2.0.0" + hast-util-whitespace "^3.0.0" + html-void-elements "^3.0.0" + mdast-util-to-hast "^13.0.0" + property-information "^7.0.0" + space-separated-tokens "^2.0.0" + stringify-entities "^4.0.0" + zwitch "^2.0.4" + hast-util-to-jsx-runtime@^2.0.0: version "2.3.0" resolved "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.0.tgz" @@ -13436,6 +13502,11 @@ onetime@^7.0.0: dependencies: mimic-function "^5.0.0" +oniguruma-parser@^0.12.2: + version "0.12.2" + resolved "https://registry.yarnpkg.com/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz#e27ca446f7fcf0969662a3ab9b4f43176d62b139" + integrity sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw== + oniguruma-to-es@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz#480e4bac4d3bc9439ac0d2124f0725e7a0d76d17" @@ -13445,6 +13516,15 @@ oniguruma-to-es@^3.1.0: regex "^6.0.1" regex-recursion "^6.0.2" +oniguruma-to-es@^4.3.4: + version "4.3.6" + resolved "https://registry.yarnpkg.com/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz#43e640280241b0d687a314e7a641d476407a1c4d" + integrity sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA== + dependencies: + oniguruma-parser "^0.12.2" + regex "^6.1.0" + regex-recursion "^6.0.2" + open@^10.0.3: version "10.2.0" resolved "https://registry.yarnpkg.com/open/-/open-10.2.0.tgz#b9d855be007620e80b6fb05fac98141fe62db73c" @@ -14728,6 +14808,11 @@ property-information@^6.0.0: resolved "https://registry.npmjs.org/property-information/-/property-information-6.4.0.tgz" integrity sha512-9t5qARVofg2xQqKtytzt+lZ4d1Qvj8t5B8fEwXK6qOfgRLgH/b13QlgEyDh033NOS31nXeFbYv7CLUDG1CeifQ== +property-information@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-7.2.0.tgz#0809b34264e995c0bfcd3227028a1e35210af80a" + integrity sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg== + proto-list@~1.2.1: version "1.2.4" resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz" @@ -15151,7 +15236,7 @@ regex-utilities@^2.3.0: resolved "https://registry.yarnpkg.com/regex-utilities/-/regex-utilities-2.3.0.tgz#87163512a15dce2908cf079c8960d5158ff43280" integrity sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng== -regex@^6.0.1: +regex@^6.0.1, regex@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/regex/-/regex-6.1.0.tgz#d7ce98f8ee32da7497c13f6601fca2bc4a6a7803" integrity sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg== @@ -15805,6 +15890,20 @@ shiki@^2.5.0: "@shikijs/vscode-textmate" "^10.0.2" "@types/hast" "^3.0.4" +"shiki@^2.5.0 || ^3.0.0": + version "3.23.0" + resolved "https://registry.yarnpkg.com/shiki/-/shiki-3.23.0.tgz#fca5332195e3afd6c94b384103ae9671a29c7fb9" + integrity sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA== + dependencies: + "@shikijs/core" "3.23.0" + "@shikijs/engine-javascript" "3.23.0" + "@shikijs/engine-oniguruma" "3.23.0" + "@shikijs/langs" "3.23.0" + "@shikijs/themes" "3.23.0" + "@shikijs/types" "3.23.0" + "@shikijs/vscode-textmate" "^10.0.2" + "@types/hast" "^3.0.4" + side-channel-list@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" From 0ff16b30b6d78ecfc7561ed13694628cc4c80fd9 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 8 Oct 2026 20:31:05 -0400 Subject: [PATCH 3/6] programs-react: load shiki languages and themes through shiki --- .github/workflows/ci.yml | 20 +++++ bin/check-react-consumers.ts | 81 +++++++++++++++++++ package.json | 1 + packages/bugc-react/CHANGELOG.md | 7 ++ packages/pointers-react/CHANGELOG.md | 5 +- packages/programs-react/CHANGELOG.md | 12 ++- packages/programs-react/package.json | 2 - .../src/shiki/useHighlighter.ts | 9 +-- yarn.lock | 4 +- 9 files changed, 128 insertions(+), 13 deletions(-) create mode 100644 bin/check-react-consumers.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41325bdf23..7690db0b9d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,6 +73,26 @@ jobs: run: yarn build working-directory: ./packages/web + check-react-consumers: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: yarn + cache-dependency-path: yarn.lock + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Build all packages + run: yarn build + + - name: Install into React 18 and React 19 consumers + run: yarn check:react-consumers + build-playground: runs-on: ubuntu-latest steps: diff --git a/bin/check-react-consumers.ts b/bin/check-react-consumers.ts new file mode 100644 index 0000000000..412faf9a70 --- /dev/null +++ b/bin/check-react-consumers.ts @@ -0,0 +1,81 @@ +import { execFileSync } from "node:child_process"; +import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +// Packs the React packages and installs them, with plain `npm install`, +// into small consumers on each supported React and shiki pair. Each consumer +// type-checks with skipLibCheck off, so a broken .d.ts fails. +const packed = [ + "format", + "pointers", + "evm", + "programs-react", + "pointers-react", +]; +const pairs = [ + { react: "18", types: "18", shiki: "2.5" }, + { react: "19", types: "19", shiki: "3" }, +]; + +const run = (cmd: string, args: string[], cwd: string) => + execFileSync(cmd, args, { cwd, stdio: "inherit" }); + +const root = mkdtempSync(join(tmpdir(), "react-consumers-")); +const tarballs = packed.map((name) => { + const out = execFileSync("npm", ["pack", "--pack-destination", root], { + cwd: join("packages", name), + encoding: "utf8", + }); + return join(root, out.trim().split("\n").pop()!); +}); + +for (const { react, types, shiki } of pairs) { + const dir = join(root, `react-${react}`); + mkdirSync(dir); + writeFileSync( + join(dir, "package.json"), + JSON.stringify({ name: "consumer", private: true, type: "module" }), + ); + writeFileSync( + join(dir, "tsconfig.json"), + JSON.stringify({ + compilerOptions: { + module: "nodenext", + moduleResolution: "nodenext", + target: "esnext", + jsx: "react-jsx", + strict: true, + types: ["node"], + skipLibCheck: false, + noEmit: true, + }, + include: ["index.tsx"], + }), + ); + writeFileSync( + join(dir, "index.tsx"), + `import { ShikiCodeBlock } from "@ethdebug/programs-react"; +import * as Pointers from "@ethdebug/pointers-react"; +export const a = ; +export const b = Pointers; +`, + ); + run( + "npm", + [ + "install", + `react@${react}`, + `react-dom@${react}`, + `@types/react@${types}`, + `@types/react-dom@${types}`, + `shiki@${shiki}`, + "typescript", + "@types/node@20", + ...tarballs, + ], + dir, + ); + run("node", [join(dir, "node_modules/typescript/bin/tsc")], dir); + console.log(`ok: React ${react} + shiki ${shiki}`); +} diff --git a/package.json b/package.json index 2bda9ce6cf..5b1f8ed5b1 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ ], "scripts": { "build": "yarn lerna run build --ignore @ethdebug/format-web --ignore @ethdebug/bug-playground", + "check:react-consumers": "tsx ./bin/check-react-consumers.ts", "bundle": "tsx ./bin/bundle-schema.ts", "typecheck": "yarn lerna run typecheck --ignore @ethdebug/format-web --ignore @ethdebug/bug-playground", "test": "vitest run", diff --git a/packages/bugc-react/CHANGELOG.md b/packages/bugc-react/CHANGELOG.md index adb2bcc6cc..d3e190f7df 100644 --- a/packages/bugc-react/CHANGELOG.md +++ b/packages/bugc-react/CHANGELOG.md @@ -7,6 +7,12 @@ specification itself are tracked in the root ## Unreleased +### Changed + +- The components' return types now read `React.JSX.Element` instead of the + global `JSX.Element`, which `@types/react` 19 removes ([#374]). The + peer ranges stay on React 18. + ## 0.1.0-preview.2 — 2026-10-07 ### Added @@ -50,3 +56,4 @@ First publication. [#359]: https://github.com/ethdebug/format/pull/359 [#363]: https://github.com/ethdebug/format/pull/363 [#365]: https://github.com/ethdebug/format/pull/365 +[#374]: https://github.com/ethdebug/format/pull/374 diff --git a/packages/pointers-react/CHANGELOG.md b/packages/pointers-react/CHANGELOG.md index a250cb0c03..d0a975d942 100644 --- a/packages/pointers-react/CHANGELOG.md +++ b/packages/pointers-react/CHANGELOG.md @@ -10,7 +10,9 @@ root [`CHANGELOG.md`](../../CHANGELOG.md). ### Changed - The `react` and `react-dom` peer ranges now include React 19 - (`^18.0.0 || ^19.0.0`). The package needs no code change for it. + (`^18.0.0 || ^19.0.0`) ([#374]). The components' return types now + read `React.JSX.Element`, which both React 18 and 19 types define; + the global `JSX` they used before is gone in `@types/react` 19. ## 0.1.0-preview.2 — 2026-10-07 @@ -51,3 +53,4 @@ First publication. [#299]: https://github.com/ethdebug/format/pull/299 [#300]: https://github.com/ethdebug/format/pull/300 [#323]: https://github.com/ethdebug/format/pull/323 +[#374]: https://github.com/ethdebug/format/pull/374 diff --git a/packages/programs-react/CHANGELOG.md b/packages/programs-react/CHANGELOG.md index aefa6ccd69..3f3883c188 100644 --- a/packages/programs-react/CHANGELOG.md +++ b/packages/programs-react/CHANGELOG.md @@ -10,9 +10,14 @@ root [`CHANGELOG.md`](../../CHANGELOG.md). ### Changed - The `react` and `react-dom` peer ranges now include React 19 - (`^18.0.0 || ^19.0.0`), and the `shiki`, `@shikijs/langs` and - `@shikijs/themes` dependency ranges now include version 3 - (`^2.5.0 || ^3.0.0`). The package needs no code change for either. + (`^18.0.0 || ^19.0.0`) ([#374]). The components' return types now + read `React.JSX.Element`, which both React 18 and 19 types define; + the global `JSX` they used before is gone in `@types/react` 19. +- The `shiki` dependency range now includes version 3 + (`^2.5.0 || ^3.0.0`) ([#374]). The package no longer depends on + `@shikijs/langs` and `@shikijs/themes`, which could resolve to a + different major than `shiki`. It loads its grammars and theme through + `shiki/langs` and `shiki/themes` instead. ## 0.1.0-preview.2 — 2026-10-07 @@ -78,3 +83,4 @@ First publication. [#323]: https://github.com/ethdebug/format/pull/323 [#342]: https://github.com/ethdebug/format/pull/342 [#349]: https://github.com/ethdebug/format/pull/349 +[#374]: https://github.com/ethdebug/format/pull/374 diff --git a/packages/programs-react/package.json b/packages/programs-react/package.json index 61c0cfb630..d4fbecd357 100644 --- a/packages/programs-react/package.json +++ b/packages/programs-react/package.json @@ -52,8 +52,6 @@ "dependencies": { "@ethdebug/format": "^0.1.0-draft.2", "@ethdebug/pointers": "^0.1.0-preview.2", - "@shikijs/langs": "^2.5.0 || ^3.0.0", - "@shikijs/themes": "^2.5.0 || ^3.0.0", "shiki": "^2.5.0 || ^3.0.0" }, "devDependencies": { diff --git a/packages/programs-react/src/shiki/useHighlighter.ts b/packages/programs-react/src/shiki/useHighlighter.ts index 82a2112efd..8c27e67a28 100644 --- a/packages/programs-react/src/shiki/useHighlighter.ts +++ b/packages/programs-react/src/shiki/useHighlighter.ts @@ -6,6 +6,8 @@ import { useEffect, useState } from "react"; import * as Shiki from "shiki/core"; import { createOnigurumaEngine } from "shiki/engine/oniguruma"; +import { bundledLanguages } from "shiki/langs"; +import { bundledThemes } from "shiki/themes"; /** * Highlighter interface for syntax highlighting. @@ -42,11 +44,8 @@ export function useHighlighter(): Highlighter | undefined { async function createHighlighter(): Promise { const shiki = await Shiki.createHighlighterCore({ - themes: [import("@shikijs/themes/github-light")], - langs: [ - import("@shikijs/langs/solidity"), - import("@shikijs/langs/javascript"), - ], + themes: [bundledThemes["github-light"]()], + langs: [bundledLanguages.solidity(), bundledLanguages.javascript()], engine: createOnigurumaEngine(import("shiki/wasm")), }); diff --git a/yarn.lock b/yarn.lock index f28a56b09f..67ef5bc1ff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4881,7 +4881,7 @@ dependencies: "@shikijs/types" "2.5.0" -"@shikijs/langs@3.23.0", "@shikijs/langs@^2.5.0 || ^3.0.0": +"@shikijs/langs@3.23.0": version "3.23.0" resolved "https://registry.yarnpkg.com/@shikijs/langs/-/langs-3.23.0.tgz#00959d8b16c7f671221ae79b3ad8cde7e6a5c112" integrity sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg== @@ -4895,7 +4895,7 @@ dependencies: "@shikijs/types" "2.5.0" -"@shikijs/themes@3.23.0", "@shikijs/themes@^2.5.0 || ^3.0.0": +"@shikijs/themes@3.23.0": version "3.23.0" resolved "https://registry.yarnpkg.com/@shikijs/themes/-/themes-3.23.0.tgz#fd96ca5ad52639057995bc2093682884e1846f27" integrity sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA== From fce4b4f34bebd1bcbac05049acc4ccc8abcaa113 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 8 Oct 2026 20:31:12 -0400 Subject: [PATCH 4/6] react packages: use React.JSX.Element so React 19 types resolve --- packages/bugc-react/src/components/AstView.tsx | 2 +- .../bugc-react/src/components/BytecodeView.tsx | 4 ++-- packages/bugc-react/src/components/CfgView.tsx | 6 +++--- packages/bugc-react/src/components/Editor.tsx | 2 +- .../bugc-react/src/components/EthdebugTooltip.tsx | 6 +++--- packages/bugc-react/src/components/IrView.tsx | 14 +++++++------- .../src/components/PointerResolverContext.tsx | 2 +- .../pointers-react/src/components/RegionMap.tsx | 2 +- .../pointers-react/src/components/RegionOutput.tsx | 4 ++-- .../src/components/ResolutionVisualizer.tsx | 8 ++++---- .../src/components/CallInfoPanel.tsx | 4 ++-- .../src/components/CallStackDisplay.tsx | 2 +- .../src/components/HighlightedInstruction.tsx | 2 +- packages/programs-react/src/components/Opcodes.tsx | 4 ++-- .../src/components/ProgramExampleContext.tsx | 2 +- .../src/components/SourceContents.tsx | 2 +- .../programs-react/src/components/TraceContext.tsx | 2 +- .../src/components/TraceControls.tsx | 4 ++-- .../src/components/VariableInspector.tsx | 6 +++--- .../programs-react/src/shiki/ShikiCodeBlock.tsx | 2 +- 20 files changed, 40 insertions(+), 40 deletions(-) diff --git a/packages/bugc-react/src/components/AstView.tsx b/packages/bugc-react/src/components/AstView.tsx index b7744567e6..e7ca7f29b8 100644 --- a/packages/bugc-react/src/components/AstView.tsx +++ b/packages/bugc-react/src/components/AstView.tsx @@ -26,7 +26,7 @@ export interface AstViewProps { * * ``` */ -export function AstView({ ast }: AstViewProps): JSX.Element { +export function AstView({ ast }: AstViewProps): React.JSX.Element { // Format AST as JSON, excluding parent references to avoid circular structure const astJson = JSON.stringify( ast, diff --git a/packages/bugc-react/src/components/BytecodeView.tsx b/packages/bugc-react/src/components/BytecodeView.tsx index c900b700af..0aa413e335 100644 --- a/packages/bugc-react/src/components/BytecodeView.tsx +++ b/packages/bugc-react/src/components/BytecodeView.tsx @@ -50,7 +50,7 @@ function InstructionsView({ instructions, onOpcodeHover, onDeclarationClick, -}: InstructionsViewProps): JSX.Element { +}: InstructionsViewProps): React.JSX.Element { const { tooltip, setTooltip, @@ -225,7 +225,7 @@ export function BytecodeView({ bytecode, onOpcodeHover, onDeclarationClick, -}: BytecodeViewProps): JSX.Element { +}: BytecodeViewProps): React.JSX.Element { const runtimeHex = Array.from(bytecode.runtime) .map((b) => b.toString(16).padStart(2, "0")) .join(""); diff --git a/packages/bugc-react/src/components/CfgView.tsx b/packages/bugc-react/src/components/CfgView.tsx index 8117bed0f8..569d21c977 100644 --- a/packages/bugc-react/src/components/CfgView.tsx +++ b/packages/bugc-react/src/components/CfgView.tsx @@ -53,7 +53,7 @@ async function loadDependencies(): Promise { function BlockNodeComponent(props: { data: BlockNodeData; selected: boolean; -}): JSX.Element { +}): React.JSX.Element { const { data, selected } = props; if (!rfModule) { @@ -84,7 +84,7 @@ function BlockNodeComponent(props: { ); } -function CfgViewContent({ ir }: CfgViewProps): JSX.Element { +function CfgViewContent({ ir }: CfgViewProps): React.JSX.Element { const [selectedNode, setSelectedNode] = useState(null); if (!rfModule || !dagreModule) { @@ -503,7 +503,7 @@ function CfgViewContent({ ir }: CfgViewProps): JSX.Element { * * ``` */ -export function CfgView(props: CfgViewProps): JSX.Element { +export function CfgView(props: CfgViewProps): React.JSX.Element { const [loaded, setLoaded] = useState(dependenciesLoaded); const [error, setError] = useState(null); diff --git a/packages/bugc-react/src/components/Editor.tsx b/packages/bugc-react/src/components/Editor.tsx index 47f5c7872b..5bae3edf3a 100644 --- a/packages/bugc-react/src/components/Editor.tsx +++ b/packages/bugc-react/src/components/Editor.tsx @@ -85,7 +85,7 @@ export function Editor({ highlightedRanges = [], theme = "auto", height = "100%", -}: EditorProps): JSX.Element { +}: EditorProps): React.JSX.Element { // eslint-disable-next-line @typescript-eslint/no-explicit-any const editorRef = useRef(null); const decorationsRef = useRef([]); diff --git a/packages/bugc-react/src/components/EthdebugTooltip.tsx b/packages/bugc-react/src/components/EthdebugTooltip.tsx index 38b7d00a82..a9f4bd5bdf 100644 --- a/packages/bugc-react/src/components/EthdebugTooltip.tsx +++ b/packages/bugc-react/src/components/EthdebugTooltip.tsx @@ -41,7 +41,7 @@ export function EthdebugTooltip({ tooltip, onUpdate, onClose, -}: EthdebugTooltipProps): JSX.Element | null { +}: EthdebugTooltipProps): React.JSX.Element | null { const tooltipRef = useRef(null); useEffect(() => { @@ -128,7 +128,7 @@ export function DebugInfoIcon({ onMouseEnter, onMouseLeave, onClick, -}: DebugInfoIconProps): JSX.Element { +}: DebugInfoIconProps): React.JSX.Element { return ( ; } diff --git a/packages/bugc-react/src/components/IrView.tsx b/packages/bugc-react/src/components/IrView.tsx index 054938b5cb..4f2fd26db3 100644 --- a/packages/bugc-react/src/components/IrView.tsx +++ b/packages/bugc-react/src/components/IrView.tsx @@ -44,7 +44,7 @@ function HoverablePart({ onLeave: () => void; onDebugIconHover?: (e: React.MouseEvent) => void; showDebugIcon?: boolean; -}): JSX.Element { +}): React.JSX.Element { return ( 0 ? "has-debug" : ""}`} @@ -112,7 +112,7 @@ function InstructionRenderer({ showTooltip: (e: React.MouseEvent, content: string) => void; pinTooltip: (e: React.MouseEvent, content: string) => void; hideTooltip: () => void; -}): JSX.Element { +}): React.JSX.Element { const debugInfo = extractInstructionDebug(instruction); const operationRanges = debugInfo.operation?.context ? extractAllSourceRanges({ operation: debugInfo.operation, operands: [] }) @@ -416,7 +416,7 @@ function TerminatorRenderer({ showTooltip: (e: React.MouseEvent, content: string) => void; pinTooltip: (e: React.MouseEvent, content: string) => void; hideTooltip: () => void; -}): JSX.Element { +}): React.JSX.Element { const debugInfo = extractTerminatorDebug(terminator); const operationRanges = debugInfo.operation?.context ? extractAllSourceRanges({ operation: debugInfo.operation, operands: [] }) @@ -529,7 +529,7 @@ function PhiRenderer({ showTooltip: (e: React.MouseEvent, content: string) => void; pinTooltip: (e: React.MouseEvent, content: string) => void; hideTooltip: () => void; -}): JSX.Element { +}): React.JSX.Element { const debugInfo = extractPhiDebug(phi); const operationRanges = debugInfo.operation?.context ? extractAllSourceRanges({ operation: debugInfo.operation, operands: [] }) @@ -621,7 +621,7 @@ function BlockRenderer({ showTooltip: (e: React.MouseEvent, content: string) => void; pinTooltip: (e: React.MouseEvent, content: string) => void; hideTooltip: () => void; -}): JSX.Element { +}): React.JSX.Element { return (
@@ -680,7 +680,7 @@ function FunctionRenderer({ showTooltip: (e: React.MouseEvent, content: string) => void; pinTooltip: (e: React.MouseEvent, content: string) => void; hideTooltip: () => void; -}): JSX.Element { +}): React.JSX.Element { const sortedBlocks = useMemo(() => { const result: [string, Ir.Block][] = []; const visited = new Set(); @@ -760,7 +760,7 @@ function FunctionRenderer({ * /> * ``` */ -export function IrView({ ir, onOpcodeHover }: IrViewProps): JSX.Element { +export function IrView({ ir, onOpcodeHover }: IrViewProps): React.JSX.Element { const { tooltip, setTooltip, diff --git a/packages/pointers-react/src/components/PointerResolverContext.tsx b/packages/pointers-react/src/components/PointerResolverContext.tsx index 9da54840ee..5eeb2c06d1 100644 --- a/packages/pointers-react/src/components/PointerResolverContext.tsx +++ b/packages/pointers-react/src/components/PointerResolverContext.tsx @@ -47,7 +47,7 @@ export interface PointerResolverProviderProps extends UsePointerResolutionOption export function PointerResolverProvider({ children, ...options -}: PointerResolverProviderProps): JSX.Element { +}: PointerResolverProviderProps): React.JSX.Element { const state = usePointerResolution(options); return ( diff --git a/packages/pointers-react/src/components/RegionMap.tsx b/packages/pointers-react/src/components/RegionMap.tsx index a590394938..8293c3f75c 100644 --- a/packages/pointers-react/src/components/RegionMap.tsx +++ b/packages/pointers-react/src/components/RegionMap.tsx @@ -73,7 +73,7 @@ export function RegionMap({ regions, selectedName, onRegionClick, -}: RegionMapProps): JSX.Element { +}: RegionMapProps): React.JSX.Element { const names = Object.keys(regions).sort(); if (names.length === 0) { diff --git a/packages/pointers-react/src/components/RegionOutput.tsx b/packages/pointers-react/src/components/RegionOutput.tsx index 60204d78e8..e0ddaedcd2 100644 --- a/packages/pointers-react/src/components/RegionOutput.tsx +++ b/packages/pointers-react/src/components/RegionOutput.tsx @@ -54,7 +54,7 @@ function RegionItem({ value: Data | undefined; showFullValues: boolean; index: number; -}): JSX.Element { +}): React.JSX.Element { const location = getLocationType(region); const locationClass = getLocationClass(location); const name = "name" in region ? (region.name as string) : undefined; @@ -119,7 +119,7 @@ export function RegionOutput({ regions, values, showFullValues = false, -}: RegionOutputProps): JSX.Element { +}: RegionOutputProps): React.JSX.Element { if (regions.length === 0) { return (
diff --git a/packages/pointers-react/src/components/ResolutionVisualizer.tsx b/packages/pointers-react/src/components/ResolutionVisualizer.tsx index 043433cb73..b4285dc74f 100644 --- a/packages/pointers-react/src/components/ResolutionVisualizer.tsx +++ b/packages/pointers-react/src/components/ResolutionVisualizer.tsx @@ -28,7 +28,7 @@ function StorageEditor({ }: { storage: Record; onChange: (storage: Record) => void; -}): JSX.Element { +}): React.JSX.Element { const entries = Object.entries(storage); const handleSlotChange = (oldSlot: string, newSlot: string) => { @@ -112,7 +112,7 @@ function StackEditor({ }: { stack: Array; onChange: (stack: Array) => void; -}): JSX.Element { +}): React.JSX.Element { const handleEntryChange = (index: number, value: string) => { const newStack = [...stack]; newStack[index] = value; @@ -181,7 +181,7 @@ function MemoryEditor({ }: { memory: string | undefined; onChange: (memory: string) => void; -}): JSX.Element { +}): React.JSX.Element { return (
@@ -206,7 +206,7 @@ export function ResolutionVisualizer({ showStateEditor = true, showControls = true, showFullValues = false, -}: ResolutionVisualizerProps): JSX.Element { +}: ResolutionVisualizerProps): React.JSX.Element { const { pointer, stateSpec, diff --git a/packages/programs-react/src/components/CallInfoPanel.tsx b/packages/programs-react/src/components/CallInfoPanel.tsx index e0e10ed2be..5ddb1cf3ad 100644 --- a/packages/programs-react/src/components/CallInfoPanel.tsx +++ b/packages/programs-react/src/components/CallInfoPanel.tsx @@ -74,7 +74,7 @@ function bannerClassName(info: ResolvedCallInfo): string { export function CallInfoPanel({ showPointers = false, className = "", -}: CallInfoPanelProps): JSX.Element | null { +}: CallInfoPanelProps): React.JSX.Element | null { const { currentCallInfo } = useTraceContext(); if (!currentCallInfo) { @@ -110,7 +110,7 @@ interface PointerRefItemProps { function PointerRefItem({ ref_, showPointer, -}: PointerRefItemProps): JSX.Element { +}: PointerRefItemProps): React.JSX.Element { return (
{ref_.label}: diff --git a/packages/programs-react/src/components/CallStackDisplay.tsx b/packages/programs-react/src/components/CallStackDisplay.tsx index f8dfc4b052..95cfef65ca 100644 --- a/packages/programs-react/src/components/CallStackDisplay.tsx +++ b/packages/programs-react/src/components/CallStackDisplay.tsx @@ -60,7 +60,7 @@ function formatAsDecimal(hex: string): string { export function CallStackDisplay({ className = "", -}: CallStackDisplayProps): JSX.Element { +}: CallStackDisplayProps): React.JSX.Element { const { callStack, resolvedCallStack, jumpToStep } = useTraceContext(); if (callStack.length === 0) { diff --git a/packages/programs-react/src/components/HighlightedInstruction.tsx b/packages/programs-react/src/components/HighlightedInstruction.tsx index 86dddf447c..d64854e0a0 100644 --- a/packages/programs-react/src/components/HighlightedInstruction.tsx +++ b/packages/programs-react/src/components/HighlightedInstruction.tsx @@ -12,7 +12,7 @@ import { ShikiCodeBlock } from "#shiki/ShikiCodeBlock"; * * @returns JSON representation of the highlighted instruction */ -export function HighlightedInstruction(): JSX.Element { +export function HighlightedInstruction(): React.JSX.Element { const { highlightedInstruction } = useProgramExampleContext(); return ( diff --git a/packages/programs-react/src/components/Opcodes.tsx b/packages/programs-react/src/components/Opcodes.tsx index 3da00432d3..b42b48824c 100644 --- a/packages/programs-react/src/components/Opcodes.tsx +++ b/packages/programs-react/src/components/Opcodes.tsx @@ -15,7 +15,7 @@ import { Data, Program } from "@ethdebug/format"; * * @returns Opcodes list element */ -export function Opcodes(): JSX.Element { +export function Opcodes(): React.JSX.Element { const { instructions, highlightedInstruction, @@ -93,7 +93,7 @@ function Opcode(props: { onClick: () => void; onMouseEnter: () => void; onMouseLeave: () => void; -}): JSX.Element { +}): React.JSX.Element { const { instruction, active, diff --git a/packages/programs-react/src/components/ProgramExampleContext.tsx b/packages/programs-react/src/components/ProgramExampleContext.tsx index ff85ee0209..ab890d26c8 100644 --- a/packages/programs-react/src/components/ProgramExampleContext.tsx +++ b/packages/programs-react/src/components/ProgramExampleContext.tsx @@ -74,7 +74,7 @@ export function ProgramExampleContextProvider({ ...props }: ProgramExampleProps & { children: React.ReactNode; -}): JSX.Element { +}): React.JSX.Element { const { sources, instructions: dynamicInstructionsWithoutOffsets } = props; const dynamicInstructions = computeOffsets(dynamicInstructionsWithoutOffsets); diff --git a/packages/programs-react/src/components/SourceContents.tsx b/packages/programs-react/src/components/SourceContents.tsx index 793e67e67d..819a4090a6 100644 --- a/packages/programs-react/src/components/SourceContents.tsx +++ b/packages/programs-react/src/components/SourceContents.tsx @@ -25,7 +25,7 @@ import { Materials, Program } from "@ethdebug/format"; */ export function SourceContents( props: Omit, -): JSX.Element { +): React.JSX.Element { const { sources, highlightedInstruction, highlightMode } = useProgramExampleContext(); diff --git a/packages/programs-react/src/components/TraceContext.tsx b/packages/programs-react/src/components/TraceContext.tsx index 943a3eec44..0f9aad8731 100644 --- a/packages/programs-react/src/components/TraceContext.tsx +++ b/packages/programs-react/src/components/TraceContext.tsx @@ -268,7 +268,7 @@ export function TraceProvider({ templates = {}, resolveVariables: shouldResolve = true, children, -}: TraceProviderProps): JSX.Element { +}: TraceProviderProps): React.JSX.Element { const [currentStepIndex, setCurrentStepIndex] = useState( Math.min(initialStepIndex, trace.length - 1), ); diff --git a/packages/programs-react/src/components/TraceControls.tsx b/packages/programs-react/src/components/TraceControls.tsx index dc3c8c4998..6ff5c6a93c 100644 --- a/packages/programs-react/src/components/TraceControls.tsx +++ b/packages/programs-react/src/components/TraceControls.tsx @@ -24,7 +24,7 @@ export function TraceControls({ showStepCount = true, showOpcode = true, className = "", -}: TraceControlsProps): JSX.Element { +}: TraceControlsProps): React.JSX.Element { const { currentStepIndex, totalSteps, @@ -124,7 +124,7 @@ export interface TraceProgressProps { */ export function TraceProgress({ className = "", -}: TraceProgressProps): JSX.Element { +}: TraceProgressProps): React.JSX.Element { const { currentStepIndex, totalSteps, jumpToStep } = useTraceContext(); const progress = totalSteps > 1 ? currentStepIndex / (totalSteps - 1) : 0; diff --git a/packages/programs-react/src/components/VariableInspector.tsx b/packages/programs-react/src/components/VariableInspector.tsx index c538fc2c91..9eda380cc7 100644 --- a/packages/programs-react/src/components/VariableInspector.tsx +++ b/packages/programs-react/src/components/VariableInspector.tsx @@ -24,7 +24,7 @@ export function VariableInspector({ showTypes = false, showPointers = false, className = "", -}: VariableInspectorProps): JSX.Element { +}: VariableInspectorProps): React.JSX.Element { const { currentVariables, currentInstruction } = useTraceContext(); if (!currentInstruction) { @@ -77,7 +77,7 @@ function VariableItem({ variable, showType, showPointer, -}: VariableItemProps): JSX.Element { +}: VariableItemProps): React.JSX.Element { const { identifier, type, pointer, value, error } = variable; // Extract type name if available @@ -136,7 +136,7 @@ export interface StackInspectorProps { export function StackInspector({ maxEntries = 10, className = "", -}: StackInspectorProps): JSX.Element { +}: StackInspectorProps): React.JSX.Element { const { currentStep } = useTraceContext(); if (!currentStep || !currentStep.stack || currentStep.stack.length === 0) { diff --git a/packages/programs-react/src/shiki/ShikiCodeBlock.tsx b/packages/programs-react/src/shiki/ShikiCodeBlock.tsx index ebee076432..3bf44081d5 100644 --- a/packages/programs-react/src/shiki/ShikiCodeBlock.tsx +++ b/packages/programs-react/src/shiki/ShikiCodeBlock.tsx @@ -23,7 +23,7 @@ export function ShikiCodeBlock({ code, className, ...highlightOptions -}: ShikiCodeBlockProps): JSX.Element { +}: ShikiCodeBlockProps): React.JSX.Element { const highlighter = useHighlighter(); if (!highlighter) { From c943c7afeb947ddeffe45f55e2f87267dd4f2d3b Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 8 Oct 2026 20:45:17 -0400 Subject: [PATCH 5/6] programs-react: keep per-language shiki imports; check bundle size --- bin/check-react-consumers.ts | 32 ++++++++++++++++++- packages/programs-react/CHANGELOG.md | 7 ++-- packages/programs-react/package.json | 2 ++ .../src/shiki/useHighlighter.ts | 9 +++--- yarn.lock | 4 +-- 5 files changed, 42 insertions(+), 12 deletions(-) diff --git a/bin/check-react-consumers.ts b/bin/check-react-consumers.ts index 412faf9a70..d2cc3907e7 100644 --- a/bin/check-react-consumers.ts +++ b/bin/check-react-consumers.ts @@ -1,5 +1,6 @@ import { execFileSync } from "node:child_process"; import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs"; +import { gzipSync } from "node:zlib"; import { tmpdir } from "node:os"; import { join } from "node:path"; @@ -18,6 +19,8 @@ const pairs = [ { react: "19", types: "19", shiki: "3" }, ]; +const maxBundleKb = 400; + const run = (cmd: string, args: string[], cwd: string) => execFileSync(cmd, args, { cwd, stdio: "inherit" }); @@ -71,11 +74,38 @@ export const b = Pointers; `@types/react-dom@${types}`, `shiki@${shiki}`, "typescript", + "esbuild", "@types/node@20", ...tarballs, ], dir, ); run("node", [join(dir, "node_modules/typescript/bin/tsc")], dir); - console.log(`ok: React ${react} + shiki ${shiki}`); + // Without code splitting, every dynamic import is inlined, so this is + // the worst-case size of the highlighter loader. Importing all grammars + // and themes (`shiki/langs`, `shiki/themes`) once made it about 1.6 MB. + writeFileSync( + join(dir, "entry.js"), + 'import { ShikiCodeBlock } from "@ethdebug/programs-react";\nconsole.log(ShikiCodeBlock);\n', + ); + const bundle = execFileSync( + join(dir, "node_modules/.bin/esbuild"), + [ + "entry.js", + "--bundle", + "--minify", + "--format=esm", + "--platform=browser", + "--external:react", + "--external:react-dom", + "--external:react/jsx-runtime", + "--external:node:*", + ], + { cwd: dir, maxBuffer: 1 << 28 }, + ); + const kb = Math.round(gzipSync(bundle).length / 1024); + if (kb > maxBundleKb) { + throw new Error(`bundle is ${kb} KB gzipped; limit is ${maxBundleKb}`); + } + console.log(`ok: React ${react} + shiki ${shiki}, bundle ${kb} KB gz`); } diff --git a/packages/programs-react/CHANGELOG.md b/packages/programs-react/CHANGELOG.md index 3f3883c188..2e762fc2fa 100644 --- a/packages/programs-react/CHANGELOG.md +++ b/packages/programs-react/CHANGELOG.md @@ -13,11 +13,8 @@ root [`CHANGELOG.md`](../../CHANGELOG.md). (`^18.0.0 || ^19.0.0`) ([#374]). The components' return types now read `React.JSX.Element`, which both React 18 and 19 types define; the global `JSX` they used before is gone in `@types/react` 19. -- The `shiki` dependency range now includes version 3 - (`^2.5.0 || ^3.0.0`) ([#374]). The package no longer depends on - `@shikijs/langs` and `@shikijs/themes`, which could resolve to a - different major than `shiki`. It loads its grammars and theme through - `shiki/langs` and `shiki/themes` instead. +- The `shiki`, `@shikijs/langs` and `@shikijs/themes` dependency ranges + now include version 3 (`^2.5.0 || ^3.0.0`) ([#374]). ## 0.1.0-preview.2 — 2026-10-07 diff --git a/packages/programs-react/package.json b/packages/programs-react/package.json index d4fbecd357..61c0cfb630 100644 --- a/packages/programs-react/package.json +++ b/packages/programs-react/package.json @@ -52,6 +52,8 @@ "dependencies": { "@ethdebug/format": "^0.1.0-draft.2", "@ethdebug/pointers": "^0.1.0-preview.2", + "@shikijs/langs": "^2.5.0 || ^3.0.0", + "@shikijs/themes": "^2.5.0 || ^3.0.0", "shiki": "^2.5.0 || ^3.0.0" }, "devDependencies": { diff --git a/packages/programs-react/src/shiki/useHighlighter.ts b/packages/programs-react/src/shiki/useHighlighter.ts index 8c27e67a28..82a2112efd 100644 --- a/packages/programs-react/src/shiki/useHighlighter.ts +++ b/packages/programs-react/src/shiki/useHighlighter.ts @@ -6,8 +6,6 @@ import { useEffect, useState } from "react"; import * as Shiki from "shiki/core"; import { createOnigurumaEngine } from "shiki/engine/oniguruma"; -import { bundledLanguages } from "shiki/langs"; -import { bundledThemes } from "shiki/themes"; /** * Highlighter interface for syntax highlighting. @@ -44,8 +42,11 @@ export function useHighlighter(): Highlighter | undefined { async function createHighlighter(): Promise { const shiki = await Shiki.createHighlighterCore({ - themes: [bundledThemes["github-light"]()], - langs: [bundledLanguages.solidity(), bundledLanguages.javascript()], + themes: [import("@shikijs/themes/github-light")], + langs: [ + import("@shikijs/langs/solidity"), + import("@shikijs/langs/javascript"), + ], engine: createOnigurumaEngine(import("shiki/wasm")), }); diff --git a/yarn.lock b/yarn.lock index 67ef5bc1ff..f28a56b09f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4881,7 +4881,7 @@ dependencies: "@shikijs/types" "2.5.0" -"@shikijs/langs@3.23.0": +"@shikijs/langs@3.23.0", "@shikijs/langs@^2.5.0 || ^3.0.0": version "3.23.0" resolved "https://registry.yarnpkg.com/@shikijs/langs/-/langs-3.23.0.tgz#00959d8b16c7f671221ae79b3ad8cde7e6a5c112" integrity sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg== @@ -4895,7 +4895,7 @@ dependencies: "@shikijs/types" "2.5.0" -"@shikijs/themes@3.23.0": +"@shikijs/themes@3.23.0", "@shikijs/themes@^2.5.0 || ^3.0.0": version "3.23.0" resolved "https://registry.yarnpkg.com/@shikijs/themes/-/themes-3.23.0.tgz#fd96ca5ad52639057995bc2093682884e1846f27" integrity sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA== From 903041da317ff0f1573dab7b0aa744837e201a3e Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Thu, 8 Oct 2026 20:52:59 -0400 Subject: [PATCH 6/6] check-react-consumers: cover mixed shiki majors and highlight --- bin/check-react-consumers.ts | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/bin/check-react-consumers.ts b/bin/check-react-consumers.ts index d2cc3907e7..6503056824 100644 --- a/bin/check-react-consumers.ts +++ b/bin/check-react-consumers.ts @@ -17,6 +17,9 @@ const packed = [ const pairs = [ { react: "18", types: "18", shiki: "2.5" }, { react: "19", types: "19", shiki: "3" }, + // the ranges allow shiki next to grammars and themes of the other major + { react: "19", types: "19", shiki: "2.5", sub: "3" }, + { react: "18", types: "18", shiki: "3", sub: "2.5" }, ]; const maxBundleKb = 400; @@ -33,8 +36,8 @@ const tarballs = packed.map((name) => { return join(root, out.trim().split("\n").pop()!); }); -for (const { react, types, shiki } of pairs) { - const dir = join(root, `react-${react}`); +for (const { react, types, shiki, sub = shiki } of pairs) { + const dir = join(root, `react-${react}-shiki-${shiki}-sub-${sub}`); mkdirSync(dir); writeFileSync( join(dir, "package.json"), @@ -73,6 +76,8 @@ export const b = Pointers; `@types/react@${types}`, `@types/react-dom@${types}`, `shiki@${shiki}`, + `@shikijs/langs@${sub}`, + `@shikijs/themes@${sub}`, "typescript", "esbuild", "@types/node@20", @@ -107,5 +112,23 @@ export const b = Pointers; if (kb > maxBundleKb) { throw new Error(`bundle is ${kb} KB gzipped; limit is ${maxBundleKb}`); } - console.log(`ok: React ${react} + shiki ${shiki}, bundle ${kb} KB gz`); + // highlight Solidity the way the package's highlighter does + writeFileSync( + join(dir, "highlight.mjs"), + `import * as Shiki from "shiki/core"; +import { createOnigurumaEngine } from "shiki/engine/oniguruma"; +const h = await Shiki.createHighlighterCore({ + themes: [import("@shikijs/themes/github-light")], + langs: [import("@shikijs/langs/solidity")], + engine: createOnigurumaEngine(import("shiki/wasm")), +}); +const html = h.codeToHtml("contract A { uint x; }", { + lang: "solidity", + theme: "github-light", +}); +if (!/