What is the purpose for the fallback option in ExtractTextPlugin.extract - javascript

Using the example from webpack-contrib/extract-text-webpack-plugin:
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
module: {
rules: [
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
//resolve-url-loader may be chained before sass-loader if necessary
use: ['css-loader', 'sass-loader']
})
}
]
},
plugins: [
new ExtractTextPlugin('style.css')
//if you want to pass in options, you can do so:
//new ExtractTextPlugin({
// filename: 'style.css'
//})
]
}
What is the purpose of the fallback option?
How I see this snippet to work is:
I instruct webpack to use the ExtractTextPlugin on every .scss file it encounter. I am telling ExtractTextPlugin to use ['css-loader', 'sass-loader'] loaders to generate the css. Webpack will then emit extra style.css file containing the css.
I can then reference this file in my index.html or if I am using html-webpack-plugin it will be added automatically to it.
I can remove the fallback: 'style-loader', option and everything continues to work.
What does it mean to fallback to style-loader?
How/When the fallback gets triggered?
I understand what style-loader is doing and how it is modifying the DOM with style tag if I am not using the ExtractTextPlugin. I just can't understand the fallback option when I am using ExtractTextPlugin.

Let's suppose we have a webpack config with:
entry: {
'pageA': './src/pageA',
'pageB': './src/pageB',
},
...
module: {
rules: [
...
{
test: /\.css$/,
exclude: helpers.root('src', 'app'),
loader: ExtractTextPlugin.extract({ fallbackLoader: 'style-loader', loader: 'css-loader?sourceMap' })
},
...
],
plugins: [
...
new CommonsChunkPlugin({
name: "commons",
chunks: ["pageA", "pageB"]
}),
new ExtractTextPlugin('[name].css')
...
]
}
which let's suppose would get some css into commons.js generated by CommonsChunkPlugin above.
When allChunks option used by new ExtractTextPlugin() is false (default) the output [name].css won't contain the css from commons.js so some css would remained loaded as javascript with commons.js (uuh, ugly). Now the fallbackLoader option would say "use the style-loader" meaning "add that css into a <style/> when loading commons.js in browser".
But you could better use allChunks = true for this case, this meaning to force new ExtractTextPlugin() to also collect (into the separate css file it creates) the css which otherwise would go into commons.js. In this case fallbackLoader option would become useless.

Related

Webpack gives eslint errors while using npm link

