15

I struggle to get webpack configured as I want it to be. I run my dev-server on localhost:8080 and want to serve my app through localhost:8080/static/js/bundle.js and that is what I get with this webpack.config.js file I've attached below. in my file structure i've alse attached I want to serve also the other files located in dist/static as static files, so localhost:8080/static/css/style.css will be serving this dist/static/css/style.css file for example.

it is probably something wrong i did in the config file, and i'm not that familiar with webpack so i don't know if im asking the question so you can understand.

My directory tree is:

client
  -- /dist
    -- /templates
      -- /admin
        -- index.html
    -- /static
      -- /css
        -- style.css
      -- /js
  -- /node_modules
  -- /src
  -- /test
  -- package.json
  -- webpack.config.json

webpack.config.js

var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');

var plugins = [
    new webpack.ProvidePlugin({
        'fetch': 'imports?this=>global!exports?global.fetch!whatwg-fetch'
    }),
    new ExtractTextPlugin('app.css', {
        allChunks: true
    }),
    new webpack.DefinePlugin({
        'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
    })
];

var cssLoader = {};

if(process.env.NODE_ENV == 'production'){
    plugins.push(
        new webpack.optimize.UglifyJsPlugin()
    );

    cssLoader = {
        test: /\.css$/,
        loader: ExtractTextPlugin.extract('style', 'css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]')
    };

} else {

    cssLoader = {
        test: /\.css$/,
        loaders: [
            'style?sourceMap',
            'css?modules&importLoaders=1&localIdentName=[path]___[name]__[local]___[hash:base64:5]'
        ]
    }

}

module.exports = {
    entry: [
        'react-hot-loader/patch',
        'webpack-dev-server/client?http://localhost:8080',
        'webpack/hot/only-dev-server',
        './src/index.js'
    ],
    module: {
        loaders: [
            {
                test: /\.jsx?$/,
                exclude: /node_modules/,
                loaders: ['babel']
            },
            cssLoader
        ]
    },
    resolve: {
        extensions: ['', '.js', '.jsx']
    },
    output: {
        path: __dirname + '/dist/static',
        publicPath: '/static/js',
        filename: 'bundle.js'
    },
    devServer: {
        contentBase: './dist/templates/admin',
        hot: true,
        historyApiFallback: true
    },
    plugins: plugins
};

dist/templates/admin/index.html

<!DOCTYPE html>
<html>
<head>
    <title>My App</title>
    <link href="static/css/style.css" type="text/css" rel="stylesheet" />
</head>
<body>
<div id="app"></div>
<script src="static/js/bundle.js"></script>
</body>
</html>

thanks guys :)

Dima Gimburg
  • 1,376
  • 3
  • 17
  • 35

3 Answers3

14

Problem

The origin of the problem is that the WebpackDevServer only serves from one folder. The folder that you should be serving from is the folder containing the index.html, which is what you're correctly doing.

So far you're serving only the content of ./dist/templates/admin, therefore when you look for files in other directories you get a 404. The only exception here is your bundle, because you're setting a publicPath that makes any request to the route /static/js be redirected to your output, which is stored in memory.

You would need the WebpackDevServer to be able to serve from other folders. In your specific case, you need to serve also from ./dist/static/css when you request the path /static/css.


Solution

You need to set some middlewares in your WebpackDevServer. You can do so as described in the documentation of devServer.setup. To do so, I suggest you use express.static, as you're probably already using Express.

You need to require Express:

const express = require('express')

And then, just modify the devServer as follows:

devServer: {
  contentBase: './dist/templates/admin',
  hot: true,
  historyApiFallback: true,
  setup (app) {
    app.use('/static/css/',
      express.static(path.join(__dirname, 'dist', 'static', 'css')));
    /* Using this commented code will break the HMR, see edit
    app.use('/static/js/',
      express.static(path.join(__dirname, 'dist', 'static', 'js')));
    */
    
    // in general
    app.use('/public/route/',
      express.static('/your/local/path'));
  }
}

