vite-plugin-nucleus
Build, serve and preview a Nucleus Stack site with one Vite plugin: pages, Quark modules and the service worker are found by convention, and the preview answers as your host does.
Features
- One line of config
plugins: [nucleus()]coversvite,vite buildandvite preview - Conventions, not entry lists Root
*.htmlfiles are pages,*.tsfiles are Quark@usemodules, the service worker is bundled - Host-true preview
vite previewanswers as Cloudflare Workers static assets do:_redirects,_headers, the 404 page - Kit from a CDN
kit: "unpkg"loads the NucleusKit from unpkg in a deploy build instead of bundling it - CSS chain included
@import/@import-glob, mixins, custom selectors and preset-env; nesting andlight-dark()shipped as written - Prerender-ready Builds what
nucleus-ssr prerenders, and serves it for browser checks
Installation
This package is available in the
npm install @excom/vite-plugin-nucleusimport { /* … */ } from "@excom/vite-plugin-nucleus";Peer dependencies (1)
Packages a consumer must install alongside this one. Workspace deps are bundled.
| Package | Version |
|---|---|
|
|
vite |
^8.3.0 |
Usage
Add the plugin to vite.config.js; vite, vite build and vite preview need nothing else. It runs on Vite 8.3 and Node 24.13, or later.
// vite.config.js
import { nucleus } from "@excom/vite-plugin-nucleus";
export default {
plugins: [nucleus()], // a deploy build: nucleus({ kit: "unpkg" })
};// vite.config.js
import { nucleus } from "@excom/vite-plugin-nucleus";
export default {
plugins: [nucleus()], // a deploy build: nucleus({ kit: "unpkg" })
};Conventions
What the project holds decides what is built:
index.html a page, as every *.html at the root
shell.ts a Quark module, built to /shell.js
public/
_redirects /shell /shell.js 200
views/cart/cart.ts a Quark module, built to /views/cart/cart.js
service-worker/service-worker.js bundled into one classic scriptindex.html a page, as every *.html at the root
shell.ts a Quark module, built to /shell.js
public/
_redirects /shell /shell.js 200
views/cart/cart.ts a Quark module, built to /views/cart/cart.js
service-worker/service-worker.js bundled into one classic script- Every
*.tsat the root and underpublicDiris a Quark module, built unhashed at its URL: keep other TypeScript in a folder of the root. Declarations,*.config.ts,*.test.tsand*.spec.tsare left alone - An extensionless module URL (
@use "/shell") needs its200line in_redirects, in dev as on the host - The files the service worker imports are left out of the build unless a page or sheet names them. Dev sends
Service-Worker-Allowed: /with the worker; on the host that is a line of your_headers - Two files that would answer at one URL stop the build
The kit
A page loads the kit from a module script: import "@excom/nucleus-kit/nucleus-kit.progressive";. That path resolves from the first NucleusKit release after 0.3.0; with 0.3.0 write …/nucleus-kit.progressive.min, which works in both modes.
kit: "bundled"(default) Vite bundles the kit like any dependencykit: "unpkg"vite buildloads it from unpkg at the installed version, so the kit must be installed in the app. Imports of@excom/nucleus-kit/<entry>in a script and of@excom/nucleus-kit/<name>.cssin a stylesheet are rewritten; the bare@excom/nucleus-kitis not. The build stops when kit code would ship, or on a path the kit does not export
What the plugin sets
- Set by the plugin The build's inputs and output file names,
css.postcss,css.lightningcss.exclude(the minifier leaveslight-dark()alone: Valence declarescolor-schemein its own stylesheet, and a loweredlight-dark()only works in a sheet that declares it itself) and, withkit: "unpkg",build.modulePreload: { polyfill: false }. A PostCSS config file is not read: add plugins incss.postcss.plugins - Yours
publicDir,build.outDir,build.emptyOutDir(defaulttrue) andbuild.assetsDir - Not supported
base: the site is served from/
Dev / preview
viteapplies the200lines of_redirectsand answers any other unknown route withindex.html. The site is read once per start: a new page or module needs a restartvite previewserves the build as Cloudflare Workers static assets do:_redirects,_headers(withoutCache-ControlandStrict-Transport-Security) and theassetsoptionsnot_found_handling/html_handlingof the nearestwrangler.jsoncorwrangler.jsonwhoseassets.directoryis the build. What it does not emulate, it refuses with an error: awrangler.toml, a Worker script (main),run_worker_first
Host / CSS entries
@excom/vite-plugin-nucleus/hostserveSite({ root, port?, shell? })serves a build as the preview does, on every network interface, and resolves{ port, close() }: for browser checks. Withshell, the filenucleus-ssr --save-shellwrote, every prerendered page answers with the untouched shell, the cold origin of nucleus-ssr'scold-render check . AlsohostHandler,answerOf,hostOf,rulesOf,redirectsOf,headersOf@excom/vite-plugin-nucleus/csscssConfigis the Vitecssoption the plugin sets, for another Vite config;transformCss(css, from)runs the same chain on one stylesheet
Credits
The host emulation holds portions ported from Cloudflare's workers-sdk: see THIRD-PARTY-NOTICES.md.
Attributes
Role —option is configurable, state
is managed by the element (read-only), or hybrid which is both.
Provision
Thisprovision property lives on the DOM node.
Read / watch it with Quark’s prop("provision"), or listen
for the neutron-provision event from app JS.
Events
Type
command event (HTML Command API): from a
<button command commandfor>, an <event-handler
command-name>, or a CommandEvent.
Recognized Elements
Child or descendant elements are recognized by and relevant to its functionality.Styles
@custom-selector synonyms so custom elements can share
semantic styles. Element aliases match the host tag / .tag-* class; state
aliases match attributes (nested under the element alias).
Release notes (1)
0.1.2
- Fix colours set with
light-dark()in a site build: every one was invalid, because the build rewrote the function into a form that only works in a stylesheet declaringcolor-schemeitself, which Valence does for the app.light-dark()now ships as written (Chrome 123, Firefox 120, Safari 17.5)
Release notes
e.preventDefault() is not
synchronously called on the event.
all: revert-layer.
Valence.css ships this as .unstyled and .unstyled-all for the entire tree.