Example of How to Load Static CSS Files from Node_Modules Using Webpack

Example of how to load static CSS files from node_modules using webpack?

For users who have encountered a similar problem, there are steps that I've done to get it working, I'm not sure that this equilibrium way.

  1. npm install file-loader --save
  2. add import 'leaflet/dist/leaflet.css'; in main app.js
  3. change webpack.config.js by following way:

add css-loader to get rid of SyntaxError: Unexpected token . and next add file-loader and match files to get rid of Uncaught Error: Cannot find module "./images/layers.png":

module.exports = {
entry: "./web/static/js/app.js",
output: {
path: "./priv/static/js",
filename: "app.js"
},
module: {
loaders: [{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel'
}, {
test: /\.scss$/,
loader: ExtractTextPlugin.extract(
'style',
'css' + '!sass?outputStyle=expanded&' + stylePathResolves
)
}, {
test: /\.css$/,
loader: "style-loader!css-loader"
}, {
test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$/,
loader: 'file-loader'
}]
},
plugins: [new ExtractTextPlugin("app.css")]
};

At the beginning I got this config from some example and it's not 100% clear why I've used ExtractTextPlugin to load scss and what the correlation is with css-loader, maybe to be more coherent should I use ExtractTextPlugin also in this part, maybe someone knows and can code review? But my solution is working and I can work further.

How to import CSS files into webpack?

You have to import them like any JavaScript module. That means, when the imported file is neither a relative path (starting with ../ or ./), nor an absolute path (starting with /), it is resolved as a module. By default webpack will look for modules in the node_modules directory (in the current directory and all parent directories). This is the same behaviour that Node.js uses.

To import webpack/static/css/myfile.css in webpack/entry.js you have to use a relative path.

import './static/css/myfile.css';

If you don't want to use a relative path, you can change the directories that webpack uses to find a module with the resolve.modules or you can use resolve.alias.


In your webpack config you also defined both module.rules and module.loaders. When webpack sees module.rules it ignores module.loaders completely, so your babel-loader wouldn't be used. You should only use module.rules.

module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
}

How to copy static files to build directory with Webpack?

You don't need to copy things around, webpack works different than gulp. Webpack is a module bundler and everything you reference in your files will be included. You just need to specify a loader for that.

So if you write:

var myImage = require("./static/myImage.jpg");

Webpack will first try to parse the referenced file as JavaScript (because that's the default). Of course, that will fail. That's why you need to specify a loader for that file type. The file- or url-loader for instance take the referenced file, put it into webpack's output folder (which should be build in your case) and return the hashed url for that file.

var myImage = require("./static/myImage.jpg");
console.log(myImage); // '/build/12as7f9asfasgasg.jpg'

Usually loaders are applied via the webpack config:

// webpack.config.js

module.exports = {
...
module: {
loaders: [
{ test: /\.(jpe?g|gif|png|svg|woff|ttf|wav|mp3)$/, loader: "file" }
]
}
};

Of course you need to install the file-loader first to make this work.

serve public static files with webpack

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.
}

CSS Loader in Webpack config is not working

Since you are loading the css file from node_modules package but you set css loader with include only your source path. I suggest to either remove that:

{
test: /\.css$/,
use: [
ExtractCssChunks.loader,
'css-loader',
'postcss-loader'
],
},

Or put more package into your list, it's up to you:

{
test: /\.css$/,
use: [
ExtractCssChunks.loader,
'css-loader',
'postcss-loader'
],
include: [path.resolve(config.srcDir, 'styles'), /node_modules/\react-date-range /]
},


Related Topics



Leave a reply



Submit