Skip to content

Keep extractor names in the browser bundles - #443

Open
paoValle wants to merge 1 commit into
kepano:mainfrom
paoValle:fix/keep-extractor-names
Open

paoValle wants to merge 1 commit into
kepano:mainfrom
paoValle:fix/keep-extractor-names

Conversation

@paoValle

Copy link
Copy Markdown

Keeps extractor class names in the browser bundles, so parse() reports extractorType: 'github' instead of 'a' for anyone consuming dist/index.js or dist/index.full.js.

Fixes #414.

Why

extractorType is derived from constructor.name, which Terser replaces with short names. Node loads the unbundled CJS and reports the site correctly, so the defect only shows up in a browser extension or any bundler that consumes the published bundles — where the field is documented to name the extractor that ran, and came back as a, i or v.

What changed

keep_classnames: /Extractor$/ on the TerserPlugin minimizer both bundles share, so only the classes the field depends on are kept and the bundle's other classes still minify. Measured cost, npm run build + npm run size: dist/index.js 90.2 → 90.4 KB gzip, dist/index.full.js 206.9 → 207.1 KB, both inside the budgets in scripts/check-bundle-size.mjs (92 and 215 KB), so no budget change.

scripts/check-extractor-names.mjs asserts the built bundles still contain the extractor class names, as npm run lint:extractor-names, wired into the build job and the release checks next to the size budget. Vitest runs on the TypeScript source, so nothing in the suite could notice this disappearing.

I went with the build option rather than a static name per extractor because the latter means touching every extractor and guarding the convention anyway; if you would prefer that shape, say so and I will redo it.

Verification

  • npm run build then a probe that loads the built full bundle with jsdom against a minimal LWN page and prints result.extractorType: "a" before this change, "lwn" after.
  • npm run lint:extractor-names → passes with the option, fails (✗ dist/index.js is missing BaseExtractor, GitHubExtractor, YoutubeExtractor) with it removed.
  • npx vitest run → 789 tests passed. The six website test files fail to load in a fresh clone (Cannot find package '@lezer/html', @lezer/highlight) and fail the same way with this change stashed.

One path this does not cover: website/scripts/build.mjs bundles the worker with esbuild (minify: true) and no keepNames, so the playground and the worker may still see a mangled name. It is a one-line change there, but it is a separate build with its own dependencies, so I left it out of this PR — happy to add it or open a follow-up if you want it.

`parse()` reports `extractorType` from `extractor.constructor.name`, but the
browser bundles are minified, so consumers of `dist/index.js` and
`dist/index.full.js` got `a`, `i` or `v` instead of `github`, `twitter` or
`youtube`. The unbundled CJS keeps the names, which is why Node was unaffected.

Terser now keeps the names of the classes that end in `Extractor`, so the
`temml` classes the bundles also carry are still mangled. Measured cost:
`dist/index.js` 90.2 → 90.4 KB gzip and `dist/index.full.js` 206.9 → 207.1 KB,
both inside the existing budgets, so no budget change is needed.

Nothing in the test suite could see this: vitest runs on the TypeScript source
and the build job only ran `build` + `size`. Added
`scripts/check-extractor-names.mjs` (`npm run lint:extractor-names`, wired into
the build job and the release checks) so dropping the option fails CI instead of
shipping.

Verified by loading the built full bundle with jsdom against a minimal LWN page:
without this change the probe prints `extractorType = "a"`, with it `"lwn"`. The
new check fails with the option removed and passes with it, and `npx vitest run`
reports 789 passing tests.
@paoValle
paoValle force-pushed the fix/keep-extractor-names branch from e4f5cca to 643dc40 Compare October 24, 2026 17:11
@paoValle

Copy link
Copy Markdown
Author

Rebased onto main 0.19.6 (e4f5cca → 643dc40); the branch had gone conflicting.

The only conflict was CHANGELOG.md, where both sides added a bullet to the 0.19.6 section — kept both, so the entry reads the security fix and then this one. package.json, webpack.config.js, the two workflow files and scripts/check-extractor-names.mjs applied cleanly.

Re-run after the rebase, with npm ci and npm ci --prefix website (the six website test files fail to import @lezer/* without the second one, which is the step CI runs):

  • TZ=UTC npx vitest run — 45 files, 848 tests passed;
  • TZ=UTC DOM=jsdom npx vitest run — 45 files, 848 tests passed;
  • npm run build, npm run lint:extractor-names, npm run lint:innerhtml, npm run lint:unused — all clean.

The CI run on this branch is action_required (the fork's first workflow run needs a maintainer's approval), so nothing has executed here yet.

This branch has not been deployed

No deployments
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.

extractorType is a minified class name in the browser bundles

1 participant