vite-plugin-cesium-engine
v1.10.0
Published
Zero-config Vite plugin for @cesium/engine — handles assets, CESIUM_BASE_URL, and Ion token automatically.
Maintainers
Readme
vite-plugin-cesium-engine
Zero-config Vite plugin for @cesium/engine.
Handles static assets, CESIUM_BASE_URL, widget CSS, and your Ion token — nothing to configure by hand.
Why this plugin?
Other Cesium Vite plugins target the full cesium package.
This one is purpose-built for @cesium/engine only — the lean, widget-free core — so you stay in control of the UI.
What it does for you automatically:
- ✅ Copies WASM workers, built files, assets, and
CesiumWidget.cssto your output - ✅ Sets
CESIUM_BASE_URLas a compile-timedefineconstant (no runtime script needed) - ✅ Injects the
CesiumWidget.css<link>tag - ✅ Optionally bakes your Ion access token in at build time (per-environment support)
- ✅ Auto-detects
CESIUM_ION_TOKEN/CESIUM_ION_TOKEN_<MODE>from.envfiles - ✅ Exposes
virtual:cesiumandvirtual:cesium/versionmodules for typed access to build-time constants - ✅ Validates your Ion token format and warns about misconfigurations at startup
- ✅ Optionally splits Cesium into its own cacheable output chunk
- ✅ Supports externally hosted or CDN-hosted Cesium assets without copying them
- ✅ Exposes
virtual:cesiumandvirtual:cesium/versionmodules for typed runtime constants
Install
# npm
npm i -D @cesium/engine vite-plugin-cesium-engine
# pnpm
pnpm add -D @cesium/engine vite-plugin-cesium-engine
# yarn
yarn add -D @cesium/engine vite-plugin-cesium-engineUsage
Add the plugin to your Vite config — that's it.
// vite.config.ts
import { defineConfig } from "vite";
import { cesiumEngine } from "vite-plugin-cesium-engine";
export default defineConfig({
plugins: [cesiumEngine()],
});Then use @cesium/engine directly — no boilerplate, no globals:
import { CesiumWidget } from "@cesium/engine";
const widget = new CesiumWidget(document.getElementById("cesium-container")!);CESIUM_BASE_URL is set via Vite's define mechanism — it's replaced as a compile-time constant inside Cesium's source during bundling, which is how Cesium intends it to be consumed. No window.CESIUM_BASE_URL script tag is injected at runtime.
Options
cesiumEngine({
// Use assets hosted outside this build instead of copying them to dist
assets: "external",
// Ion token — string, per-environment map, sync/async callback, or omit to auto-read from .env
ionToken: "eyJhbGci...",
// Override where assets are served from (default: derived from Vite's base)
cesiumBaseUrl: "/static/cesium",
// Override the injected CesiumWidget stylesheet URL
widgetCssUrl: "/styles/CesiumWidget.css",
// Override where assets are copied to in the output dir (default: "cesium")
assetsPath: "static/cesium",
// Override the @cesium/engine package directory (relative to Vite's root)
enginePath: "../vendor/cesium-engine",
// Split @cesium/engine into its own named chunk (optional)
chunkName: "vendor-cesium",
// Print what the plugin is doing at startup
debug: true,
})| Option | Type | Default | Description |
| --- | --- | --- | --- |
| assets | "copy" \| "external" | "copy" | Copy and locally serve Cesium assets, or use externally hosted assets. External mode requires cesiumBaseUrl. |
| ionToken | string \| Record<string, string> \| (mode) => string \| Promise<string> | undefined | Ion access token. String, per-mode map, or sync/async callback. Omit to auto-read from .env. |
| cesiumBaseUrl | string | "/${assetsPath}" | URL from which Cesium assets are served. Defaults to Vite's base + assetsPath. |
| widgetCssUrl | string | "${cesiumBaseUrl}/Widget/CesiumWidget.css" | URL of the injected CesiumWidget stylesheet. Useful when an external CDN has a different layout. |
| assetsPath | string | "cesium" | Output subfolder (relative to build.outDir) where static assets are copied. |
| enginePath | string | "node_modules/@cesium/engine" | Package directory, resolved relative to Vite's root. Absolute paths are also supported. |
| chunkName | string | undefined | Split @cesium/engine into a dedicated output chunk with this name. Omit to bundle Cesium with your app. |
| debug | boolean | false | Log asset copy targets, resolved token, and base URL at startup. |
Ion token
Callback (async)
Pass an async function to fetch the token from a secrets manager, vault, or any
async source. The callback receives the current Vite mode and must return a
string or Promise<string>. It is called once at build start, after
configResolved — which means sync token sources (string, map, env vars) are
available immediately, while the callback result is ready before any module
transforms run.
cesiumEngine({
ionToken: async (mode) => {
// AWS Secrets Manager, HashiCorp Vault, Azure Key Vault, etc.
const secret = await getSecret(`cesium-ion-token-${mode}`);
return secret.value;
},
})The callback takes priority over .env variables. Returning an empty string is
treated as no token (Cesium's built-in default is used).
Auto .env detection
When ionToken is not set in the plugin options, the plugin automatically reads
your Ion token from Vite's loaded environment variables — no boilerplate needed.
Place your token in a .env file at the project root:
# .env
CESIUM_ION_TOKEN=eyJhbGci...Or use a mode-specific file to keep tokens per environment:
# .env.production
CESIUM_ION_TOKEN_PRODUCTION=eyJhbGci...
# .env.development
CESIUM_ION_TOKEN_DEVELOPMENT=eyJhbGci...The lookup order is:
CESIUM_ION_TOKEN_<MODE>(uppercased, e.g.CESIUM_ION_TOKEN_PRODUCTION)CESIUM_ION_TOKEN— generic fallback for any mode- Nothing — Cesium's own built-in default token is used
Explicit ionToken in the plugin options always takes priority over env vars.
Note: these variables do not need the
VITE_prefix. The plugin reads them server-side during the build and bakes the value in as a string literal.
When debug: true is set, the plugin logs which variable it picked up:
[cesium-engine] ionToken : read from env var CESIUM_ION_TOKEN_PRODUCTIONPer-environment Ion tokens
Pass an object keyed by Vite mode to use different tokens per environment.
This is baked in at build time — no runtime env variables needed.
// vite.config.ts
cesiumEngine({
ionToken: {
development: process.env.CESIUM_ION_TOKEN_DEV,
staging: process.env.CESIUM_ION_TOKEN_STAGING,
production: process.env.CESIUM_ION_TOKEN_PROD,
// Optional fallback for any unrecognized mode:
default: process.env.CESIUM_ION_TOKEN_DEV,
},
})vite build --mode staging # uses CESIUM_ION_TOKEN_STAGING
vite build # uses CESIUM_ION_TOKEN_PROD (mode defaults to "production")
vite dev # uses CESIUM_ION_TOKEN_DEV (mode defaults to "development")Virtual modules
The plugin exposes typed virtual modules so app code never needs to touch
window globals or hardcode paths.
Add the types to your tsconfig.json:
{
"compilerOptions": {
"types": ["vite-plugin-cesium-engine/virtual"]
}
}virtual:cesium
import { CESIUM_BASE_URL, ION_TOKEN } from "virtual:cesium";
console.log(CESIUM_BASE_URL); // e.g. "/cesium/"
console.log(ION_TOKEN); // your token, or nullvirtual:cesium/version
import { CESIUM_VERSION } from "virtual:cesium/version";
console.log(CESIUM_VERSION); // e.g. "25.0.0"Useful for logging, bug reports, or conditional behavior when supporting multiple Cesium versions in the same codebase.
Splitting Cesium into its own chunk
Use the chunkName option to isolate all @cesium/engine modules into a dedicated output chunk. This keeps Cesium independently cacheable — a change to your app code won't bust the Cesium bundle in the browser cache.
cesiumEngine({ chunkName: "vendor-cesium" })That's equivalent to manually configuring manualChunks in your Rollup output options, but in one place. If you need more control (e.g. combining Cesium with other vendor chunks), the exported cesiumChunks() helper is still available:
import { cesiumEngine, cesiumChunks } from "vite-plugin-cesium-engine";
export default defineConfig({
plugins: [cesiumEngine()],
build: {
rollupOptions: {
output: {
manualChunks: (id) => {
if (id.includes("three")) return "vendor-3d";
return cesiumChunks("vendor-cesium");
},
},
},
},
});Custom asset path
Use assetsPath + cesiumBaseUrl together when deploying to a CDN or a
framework with an opinionated public directory (Laravel, Rails, etc.).
// vite.config.ts
export default defineConfig({
base: "/app/",
plugins: [
cesiumEngine({
assetsPath: "vendor/cesium", // copied to dist/vendor/cesium/
cesiumBaseUrl: "/app/vendor/cesium", // served from this URL
}),
],
});The plugin will warn you at startup if cesiumBaseUrl doesn't start with
Vite's base, which would cause assets to resolve incorrectly.
External or CDN-hosted assets
Set assets to "external" when Cesium assets are deployed independently.
The plugin will not copy assets into dist or register local asset middleware
in the Vite development server. Bundling, the base URL define, widget CSS link,
Ion token handling, and virtual modules continue to work normally.
cesiumEngine({
assets: "external",
cesiumBaseUrl:
"https://cdn.jsdelivr.net/npm/[email protected]/Build/Cesium",
widgetCssUrl:
"https://cdn.jsdelivr.net/npm/@cesium/[email protected]/Source/Widget/CesiumWidget.css",
})The full cesium package provides the Assets, ThirdParty, and Workers
directories under one CDN base. Its version must match the installed engine;
for example, CesiumJS 1.144 depends on @cesium/engine 26.2. Pin both URLs so a
future CDN release cannot become incompatible with the application bundle.
For a custom CDN, cesiumBaseUrl must contain the same runtime asset layout
normally emitted by the plugin. When it also contains
Widget/CesiumWidget.css, widgetCssUrl can be omitted.
Debug mode
cesiumEngine({ debug: true })Output at dev-server startup:
[cesium-engine] mode : development
[cesium-engine] vite base : "(empty)"
[cesium-engine] cesiumBaseUrl: "/cesium"
[cesium-engine] assetsPath : "cesium"
[cesium-engine] ionToken : eyJhbGciOiJ... (mode: development)Examples
Complete, ready-to-run starter projects are available in the examples/ directory:
| Example | Stack | Description |
| --- | --- | --- |
| examples/react | React 19 + TypeScript | useEffect lifecycle, HMR-safe widget init |
| examples/vue | Vue 3 + TypeScript | Composition API, onMounted / onBeforeUnmount |
| examples/svelte | Svelte 5 + TypeScript | onMount with return-value cleanup |
| examples/vanilla-ts | Vanilla TypeScript | Zero framework, import.meta.hot HMR cleanup |
| examples/vanilla-ts-external | Vanilla TypeScript | Version-pinned jsDelivr assets using external mode |
Each example includes all project files and can be run with:
pnpm install
pnpm dev:react
pnpm dev:svelte
pnpm dev:vanilla-ts
pnpm dev:vanilla-ts-external
pnpm dev:vueKnown warnings
[EVAL] Use of direct eval function is strongly discouraged
This comes from protobufjs, a transitive dependency of @cesium/engine. The eval is intentional in that library (used for optional require() resolution) and is not a security risk in practice. Suppress it in your app's vite.config.ts:
build: {
rollupOptions: {
onwarn(warning, defaultHandler) {
if (warning.code === "EVAL" && warning.id?.includes("protobufjs")) return;
defaultHandler(warning);
},
},
},