This way your devs and build paths stay the same, and the WebpackDevServer serves the static files you need, at the routes you need.

Edit

I just discovered that the code above breaks the Hot Module Replacement. The reason is that the middleware in the setup is handling /static/js/, so the bundle is served from the file system instead of memory.

To keep fetching the bundle from memory define a publicPath in the output property, and don't handle it from a custom middleware inside devServer.setup.

module.exports = {
  
  output: {
    path: ...,
    filename: ...,
    publicPath: '/static/js/'
  },
  
  devServer: {
    contentBase: './dist/templates/admin',
    hot: true,
    historyApiFallback: true,
    setup (app) {
      app.use('/static/css/',
        express.static(path.join(__dirname, 'dist', 'static', 'css')));
    }
  },

  // other properties: entry, module, etc.
}
Community
  • 1
  • 1
Daniel Reina
  • 5,764
  • 1
  • 37
  • 50
  • I've tried a few versions of this mate but can't seem to get it going, if you're able to checkout my question for some help, that'd be great, thanks. https://stackoverflow.com/questions/47054824/webpack-middleware-with-jekyll-setup-for-context-path – Rhys Nov 02 '17 at 22:39
2

You are serving the app on port 8080. In your webpack config I see that you set content base dist/template/Admin but I don't see that folder any where. When you set content base it will serve Admin.html from that folder and if you have declared dependencies in that file it will load from you. You can take a look at this seed to learn more about it.

Jorawar Singh
  • 7,463
  • 4
  • 26
  • 39
  • you are right i forgot to include this dir. the admin/index.html is served as expected in localhost:8080/ and the static/js/bundle.js is served as expected but statc/css/style.css is 404. ill try to look at the resource youve attached. ive edited the question – Dima Gimburg Aug 21 '16 at 20:29
  • oh sorry, added html file that importing css via regular link tag, just static file with relative path. – Dima Gimburg Aug 21 '16 at 20:43
  • can you see if file being loaded in browser? – Jorawar Singh Aug 21 '16 at 20:43
  • i would try to change contentbase to only dist and later just navigate to template/admin.html using contentbase will serve the static file from current directory. – Jorawar Singh Aug 21 '16 at 20:47
  • Thanks man, the thing is that I want to keep order in my directory structure and store all HTML templates in one dir, but serve the admin/index.html in root of localhost. I'll try to 0lay a little bit with the contentbase. Thanks aga8n – Dima Gimburg Aug 21 '16 at 20:50
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/121487/discussion-between-mrjsingh-and-dima-gimburg). – Jorawar Singh Aug 21 '16 at 20:51
1

So apparently, and it make sense in a way, webpack-dev-server is not supposed to be used as a backend to load static files such as CSS or other static JS when you are running another server aside. when you set the contentBase key to a specific directory, the documentation says that you have to make there an index.html file and you can serve files base on this directory.

If you want to serve static files from other dir and you have another server doing it running side by side with webpack-dev-server, please pay attention to this section of the documentation.

What I did is run webpack-dev-server on localhost:8080 and my backend server on localhost:3000 and my backend server serves the admin/index.html from the root rout of it, on localhost:3000/, and in the html file itself changed the import of the bundle.js to be absolute path <script src="http://localhost:8080/static/js/bundle.js"></script>.

Hot reload now works fine, and now i could also remove the contentBase param off of my webpack config file.

Important:

  1. now to run the webpack like we want and make hot reload really work, just add --inline when you are running it, something like: webpack-dev-server --progress --colors --hot --config ./webpack.config.js --inline
  2. you can also use the iframe mode, but i didn't dived into it.
  3. still got an issue that when building the project i have to manually remove the absolute path, that's annoying. looking how to solve it automatically now.
Dima Gimburg
  • 1,376
  • 3
  • 17
  • 35