Skip to content

Verify every integration against its real host, before tagging 0.4.0 - #20

Merged
likeBloodMoon merged 4 commits into
masterfrom
claude/v0-3-0-roadmap-tzy5qc
Sep 1, 2026
Merged

Verify every integration against its real host, before tagging 0.4.0#20
likeBloodMoon merged 4 commits into
masterfrom
claude/v0-3-0-roadmap-tzy5qc

Conversation

@likeBloodMoon

@likeBloodMoon likeBloodMoon commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Everything needed before v0.4.0 is tagged. Found while checking release readiness after #19 merged.

The CommonJS blocker

require('eaa-kit/webpack') — the exact line docs/integrations.md prints — failed with ERR_PACKAGE_PATH_NOT_EXPORTED. Every subpath export named an import condition and nothing else, so CommonJS resolution had no way in. That is most of these hosts: a webpack.config.js is ordinarily CommonJS, and so is .eleventy.js.

Each subpath now names a require condition beside import, pointing at the same file. Node's require(esm) loads it on every version in engines (^22.22.2 || ^24.15.0 || >=26.0.0), so nothing is compiled twice and the package stays ESM-only.

The one thing that would break it again is a top-level await anywhere in a graph an integration pulls in — require(esm) refuses those. So scripts/test-packaged.mjs now loads every entry both ways, and a future top-level await fails there rather than in somebody's build.

Every integration now driven by its real host

#19 shipped a Nuxt module that could never have worked: it read a field Nuxt never populates, and its hand-written stand-in agreed with the bug, so the suite was green over a module that would have thrown on every build. That is a gap in method, not a one-off, so the remaining assumptions were checked the same way.

webpack — correct as written. Probed against a real compiler. All three claims held: afterEmit fires with every file on disk, compilation.errors reports a failed build, and compiler.watchMode separates a one-shot run from a dev server. watchMode is sampled inside the watch callback, because close() puts it back to false.

Vite — correct, and one line is load-bearing. Four claims checked, four held. build.outDir arrives relative to the root (zielordner, not an absolute path), so the plugin's path.resolve(config.root, …) is doing real work — a plugin using the value as given would audit the working directory and find nothing. apply: 'build' is verified against Vite's own resolved plugin list for serve vs build rather than by reading the object, and a throw in closeBundle rejects the build.

The enforce: 'post' case is ordered so only enforce can save it: this plugin is declared first and a page-emitting plugin after it. Stripping enforce makes that same build pass over an unaudited broken page, which is what the test exists to catch.

Docusaurus — withdrawn. The hook is the cleanest of any builder here: postBuild runs when the whole site is on disk and hands over outDir. The plugin still cannot work, for a reason outside its own code. Docusaurus loads plugins through jiti, which intercepts dynamic import(); axe-core then reaches jiti's module evaluator instead of Node's and is evaluated without the globals it expects, so the audit dies on Cannot read properties of undefined (reading 'document') at the end of a build that otherwise succeeded. Its unit tests passed because they call the plugin directly, and jiti is never in the picture.

Shipping it would have meant a documented integration that fails in every real project, which for an accessibility check is worse than having none — the same reasoning that already keeps a Next.js plugin out of this release. So the same answer, and it was run rather than assumed:

$ docusaurus build && eaa-kit audit
Found a build in build/
...
    docs/intro.html  docs/intro.md

No directory argument needed: the project is recognised and its output is known to be build/, and pages still map back to their Markdown. Detection and route mapping are untouched — only the build-time hook is gone.

Also here

  • The two GitHub Action pins bumped v0.3.0v0.4.0, which the docs tell people to set to an exact release tag.
  • The changelog entry folded into the dated 0.4.0 section rather than left under Unreleased, so tagging does not ship a release whose changelog says part of it is unreleased.

Verification

lint, typecheck, smoke clean. 1031 tests pass, 13 skipped. test:packaged loads all five integration entries by both import and require from a real npm pack install.

Per suite: astro 17, vite 19, eleventy 4, webpack 5, nuxt 5, adapters 17 — Astro, Vite, Eleventy, webpack and Nuxt are each driven by a real build of their host.

The one local test failure is this container's Playwright expecting Chromium build 1234 against the 1194 it ships; it is green on CI, which installs Chromium itself.

Not in this PR

Two carried-over cleanups, neither a release blocker: running the real-build suites on one matrix job instead of four (~3 min/push), and folding the four separate detectFramework + readPackageJson call sites into one (~3 ms, tidiness).

