The web client is built by Vite, not by Meteor. By default the Rocket.Chat server serves it: the server renders the page and the page loads the client. This page explains how that works and how to serve the client from another host.
yarn build:client (apps/meteor/vite/buildForServer.mjs) builds the client with RC_CLIENT_TARGET=server and
copies:
vite/dist/bundletopublic/bundle, which the server ships as static files;vite/dist/.vite/manifest.jsontoprivate/vite-manifest.json.
yarn build:ci runs it before meteor build, so both end up in the server bundle.
Meteor still builds a web.browser program, but its main module (client/meteorShell.ts) is empty: Meteor only
renders the page. Meteor bundles every CSS file under client/ whether or not a module imports it, so
.meteorignore hides them; otherwise the page would load a second copy of the client's styles. Everything the server already did for that page keeps working:
- the head and body injections of
server/lib/ui-master(title, metas, favicons, theme variables, custom CSS and theCustom_Script_*scripts); __meteor_runtime_config__, loaded from/meteor_runtime_config.jswhen the Content-Security-Policy is on;public/assets, the/assets/*defaults and the livechat widget files;- the new-version notice: the client files are in
public/, so a new build changes the version Meteor publishes.
At startup server/lib/ui-master/clientEntry.ts reads the manifest and adds to the page the client's stylesheets, a
modulepreload for each chunk it imports up front, and its entry as a <script type="module">. No tag is inline, so
the Content-Security-Policy needs no change. The URLs get the same prefix Meteor gave its own bundle:
CDN_PREFIX when CDN_PREFIX_ALL is on, otherwise CDN_JSCSS_PREFIX, otherwise the ROOT_URL path prefix.
The client is built with a relative base, so its chunks, fonts and images load from wherever the entry loads: the
server root, a path prefix (ROOT_URL=https://example.com/chat) or a CDN. Their names carry a content hash, so the
server serves /bundle/* with Cache-Control: public, max-age=31536000, immutable.
The build the server serves has hidden source maps: every chunk gets a .map next to it, but the chunk does not
reference it, so browsers never request one. The Docker images strip the maps; the build tarball keeps them, at
bundle/programs/web.browser/app/bundle/<chunk>.js.map. That tarball is the CI artifact build-production and is
published at https://download.rocket.chat/build/rocket.chat-<version>.tgz for releases and at
https://download.rocket.chat/build/rocket.chat-<version>.<commit>.tgz for develop builds.
A chunk name carries its content hash, so a map named after the chunk belongs to that exact build. To debug a workspace:
- Download the tarball of the build the workspace runs and extract the maps.
<version>isinfo.versionand<commit>is the fullinfo.commit.hashthat/api/inforeturns to a user withview-statistics; anonymous callers only get the major and minor version.tar xzf rocket.chat-<version>[.<commit>].tgz --wildcards 'bundle/programs/web.browser/app/bundle/*.map'. - Serve them with CORS, for example
npx http-server bundle/programs/web.browser/app/bundle --cors -p 8080. - In Chrome DevTools, open the chunk under Sources, right-click its code, choose Add source map… and enter
http://localhost:8080/<chunk>.js.map. Firefox has the same entry in its debugger.
To map a stack trace without a browser, resolve each chunk:line:column against the same files with the
source-map package.
Set CDN_PREFIX with CDN_PREFIX_ALL on (the default), or turn CDN_PREFIX_ALL off and set CDN_JSCSS_PREFIX, to a
CDN that pulls from the server. The server still renders the page, and the entry, chunks and stylesheets load from the
CDN. Nothing else changes.
yarn build:vite (without RC_CLIENT_TARGET=server) builds a standalone client into apps/meteor/vite/dist:
index.html, bundle/ and a copy of public/. To serve it yourself, in front of a Rocket.Chat server:
- Serve
vite/distas static files. Answer page navigations (GETrequests that accepttext/html) withindex.html, since the client routes on the browser side, except under the server's own pages:/api,/oauth,/_oauth,/_saml,/_cas,/file-upload,/file-decrypt,/data-exportand/livechat. Of those, only/oauth/authorizeand/oauth/errorare client pages. - Proxy every other path to the Rocket.Chat server, and the
/websocketand/sockjsWebSockets. Rewrite the cookie domain to your host, sorc_tokenreaches avatars and downloads. - Define
window.__meteor_runtime_config__before the entry runs, with at leastROOT_URL(your host) andROOT_URL_PATH_PREFIX. Use an external script if your host sends a Content-Security-Policy.
The standalone build builds full URLs from the page's own origin instead of Site_Url, so links keep going through
your proxy, and does not offer admins to change Site_Url to that origin. What the server-rendered page adds is missing: the title and metas, the theme variables, custom CSS, the
Custom_Script_* scripts and the new-version notice. Add what you need to your index.html.
packages/ui-preview does exactly this, with vite preview and a proxy, and is the reference setup.
yarn dev runs Vite in front of a local Meteor server. Vite fetches the page Meteor renders, adds its own entry, and
proxies the server routes, so the page matches production. With RC_SERVER_URL set, Vite serves the standalone
index.html against that server instead. See getting-started.md.