In my Node React web application written in TypeScript and TSX, I make use of CSS modules. Hence, when building the application I instruct Webpack to create a style.css.d.ts file out of a style.css so that I can access its classes like any other param by importing the CSS file into the TypeScript class with import * as style from "./style.css"; and then access the params like this:
export class Foot extends React.Component<FootProps, {}> {
render() {
return <div className={style.foot}>© MySite</div>;
}
}
What I'd like to do now is change from CSS to the SASS format SCSS and basically do the same thing. I want to have my SASS file i.e. style.scss and instruct Webpack to create a style.scss.d.ts file at build time. Though I can't figure out how to do that.
Module rule in my webpack.config.js:
module: {
rules: [
// All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'.
{ test: /\.tsx?$/, loader: "awesome-typescript-loader" },
// All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
{ enforce: "pre", test: /\.js$/, loader: "source-map-loader" },
{ test: /\.css$/, use: [
"style-loader",
{ loader: "typings-for-css-modules-loader", options: { modules: true, namedExport: true, camelCase: true, localIdentName: "[name]_[local]_[hash:base64]" }}
] },
{ test: /\.(scss)$/, use : [
{
// Adds CSS to the DOM by injecting a `<style>` tag.
loader: "style-loader"
},
{
// Interprets `#import` and `url()` like `import/require()` and will resolve them.
loader: "css-loader"
},
{
// Loader for webpack to process CSS with PostCSS.
loader: "postcss-loader",
options: {
plugins: function () {
return [
require("autoprefixer")
];
}
}
},
{
// Loads a SASS/SCSS file and compiles it to CSS.
loader: "sass-loader"
}
]
}
]
},
Dependencies in my package.json:
"dependencies": {
"#types/jquery": "^3.3.22",
"#types/react": "^16.4.18",
"#types/react-dom": "^16.0.9",
"bootstrap": "^4.1.3",
"jquery": "^3.3.1",
"popper.js": "^1.14.4",
"react": "^16.6.0",
"react-dom": "^16.6.0"
},
"devDependencies": {
"autoprefixer": "^9.3.1",
"awesome-typescript-loader": "^5.2.1",
"css-loader": "^1.0.1",
"html-webpack-plugin": "^3.2.0",
"node-sass": "^4.10.0",
"postcss-loader": "^3.0.0",
"sass-loader": "^7.1.0",
"source-map-loader": "^0.2.4",
"style-loader": "^0.23.1",
"typescript": "^3.1.6",
"typings-for-css-modules-loader": "^1.7.0",
"webpack": "^4.25.1",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.10"
}
What must I do to change from CSS modules to SASS Modules?
I found it out myself. So, I post it here in hope to help you guys if you have the same problem - +1 like if it did help ;).
I had to pay attention to a Bootstrap SCSS import, so I had to use Webpack's exclude and include functionality for differentiating between local and global CSS imports. Everything works fine now and these are my new module rules within my webpack.config.js file:
{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{ loader: "typings-for-css-modules-loader", options: { modules: true, namedExport: true, camelCase: true, localIdentName: "[name]_[local]_[hash:base64]" }}
]
},
{
test: /\.scss$/,
exclude: /\.global.scss$/,
use : [
{ loader: "style-loader" },
{ loader: "typings-for-css-modules-loader", options: { modules: true, namedExport: true, camelCase: true, localIdentName: "[name]_[local]_[hash:base64]" }},
{ loader: "postcss-loader", options: { plugins: function () { return [ require("autoprefixer") ]; }}},
{ loader: "sass-loader" }
]
},
{
test: /\.scss$/,
include: /\.global.scss$/,
use : [
{ loader: "style-loader" },
{ loader: "css-loader" },
{ loader: "postcss-loader", options: { plugins: function () { return [ require("autoprefixer") ]; }}},
{ loader: "sass-loader" }
]
}
I can't find any flaw in your rules.Better try the following:
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
sourceMap: IS_DEV
}
}
]
},
{
test: /\.(scss)$/,
use: [
{
// Adds CSS to the DOM by injecting a `<style>` tag
loader: 'style-loader'
},
{
// Interprets `#import` and `url()` like `import/require()` and will resolve them
loader: 'css-loader'
},
{
// Loader for webpack to process CSS with PostCSS
loader: 'postcss-loader',
options: {
plugins() {
return [
require('autoprefixer')
];
}
}
},
{
// Loads a SASS/SCSS file and compiles it to CSS
loader: 'sass-loader'
}
]
}
This works for me.
Related
I've just started developing a system using asp.net MVC and react. While trying to integrate CSS (specifically for the react-table package styling, 'react-table/react-table.css'), webpack is refusing to compile it.
I've tried adding css-loader, style-loader, Mini-CSS-Extract-Plugin and an array of other rules but to no avail. I think I've looked at just about every relevant post here but no progress. I've also tried using a very basic css file to see if it was react-table. Whatever I try I get the error:
"Uncaught Error: Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file."
Here's the current webpack.config.js:
"use strict";
var path = require("path");
var WebpackNotifierPlugin = require("webpack-notifier");
var BrowserSyncPlugin = require("browser-sync-webpack-plugin");
module.exports = {
entry: "./Scripts/Home/react/index.js",
output: {
path: path.resolve(__dirname, "./Scripts/dist/Home/react"),
filename: "bundle.js"
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.css$/,
exclude: /node_modules/,
use: ['style-loader', 'css-loader?modules=true&camelCase=true'],
},
{
test: /\.css$/,
include: /node_modules/,
loaders: ['style-loader', 'css-loader?modules=true&camelCase=true'],
}
]
},
devtool: "inline-source-map",
plugins: [new WebpackNotifierPlugin(), new BrowserSyncPlugin()]
};
And my package.json
{
"version": "1.0.0",
"name": "asp.net",
"private": true,
"scripts": {
"dev": "webpack --mode development --watch",
"build": "webpack"
},
"devDependencies": {
"#babel/cli": "^7.4.4",
"#babel/core": "^7.4.5",
"#babel/plugin-proposal-class-properties": "^7.4.4",
"#babel/preset-env": "^7.4.5",
"#babel/preset-react": "^7.0.0",
"babel-loader": "^8.0.6",
"browser-sync": "^2.26.7",
"browser-sync-webpack-plugin": "^2.2.2",
"css-loader": "^3.0.0",
"webpack": "^4.35.2",
"webpack-cli": "^3.3.5",
"webpack-notifier": "^1.8.0"
},
"dependencies": {
"bootstrap": "^4.3.1",
"lodash": "^4.17.11",
"mini-css-extract-plugin": "^0.7.0",
"namor": "^1.1.2",
"react": "^16.8.6",
"react-bootstrap": "^1.0.0-beta.9",
"react-dom": "^16.8.6",
"react-table": "^6.10.0",
"style-loader": "^0.23.1",
"styled-components": "^4.3.2"
}
}
I was under the impression that it was as simple as adding the css-loader & style-loader to produce the desired result but unfortunately I've had no luck
Thanks in advance for any support!
You can try with my configuration. I'm using sass and you can remove it
module: {
rules: [{
test: /\.scss$/,
use: [
'style-loader',
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
minimize: true,
sourceMap: true
}
},
{
loader: "sass-loader"
}
]
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
loader: ["babel-loader", "eslint-loader"]
},
{
test: /\.(jpe?g|png|gif)$/i,
loader: "file-loader"
},
{
test: /\.(woff|ttf|otf|eot|woff2|svg)$/i,
loader: "file-loader"
}
]
}
Full code can be found here
I'm building an app using webpack, babel, and Sass. Everything working fine but sass file not compiling css file even though it's not throwing any errors.
My Packages related to this are:
"webpack": "^3.10.0",
"css-loader": "^0.28.9",
"extract-text-webpack-plugin": "^3.0.2",
"glob-all": "^3.1.0",
"node-sass": "^4.7.2",
"purify-css": "^1.2.5",
"purifycss-webpack": "^0.7.0",
"sass-loader": "^6.0.6"
webpack.config.js:
var path = require('path');
var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
const glob = require('glob-all');
var PurifyCSSPlugin= require('purifycss-webpack');
module.exports = {
entry: './src/js/index.js',
output: {
path: __dirname,
filename: 'dist/js/bundle.js'
},
watch: true,
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['env', 'stage-3'],
}
}
},
{
test:/\.css$/,
use: ExtractTextPlugin.extract({
use: [
{
loader: 'css-loader',
options: {
url: false
}
}
]
})
},
{
test:/\.scss$/,
use: ExtractTextPlugin.extract({
use: [
{
loader: 'css-loader',
options: {
url: false
}
},
'sass-loader'
]
})
}
]
},
plugins: [
new ExtractTextPlugin({
filename: './dist/css/styles.css'
}),
new PurifyCSSPlugin({
paths: glob.sync([
path.join(__dirname, 'dist/index.html'),
path.join(__dirname, 'src/js/*.js')
])
})
]
}
Files Directory:
This is my project Files Directory
test:/\.scss$/,
use: ExtractTextPlugin.extract({
use: [
{
loader: ['style-loader', 'css-loader', 'sass-loader']
options: {
url: false
}
},
]
})
I have tried solutions proposed for this identical error message without success. When i run my angular project with the angular cli through ng serve, the project runs fine without errors. However, when i compile it with webpack, i get the (full) error message:
Uncaught Error: Expected 'styles' to be an array of strings.
at z (vendor.js:1)
at t.getNonNormalizedDirectiveMetadata (vendor.js:1)
at t.loadDirectiveMetadata (vendor.js:1)
at vendor.js:1
at Array.forEach (<anonymous>)
at vendor.js:1
at Array.forEach (<anonymous>)
at t._loadModules (vendor.js:1)
at t._compileModuleAndComponents (vendor.js:1)
at t.compileModuleAsync (vendor.js:1)
at e._bootstrapModuleWithZone (vendor.js:1)
at e.bootstrapModule (vendor.js:1)
at Object.<anonymous> (app.js:1)
at Object.199 (app.js:1)
at e (vendor.js:1)
at window.webpackJsonp (vendor.js:1)
at app.js:1
My webpack.config.js:
// Plugins
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
// Config
module.exports = {
entry: {
app: "./src/main.ts",
vendor: ["./src/vendor.ts", "./src/polyfills.ts"]
},
output: {
publicPath: "",
path: __dirname + "/dist/",
filename: "[name].js"
},
resolve: {
extensions: ['.ts', '.js']
},
module: {
rules: [
{
test: /\.ts$/,
loaders: [
{
loader: "awesome-typescript-loader",
options: { tsconfig: "tsconfig.json" }
}, "angular2-template-loader"
]
},
{
test: /\.(html)$/,
loaders: [
{
loader: "html-loader"
}
]
},
{
test: /\.(css|scss)$/,
loaders: ['to-string-loader', 'css-loader', 'sass-loader']
}
]
},
plugins: [
new webpack.optimize.UglifyJsPlugin({
comments: false
}),
new webpack.optimize.CommonsChunkPlugin({
names: ["app", "vendor"]
}),
new HtmlWebpackPlugin({
template: "src/index.tpl.html",
inject: "body",
filename: "index.html"
})
]
}
.angular-cli.json
{
"apps": [
{
"root": "src",
"outDir": "dist",
"index": "index.tpl.html",
"main": "main.ts",
"polyfills": "polyfills.ts"
}
],
"defaults": {
"styleExt": "css"
}
}
app-root / app.component.ts
import { Component } from "#angular/core";
import { Observable } from "rxjs/Rx";
import { NgFor } from "#angular/common";
#Component ({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
}
Any suggestions as to what i am doing wrong?
This was my webpack setup that worked for scss and css files. For this setup (using Angular Storybook, so this webpack config was located in ./storybook), I had a global stylesheet in app/styles that needed some extra massaging because it imported other stylesheets in that directory that used relative paths. I also had a custom stylesheet in the same directory as this webpack (./):
webpack.config.js
const path = require('path');
const autoprefixer = require('autoprefixer');
const postCSSCustomProperties = require('postcss-custom-properties');
const postcssPlugins = () => [
require('postcss-flexbugs-fixes'),
autoprefixer({ flexbox: 'no-2009' }),
postCSSCustomProperties({ warnings: true })
];
module.exports = (baseConfig, env, config) => {
baseConfig.module.rules = [
...baseConfig.module.rules.filter(r => !r.test.toString().includes(".s(c|a)ss")), // base rules supplied by Angular Storybook
...
{
test: /\.css$/,
use: [
'raw-loader',
{
loader: 'postcss-loader',
options: {
plugins: postcssPlugins
}
}
]
},
{ // component scss loader
test: /\.scss$/,
include: [path.resolve(__dirname, '../')],
exclude: [path.resolve(__dirname, '../app/styles'), path.resolve(__dirname, './')],
use: [
'raw-loader',
{
loader: 'postcss-loader',
options: {
plugins: postcssPlugins
}
},
'sass-loader'
]
},
{ // global and storybook scss loader
test: /\.scss$/,
include: [path.resolve(__dirname, '../app/styles'), path.resolve(__dirname, './')],
loaders: [
'style-loader',
'css-loader',
'resolve-url-loader', // resolves relative imports
'sass-loader?sourceMap' // sourcemap = true to assist resolve-url-loader
]
},
...
];
baseConfig.resolve.extensions.push(".css", ".scss");
baseConfig.resolve.alias = {
...baseConfig.resolve.alias,
"assets": path.resolve(__dirname, "../app/assets") // needed to resolve relative paths in imported scss using an alias, where the imported files referred to sheets like "url(~assets/path/to/pic.jpg)"
}
return baseConfig;
};
package.json
"dependencies": {
"#angular/core": "^6.1.3",
"resolve-url-loader": "^3.0.0",
},
"devDependencies": {
"#ngtools/webpack": "6.0.8",
"#storybook/angular": "^4.1.4",
"css-loader": "^0.28.11",
"postcss-custom-properties": "^7.0.0",
"postcss-flexbugs-fixes": "^3.3.1",
"postcss-loader": "^2.1.5",
"raw-loader": "^0.5.1",
"sass-loader": "^7.0.3",
"style-loader": "^0.23.1",
"webpack": "4.15.0"
}
I solved my issue, by following Shobhit's suggestion, adding the following to webpack.config.json:
...
rules: [
{
test: /\.(scss|css)$/,
use: ["raw-loader", "sass-loader"]
},
{
test: /\.scss$/,
exclude: [/node_modules/, /src\/app/],
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: ["css-loader", "sass-loader"]
})
}
...
And
plugins: [
new ExtractTextPlugin("styles.css")
...
When using Gulp to convert Sass to CSS, I can choose output style of the CSS file between: nested, expanded, compact and compressed. Do I have the same opportunity using Webpack? If yes, can you show me how to configurate Webpack to achieve my goal?
Below is my current webpack.config.js - it converts sass to css, translate ES6 to ES5 and it works perfectly:
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
entry: [
"./js/app.js",
"./scss/main.scss"
],
output: {
filename: "./js/out.js"
},
watch: true,
module: {
loaders: [
{
test: /.js$/,
exclude: /node_modules/,
loader: "babel-loader",
query: {
presets: ["es2015"]
}
}
],
rules: [
{
test: /.scss$/,
use: ExtractTextPlugin.extract({
fallbackLoader: "style-loader",
use: ["css-loader?-url", "sass-loader?-url"]
})
}
]
},
plugins: [
new ExtractTextPlugin({
filename: "./css/main.css",
disable: false,
allChunks: true
})
]
}
Thanks in advance!
You are using sass-loader, which uses node-sass under the hood. The readme says that you can pass options directly to node-sass by specifying an options property. You can pass in the outputStyle setting this way. It would look like this:
{
test: /.scss$/,
use: ExtractTextPlugin.extract({
fallbackLoader: "style-loader",
use: [{
loader: 'css-loader'
}, {
loader: 'sass-loader',
options: {
outputStyle: 'expanded'
}
}]
})
}
#Arnelle Balane
That ticked solution won't work with upgraded webpack and sass-loader.
This is the latest working model using:
"webpack": "^4.44.1",
"sass-loader": "~9.0.3",
{
test: /.scss$/,
use: ExtractTextPlugin.extract({
fallbackLoader: "style-loader",
use: [
{
loader: 'css-loader'
},
{
loader: 'sass-loader',
options: {
sassOptions: {
outputStyle: 'expanded'
}
}
}
]
})
}
UPDATE Wepback 5: ExtractTextplugin is now replace to MiniCSSExtraPlugin.
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true,
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true,
sassOptions: {
outputStyle: "expanded",
},
}
},
]
I have following configurations in my webpack.config.js and package.json respectively:
var extractSCSS = new ExtractTextPlugin({filename: '[name].css', disable: false, allChunks: true});
module: {
loaders: [
{
test: /\.jsx?$/,
include: SRC_DIR,
exclude: /(node_modules)/,
loader: 'babel-loader',
query: {
presets: ["react", "es2015", "stage-2"]
}
},
{
test: /\.scss$/i,
include: SRC_DIR,
exclude: /(node_modules)/,
loader: extractSCSS.extract(['css','sass'])
}
]
},
plugins: [
extractSCSS
]
and
"css-loader": "^0.26.1",
"extract-text-webpack-plugin": "^2.0.0-beta",
"style-loader": "^0.13.1",
"webpack": "^2.2.0",
But I am not able to generate the css files. Is there something I am missing here?
EDIT
I updated the files as below:
"css-loader": "^0.26.1",
"extract-text-webpack-plugin": "^2.0.0-beta.5",
"sass-loader": "^4.1.1",
"style-loader": "^0.13.1",
and
{
test: /\.css$/,
exclude: /(node_modules)/,
loader: ExtractTextPlugin.extract({
fallbackLoader: "style-loader",
loader: "css-loader",
publicPath: "/dist"
})
}
I have ExtractTextPlugin set up and it works, but it looks totally different from the configuration that you have. This is my configuration
module: {
rules: [
...
{
loader: ExtractTextPlugin.extract({
fallbackLoader: "style-loader",
loader: [
{
loader: "css-loader"
},
{
loader: "postcss-loader"
},
{
loader: "sass-loader"
}
]
}),
test: /\.s(a|c)ss$/
}
...
},
plugins: [
...
new ExtractTextPlugin({
allChunks: true,
filename: "style.[contenthash].css"
})
...
]
How it works is that the loaders get called from the back to the front. So first is the sass-loader, then the postcss-loader, etc. The fallbackLoader option is used when there is no CSS which can be extracted.
Last but not least, I want to add that I don't use ExtractTextPlugin in development, since it can result in longer build times.
Edit
I forgot to include the plugins part of my configuration. And just to clarify, the dots mean that there it is a piece of my configuration. All content relevant to the question is provided.
You have to install sass-loader
npm install --save-dev sass-loader
Note that with Webpack2 you should/have to update the configuration file :
It is not possible anymore to omit the -loader extension when referencing loaders (Automatic -loader)
module.loaders is now module.rules ( webpack 2 migration guide )
chaining loaders is only supported using the legacy option module.loaders
in V2 rule.use entry specifies a loader to be used. (rule.use)
Here is extracts of my working config :
const extractCss = new ExtractTextPlugin('app.bundle.css');
add this rules :
{
test: /\.scss$/,
loader: extractCss.extract([
{ loader: 'css-loader', query: { sourceMaps: true }},
{ loader: 'sass-loader', query: { sourceMaps: true }}
])
},
and the plugin :
plugins: [
extractCss,
Not sure if this will help, but for me moving over from Browserify I had some grief with this same issue.
I didn't realise in order for the ExtractTextPlugin to produce any css, I had to include the scss in the javascript somewhere (even though it's extracting to app.bundle.css or similar) otherwise it will silently produce no output.
application.js
require('../scss/container.scss')
or
import css from '../scss/container.scss'
will result in injected <style> tags in the header in development, and an extracted app.bundle.css file in production.