@jts-studios/web-build
v0.1.0
Published
The build for a multi-frontend PHP + Vite project: one command, generated server config, a staged deploy directory.
Maintainers
Readme
@jts-studios/web-build
The build for a multi-frontend PHP + Vite project: one command, generated server config, and a staged deploy directory that is complete or refuses to exist.
Why this is a tool and not a Vite plugin
Mostly a tool, with one plugin inside it.
runBuild drives Vite once per site, then stages a deploy directory, installs
a production backend and renders the server config for all sites together. A
Vite plugin runs inside a single build and cannot orchestrate other builds or
see other sites, so that part could never be one.
htaccessPlugin is a plugin, and has to be: it hooks writeBundle so each
site emits its own .htaccess, including when you build that one site on its
own rather than through the top-level command.
createFrontendViteConfig is neither — it is a config factory that returns a
Vite config with the plugin already applied.
Install
npm install -D @jts-studios/web-build{
"scripts": {
"build": "jts-build",
"build:list": "jts-build --list"
}
}Configure
One build.config.mjs at the project root. Everything in it is a fact about
that project or its server; everything else lives in the package.
import { SITE_LIST } from "./shared/frontend/tooling/sites.mjs";
export default {
sites: SITE_LIST,
server: {
// apache or nginx. A machine runs one, and the build emits only that
// one's config — the other would be a file read by nothing.
type: "nginx",
root: "/srv/example/current",
phpSocket: "/run/php/php8.3-fpm.sock",
certificate: "/etc/ssl/cloudflare/example.com.pem",
certificateKey: "/etc/ssl/cloudflare/example.com.key",
uploadLimit: "24m",
},
nginx: { name: "example" },
probe: {
classes: ["App\\Backend\\ServiceFactory"],
methods: ["users"],
},
};| | |
|---|---|
| sites | every site: dir, docroot, host, and optionally csp, noindex, cache, api, redirects, notes |
| server.type | "apache" or "nginx". Decides which config is generated; anything else is an error |
| server | paths on the deploy target. Only the deploy knows these, which is why they are not in an .env |
| nginx.name | names the output: <name>.conf and <name>-http.conf |
| probe | classes that must load from the staged autoloader, and accessors classes[0] must carry |
| backendInclude | optional; defaults to bootstrap, cleanup, migrations, src, composer files, schema.sql |
sites stays in your project. It is the part that genuinely differs, and
splitting it out is what lets the renderers be shared at all.
Each site's vite.config.js
import { defineConfig } from "vite";
import { PHP } from "@jts-studios/vite-plugin-php";
import { createFrontendViteConfig } from "@jts-studios/web-build/vite";
import { SITES, SERVER_TYPE } from "../shared/frontend/tooling/sites.mjs";
export default defineConfig(
createFrontendViteConfig({ phpPlugin: PHP, site: SITES.www, server: SERVER_TYPE })
);What it produces
deploy/
backend/ production vendor, installed not copied
<docroot>/ one per site
nginx/ only when server.type is "nginx"Under "apache" each docroot gets its own generated .htaccess and there is
no nginx/. Under "nginx" there are no .htaccess files at all and the
server blocks are staged instead. Never both: a machine runs one web server,
and shipping the other's rules produces a file that looks like configuration,
is read by nothing, and will eventually be edited by someone who assumes it
matters.
The same choice reaches each site's vite.config.js, which is why it is
declared once in your sites.mjs and imported by both.
Two renderers, one set of site definitions. A CSP change or a new host is
written once and comes out correct in whichever config you generate — and
identical in the other if you ever switch. Four hand-maintained .htaccess
files once drifted apart and took a host down; that is what this exists to
prevent, and keeping a copy of the renderers per project would recreate it one
level up.
Exports
import {
runBuild, // the orchestrator
createFrontendViteConfig, // config factory
renderHtaccess, // Apache, one site
htaccessPlugin, // the Vite plugin that writes it
renderNginxSites, // nginx server blocks, all sites
renderNginxHttp, // the http-context half: log_format, map, gzip
buildCsp, // the shared policy builder
} from "@jts-studios/web-build";Checks it will not skip
The build fails rather than produce a deploy directory you should not upload:
- a site with no CSS or JS emitted
- a missing or empty
index.phporrobots.txt, and.htaccessunder Apache - an empty file anywhere in
public/, which would ship as-is - a staged autoloader that cannot resolve the classes in
probe - a working checkout left with an authoritative classmap
