Files
OpenNoodl/dev-docs/tasks/phase-4-canvas-visualisation-views/PREREQ-001-webpack-caching/WEBPACK-CACHING-FIX.md
2026-01-04 00:17:33 +01:00

6.3 KiB

Permanent Webpack Caching Fix for Nodegx

Overview

This document provides the complete fix for the webpack caching issues that require constant npm run clean:all during development.


File 1: packages/noodl-editor/webpackconfigs/shared/webpack.renderer.core.js

Change: Disable Babel cache in development

module.exports = {
  target: 'electron-renderer',
  module: {
    rules: [
      {
        test: /\.(jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            babelrc: false,
            // FIXED: Disable cache in development to ensure fresh code loads
            cacheDirectory: false,
            presets: ['@babel/preset-react']
          }
        }
      },
      // ... rest of rules unchanged
    ]
  },
  // ... rest unchanged
};

File 2: packages/noodl-editor/webpackconfigs/webpack.renderer.dev.js

Change: Add explicit cache: false for development mode

const webpack = require('webpack');
const child_process = require('child_process');
const merge = require('webpack-merge').default;
const shared = require('./shared/webpack.renderer.shared.js');
const getExternalModules = require('./helpers/get-externals-modules');

let electronStarted = false;

module.exports = merge(shared, {
  mode: 'development',
  devtool: 'eval-cheap-module-source-map',
  
  // CRITICAL FIX: Disable ALL webpack caching in development
  cache: false,
  
  externals: getExternalModules({
    production: false
  }),
  output: {
    publicPath: `http://localhost:8080/`
  },
  
  // Add infrastructure logging to help debug cache issues
  infrastructureLogging: {
    level: 'warn',
  },
  
  devServer: {
    client: {
      logging: 'info',
      overlay: {
        errors: true,
        warnings: false,
        runtimeErrors: false
      }
    },
    hot: true,
    host: 'localhost',
    port: 8080,
    // ADDED: Disable server-side caching
    headers: {
      'Cache-Control': 'no-store',
    },
    onListening(devServer) {
      devServer.compiler.hooks.done.tap('StartElectron', (stats) => {
        if (electronStarted) return;
        if (stats.hasErrors()) {
          console.error('Webpack compilation has errors - not starting Electron');
          return;
        }
        
        electronStarted = true;
        console.log('\n✓ Webpack compilation complete - launching Electron...\n');
        
        // ADDED: Build timestamp canary for cache verification
        console.log(`🔥 BUILD TIMESTAMP: ${new Date().toISOString()}`);
        
        child_process
          .spawn('npm', ['run', 'start:_dev'], {
            shell: true,
            env: process.env,
            stdio: 'inherit'
          })
          .on('close', (code) => {
            devServer.stop();
          })
          .on('error', (spawnError) => {
            console.error(spawnError);
            devServer.stop();
          });
      });
    }
  }
});

File 3: packages/noodl-editor/webpackconfigs/webpack.renderer.prod.js (if exists)

Keep filesystem caching for production (CI/CD speed benefits):

module.exports = merge(shared, {
  mode: 'production',
  // Filesystem cache is FINE for production builds
  cache: {
    type: 'filesystem',
    buildDependencies: {
      config: [__filename],
    },
  },
  // ... rest of config
});

File 4: packages/noodl-viewer-react/webpack-configs/webpack.common.js

Also disable caching here (the viewer runtime):

module.exports = {
  externals: {
    react: 'React',
    'react-dom': 'ReactDOM'
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.jsx', '.js'],
    fallback: {
      events: require.resolve('events/'),
    }
  },
  module: {
    rules: [
      {
        test: /\.(jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            babelrc: false,
            // FIXED: Disable cache
            cacheDirectory: false,
            presets: ['@babel/preset-react']
          }
        }
      },
      // ... rest unchanged
    ]
  }
};

File 5: New NPM Scripts in package.json

Add these helpful scripts:

{
  "scripts": {
    "dev": "npm run dev:editor",
    "dev:fresh": "npm run clean:cache && npm run dev",
    "clean:cache": "rimraf node_modules/.cache packages/*/node_modules/.cache",
    "clean:electron": "rimraf ~/Library/Application\\ Support/Electron ~/Library/Application\\ Support/OpenNoodl",
    "clean:all": "npm run clean:cache && npm run clean:electron && rimraf packages/noodl-editor/dist",
    "dev:nuke": "npm run clean:all && npm run dev"
  }
}

Add to your entry point (e.g., packages/noodl-editor/src/editor/src/index.ts):

// BUILD CANARY - Verifies fresh code is running
if (process.env.NODE_ENV === 'development') {
  console.log(`🔥 BUILD LOADED: ${new Date().toISOString()}`);
}

This lets you instantly verify whether your changes loaded by checking the console timestamp.


Why This Works

Before (Multiple Stale Cache Sources):

Source Code → Babel Cache (stale) → Webpack Cache (stale) → Bundle → Electron Cache (stale) → Browser

After (No Persistent Caching in Dev):

Source Code → Fresh Babel → Fresh Webpack → Bundle → Electron → Browser (no-store headers)

Trade-offs

Aspect Before After
Initial build Faster (cached) Slightly slower
Rebuild speed Same Same (HMR unaffected)
Code freshness Unreliable Always fresh
Developer sanity 😤 😊

The rebuild speed via Hot Module Replacement (HMR) is unaffected because HMR works at runtime, not via filesystem caching.


Verification Checklist

After implementing, verify:

  1. Add console.log('TEST 1') to any file
  2. Save the file
  3. Check that TEST 1 appears in console (without restart)
  4. Change to console.log('TEST 2')
  5. Save again
  6. Verify TEST 2 appears (TEST 1 gone)

If this works, you're golden. No more clean:all needed for normal development!


When You Still Might Need clean:all

  • After switching git branches with major changes
  • After npm install/update
  • If you modify webpack config itself
  • If something feels "really weird"

But for normal code edits? Never again.