Importing es6 inspector window not defined

So I have been trying to get es6 imports to work for some while now. I have finally been able to load the core library. But I seem unable to import the inspector without running into window not defined errors. I am using babylonjs along with react and server side rendering for it.
plus the inspector bundle size is too huge that my webpack build goes into a stack error. I am using a custom webpack config for the server side rendering.
I dunno how to resolve this issue.

const path = require('path');
const webpack = require('webpack');
const nodeExternals = require('webpack-node-externals');
const Dotenv = require('dotenv-webpack');
require('babel-polyfill');

const browserConfig = {
  entry: {
    'react-bundle': ['babel-polyfill', './new-visualisation-react/src/index.js'],
    'react-product-pricer': ['babel-polyfill', './new-visualisation-react/src/components/productPricer/index.js'],
    'react-product-item': ['babel-polyfill', './new-visualisation-react/src/pages/productItem/index.js'],
    'react-curated-item': ['babel-polyfill', './new-visualisation-react/src/pages/curatedItem/index.js'],
  },
  output: {
    path: path.resolve(__dirname, 'public', 'dist/js'),
    filename: '[name].js',
    publicPath: '/',
  },
  module: {

    rules: [
      {
        test: /\.(js)$/,
        loader: 'babel-loader',
        include: path.join(__dirname, 'node_modules', '@babylonjs'),
        exclude: [path.resolve(__dirname, './node_modules/')],
        options: {
          presets: ['@babel/preset-env'],
          compact: false,
        },
      },
      {
        test: /\.(js|jsx)$/,
        loader: 'babel-loader',
        exclude: [path.resolve(__dirname, './node_modules/')],
        options: {
          presets: ['@babel/preset-react'],
        },
      },
      {
        test: /\.css?$/i,
        use: ['style-loader', 'css-loader'],
      },
      {
        test: /\.(png|jpe?g|gif|svg|eot|ttf|woff|woff2)$/,
        loader: 'url-loader',
        options: {
          limit: 8192,
        },
      },
    ],
  },
  resolve: {
    extensions: ['.js', '.jsx', '.css'],
  },
  plugins: [
    new webpack.DefinePlugin({
      __isBrowser__: 'true',
    }),
    new Dotenv({
      path: path.resolve(__dirname, './.env'),
    }),
  ],
};

const serverConfig = {
  entry: ['babel-polyfill', './app.js'],
  target: 'node',
  externals: [nodeExternals({
    whitelist: ['bootstrap/dist/css/bootstrap.min.css', /^@babylonjs/],
  })],
  output: {
    path: __dirname,
    filename: 'server.js',
    publicPath: '/',
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-react', '@babel/preset-env'],
          plugins: ['@babel/plugin-transform-modules-commonjs'],
          compact: false,
        },
      },
      {
        test: /\.css?$/,
        use: ['css-loader'],
      },
      {
        test: /\.(png|jpe?g|gif|svg|eot|ttf|woff|woff2)$/,
        loader: 'url-loader',
        options: {
          limit: 8192,
        },
      },
    ],
  },
  resolve: {
    extensions: ['.js', '.jsx', '.css'],
  },
  node: {
    __dirname: true,
  },
  plugins: [
    new webpack.DefinePlugin({
      __isBrowser__: 'false',
    }),
    new Dotenv(),
  ],
};

module.exports = [browserConfig, serverConfig];

Error Log

/home/shailesh96/Denkali/denkali-website/server.js:330275
          return window && document && document.all && !window.atob;
          ^

