Skip to content

Build webapp Tailwind via Vite instead of the in-browser Play CDN - #40

Merged
aaronbrethorst merged 1 commit into
developfrom
vite-npm
Apr 29, 2026
Merged

aaronbrethorst merged 1 commit into
developfrom
vite-npm

Conversation

@aaronbrethorst

@aaronbrethorst aaronbrethorst commented Apr 29, 2026

Copy link
Copy Markdown
Member

Summary

  • Wires npm + Vite + Vitest into transitclockWebapp via frontend-maven-plugin. mvn package now downloads a pinned Node, runs npm ci, and builds Tailwind into target/frontend-dist/; maven-war-plugin's <webResources> folds that into the WAR at /dist. Single command, no extra build steps for Docker or CI.
  • Retires the Tailwind Play CDN (@tailwindcss/browser@4) — includes.jsp now links the Vite-built /dist/tailwind.css instead of compiling utility classes in-browser at every page load. Play CDN is explicitly not for production.
  • Adds JspStylesheetAuditTest, modeled on the existing JspTaglibAuditTest, as a regression guard. It asserts no JSP or tag references the Play CDN and that includes.jsp links the bundled stylesheet — same silent-failure shape (page returns 200, classes don't apply) the taglib test was written for.

Out of scope

Explicitly deferred to follow-up PRs that can adopt this toolchain incrementally:

  • Vendoring jQuery / Leaflet / Stimulus / Select2 via npm (still on CDNs).
  • Converting the existing ~38 KLoC of classic-script JS to ES modules.
  • Manifest-driven JSP asset resolution with hashed filenames (Vite's manifest.json not yet emitted; assetFileNames: '[name][extname]' keeps the stylesheet at a literal path the JSP can hardcode).
  • Vite dev server with HMR — dev iteration uses npm run dev (watch mode) alongside the existing docker-compose.dev.yml bind-mount.

Notable choices

  • Node installs under ${user.home}/.m2/node rather than target/node so mvn clean doesn't redownload the ~30 MB tarball every full rebuild.
  • frontend/utils.js (a tiny classNames helper) plus its Vitest test is intentional scaffolding so the test runner exercises a real module — gets replaced when the first real frontend module lands.
  • Tailwind v4 @source directives in frontend/tailwind.css scan *.jsp, *.tag, *.html, and *.js under the webapp; the *.tag glob is required because WEB-INF/tags/layout.tag (page chrome) uses utility classes.

Test plan

  • mvn -pl transitclockWebapp -am clean package -DskipTests produces target/web.war containing dist/tailwind.css (verified via unzip -l).
  • mvn -pl transitclockWebapp -am test runs JspStylesheetAuditTest (2 tests), JspTaglibAuditTest (2 tests), and Vitest (2 tests). All pass.
  • cd transitclockWebapp && npm ci && npm run build && npm test works standalone.
  • Production-style WAR deploy: docker compose build tomcat && docker compose up -d tomcat (no .dev override) — Tomcat unpacks web.war and serves /web/dist/tailwind.css (HTTP 200, 23 107 bytes) directly from the unpacked WAR contents at /usr/local/tomcat/webapps/web/dist/tailwind.css.
  • Tomcat (via dev compose) also serves http://localhost:8080/web/dist/tailwind.css with HTTP 200 and real Tailwind v4 output.
  • /web/, /web/reports/?a=1, and /web/maps/map.jsp?a=1 all render with full Tailwind styling and zero console errors. Verified via Playwright.
  • Rendered pages link /web/dist/tailwind.css and do not reference @tailwindcss/browser.
  • CI green on this branch.

Introduces an npm + Vite + Vitest frontend toolchain in
transitclockWebapp, wired into Maven via frontend-maven-plugin so
mvn package downloads a pinned Node, runs npm ci, and builds the
Tailwind bundle automatically. Output lands in target/frontend-dist/
and is folded into the WAR at /dist by maven-war-plugin's webResources.
includes.jsp now links the bundled stylesheet instead of loading
@tailwindcss/browser at every page load.

A new JspStylesheetAuditTest pins the migration: it asserts no JSP
or tag still references the Play CDN and that includes.jsp links the
bundled CSS. Modeled on JspTaglibAuditTest, same silent-failure
shape (page renders 200, classes silently don't apply).

Node is installed under ~/.m2/node so mvn clean doesn't redownload
the ~30 MB tarball every full rebuild. The dev-compose override
gains a third bind mount for target/frontend-dist so npm run dev
output reaches Tomcat without a docker rebuild.

Out of scope: vendoring jQuery / Leaflet / Stimulus / Select2 via
npm (still on CDNs); converting the existing 38 KLoC of classic
JS to ES modules; manifest-driven hashed-filename JSP wiring; Vite
dev server with HMR. All deferred to follow-up PRs that can adopt
this same toolchain incrementally.
@coderabbitai

coderabbitai Bot commented Apr 29, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR introduces a Vite-based frontend build system with Tailwind CSS v4 integration for the transitclockWebapp. It includes Maven plugin configuration to automate frontend dependency installation and builds, updates Docker setup for development iteration, adds frontend utilities and tests, and switches CSS delivery from CDN to precompiled Vite output.

Changes

Cohort / File(s) Summary
Frontend Build Configuration
transitclockWebapp/package.json, transitclockWebapp/vite.config.js, transitclockWebapp/frontend/tailwind.css
Establishes Vite+Tailwind ESM build with npm scripts for dev/build/test, Vite config with Tailwind plugin and jsdom tests, and Tailwind entrypoint with JSP/HTML/JS content scanning.
Frontend Code and Tests
transitclockWebapp/frontend/utils.js, transitclockWebapp/frontend/utils.test.js
Adds classNames() utility for conditional CSS class concatenation and corresponding Vitest test cases validating truthy/falsy filtering.
Maven Build Integration
transitclockWebapp/pom.xml
Integrates frontend-maven-plugin to install Node v22.12.0, run npm ci/build/test, and packages output to WAR dist directory; maven-war-plugin updated to include frontend artifacts.
Template and CSS Delivery
transitclockWebapp/src/main/webapp/template/includes.jsp
Switches Tailwind delivery from in-browser CDN compilation to precompiled stylesheet from WAR /dist/tailwind.css path.
Frontend Testing
transitclockWebapp/src/test/java/org/transitclock/webapp/jsp/JspStylesheetAuditTest.java
Adds two JUnit tests enforcing Vite-built Tailwind usage: one scans JSPs for CDN/play references (fails if found), another verifies includes.jsp references bundled CSS.
Docker Development Setup
docker-compose.dev.yml, .dockerignore
Documents npm run dev watch workflow; adds read-only bind mount exposing target/frontend-dist/ to container at /dist; excludes node_modules from build context.
Repository Configuration
.gitignore, CLAUDE.md
Ignores frontend node_modules directory; documents frontend iteration workflow including mvn package, npm run dev, and Vite output mounting.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: migrating Tailwind CSS compilation from in-browser CDN to Vite-based build output.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch vite-npm

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
transitclockWebapp/vite.config.js (1)

8-13: Add Node version constraint or use compatibility-safe dirname.

The code uses import.meta.dirname (lines 8, 12) which requires Node v18.17.0+. The package.json lacks an "engines.node" declaration, so developers running npm run dev directly with older Node versions will fail. Either add "engines": { "node": ">=18.17.0" } to package.json or adopt the compatibility-safe pattern using fileURLToPath and path.dirname.

🔧 Compatibility-safe alternative
 import { defineConfig } from 'vite';
 import tailwindcss from '@tailwindcss/vite';
 import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));

 export default defineConfig({
   plugins: [tailwindcss()],
   build: {
-    outDir: path.resolve(import.meta.dirname, 'target/frontend-dist'),
+    outDir: path.resolve(__dirname, 'target/frontend-dist'),
     emptyOutDir: true,
     rollupOptions: {
       input: {
-        tailwind: path.resolve(import.meta.dirname, 'frontend/tailwind.css'),
+        tailwind: path.resolve(__dirname, 'frontend/tailwind.css'),
       },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@transitclockWebapp/vite.config.js` around lines 8 - 13, The project uses
import.meta.dirname in vite.config.js (used for outDir and rollupOptions.input
tailwind paths) which requires Node >=18.17.0; either add an engines.node entry
to package.json (e.g., "engines": { "node": ">=18.17.0" }) or make the config
Node-version-safe by deriving a dirname: import { fileURLToPath } from 'url' and
const __dirname = path.dirname(fileURLToPath(import.meta.url)), then replace
import.meta.dirname usages with __dirname so outDir and rollupOptions.input
(tailwind) resolve correctly on older Node versions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@transitclockWebapp/vite.config.js`:
- Around line 8-13: The project uses import.meta.dirname in vite.config.js (used
for outDir and rollupOptions.input tailwind paths) which requires Node
>=18.17.0; either add an engines.node entry to package.json (e.g., "engines": {
"node": ">=18.17.0" }) or make the config Node-version-safe by deriving a
dirname: import { fileURLToPath } from 'url' and const __dirname =
path.dirname(fileURLToPath(import.meta.url)), then replace import.meta.dirname
usages with __dirname so outDir and rollupOptions.input (tailwind) resolve
correctly on older Node versions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e4003d56-89db-4638-b8f1-405fb25f73fa

📥 Commits

Reviewing files that changed from the base of the PR and between 62ad55c and b1967d3.

⛔ Files ignored due to path filters (1)
  • transitclockWebapp/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (12)
  • .dockerignore
  • .gitignore
  • CLAUDE.md
  • docker-compose.dev.yml
  • transitclockWebapp/frontend/tailwind.css
  • transitclockWebapp/frontend/utils.js
  • transitclockWebapp/frontend/utils.test.js
  • transitclockWebapp/package.json
  • transitclockWebapp/pom.xml
  • transitclockWebapp/src/main/webapp/template/includes.jsp
  • transitclockWebapp/src/test/java/org/transitclock/webapp/jsp/JspStylesheetAuditTest.java
  • transitclockWebapp/vite.config.js

@aaronbrethorst
aaronbrethorst merged commit 24f188a into develop Apr 29, 2026
3 checks passed
@aaronbrethorst
aaronbrethorst deleted the vite-npm branch April 29, 2026 01:28
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.

1 participant