Restore CSS injection, alias vtk-lite.js, docs/build cleanups#1
Merged
Conversation
…y-guard The top-level fullyParallel/forbidOnly/retries/workers/use keys are ignored by Vitest, so GPU/browser tests were not being forced to a single worker and CI retries were not enabled. Move into test: with the actual Vitest 4 spellings (fileParallelism, maxWorkers, retry, allowOnly). Drop use.trace since it is a Playwright Test option that has no Vitest browser equivalent.
The migrated Vitest suite uses it()/describe() in addition to test(), so the .only-guard needs to catch all three forms. Switch to an -E pattern so a forgotten it.only(...) or describe.only(...) fails CI.
compact is a Rollup input option, not an output option, so this key was a no-op that produced a warning during ESM build.
The old webpack (UMD via style-loader) and rollup (ESM via rollup-plugin-postcss with default inject:true) both injected stylesheets as <style> tags on import. The Vite migration silently dropped this: UMD emitted vtk.js.css alongside vtk.js, and ESM emitted orphan .module.css files that nothing imported, so UI/widget consumers lost their styling without any warning. - inlineUmdCssPlugin: add enforce:'post' so generateBundle sees the CSS asset emitted by Vite's css-post plugin. Without it, the bundle filter returned empty and the plugin no-op'd. - injectEsmCssPlugin: new plugin that inlines each *.module.css into its corresponding *.module.css.js wrapper as a <style> tag injected on first evaluation, then deletes the orphan stylesheet from the bundle. Verified by diffing against the published @kitware/vtk.js@35.15.1 artifacts, which inject CSS via styleInject; the new wrappers behave equivalently.
Application demos are emitted as standalone single-file HTML (per each app's index.md: "the only requirement is the single HTML file without any web server"). The old Rollup path ran terser; the Vite migration disabled minification, bloating each demo from ~1.5 MB to ~2.5 MB. Re-enable esbuild minification, which is the fast path Vite already bundles, no extra dependency. Readable source for learning still lives in Examples/Applications/* on GitHub; the inlined script in the deployable HTML was never intended as a view-source artifact.
The old webpack ESLint plugin honored NOLINT to skip linting during the release build (since lint runs separately on the line). The Vite migration has no equivalent consumer, so the env var is dead config.
The previous cssRuntimePlugin hand-rolled ~130 lines of CSS Modules logic — regex class-name parsing, sha256 hashing, manual composes resolution — that re-implemented (incompletely) what Vite handles via postcss-modules. Cross-file composes, nested rules, complex selectors, and pseudo-classes were all silent footguns waiting for a contributor to write a more complex CSS module. Let Vite do the CSS parsing and class-name scoping. A new ~25-line inlineExtractedCssPlugin walks chunk.viteMetadata.importedCss at generateBundle time, inlines each chunk's extracted CSS as a <style> injection IIFE, and deletes the orphan asset. Same end behavior as the old plugin (CSS bundled into the standalone HTML, class names work), but with Vite's battle-tested CSS Modules implementation underneath. Also set assetsInlineLimit:Infinity on the ES module build to match the application build; the old plugin always inlined url() refs as data URIs, and extracted assets would otherwise resolve to /_assets/ paths that don't honor the VitePress base prefix. Net: -196 / +41 lines.
Forrest Li added matched ESM and UMD .d.ts validation configs in 2022 (c925926); both have been in CI ever since. The Vite migration kept the ESM half but dropped the UMD half, leaving the UMD .d.ts rewriting pipeline (copyUmdAssetsPlugin in vite.config.js) with no in-repo check. The UMD .d.ts files use absolute "vtk.js/Sources/..." imports rewritten from relative paths at build time. If that rewriting breaks, the only signal today is downstream TypeScript users hitting unresolvable imports. Restore the config and wire it into both build-test and publish workflows so the next .d.ts regression fails at PR time.
Two related broken URL behaviors in the generated examples:
generate-examples.mjs hard-coded the iframe src and full-screen <a>
href to '${name}/index.html' without forwarding window.location.search
or window.location.hash, so navigating to examples/SkyboxViewer.html
?fileURL=... loaded the example wrapper but the iframe inside got the
URL without any parameters. Add a <script setup> block with an
onMounted hook that re-binds the iframe and link to the page URL plus
the current search/hash; Vue's :src/:href bindings then update both.
develop_webxr.md linked to legacy paths like GeometryViewer/Geometry-
Viewer.html and WebXRVolume/WebXRVolume.html that the new build never
emits. After the new build the working paths are GeometryViewer.html
(the VitePress wrapper, which forwards its query string to the iframe
per the change above) and WebXRVolume.html. Rewrite all ten broken
references and the four nested ones inside fileURL=[...] values.
The Testing sidebar items hard-coded link: '/vtk-js/coverage/...', but VitePress automatically prepends base: '/vtk-js/' to absolute links. The built sidebar ended up with /vtk-js/vtk-js/coverage/... and docs:build logged "No matching file" warnings. Drop the manual prefix so VitePress emits the intended single-prefixed path. Coverage report files are still generated separately by CI and served at /vtk-js/coverage/ on the deployed site; this fix only corrects the sidebar link the docs site emits.
docs:generate writes ~200 files into Documentation/api/, ~179 into Documentation/examples/, and a Documentation/examples/gallery.js manifest. None were gitignored, so a casual `git add Documentation/` after running the docs pipeline locally would commit hundreds of build artifacts. Both directories keep one hand-authored index.md that stays tracked via negated patterns. sidebar.ts is left tracked-as-placeholder for now (config.ts imports it; gitignoring needs a fresh-checkout strategy that's out of scope).
The Vite migration dropped the slimmed-down vtk-lite.js UMD bundle. It was never documented but was published to npm and CDN for years, and at least one downstream (sphinxcontrib-cadquery) vendors it. Copy vtk.js to vtk-lite.js so existing <script src=.../vtk-lite.js> and CDN consumers keep working. Byte-identical so the shared sourceMappingURL still resolves vtk.js.map. Adds ~2.65MB to the UMD npm tarball — accepted as a one-version deprecation alias. BREAKING_CHANGES.md notes the alias and the one behavior change worth flagging: anything indexing into the ColorMaps array by position now sees the full preset set instead of the lite subset.
Merged
12 tasks
daker
requested changes
May 17, 2026
Author
|
i pushed a few more changes: Extracted vite.config.js's inline plugins into Utilities/build/vtk-plugins.mjs, enabled ESM sourcemaps, fixed compareImages's expect() shape, rewrote the example-runner CLI as ESM with a direct config function, and defaulted vitest browser mode to headless |
vite.config.js had grown to ~450 lines holding six inline Rollup
plugins (copyEsmAssetsPlugin, copyUmdAssetsPlugin,
generateDtsReferencesPlugin, cleanupAssetsPlugin, injectEsmCssPlugin,
inlineUmdCssPlugin) plus five filesystem helpers and the
SOURCE_IGNORE_LIST/ignoreSourceFile pair. Move all of that to a new
Utilities/build/vtk-plugins.mjs alongside the existing generic
plugins.mjs, so vite.config.js becomes config wiring only (~150
lines). Same closeBundle/generateBundle behavior, including the
vtk-lite.js alias and the .module.css inlining.
Drive-by cleanups while moving:
- Extract flattenIndexEntry so both the ESM entryFileNames callback
and generateDtsReferencesPlugin use a single Foo/index -> Foo
helper instead of duplicate regexes.
- Tighten externals from new RegExp('^' + name) to ^name(/|$) so a
hypothetical @types/webxray-foo wouldn't be matched by the
@types/webxr pattern. Today nothing collides, but the old form
was a quiet footgun.
- Replace the hand-rolled copyDir with fs.cpSync({recursive:true}),
stable in the Node 22 we already pin in CI.
- Share the style-injection IIFE template between the ESM and UMD
CSS plugins.
Also flip ESM sourcemap to true. UMD already had it; ESM was off
without a stated reason and the old rollup ESM build emitted them.
Per-file .map files are siblings of each preserveModules chunk.
The migration left compareImages calling expect() with the condition as the value and the diagnostic message as the second positional arg, then chaining .toBeTruthy(): expect(minDelta < mismatchTolerance, '...').toBeTruthy(); This works because Vitest 4's expect(value, message) carries the message into the assertion error, but it reads like the old tape t.ok(cond, msg) shape and obscures both the matcher and the actual value on failure. Switch to expect(value, msg).toBeLessThan(...) so the failure report shows the actual delta percentage instead of just "true is not truthy". Mirror the dimensions check with .toBe(true).
example-runner-cli.js was CJS (require) that loaded Vite via
dynamic import, then spawned createServer({ configFile: ... })
pointing at vite.example.config.mjs (ESM). The two halves
communicated by mutating process.env with EXAMPLE_ENTRY,
EXAMPLE_NAME, EXAMPLE_HOST, etc., which the config file then read
back at module-load time. Mixing module formats in a modernization
PR is jarring, and the env-var protocol forces a singleton vite
process per CLI invocation.
Rewrite the CLI as .mjs and switch vite.example.config.mjs from a
defineConfig'd default export to an exported
createExampleConfig({ repoRoot, entry, name, host, port,
openBrowser, useHttps }) function. The CLI imports that function
and passes the result directly to createServer() with
configFile:false, so there's no env-var handoff and no file path
to keep in sync. Also expose --host and --port flags now that
they're cheap to thread through.
Run via the new path in package.json scripts:
example, example:https, example:webgpu
Owner
|
@PaulHax i am ok with the changes, do you want to merge or wait until you finish ? |
Local runs popped the Vitest browser-mode test runner UI every invocation. CI was already headless (vitest detects CI=1 automatically); set browser.headless explicitly so local matches. browser.headless suppresses both the UI tab and the test execution browser; setting it on the per-instance launch options only affects the latter.
Author
|
🙏 I done for now and ready to merge. |
Owner
|
@PaulHax rebase & merge right ? |
Author
sounds right, Hopoefully this updates your PR on main repo. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
12 small commits addressing review findings.
cssRuntimePluginreplaced with Vite native CSS Modules + post-process injectvtk-lite.jsshipped as byte-identical alias ofvtk.js(BREAKING_CHANGES entry added)tsconfig.umd-check.jsonrestored (ESM counterpart was kept)test-only-check.shextended toit.only/describe.only/vtk-js/vtk-js/coverage/doubling fixedlocation.search/hashto iframescompact: falseand deadNOLINT=1