I have a SSR React-TypeScript app, built on Webpack 4 and I use SCSS for each React component. I use two Webpack config files, one for the client bundle and one for the server bundle.
I am in a bind as to how to use the MiniCssExtractPlugin to load my SCSS on the client Webpack config. The documentation isn't very helpful. It is evident that this rule is necessary inside modules:
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"sass-loader"
]
}
But it isn't clear what file should go here:
plugins: [
new webpack.HotModuleReplacementPlugin(),
new MiniCssExtractPlugin({
filename: devMode ? '[name].css' : '[name].[hash].css',
chunkFilename: devMode ? '[id].css' : '[id].[hash].css',
})
],
My SCSS files are spread throughout my application: each React component has its own SCSS file. I don't know how to pass these files to MiniCssExtractPlugin.
For the server Webpack config, I have the following to load my SCSS files and I don't know whether or not it's sufficient:
{
test: /\.scss$/,
use: ["css-loader", "sass-loader"]
}
I would love to see an actual working example of a similar app, if possible.
My css file as generated by MiniCSS:
.home {
text-align: center; }
.home-logo {
animation: home-logo-spin infinite 20s linear;
height: 40vmin;
pointer-events: none; }
.home-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white; }
.home-link {
color: #61dafb; }
#keyframes home-logo-spin {
from {
transform: rotate(0deg); }
to {
transform: rotate(360deg); } }
/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi9zcmMvY2xpZW50L3ByZXNlbnRhdGlvbmFsLWNvbXBvbmVudHMvaG9tZS9Ib21lLnNjc3MiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQSxxQkFBcUI7O0FBRXJCO0FBQ0E7QUFDQTtBQUNBLHVCQUF1Qjs7QUFFdkI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGVBQWU7O0FBRWY7QUFDQSxpQkFBaUI7O0FBRWpCO0FBQ0E7QUFDQSw0QkFBNEI7QUFDNUI7QUFDQSw4QkFBOEIsRUFBRSIsImZpbGUiOiJzdHlsZXMuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiLmhvbWUge1xuICB0ZXh0LWFsaWduOiBjZW50ZXI7IH1cblxuLmhvbWUtbG9nbyB7XG4gIGFuaW1hdGlvbjogaG9tZS1sb2dvLXNwaW4gaW5maW5pdGUgMjBzIGxpbmVhcjtcbiAgaGVpZ2h0OiA0MHZtaW47XG4gIHBvaW50ZXItZXZlbnRzOiBub25lOyB9XG5cbi5ob21lLWhlYWRlciB7XG4gIGJhY2tncm91bmQtY29sb3I6ICMyODJjMzQ7XG4gIG1pbi1oZWlnaHQ6IDEwMHZoO1xuICBkaXNwbGF5OiBmbGV4O1xuICBmbGV4LWRpcmVjdGlvbjogY29sdW1uO1xuICBhbGlnbi1pdGVtczogY2VudGVyO1xuICBqdXN0aWZ5LWNvbnRlbnQ6IGNlbnRlcjtcbiAgZm9udC1zaXplOiBjYWxjKDEwcHggKyAydm1pbik7XG4gIGNvbG9yOiB3aGl0ZTsgfVxuXG4uaG9tZS1saW5rIHtcbiAgY29sb3I6ICM2MWRhZmI7IH1cblxuQGtleWZyYW1lcyBob21lLWxvZ28tc3BpbiB7XG4gIGZyb20ge1xuICAgIHRyYW5zZm9ybTogcm90YXRlKDBkZWcpOyB9XG4gIHRvIHtcbiAgICB0cmFuc2Zvcm06IHJvdGF0ZSgzNjBkZWcpOyB9IH1cbiJdLCJzb3VyY2VSb290IjoiIn0=*/
Setup is a React app with Material-ui
And my SASS variable names aren't working:
Here the actual variable name is trying to render?
.login-hero {
max-width: 460px;
height: 400px;
background-color: $cyan700;
border: 1px solid black;
border-radius: 8px 0px 0px 8px;
}
// Material UI colors
$cyan700: #0097A7;
$gray100: #F5F5F5;
$gray800: #424242;
// Elements
$body: #fff;
package.json
"material-ui": "^0.19.2",
"react": "^15.5.4",
"node-sass": "^4.5.3",
"sass-loader": "^6.0.6",
"style-loader": "^0.18.2",
"webpack": "1.13.2",
Base object inside of webpack
const base = {
entry: [
PATHS.app
],
output: {
path: PATHS.build,
filename: 'bundle.js'
},
module: {
loaders: [
{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' },
{ test: /\.css|\.scss$/, loader: 'style-loader!css-loader' }
]
}
};
Anything else you need to see? Or anyone else run into this problem?
I was importing my _components.scss before my _base which contained the colors and sass mixin
/* Vendors */
#import "./vendors/normalize.scss";
#import "./vendors/reset.scss";
/* Modules */
#import "./modules/_base.scss";
#import "./modules/_inputs.scss";
#import "./modules/_buttons.scss";
#import "./modules/_svg.scss";
#import "./modules/_queries.scss";
#import "./modules/defaults.scss";
/* Components */
#import "./modules/_components.scss";
Also updated and corrected my config to webpack 3
I am using webpack(3.6.0) for an isomorphic react app and have everything working except the background image in my css file. All other styles work, the image is being output to my /dist folder as expected on my local machine, I see the correct file path in the browser at localhost if I inspect...but if I go to sources in dev-tools the entire directory under /dist/client for these static files is missing. I am trying to figure out isomorphic rendering, so I honestly only have a little idea of what is going on in terms of the server vs client render dance, on top of admittedly not having my head totally around webpack configuration. Been able to troubleshoot my way thus far but this has me truly stumped and any insight would be greatly appreciated!
computer /dist folder:
/dist
---- /client // client bundled
---- ---- /media // where webpack moved static stuff
---- ---- ---- image.hash.jpg // where webpack ouput img
---- ---- app.bundle.js // webpack js bundle
---- ---- style.css // webpack bundled+extracted css
---- /media // server-side webpacked media
---- index-main.html // index file
---- server.js // server-side webpack js bundle
---- style.css // server-side webpack bundled+ext css
browser Sources (testing w/chrome):
/localhost:port
---- /dist/client
---- ---- app.bundle.js
---- ---- style.css
targeted DOM element background style:
background: url(./client/media/bg.58dfa049.jpg) no-repeat center center;
webpack.config.js (client):
const path = require('path'),
autoprefixer = require('autoprefixer'),
ExtractTextPlugin = require('extract-text-webpack-plugin'),
webpack = require('webpack');
module.exports = {
context: path.resolve(__dirname, './src'),
entry: {
app: './client/index.js',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, './dist/client/'),
publicPath: './client/',
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: [/node_modules/],
use: [{
loader: 'babel-loader',
options: { presets: ['es2015', 'react'], plugins: ['transform-object-rest-spread', 'async-to-promises'] }
}],
},
//loaders for other file types can go here
{
exclude: [
/\.html$/,
/\.(js|jsx)$/,
/\.css$/,
/\.json$/,
/\.bmp$/,
/\.gif$/,
/\.jpe?g$/,
/\.png$/,
],
loader: 'file-loader',
options: {
name: 'media/[name].[hash:8].[ext]',
},
},
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: 'url-loader',
options: {
limit: 10000,
name: 'media/[name].[hash:8].[ext]',
},
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback:'style-loader',
use:[
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[path][name]_[local]--[hash:base64:8]',
importLoaders: 1
},
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
],
flexbox: 'no-2009',
}),
],
},
}],
}),
},
]
},
plugins: [
new ExtractTextPlugin('style.css'),
new webpack.HotModuleReplacementPlugin(),
],
};
webpack.config.server.js (server):
const nodeExternals = require('webpack-node-externals'),
autoprefixer = require('autoprefixer'),
ExtractTextPlugin = require('extract-text-webpack-plugin'),
path = require('path');
module.exports = {
context: path.resolve(__dirname, './src'),
entry: './server/server.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'server.js',
libraryTarget: 'commonjs',
publicPath: './server/'
},
target: 'node',
node: {
__dirname: false,
__filename: false
},
externals: nodeExternals({
modulesFromFile: true,
}),
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: [/node_modules/],
use: [{
loader: 'babel-loader',
options: { presets: ['es2015', 'react'], plugins: ['transform-object-rest-spread'] }
}],
},
//loaders for other file types can go here
{
exclude: [
/\.html$/,
/\.(js|jsx)$/,
/\.css$/,
/\.json$/,
/\.bmp$/,
/\.gif$/,
/\.jpe?g$/,
/\.png$/,
],
loader: 'file-loader',
options: {
name: 'media/[name].[hash:8].[ext]',
},
},
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: 'url-loader',
options: {
limit: 10000,
name: 'media/[name].[hash:8].[ext]',
},
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback:'style-loader',
use:[
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[path][name]_[local]--[hash:base64:8]',
importLoaders: 1
},
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
],
flexbox: 'no-2009',
}),
],
},
}],
}),
},
]
},
plugins: [
new ExtractTextPlugin('style.css'),
],
};
css file:
.adoptly {
margin: 0;
padding: 0;
font-family: 'Montserrat', sans-serif;
}
.header {
background-color: #30627E;
}
.container {
/*max-width: 940px;*/
margin: 0 auto;
padding: 0;
}
.header ul {
list-style: none;
text-align: center;
margin: 0;
padding: 0;
}
.header .main {
font-size: 28px;
background-color: #e52364;
}
.header li {
color: #fff;
display: inline-block;
font-size: 20px;
text-align: center;
padding: 20px 30px;
margin: 0;
}
.header li a:link {
color:white;
text-decoration: none;
}
.header li a:visited {
color:white;
text-decoration: none;
}
.header li a:hover {
color:blue;
text-decoration: none;
}
.jumbotron {
background: url('../img/bg.jpg') no-repeat center center;
background-size: cover;
height: 500px;
text-align: center;
margin-bottom: 0;
}
.jumbotron .container {
max-width: 100%;
}
.jumbotron h1 {
color: #e52364;
font-weight: 700;
}
.supporting {
text-align: center;
padding: 60px 30px 80px;
}
.row {
margin-right: -15px;
margin-left: -15px;
}
.supporting p {
font-family: 'Open Sans', sans-serif;
font-size: 14px;
min-height: 80px;
}
.btn {
font-size: 16px;
border-radius: 0px;
margin: 20px auto 60px;
padding: 10px 20px;
color: #30627E;
width: 200px;
border: 1px solid #30627E;
text-transform: uppercase;
}
.footer {
color: #fff;
background-color: #e52364;
padding: 20px;
}
.copy {
padding-top: 10px;
}
.pull-right {
float: right!important;
}
.nav-pills>li {
float: left;
}
.nav li a {
color: #fff;
}
.nav-pills>li>a {
border-radius: 4px;
}
.nav>li>a {
position: relative;
display: block;
padding: 10px 15px;
}
#media (min-width: 992px) {
.col-md-4 {
width: 33.33333333%;
}
}
#media (max-width: 500px) {
ul li {
width: 100%;
}
}
I have a sass component like so:
#font-face {
font-family: 'icomoon';
src: url('../fonts/icomoon.eot');
src: url('../fonts/icomoon.eot') format('embedded-opentype'),
url('../fonts/icomoon.ttf') format('truetype'),
url('../fonts/icomoon.woff') format('woff'),
url('../fonts/icomoon.svg') format('svg');
font-weight: normal;
font-style: normal;
}
[class^="icon-"], [class*=" icon-"] {
/* use !important to prevent issues with browser extensions that change fonts */
font-family: 'icomoon' !important;
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-close2:before {
content: "\e66d";
}
.icon-checkmark2:before {
content: "\ea11";
}
and a webpack config like this:
var webpack = require('webpack');
var path = require('path');
var BUILD_DIR = path.resolve(__dirname, 'public/js/');
var APP_DIR = path.resolve(__dirname, 'jsx');
var config = {
entry: APP_DIR + '/index.jsx',
output: {
path: BUILD_DIR,
filename: 'app.js'
},
module : {
loaders : [
{
test : /\.jsx?/,
include : APP_DIR,
loader : 'babel'
},
{
test: /\.scss$/,
loaders: ['style', 'css', 'sass']
}
]
}
};
module.exports = config;
as soon as I had the sass component above I get the following error:
ERROR in ./public/fonts/icomoon.ttf Module parse failed:
/Users/alessandro.santese/Desktop/Alessandro/AIA/projects/accenture-tshirtbuilder/tshirtbuilder/public/fonts/icomoon.ttf
Unexpected character '' (1:0) You may need an appropriate loader to
handle this file type.
and many others like this for the other fonts
Your webpack configuration misses a loader for ttf file. I think you should use the url loader in your case.
Add to your loader
{
test: /\.(eot|woff|woff2|ttf|svg|png|jpg)$/,
loader: 'url-loader?limit=30000&name=[name]-[hash].[ext]'
}
You'll need to install it: npm install url-loader --save-dev
I'm trying to upgrade an AngularJS (Angular 1) application using WebPack 2.1.0 beta 8 to WebPack 2.2.0 RC3 but I'm having issues with the ExtractTextPlugin.
I started from the following, working, setup:
module: {
...
loaders: [{
test: /\.(css|less)$/,
loaders: ExtractTextPlugin.extract('style', 'css!postcss!less')
}]
...
}
,
plugins: [
new ExtractTextPlugin('styles/index-[contenthash].css')
]
The versions used in my package.json for this are:
"css-loader": "^0.23.1"
"less-loader": "^2.2.2"
"postcss-loader": "^0.9.1"
"style-loader": "^0.13.0",
"extract-text-webpack-plugin": "^1.0.1"
"webpack": "2.1.0-beta.8"
My webpack config has a few other loaders and plugins, but I currently am only facing issues with the ExtractTextPlugin. After upgrading my setup, I ended up with the following code:
module: {
...
rules: [{
test: /\.(css|less)$/,
use: ExtractTextPlugin.extract(['css', 'postcss', 'less'])
}]
...
}
,
plugins: [
new ExtractTextPlugin('styles/index-[contenthash].css')
]
This configuration uses the following versions:
"css-loader": "^0.26.1"
"less-loader": "^2.2.3"
"postcss-loader": "^1.2.1"
"style-loader": "^0.13.1"
"extract-text-webpack-plugin": "2.0.0-beta.4"
"webpack": "2.2.0-rc.3"
The above configuration results in the following exceptions:
ERROR in ./src/index.less Module parse failed:
D:...\node_modules\extract-text-webpack-plugin\loader.js?{"omit":0,"remove":true}!style-loader!css-loader!postcss-loader!less-loader!D:...\src\index.less
Unexpected character '#' (3:0) You may need an appropriate loader to
handle this file type. | /* 1. Import vendor styles / | | #import
'./index.vendor.less'; | | / 2. Import general styles */ #
./src/index.ts 24:0-23 # multi app
ERROR in ./~/animate.css/animate.css
Module parse failed: D:...\node_modules\animate.css\animate.css
Unexpected character '#' (1:0)
You may need an appropriate loader to handle this file type.
| #charset "UTF-8";
|
| /*!
ERROR in
./~/bootstrap-material-datetimepicker/css/bootstrap-material-datetimepicker.css
Module parse failed: D:...\node_modules\bootstrap-material-datetimepicker\css\bootstrap-material-datetimepicker.css
Unexpected token (1:0)
You may need an appropriate loader to handle this file type.
| .dtp { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.4); z-index: 2000; font-size: 15px;
-webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; }
| .dtp > .dtp-content { background: #fff; max-width: 300px; box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0,
0, 0.12); max-height: 520px; position: relative; left: 50%; }
| .dtp > .dtp-content > .dtp-date-view > header.dtp-header { background: #689F38; color: #fff; text-align: center; padding: 0.3em;
}
Apart from the above loader configuration, I also tried the following configuration but it didn't work neither:
use: ExtractTextPlugin.extract(['style-loader', 'css-loader', 'postcss-loader','less-loader'])
use: ExtractTextPlugin.extract({
fallbackLoader: "style-loader",
loader: "css-loader!less-loader"
})
I've removed my node_modules folder and installed everything from scratch, but this didn't help.
Note: Removing the ExractTextPlugin configuration and replacing it with the following does allow me to succesfuly build the application, so I'd say the rest of my webpack configuration has been succesfully migrated!
{
test: /\.(css|less)$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
}
I've uploaded a sample to reproduce the issue: https://dl.dropboxusercontent.com/u/87239305/webpack-less.zip
Steps to reproduce:
npm install
node_modules\ .bin\webpack.cmd --config
conf\webpack.conf.js
I've also added a second config file which is working, without ExtractTextPlugin:
node_modules\ .bin\webpack.cmd --config conf\webpack.conf2.js
Any guidance in what I'm missing here is appreciated!
Thanks in advance.
It appeared to be an issue with ExtractWebpackPlugin and should have been fixed with the latest release: https://github.com/webpack/extract-text-webpack-plugin/releases/tag/v2.0.0-beta.5.
I verified my reproduce app mentioned above is working when upgrading ETP to beta5 using the following syntax in my webpack.config file:
{
test: /\.(css|less)$/,
loader: ExtractTextPlugin.extract(["css-loader", "postcss-loader", "less-loader"])
}
Aswell as using the following syntax
{
test: /\.(css|less)$/,
loader: ExtractTextPlugin.extract({
fallbackLoader: "style-loader",
loader: ["css-loader", "postcss-loader", "less-loader"]
})
},