I have a multi-package project set up, where I have one JavaScript package that relies on a TypeScript library. Initially I installed Sinopia and was reinstalling the library every time I made changes to it. Then I saw npm link and thought that it would be easier for development. Unfortunately, when I linked the library (using npm link ../typescript-package) and built, it gives an error:
ERROR in ../typescript-package/dist/index.js
Module build failed: Error: No ESLint configuration found.
Since they are separate packages, I'm not quite sure why Webpack is trying to apply eslint to this package. Here is my webpack.common.js file (using merge and the dev vs prod configs shouldn't matter):
// webpack.common.js
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const babelOptions = {
presets: ['react', 'es2015', 'stage-0'],
sourceMaps: true,
retainLines: true,
};
module.exports = {
entry: {
solver: './source/index.jsx',
},
output: {
path: `${__dirname}/dist`,
filename: '[name].js',
publicPath: '/dist/',
},
resolve: {
modules: ['source', 'node_modules/'],
extensions: ['.js', '.jsx', '/index.jsx', '.json', '.ts', '/index.ts', '.scss', '/index.scss', '.css'],
},
module: {
rules: [
{
test: /\.jsx?$/,
use: [
{
loader: 'babel-loader',
options: babelOptions,
},
{
loader: 'eslint-loader',
options: {
emitWarnings: true,
},
},
],
exclude: /node_modules/,
}, {
test: /\.js$/,
loader: 'source-map-loader',
enforce: 'pre',
exclude: /node_modules/,
}, {
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [{
loader: 'css-loader',
options: {
minimize: true,
localIdentName: '[local]_[hash:base64:5]',
},
}, {
loader: 'sass-loader',
options: {
includePaths: ['source/design'],
},
}],
}),
},
],
},
plugins: [
new ExtractTextPlugin({
filename: '[name].css',
allChunks: true,
}),
],
node: {
global: true,
},
};
I can also provide other config or package.json files if need be.
Way 1 - Webpack recommendation
According to webpack doc : https://webpack.js.org/configuration/module/#rule-conditions
Be careful! The resource is the resolved path of the file, which means symlinked resources are the real path not the symlink location. This is good to remember when using tools that symlink packages (like npm link), common conditions like /node_modules/ may inadvertently miss symlinked files. Note that you can turn off symlink resolving (so that resources are resolved to the symlink path) via resolve.symlinks.
So according to it you can disable symlink : https://webpack.js.org/configuration/resolve/#resolvesymlinks
Way 2 - Fancy hack
But maybe you need symlinks for your project. So, I use my eslint rule like this :
{
test: /\.js$/,
enforce: 'pre',
use: 'eslint-loader',
include: path.resolve(__dirname), // <-- This tell to eslint to look only in your project folder
exclude: /node_modules/
}
Plus obviously your own config of this loader.
I was dealing with this, as well. I'm not exactly sure why ESLint is looking for the config file in the external package (I would expect the local rc file to be adequate) but the symlink created by npm link takes the external package out of ./node_modules/, which otherwise would have been excluded by the loader.
The fix I've come up with is to copy the package into ./node_modules/. It then gets filtered out through the excludes rule in your Webpack config.
I know this is incredibly inelegant, and shouldn't be "good enough", but I've spent some time trying to get around this issue, and this is the best I was able to come up with. Until something better comes along, you can at least get moving on more pressing issues.
You can also add a .eslintignore file and add the real path to the linked module
// Content of .eslintignore
C:/path/to/your/linked/module
This is needed, because webpack resolves the module by its real path and not by the path in the node_modules folder (see webpack docs). Usually eslint ignores node_modules by default, but because of that it does not work here.

Webpack loader order misbehaved

I have used webpack ^2.2.1. I have added some loaders In my webpack.config.js file.
But my loader have not call in an order.
I used babel-loader for transform react-es6 codes to react-es5 codes. My custom-loader need react-es6 code. So I put my loader to first. I have print source content in each loaders. But every time first printing babel-loader info. After printing my info.
Is my loader order correct?
Help me! Thanks in advance!
// webpack.config.js
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './build')
}
module: {
loaders: [
{
test: /\.js$/,
use: 'my-custom-loader'
},
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['babel-preset-es2015', 'babel-preset-react']
}
}
]
}
]
}
}
Loaders in Webpack are used in order "right to left" so the last loader in your array is used first. Therefore babel is translating everything and your loader is second in line.
See: What is the loader order for webpack?
Try switching the order of your loaders (and of course use module.rules instead of module.loaders, so that you are using the new Pattern in Webpack 2)

How to import css from libraries via postCSS?

I am using this library for slideshows called Flickity that requires to use its css file flickity.min.css. In my project I use postCSS and when including this flickity.min.css into my components css like:
#import ./lib/flickity.min
its classes get prefixed in the following way: MyComponent__flickity-class_35aw issue with this is that flickity creates new dom elements and relies on its classes, so in inspector the class for it would be .flickity-class hence no styles are applied to it, I'm trying to figure out how to include it correctly.
Using react + webpack setup
It looks like you're importing the CSS as CSS Modules. If you didn't intend to use CSS Modules you just need to remove 'modules' from your webpack config, i.e.
loaders: [
{
test: /\.css$/,
loaders: 'style!css?modules'
}
]
Should just become:
loaders: [
{
test: /\.css$/,
loaders: 'style!css'
}
]
If however you want to use CSS modules for some files but not others I would recommend defining multiple CSS loader configs based on an appropriate heuristic, e.g. assuming your /lib/ directory will only ever contain 'global' CSS you could do this:
loaders: [
{
test: /\.css$/,
exclude: /lib/,
loaders: 'style!css?modules'
},
{
test: /\.css$/,
include: /lib/,
loaders: 'style!css'
}
]