ReferenceError: window is not defined
    at /home/shailesh96/Denkali/denkali-website/server.js:330275:11
    at /home/shailesh96/Denkali/denkali-website/server.js:330264:56
    at module.exports (/home/shailesh96/Denkali/denkali-website/server.js:330331:97)
    at Object.componentsActionTabsActionTabsScss (/home/shailesh96/Denkali/denkali-website/server.js:332206:66)
    at __webpack_require__ (/home/shailesh96/Denkali/denkali-website/server.js:279697:27)
    at Module.componentsActionTabsActionTabsComponentTsx (/home/shailesh96/Denkali/denkali-website/server.js:332325:9)
    at __webpack_require__ (/home/shailesh96/Denkali/denkali-website/server.js:279697:27)
    at Module.inspectorTs (/home/shailesh96/Denkali/denkali-website/server.js:354751:87)
    at __webpack_require__ (/home/shailesh96/Denkali/denkali-website/server.js:279697:27)
    at Module.indexTs (/home/shailesh96/Denkali/denkali-website/server.js:354674:55)
    at __webpack_require__ (/home/shailesh96/Denkali/denkali-website/server.js:279697:27)
    at Module.<anonymous> (/home/shailesh96/Denkali/denkali-website/server.js:355317:53)
    at Module.legacyLegacyTs (/home/shailesh96/Denkali/denkali-website/server.js:355335:12)
    at __webpack_require__ (/home/shailesh96/Denkali/denkali-website/server.js:279697:27)
    at ../../../../node_modules/@fortawesome/fontawesome-svg-core/index.es.js (/home/shailesh96/Denkali/denkali-website/server.js:279891:14)
    at /home/shailesh96/Denkali/denkali-website/server.js:279893:6
    at webpackUniversalModuleDefinition (/home/shailesh96/Denkali/denkali-website/server.js:279638:135)
    at Object.<anonymous> (/home/shailesh96/Denkali/denkali-website/server.js:279642:3)
    at Object../node_modules/@babylonjs/inspector/babylon.inspector.bundle.max.js (/home/shailesh96/Denkali/denkali-website/server.js:355525:30)
    at __webpack_require__ (/home/shailesh96/Denkali/denkali-website/server.js:20:30)
    at Object../new-visualisation-react/src/App.js (/home/shailesh96/Denkali/denkali-website/server.js:21909:1)
    at __webpack_require__ (/home/shailesh96/Denkali/denkali-website/server.js:20:30)
    at Object../controllers/static/staticController.js (/home/shailesh96/Denkali/denkali-website/server.js:5647:35)
    at __webpack_require__ (/home/shailesh96/Denkali/denkali-website/server.js:20:30)
    at Object../routes/static.js (/home/shailesh96/Denkali/denkali-website/server.js:382971:48)
    at __webpack_require__ (/home/shailesh96/Denkali/denkali-website/server.js:20:30)
    at Object../routes/index.js (/home/shailesh96/Denkali/denkali-website/server.js:382694:38)
    at __webpack_require__ (/home/shailesh96/Denkali/denkali-website/server.js:20:30)
    at Object../app.js (/home/shailesh96/Denkali/denkali-website/server.js:110:37)
    at __webpack_require__ (/home/shailesh96/Denkali/denkali-website/server.js:20:30)
    at Object.0 (/home/shailesh96/Denkali/denkali-website/server.js:383489:18)
    at __webpack_require__ (/home/shailesh96/Denkali/denkali-website/server.js:20:30)
    at ./app.js.Object.defineProperty.value (/home/shailesh96/Denkali/denkali-website/server.js:84:18)
    at Object.<anonymous> (/home/shailesh96/Denkali/denkali-website/server.js:87:10)
    at Module._compile (internal/modules/cjs/loader.js:778:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
    at Module.load (internal/modules/cjs/loader.js:653:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
    at Function.Module._load (internal/modules/cjs/loader.js:585:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:831:12)
    at startup (internal/bootstrap/node.js:283:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:623:3)
[nodemon] app crashed - waiting for file changes before starting...

Import lines

import '@babylonjs/core/Debug/debugLayer';
import '@babylonjs/inspector';

Commenting the above lines resolves the issue but makes me unable to use the inspector when using es6.

Well the inspector is a HTML based tool so you need to provide a DOM capable environment.
SO if you are rendering server side you have no DOM context so you should not import the inspector

true but the issue I do require the inspector in the page when it loads on the client side so while the page is rendered server side. Its just that the basic markup is being generated on the server side.
Though essentially the question I might be trying to get at is. how can I get the es6 inspector bundle to play nicely while rendering my react page server side?

You can’t :frowning: it requires document object in a lot of places

Maybe this is worth an exploration on your side? If you can submit a PR that removes the need for document in the Inspector I will gladly merge it

I will look into what I can do. :slight_smile:

So I have found a workaround to including the inspector using loadable-components for react which is a code splitting library supported by the react team. It allows for server side splitting while also allowing for async loading of chunks. Below are the steps I followed to get the inspector to load while still server side rendering everything else.
So just a brief explanation about my react server side rendering setup. It is a custom webpack based setup where I am manually rendering react server side on my nodejs backend. I have a seperate webpack configuration for my nodejs backend for allowing me to use es6 and a seperate one for my frontend react code.

  1. Excluding the inspector in the nodeExternals so that it is not picked up for being built for transpiling on the server side.
// server side webpack setup
externals: [
    nodeExternals({
/*
This regex is a negative lookahead regex that will 
pass all the other @babylonjs modules as true except 
for the inspector module which is what we want not to
be parsed and read on the server side.
*/
      whitelist: [ /^@babylonjs\/(?!inspector)/],
    }),
  ],
  1. Now since we want to do treeshaking and parse our modules you would have to include them specifically if you have excluded ‘node_modules’.using the include command on your babel loader on webpack.
// client side react webpack setup
{
        test: /\.(js)$/,
        loader: 'babel-loader',
        include: path.join(__dirname, 'node_modules', '@babylonjs'),
        exclude: [path.resolve(__dirname, './node_modules/')],
        options: {
          presets: ['@babel/preset-env'],
          compact: false,
        },
 },

From this point forward you can follow the docs from loadable components for server side rendering
3. Installing loadable-components from the console.

npm install --save @loadable/component @loadable/server @loadable/babel-plugin @loadable/webpack-plugin
  1. Adding the @loadable babel plugin to .babelrc or to plugins for the babel transpiler in webpack for both the server side webpack config and client side webpack config.
{
  "plugins": ["@loadable/babel-plugin"]
}
  1. Now to add the loadable plugin to webpack plugins for the client side webpack setup for react.
// Client Side react webpack setup
const LoadablePlugin = require('@loadable/webpack-plugin')
module.exports = {
  // ...
  plugins: [new LoadablePlugin()],
}
  1. For server side rendering you will have to setup chunk extractor on the server side to render html
import { ChunkExtractor } from '@loadable/server'
// This is the stats file generated by webpack loadable plugin
const statsFile = path.resolve('../dist/loadable-stats.json')
// We create an extractor from the statsFile
const extractor = new ChunkExtractor({ statsFile })
const html = renderToString(
  <ChunkExtractorManager extractor={extractor}>
    <YourApp />
  </ChunkExtractorManager>,
)
// You can now collect your script tags
const scriptTags = extractor.getScriptTags() // or extractor.getScriptElements();
// You can also collect your "preload/prefetch" links
const linkTags = extractor.getLinkTags() // or extractor.getLinkElements();
// And you can even collect your style tags (if you use "mini-css-extract-plugin")
const styleTags = extractor.getStyleTags() // or extractor.getStyleElements();
  1. Since this a server side rendered react app. the actual app which was rendered as html above and the index.js which hydrates the app are seperate. So in the file which hydrates the app we will have add loadableReady on the hydrate.
import { loadableReady } from '@loadable/component'
loadableReady(() => {
  const root = document.getElementById('main')
  hydrate(<App />, root)
})

Now since the basic setup for using loadable-component for code-splitting and server side rendering is done. Lets come back to babylonjs and how to get the inspector to load up when using the es6 modules.

  1. Create a react component for the inspector. This is done just to enclose it as a component for code-splitting.
/*Inspector.js*/
import React from 'react';

import '@babylonjs/core/Debug/debugLayer';
import '@babylonjs/inspector';

const InspectorComponent = () => (
  <div>Inspector</div>
);
export default InspectorComponent;
  1. Using loadable-components and webpack splitting split the inspector module away from the rest of the app code. The magic webpack comment is important for creating a seperate js file for just the inspector. This is done in the main file. Here in the loadable options you will have to put ssr as false this disable server side rendering for the inspector and thats what makes it work.
/*app.js*/
import loadable from '@loadable/component';

const InspectorComponent = loadable(() => import(/* webpackChunkName: "babylonjs-inspector" */'./inspector'), {
  fallback: <div>...Loading</div>,
  ssr: false,
});

const App = (props) =>{
return (
<div>
  <InspectorComponent />
  <MyApp ...props/>
</div>
)
}
  1. You will have to show the inspector from the debug layer.
 scene.debugLayer.show();
  1. The inspector now works alongside server side rendering react app. Though whenever you do not want the inspector you can comment it out in your app and the inspector should ideally be not pushed into production unless thats a feature for you. :smile:

Thanks for reading through this. Do let me know if certain things are unclear. :slight_smile:

I did get a work around to the above issue. I was curious how does the babylonjs core know not to start on the server side. It does have to bind to canvas right. Would it be possible to tie in the inspector initialisation to understand whether there is DOM or not and to not bind then?

Inspector is HEAVILY relying on DOM as it is a react control :slight_smile: