Skip to content

feat!: externalize codemirror, react-codemirror2, and prop-types (semver-major) - #648

Draft
erunion wants to merge 2 commits into
mainfrom
reduce-library-bundle-size-phase-2
Draft

feat!: externalize codemirror, react-codemirror2, and prop-types (semver-major)#648
erunion wants to merge 2 commits into
mainfrom
reduce-library-bundle-size-phase-2

Conversation

@erunion

@erunion erunion commented Jul 12, 2026

Copy link
Copy Markdown
Member

Summary

Stacked on #647 (Phase 1). This is Phase 2 from the same bundle-size plan: semver-major, should not ship until Phase 1 is merged, released, and this has been smoke-tested in the main readme app.

  • codemirror, react-codemirror2, and prop-types are already runtime dependencies — every consumer installs them — yet they were also compiled into dist/index.js. That's double-shipping, and it prevented the consumer's own bundler from deduping CodeMirror against any other copy already in their app.
  • Extends externals in webpack.config.js (same precedent as the existing react/react-dom/@readme/variable entries) to cover codemirror, react-codemirror2, prop-types, plus a function external for deep requires (codemirror/mode/*/*, codemirror/addon/*, codemirror/lib/codemirror) — excluding .css imports, which stay bundled via style-loader so consumers don't need their own CSS handling for our node_modules imports.
  • dist/index.node.js is unaffected — src/index.node.ts only imports the pure utils, no CodeMirror.

Result, on top of Phase 1's ~510 KB / ~158 KB gzip:

State Raw Gzip
Phase 1 only (#647) ~510 KB ~158 KB
+ this PR 47.2 KB 14.5 KB

The published bundle becomes just ReadMe's own code + CSS. Consumers still load CodeMirror, but exactly one copy, resolved and tree-shaken by their own build.

Test plan

  • npm test — full vitest suite passes (294 tests)
  • npm run builddist/index.js is 47,200 bytes raw / 14,557 bytes gzip
  • npm pack, installed the tarball into a scratch consumer with real codemirror/react/prop-types/react-codemirror2 deps
  • Built a second, separate webpack config in that scratch consumer that imports @readme/syntax-highlighter/browser and bundles it — confirmed the consumer's own webpack resolves codemirror (907 KB, 41 modules) from its own node_modules, proving the externalized require() calls pass through correctly
  • Executed the resulting consumer bundle in jsdom and rendered a graphql and a javascript sample — output tokenizes identically to Phase 1 / main
  • Smoke-test in the actual main readme app before this is released (not done here — needs a maintainer with access to that repo)

Not in scope

  • Lazy per-language mode loading and a CodeMirror 6/Lezer migration remain future options, not addressed here.

erunion added 2 commits July 12, 2026 10:16
Re-enables minification (disabled by a workaround in #595 that no longer
applies, since the dev demo uses a fully separate webpack config), vendors
just the ~600 lines of the `codemirror-graphql` mode that we actually use
instead of pulling in `graphql-language-service` and all of `graphql`
(566 KB of unused validation rules), drops the now-dead `codemirror/mode/meta`
import, and adds a `files` allowlist so the npm tarball only ships `dist`.
These are already runtime `dependencies` that every consumer installs,
so bundling them into `dist/index.js` too was double-shipping and
prevented the consumer's own bundler from deduping CodeMirror against
any other copy in their app. Extends `externals` (following the existing
`react`/`react-dom`/`@readme/variable` precedent) to cover `codemirror`,
`react-codemirror2`, `prop-types`, and deep `codemirror/mode/*`,
`codemirror/addon/*` requires -- excluding `.css` imports, which stay
bundled via style-loader so consumers don't need their own CSS handling
for our node_modules imports.

BREAKING CHANGE: consumers must have `codemirror`, `react-codemirror2`,
and `prop-types` resolvable in their own dependency tree (they already
are, since these are `dependencies` of this package) and use a bundler
that can resolve `require()` calls against node_modules.
Base automatically changed from reduce-library-bundle-size to main July 20, 2026 19:41

@domharrington domharrington left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this LGTM! I'm guessing you're not focused on this right now with other things - can I help get this over the line? What else needs to happen with it?

@erunion

erunion commented Jul 22, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: accf00ef-7a8b-452a-998b-87fcdcf5a8d0

📥 Commits

Reviewing files that changed from the base of the PR and between cf705cf and 33502d2.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (10)
  • package.json
  • src/codeMirror/index.jsx
  • src/utils/cm-mode-imports.js
  • src/utils/modes/graphql/RuleHelpers.js
  • src/utils/modes/graphql/Rules.js
  • src/utils/modes/graphql/mode-factory.js
  • src/utils/modes/graphql/mode-indent.js
  • src/utils/modes/graphql/mode.js
  • src/utils/modes/graphql/onlineParser.js
  • webpack.config.js
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • readmeio/ai (manual)
  • readmeio/gitto (manual)
  • readmeio/markdown (manual)
  • readmeio/readme (manual)
💤 Files with no reviewable changes (1)
  • src/codeMirror/index.jsx

Walkthrough

The change vendors GraphQL lexer, grammar, parser, indentation, and CodeMirror mode modules locally. CodeMirror loading now registers the local GraphQL mode instead of the external package, while an unused metadata import is removed. The package whitelist is restricted to build outputs, the external GraphQL mode dependency is removed, production minification is enabled, and webpack externalizes non-CSS CodeMirror modules.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Checkov (3.3.8)
package.json

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'


Comment @coderabbitai help to get the list of available commands.

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.

2 participants