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.