Files
md-files/.agents/skills/reveal-3d/references/vite-config.md
T
2026-05-31 20:25:41 +00:00

9.6 KiB

Vite Configuration for @cognite/reveal in a Flows monorepo app

src/main.tsx — process polyfill must be the very first two lines

Prepend these two lines before any other import:

import process from 'process';
(window as unknown as Record<string, unknown>).process = process;

// all other existing imports below ↓

The rest of main.tsx stays as-is. Order matters — the polyfill must run before any module that reads process.


vite.config.ts — standalone config (not mergeConfig)

Flows apps use a standalone vite.config.ts (not a shared base config from the monorepo root). Replace the file entirely with the following:

import path from 'node:path';

import { fusionOpenPlugin } from '@cognite/dune/vite';
import tailwindcss from '@tailwindcss/vite';
import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';
import mkcert from 'vite-plugin-mkcert';

export default defineConfig({
  base: './',
  plugins: [react(), mkcert(), fusionOpenPlugin(), tailwindcss()],
  define: {
    // Some CJS deps use `global` instead of `globalThis`
    'process.env': {},
    'process.platform': JSON.stringify(''),
    'process.version': JSON.stringify(''),
    global: 'globalThis',
  },
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),

      // --- Node built-in polyfills ---
      // Use explicit package aliases, not vite-plugin-node-polyfills.
      // The plugin introduces transitive dep conflicts ("Could not resolve 'inherits'").
      // The `process`, `util`, and `assert` packages must be in app dependencies.
      util: 'util/',
      assert: 'assert/',
      process: 'process/browser',

      // --- Single Three.js instance ---
      // @cognite/reveal bundles its own Three.js copy. Without this alias, the app
      // and Reveal load two separate copies → "Multiple instances of Three.js" warning
      // and broken rendering. Requires `three` as a direct app dependency.
      three: path.resolve(__dirname, 'node_modules/three/build/three.module.js'),
    },

    // pnpm uses a virtual store with symlinks. Packages that symlink to different
    // paths can resolve to separate module instances even for the same package.
    // `dedupe` forces a single physical copy for all pre-bundles.
    // Missing `react`/`react-dom` here causes ReactCurrentDispatcher errors.
    // Missing `three` causes "Multiple instances" warnings.
    dedupe: ['react', 'react-dom', 'react/jsx-runtime', '@tanstack/react-query', 'three'],

    conditions: ['import', 'module', 'browser', 'default'],
  },
  optimizeDeps: {
    // Do not exclude the copied Reveal feature bundle. Let Vite pre-bundle
    // React, React Query, Three.js, and Reveal dependencies as one graph.
    esbuildOptions: {
      define: { global: 'globalThis' },
    },
    include: [
      // Vite can't auto-discover bare polyfill imports (no source file imports them
      // directly). List them explicitly so esbuild pre-bundles them.
      'process',
      'util',
      'assert',
      // Heavy/complex deps — explicit listing speeds up cold starts
      'three',
      '@cognite/reveal',
      // React ecosystem — pre-bundling creates the CJS→ESM singleton all deps share.
      // If any of these are missing, a dep that imports React raw can get a second copy.
      'react',
      'react-dom',
      '@tanstack/react-query',
    ],
  },
  server: {
    port: 3002,
  },
  worker: {
    // @cognite/reveal spawns ES module web workers. Without 'es' format they fail
    // silently — black screen with no console error.
    format: 'es',
  },
});

Why each setting is needed

Setting Reason
util/, assert/, process/browser aliases Browser-compatible replacements for Node built-ins. Packages must be in dependencies. Do NOT use vite-plugin-node-polyfills — causes "Could not resolve 'inherits'"
process polyfill in main.tsx first @cognite/reveal deps call process.env at runtime (not build-time). The define replacements handle build-time; the window assignment handles runtime
define.global = 'globalThis' Some CJS deps use global instead of globalThis
resolve.alias.three Single Three.js instance — without this, Reveal's bundled copy and the app's copy conflict
resolve.dedupe with react + react-dom + react/jsx-runtime pnpm symlinks can create separate module instances. dedupe forces one copy. Critical — missing these causes ReactCurrentDispatcher errors
resolve.dedupe with three Ensures the copied feature bundle and Reveal share the same Three.js
optimizeDeps.include for process/util/assert No source file imports them, so Vite cannot auto-discover them for pre-bundling
optimizeDeps.include for react + react-dom + @tanstack/react-query Converts CJS → ESM and creates a single shared instance. All pre-bundled deps that import React get the same copy
worker.format: 'es' Reveal spawns ES module workers; Vite defaults to IIFE/UMD which breaks them
conditions: ['import', 'module', 'browser', 'default'] Ensures browser ESM variants are preferred over CJS/Node variants

Common mistakes that break the setup

Mistake Symptom Fix
Installing three without checking @cognite/reveal's peer requirement unmet peer three@0.180.0: found 0.177.x warning; potential rendering bugs After install, compare versions; pnpm add three@^<peer-version> if mismatched
Not adding ajv as a direct dependency unmet peer ajv@>=8: found 6.x pnpm add ajv (installs ^8) in the app
Copied Reveal feature folder in optimizeDeps.exclude ReactCurrentDispatcher undefined or No QueryClient set Remove from exclude and let Vite pre-bundle shared deps
vite-plugin-node-polyfills instead of manual aliases Could not resolve "inherits" on transitive deps Remove the plugin; add util, assert, process to dependencies and use aliases
RevealKeepAlive inside conditional component ObjectUnsubscribedError: object unsubscribed at model load Move CacheProvider + RevealKeepAlive to always-mounted app/page level
Inline arrow as onSelect/onLoad prop Maximum update depth exceeded useCallback at call site; call onSelect from useEffect inside model browser, never from render
Model browser calls onSelect during render (if (revision) onSelect(...)) Maximum update depth exceeded Move to useEffect([revision, onSelect])
Missing worker.format: 'es' Black screen, no error Add worker: { format: 'es' }
react/react-dom missing from resolve.dedupe ReactCurrentDispatcher in pnpm monorepo Add both to dedupe
Container has no height Canvas collapses to 0px, nothing renders Add height: '70vh' (or flex/grid height) to the parent element