🤖 Generated with Claude Code

https://claude.ai/code/session_01HKU933TcRaKinharQxrNht

`require('eaa-kit/webpack')` — the line the docs print — failed with
ERR_PACKAGE_PATH_NOT_EXPORTED. Every subpath export named an `import`
condition and nothing else, so CommonJS resolution found no way in.

That is most of these hosts. A webpack.config.js is ordinarily CommonJS,
so are docusaurus.config.js and .eleventy.js, and none of them could
load the integration written for them. It was a documented feature that
did not work as documented, which is worse than not shipping it.

Each subpath now names a `require` condition beside `import`, pointing
at the same file. Node's require(esm) loads it on every version in
`engines`, so nothing is compiled twice and the package stays ESM-only.

The one thing that would break it again is a top-level await anywhere in
a graph an integration pulls in — require(esm) refuses those. So the
packaged harness now loads all six entries both ways, and a future
top-level await fails there rather than in somebody's build.

Verified against a real `npm pack` install: the webpack config exactly
as the docs print it now loads and yields a plugin with an apply().

Also bumps the two GitHub Action pins from v0.3.0 to v0.4.0, which the
docs tell people to set to an exact release tag.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HKU933TcRaKinharQxrNht
It sat under Unreleased, which would have tagged v0.4.0 with a changelog
saying part of the release was not in it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HKU933TcRaKinharQxrNht
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

The plugin makes three claims about somebody else's software: that
`afterEmit` fires with every file on disk, that `compilation.errors` says
whether the build failed, and that `compiler.watchMode` separates a
one-shot run from a dev server. The Nuxt module shipped in this branch
with a wrong claim of exactly that kind — it read a field Nuxt never
populates, and its hand-written stand-in agreed with the bug — so these
are now checked against webpack itself rather than a stub.

All three held. The suite covers a failing compile, a clean build,
`failBuild: false`, the resolved output directory (deliberately not
`dist`, so a guess would audit nothing), and the watch-mode skip.

`watchMode` is sampled inside the watch callback: `close()` puts it back
to false, so reading it afterwards would report that the guard had
nothing to act on when it did.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HKU933TcRaKinharQxrNht
The Docusaurus hook is the cleanest of any builder here — `postBuild`
runs when the whole site is on disk and hands over `outDir` — and the
plugin written against it cannot work, for a reason outside its own code.
Docusaurus loads plugins through jiti, which intercepts dynamic
`import()`; axe-core then reaches jiti's module evaluator instead of
Node's and is evaluated without the globals it expects, so the audit dies
on `Cannot read properties of undefined (reading 'document')` at the end
of a build that otherwise succeeded. Its unit tests passed because they
call the plugin directly and jiti is never in the picture.

Shipping it would have meant a documented integration that fails in every
real project, which for an accessibility check is worse than having none
— the same reasoning that already keeps a Next.js plugin out. So the same
answer: `docusaurus build && eaa-kit audit`, which needs no directory
argument because the project is recognised and its output is known to be
`build/`. Both halves of that were run against the CLI, not assumed: it
reported "Found a build in build/" and mapped `docs/intro.html` back to
`docs/intro.md`. Detection and route mapping are untouched; only the
build-time hook is gone.

That leaves Vite as the one integration never driven by its real host, so
it is now. Four of its claims about Vite were checked rather than
believed, and all four held: `configResolved` and `closeBundle` are
called, `build.outDir` arrives relative to the root (so the plugin's
`path.resolve` is load-bearing — a plugin using it as given would audit
the working directory), `apply: 'build'` keeps it out of a dev server's
resolved plugin list, and a throw in `closeBundle` rejects the build.

The `enforce: 'post'` case is ordered so only `enforce` can save it: this
plugin is declared first and a page-emitting plugin after it. Stripping
`enforce` makes that build pass over an unaudited broken page, which is
what the test exists to catch.

Every shipped integration is now driven by a real build of its host:
Astro, Vite, Eleventy, webpack, Nuxt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HKU933TcRaKinharQxrNht
@likeBloodMoon likeBloodMoon changed the title fix: let the integrations load from a CommonJS config, before tagging 0.4.0 Verify every integration against its real host, before tagging 0.4.0 Sep 1, 2026
@likeBloodMoon
likeBloodMoon merged commit c3df84b into master Sep 1, 2026
4 checks passed
@likeBloodMoon
likeBloodMoon deleted the claude/v0-3-0-roadmap-tzy5qc branch September 2, 2026 07:36
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