Using Webpack custom loader in loaders array

I am trying to build a Webpack custom loader:
module.exports = function(source) {
// Transform the source and return it
console.log('$$$$$$$$$$$$$$$$$$$$$');
return source;
};
Now, I want to use it in my loaders array, something like:
loaders: [
{test: /\.vm$/, loader: 'vm-loader', exclude: [/node_modules/, /dist/]}
]
I tried to use resolveLoader's alias, but it did not work:
resolveLoader: {
alias: {
"vm-loader": path.join(__dirname, "./lib/velocity-plugin")
},
root: [
path.join(__dirname, 'node_modules'),
path.resolve('./node_modules')
]
}
What Am I missing?
Answering my own question:
As it turned out, my custom loader doesn't work because webpack does not work on files which where not required. In my case those html/vm files are not required and not part of the bundle (they are rendered by the server).
My solution was adding gulp in order to have this working. Another (hacky) solution will be using this method

Load javascript in webpack

I am new to javascript dev in general and webpack in particular. I want to use this chess board module (https://github.com/oakmac/chessboardjs/) in my project. It sees to be exporting ChessBoard object. My project is using ES6, so I would love to be able to
import { ChessBoard } from 'chessboard'
or
import ChessBoard from 'chessboard'
I understand that I need some sort of loader for this. I have tried to add expose loader in the same way I use it for jQuery
{test: require.resolve("jquery"), loader: "expose?$!expose?jQuery"},
{test: require.resolve("chessboard"), loader: "expose?ChessBoard!./vendor/chessboard/js/chessboard-0.3.0.min.js"}
But I get "Error: Cannot find module 'chessboard'" error. Same if I replace ChessBoard with $. Not sure what I am doing wrong. Is expose even the right loader for what I am trying to do?
Here is my webpack config for reference (without the broken chessboard expose test)
var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: ['webpack/hot/dev-server', path.resolve(__dirname, 'app/main.js')],
output: {
path: path.resolve(__dirname, 'build'),
filename: 'bundle.js',
},
module: {
loaders: [
{test: require.resolve("jquery"), loader: "expose?$!expose?jQuery"},
{test: /\.jsx?$/, exclude: /(node_modules|bower_components)/, loader: 'babel', query: {presets: ['react', 'es2015']} },
/* CSS loaders */
{test: /\.css$/, loader: 'style!css'},
{test: /\.less$/, loader: 'style!css!less'},
/* font loaders for bootstrap */
{test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/font-woff'},
{test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/octet-stream'},
{test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file'},
{test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml'},
],
},
plugins: [
new HtmlWebpackPlugin({
title: 'Test',
inject: false,
template: 'node_modules/html-webpack-template/index.ejs',
appMountId: 'app',
devServer: 'http://localhost:8080',
})
]
};
The problem seems to be that the chessboard.js is just a anonymous function and not a AMD or CommonJS module, and you may have to look at adding shims using webpack.
Not all JS files can be used directly with webpack. The file might be
in an unsupported module format, or not even in any module format.
https://github.com/webpack/docs/wiki/shimming-modules
Without seeing your entire webpack.config.js file it's tricky to say what the issue is. Basically you need to tell webpack to include `/node_modules/' into the list of paths it looks in for js modules.
You will need to add something like this to the resolve section of webpack.config.js.
modulesDirectories: ["node_modules"]
I guess you will need something like this in your webpack.config.js:
...
resolve: {
modules: [
'node_modules',
path.join( __dirname, 'node_modules' ),
path.resolve( './src' ),
...
You have to do two things:
1.) under plugins add:
new webpack.ProvidePlugin({
"window['jQuery']": "jquery"
})
2.) Install the script-loader plugin and import the script like this:
import 'script-loader!./chessboard.js';

Categories

Resources