简体   繁体   中英

How to make a javascript webpack module made for the browser safe to load in node environment?

I am trying to upgrade an old framework I built in javascript to es6/module standards and I have a lot of troubles.

One of my current problems is that due to server side rendering my modules are sometime loaded in the node environment and are trying to access the window, causing errors.

Is there a principled way to manage this ?

The main jQuery file has a nice failback if window is undefined and can load in node without a fuss. I am trying to implement this in web-pack by I am stumbling.

This is my current webpack config

// @flow

// import path from 'path'
import webpack from 'webpack'

const WDS_PORT = 7000

const PROD = JSON.parse(process.env.PROD_ENV || '0')

const libraryName = 'experiment'
const outputFile = `${libraryName}${PROD ? '.min' : '.max'}.js`
const plugins = [
  new webpack.optimize.OccurrenceOrderPlugin(),
]
const prodPlugins = plugins.concat(new webpack.optimize.UglifyJsPlugin())
// not really working
export default {
  entry: './builder.js',
  target: 'web',
  output: {
    path: `${__dirname}/lib`,
    filename: outputFile,
    library: libraryName,
    libraryTarget: 'umd',
    umdNamedDefine: true,
  },
  module: {
    loaders: [
      {
        test: /(\.jsx|\.js)$/,
        loader: 'babel-loader',
        exclude: /(node_modules|bower_components)/,
      },
    ],

  },
  devtool: PROD ? false : 'source-map',
  resolve: {
    extensions: ['.js', '.jsx'],
  },
  externals: {
    chartjs: {
      commonjs: 'chartjs',
      amd: 'chartjs',
      root: 'Chart', // indicates global variable
    },
    lodash: {
      commonjs: 'lodash',
      amd: 'lodash',
      root: '_', // indicates global variable
    },
    jquery: 'jQuery',
    mathjs: {
      commonjs: 'mathjs',
      amd: 'mathjs',
      root: 'math', // indicates global variable
    },
    'experiment-boxes': {
      commonjs: 'experiment-boxes',
      amd: 'experiment-boxes',
      root: 'experimentBoxes', // indicates global variable
    },
    'experiment-babylon-js': {
      commonjs: 'experiment-babylon-js',
      amd: 'experiment-babylon-js',
      root: 'EBJS', // indicates global variable
    },
  },
  devServer: {
    port: WDS_PORT,
    hot: true,
  },
  plugins: PROD ? prodPlugins : plugins,
}

And this is my main entry point builder.js

/* --- Import the framwork --- */
 import TaskObject from './src/framework/TaskObject'
 import StateManager from './src/framework/StateManager'
 import State from './src/framework/State'
 import EventData from './src/framework/EventData'
 import DataManager from './src/framework/DataManager'
 import RessourceManager from './src/framework/RessourceManager'

 import {
   Array,
   String,
   diag,
   rowSum,
   getRow,
   matrix,
   samplePermutation,
   rep,
   Deferred,
   recurse,
   jitter,
   delay,
   looksLikeAPromise,
   mustHaveConstructor,
   mustBeDefined,
   mandatory,
   debuglog,
   debugWarn,
   debugError,
   noop,
 } from './src/framework/utilities'


/* add it to the global space in case user want to import in a script tag */
 if (typeof window !== 'undefined') {
   window.TaskObject = TaskObject
   window.StateManager = StateManager
   window.State = State
   window.EventData = EventData
   window.DataManager = DataManager
   window.RessourceManager = RessourceManager
   window.jitter = jitter
   window.delay = delay
   window.Deferred = Deferred
 }


 export {
  TaskObject,
  StateManager,
  State,
  EventData,
  DataManager,
  RessourceManager,
  Array,
  String,
  diag,
  rowSum,
  getRow,
  matrix,
  samplePermutation,
  rep,
  Deferred,
  recurse,
  jitter,
  delay,
  looksLikeAPromise,
  mustHaveConstructor,
  mustBeDefined,
  mandatory,
  debuglog,
  debugWarn,
  debugError,
  noop,
}

Am I on the right track?

Ok my solution so far, although feels like a hack, protects against require() in node environment.

In the ENTRY FILE of your webpack config check for window being defined.

Here is an example when trying to re-bundle babylonjs which relies heavily on window and would generate an error when required by node:

builder.js

let BABYLON = {}
let OIMO = {}

if (typeof window !== 'undefined')  {
  BABYLON = require('./src/babylon.2.5.full.max')
  OIMO = require('./src/Oimo').OIMO
  window.BABYLON = BABYLON
  window.OIMO = OIMO
}

module.exports = { BABYLON, OIMO }

webpack.config.babel.js

import path from 'path' 
import webpack from 'webpack'

const WDS_PORT = 7000

const PROD = JSON.parse(process.env.PROD_ENV || '0')

const plugins = [
  new webpack.optimize.OccurrenceOrderPlugin(),
]
const prodPlugins = plugins.concat(new webpack.optimize.UglifyJsPlugin())

export default {
  entry: [
    './builder.js',
  ],
  output: {
    filename: PROD ? 'babylon.min.js' : 'babylon.max.js',
    path: path.resolve(__dirname, 'lib/'),
    publicPath: `http://localhost:${WDS_PORT}/lib/`,
    library: 'EBJS',
    libraryTarget: 'umd',
    umdNamedDefine: true,
  },
  module: {
    rules: [
      { test: /\.(js|jsx)$/, use: 'babel-loader', exclude: /node_modules/ },
    ],
  },
  devtool: PROD ? false : 'source-map',
  resolve: {
    extensions: ['.js', '.jsx'],
  },
  devServer: {
    port: WDS_PORT,
    hot: true,
  },
  plugins: PROD ? prodPlugins : plugins,
}

Testing the bundle in node with a simple file like so:

bundle.test.js

const test = require('./lib/babylon.min.js')

console.log(test)

Will produce in the terminal:

$ node bundle.test.js 
{ BABYLON: {}, OIMO: {} }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM