Wepack 2 resolve alias does not work in the server-side

Viewed 685

Wepack 2 resolve alias does not work server side

I run wepback middleware with my express server. If the server is started when the import is relative, it'll work fine:

`import from '../../foobar/stuff'` 

While the server is already running and the initial server side render is ready in memory; If we change the import in the component, to use the resolve property previously defined through webpack, it'll work:

`import from 'foobar/stuff'`

Meanwhile, if the server is stopped, and re-run with this last change, where the import uses the webpack resolve property defined in the configuration, this will fail (triggers the error not found)

File structure:

[root]
  node_modules
  webpack.config.js
  |____ src
  |____ src/js
  |____ src/js/modules/
  |____ src/js/modules/foobar/containers|components/**/*.js
  |____ src/js/modules/lorem/containers|components/**/*.js

It seems that the express server does not know how to resolve the path that it's defined in the webpack 2 resolve property (even though I pass the webpackDevConfig file to the middleware).

Here's the server.dev.js:

import express from 'express'
import path from 'path'
import superagent from 'superagent'
import chalk from 'chalk'

import React from 'react'
import { renderToString } from 'react-dom/server'
import { StaticRouter } from 'react-router'

import configureStore from './src/js/root/store'
import { Provider } from 'react-redux'

import MyApp from './src/js/modules/app/containers/app'

import Routes from './src/js/root/routes'

const myAppChildRoutes = Routes[0].routes
const app = express()
const port = process.env.PORT ? process.env.PORT : 3000
var serverInstance = null
var dist = path.join(__dirname, 'dist/' + process.env.NODE_ENV)
var config = null
const webpack = require('webpack')
const webpackHotMiddleware = require('webpack-hot-middleware')
const webpackDevConfig = require('./webpack.dev.config')
const compiler = webpack(require('./webpack.dev.config'))
var webpackDevMiddleware = require('webpack-dev-middleware')
const webpackAssets = require('./webpack-assets.json')

config = require('./config')

/**
 * Process error handling
 */
process.on('uncaughtException', (err) => {
  throw err
})

process.on('SIGINT', () => {
  serverInstance.close()
  process.exit(0)
})

app.set('views', path.join(__dirname, 'src'))
app.set('view engine', 'ejs')

app.use(webpackDevMiddleware(compiler, {
  noInfo: true,
  publicPath: webpackDevConfig.output.publicPath,
  stats: {
    colors: true,
    hash: false,
    version: true,
    timings: false,
    assets: false,
    chunks: false,
    modules: false,
    reasons: false,
    children: false,
    source: false,
    errors: true,
    errorDetails: true,
    warnings: true,
    publicPath: false
  }
}))

app.use(webpackHotMiddleware(compiler, {
  log: console.log
}))

/**
 * The Cross origin resource sharing rules
 */
app.use((req, res, next) => {
  res.setHeader('Access-Control-Allow-Origin', '*')
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE')
  res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type')
  res.setHeader('Access-Control-Allow-Credentials', true)
  next()
})

/**
 * Health check
 */
app.use('/healthcheck', (req, res) => {
  res.json({
    'env': {
      'NODE_ENV': process.env.NODE_ENV
    }
  })
  res.end()
})

app.use('/api/test', (req, res) => {
  superagent
    .get('https://jsonip.com/')
    .end((err, response) => {
      if (err) {
        console.log('api test err', err)
      }
      res.send(response.body)
    })
})

app.use('/assets', express.static(dist))

app.get('*', (req, res) => {
  // (wip) migration to react-router v4 temporary solution
  // let matches
  // if (typeof routes.props.children !== 'undefined' && Array.isArray(routes.props.children)) {
  //   matches = routes.props.children.find((v) => {
  //     return v.props.path === req.url
  //   })
  // } else {
  //   matches = routes.props.children.props.path === req.url
  // }
  let matches = true
  if (!matches) {
    res.status(404).send('Not found')
  } else {
    const preloadedState = {'foobar': 1}
      // Create a new Redux store instance
    const store = configureStore(preloadedState)
      // Render the component to a string
    const myAppHtml = renderToString(<StaticRouter context={{}} location={req.url}>
      <Provider store={store}>
        <MyApp routes={myAppChildRoutes} />
      </Provider>
    </StaticRouter>)
      // Grab the initial state from our Redux store
    const finalState = store.getState()
    res.render('index', {
      app: myAppHtml,
      state: JSON.stringify(finalState).replace(/</g, '\\x3c'),
      bundle: webpackAssets.main.js,
      build: config.build_name,
      css: '/assets/css/main.min.css'
    })
  }
})

serverInstance = app.listen(port, (error) => {
  if (error) {
    console.log(error) // eslint-disable-line no-console
  }
  console.log(chalk.green('[' + config.build_name + '] listening on port ' + port + '!'))
})

Finally, the webpack dev config file is:

var path = require('path')
var webpack = require('webpack')
var AssetsPlugin = require('assets-webpack-plugin')
var assetsPluginInstance = new AssetsPlugin()
var ExtractTextPlugin = require('extract-text-webpack-plugin')

module.exports = {
  context: path.resolve(__dirname, 'src'),
  entry: [
    'react-hot-loader/patch',
    'webpack/hot/dev-server',
    'webpack-hot-middleware/client',
    'babel-polyfill',
    './js/index.js'
  ],
  output: {
    path: path.join(__dirname, '/dist/development'),
    publicPath: '/assets/',
    filename: 'js/bundle.js?[hash]'
  },
  devtool: 'inline-source-map',
  devServer: {
    hot: true,
    // match the output path
    contentBase: path.join(__dirname, '/dist/development'),
    // match the output `publicPath`
    publicPath: '/assets/'
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /(node_modules)/,
        use: [{
          loader: 'babel-loader'
        }]
      },
      {
        test: /\.scss$/,
        use: ['css-hot-loader'].concat(ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: ['css-loader', 'sass-loader']
        }))
      },
      {
        test: /\.(ttf|eot|svg|woff(2)?)(\?[a-z0-9=&.]+)?$/,
        use: [
          'file-loader?name=[path][name].[ext]'
        ]
      },
      {
        test: /\.(jpg|png|gif|svg)$/i,
        use: [
          'file-loader?name=[path][name].[ext]&emitFile=false'
        ]
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin('css/[name].min.css?[hash]'),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin(),
    new webpack.DefinePlugin({
      'process.env': {
        'NODE_ENV': JSON.stringify('development')
      }
    }),
    assetsPluginInstance
  ],
  resolve: {
    alias: {
      modules: path.resolve(__dirname, 'src/js/modules')
    }
  }
}

Also, tried to include the path to resolve automaticaly, without success:

module.paths.unshift(path.resolve(__dirname, 'src/js'))

The process.env.NODE_PATH is:

process.env.NODE_PATH Module {
  id: '.',
  exports: {},
  parent: null,
  filename: '/Users/johnColtrane/www/coolApp/server.js',
  loaded: false,
  children: [],
  paths:
   [ '/Users/johnColtrane/www/coolApp/src/js',
     '/Users/johnColtrane/www/coolApp/node_modules',
     '/Users/johnColtrane/www/node_modules',
     '/Users/johnColtrane/node_modules',
     '/Users/node_modules',
     '/node_modules' ] }

NOTE: someone suggested babel-plugin-module-alias instead, because of some issues with the approach I want to take. I'll check it and if better then this approach, I'll post here.

0 Answers
Related