liferay-cx-hmr-setup
v1.0.4
Published
One-command HMR dev setup for Liferay React client extensions — no rebuild, no redeploy, instant hot swap.
Maintainers
Readme
liferay-cx-hmr-setup
One-command HMR setup for Liferay React Client Extensions — zero rebuilds, zero redeploys, instant hot-swap.
The Problem
Liferay client extensions require a full Gradle build + deploy cycle on every code change. This tool eliminates that by setting up Vite HMR so that:
- Your local Vite dev server serves source files directly to Liferay
- React components hot-swap in the browser instantly on save
- Your production build/deploy workflow remains completely unaffected
Quick Start
Run inside any Liferay Vite-based client extension directory:
npx liferay-cx-hmr-setupThe CLI will:
- Validate your project structure
- Auto-detect element key, entry point, and port
- Generate
client-extension.dev.yamlandsrc/dev-preamble.js - Patch
vite.config.jswith CORS headers and theliferayAutoExternalize()plugin - Optionally run
./gradlew <project>:deployDev
After Setup
# One-time (if not already run by the tool):
./gradlew :client-extensions:<your-cx>:deployDev
# Every dev session:
npm run devOpen your Liferay page, edit any .jsx/.tsx file, and watch it update instantly.
What Gets Generated
client-extension.dev.yaml
Overrides the production URL list to point Liferay at your local Vite dev server.
Only active when you run deployDev (not deploy).
your-element-key:
baseURL: "http://localhost:5173"
urls:
- "/@vite/client"
- "/src/dev-preamble.js"
- "/src/main.jsx"src/dev-preamble.js
Installs the React Fast Refresh runtime on window before your app loads.
Required because Liferay loads scripts directly (bypassing Vite's index.html).
liferayAutoExternalize() Vite Plugin
A dev-only Vite plugin (apply: 'serve') imported from the package into your vite.config.js.
It handles two critical problems automatically:
- Unresolvable imports — Packages provided by Liferay at runtime (e.g.,
@liferay/oauth2-provider-web/client) are proxied to the browser's import map instead of crashing Vite. - Cross-CX dependency pre-bundling — When your CX imports components from sibling CXs via relative paths, Vite's dependency scanner is extended to cover those directories at startup, preventing mid-session reloads.
vite.config.js Patch
Adds a server block with CORS enabled and registers liferayAutoExternalize() as the first plugin.
How It Works
Browser loads Liferay page
│
├── loads /@vite/client → opens HMR WebSocket to Vite
├── loads /src/dev-preamble.js → installs React Refresh on window
└── loads /src/main.jsx → renders your custom element
You save a file in src/
│
▼
Vite detects change → sends update via WebSocket
│
▼
React swaps only the changed component — no page reloadRequirements
- Node.js >= 16
- A Liferay Workspace with Gradle Wrapper (
gradlew) - A
customElementtype client extension built with Vite + React
Production Safety
client-extension.dev.yamlis only used when runningdeployDevdeploy,build, and all production tasks are completely unaffectedsrc/dev-preamble.jsis never bundled by Vite (it's not imported in production source)liferayAutoExternalize()usesapply: 'serve'— it is inactive during production builds
License
MIT
