Webpack - Export SASS (.scss) files - css

I have a package and i want export my SASS variables to other packages use it. Currently my all .scss files are compiles and put in /dist/main.css file. My webpack config:
var webpack = require('webpack');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
entry: ['./src/index.js'],
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel'
},
{
test: /\.(scss|sass|css)$/,
loader: ExtractTextPlugin.extract("style", "css!sass")
},
{
test: /\.(png|woff|woff2|eot|ttf|svg)$/,
loader: 'url-loader?limit=10000&name=fonts/[hash].[ext]'
},
{
test: /\.scss$/, loader: 'style!css!sass!sass-resources'
}
]
},
resolve: {
extensions: ['', '.js', '.jsx']
},
output: {
path: __dirname + '/build',
publicPath: '/',
filename: 'index.js',
library: 'Supernova',
libraryTarget: 'umd'
},
externals: {
'react': 'react',
'react-dom': 'react-dom'
},
plugins: [
new ExtractTextPlugin("[name].css")
]
};
My objective is create a package like bootstrap-sass.

If you want to make the variables you use within your sass files available to consumers of your published package, then you'll need to look at some special configuration for node-sass.
Currently (and as of the time you posted this) node-sass supports writing your own custom sass functions in javascript: https://github.com/sass/node-sass#functions--v300---experimental
This is untested, but we did this a while ago at a company i worked for...to do what you want, you'd need something like:
src/
your-package.js
your-styles.scss
tools/
constants/
colours.js
webpack/
...
base.sass.js
base.js
development.js
production.js
sass/
functions/
colours.js
# tools/webpack/base.sass.js
const Config = require('webpack-config').default
import {
signature as ColourSignature,
handler as ColourHandler
} from '#tools/sass/functions/colours
module.exports = new Config()
.merge({
module: {
rules: [
{
test: /\.scss$/,
use: [
...
{ loader: 'sass-loader',
options: {
sourceMap: true,
functions: {
[ColourSignature]: ColourHandler
}
}
},
]
}
]
}
})
# src/your-package.js
import Colours from '#tools/constants/colours'
import "./your-styles.scss"
export default YourAwesomeComponent {
static Colours = Colours
}
export const colours = Colours
# src/your-styles.scss
.your-awesome-component {
background-color: ColourGet(veganvomit, sobrightithurts);
}
# tools/sass/functions/colour.js
import Colours from '#tools/constants/colours'
export signature = 'ColourGet($name, $shade: default)'
export handler = function(name, shade) {
const colour = Colours[name]
if (!colour) return
if (typeof colour === 'string') return colour
return colour[shade]
}
# tools/sass/constants/colours.js
export default {
veganvomit: {
sobrightithurts: "darkkhaki",
light: "#D2691E",
default: "#8B4513",
somethingsomethingsomethingdarkside: "#000"
}
}
So now when you publish your package, they can access sass variables from your default export YourAwesomeClass.Colours or they can import it directly `import { Colours } from 'your-awesome-package'

I highly recommend using webpack-merge to separate out your Sass config to make it easy for other packages to use it. For your current config, I would do three things:
Add webpack-merge to your project (npm i --save-dev webpack-merge).
Put your Sass config into a separate file, named something like webpack.sass-config.js. Have it include the following:
var ExtractTextPlugin = require('extract-text-webpack-plugin');
exports.config = function(options) {
return {
module: {
loaders: [
{
test: /\.(scss|sass|css)$/,
loader: ExtractTextPlugin.extract("style", "css!sass")
},
{
test: /\.scss$/, loader: 'style!css!sass!sass-resources'
}
]
},
plugins: [
new ExtractTextPlugin("[name].css")
]
}
}
// Side note: returning a function instead of a plain object lets
// you pass optional parameters from your main config file. This
// is useful if you want to make something like your compiled css
// file name different for another Webpack project without having
// to edit your Sass configuration file.
Update your webpack.config.js to the following:
var merge = require('webpack-merge');
// import your separated Sass configuration
var sassConfig = require('webpack.sass-config');
// Define your common config for entry, output, JSX, fonts, etc.
var common = {
entry: ['./src/index.js'],
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel'
},
{
test: /\.(png|woff|woff2|eot|ttf|svg)$/,
loader: 'url-loader?limit=10000&name=fonts/[hash].[ext]'
}
]
},
resolve: {
extensions: ['', '.js', '.jsx']
},
output: {
path: __dirname + '/build',
publicPath: '/',
filename: 'index.js',
library: 'Supernova',
libraryTarget: 'umd'
},
externals: {
'react': 'react',
'react-dom': 'react-dom'
}
};
// Merge your common config and Sass config
var config = merge(
common,
sassConfig.config()
);
// Export the merged configuration
modules.exports = config;
Obviously, this can go far beyond just your Sass config. I use webpack-merge to separate my development config from my production config. This article on Survive JS is a great resource for how to make the most of your Webpack setup.

Related

webpack not bundling scss when using import instead of require

Simple webpack setup, when I use import to load my scss it is completely missing from the bundle. The line where the import should be is simply missing. When I use require instead, it works.
optimization: {usedExports: true} is not the problem, I tried with and without
mini-css-extract-plugin also did not work.
when I put a typo in the scss it complains, so it is parsed but simply not bundled in the end?
index.js
require("./scss/style.scss");
//import "./scss/style.scss" <--- not working
import { createApp } from 'vue';
import App from './components/App.vue';
const el = document.getElementById('app');
createApp(App).mount(el);
webpack config
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader');
const { DefinePlugin } = require('webpack');
const dist = path.resolve(__dirname, "dist");
module.exports = env => {
const mode = env.production == true ? "production" : "development";
const devtool = mode == "production" ? false : "inline-source-map";
return {
mode: mode,
entry: './web/index.js',
output: {
filename: 'bundle.js',
path: dist
},
optimization: {
usedExports: true,
},
devServer: {
static: {
directory: dist
},
port: 8888
},
module: {
rules: [{
test: /\.(sa|sc|c)ss$/,
use: [
'style-loader',
'css-loader',
'sass-loader',
],
}, {
test: /\.(ttf|eot|woff|woff2|svg)$/,
use: {
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/'
},
},
}, {
test: /\.vue$/,
loader: 'vue-loader'
}]
},
plugins: [
new CleanWebpackPlugin(),
new DefinePlugin({
__VUE_OPTIONS_API__: false,
__VUE_PROD_DEVTOOLS__: false,
}),
new HtmlWebpackPlugin({
template: path.resolve("./web/index.html")
}),
new VueLoaderPlugin()
],
resolve: {
extensions: ['.js'],
alias: {
"#": path.resolve(__dirname, 'web')
}
},
devtool
};
};
I found the problem but I don't understand why webpack drops it.
Quote from https://webpack.js.org/guides/tree-shaking/
Note that any imported file is subject to tree shaking. This means if you use something like css-loader in your project and import a CSS file, it needs to be added to the side effect list so it will not be unintentionally dropped in production mode:
In my package.json I put
"sideEffects": false,
to be able to use treeshaking.
But I had to disable it in the loader rule
{
test: /\.(sa|sc|c)ss$/,
use: ['style-loader','css-loader','sass-loader'],
sideEffects: true <----
}

Compile CSS and JS in difference files / WEBPACK

For 2 days I have been trying to compile the js and css file to a separate file because now everything is together. Does anyone have any idea how this can be solved?
I would be very grateful for your help.
There is my code webpack.config.js
const path = require('path');
const webpack = require('webpack');
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'src/dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
},
{
test: /\.scss$/,
use: [
"style-loader", // creates style nodes from JS strings
{
loader: "css-loader",
options: {
url: false
}
},
"sass-loader" // compiles Sass to CSS, using Node Sass by default
]
},
]
},
plugins: [
new BrowserSyncPlugin({
// browse to http://localhost:3000/ during development,
// ./public directory is being served
host: 'localhost',
port: 3000,
files: ['./src/*.html'],
server: { baseDir: ['src'] }
}),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
})
]
};
I think MiniCssExtractPlugin is what you are looking for.
It takes the output of css-loader and create .css bundles. It takes care of downloading them in the browser (by pushing a section of code in webpack runtime code), and also yeah, it minifies the .css :).
Simple usage:
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
plugins: [new MiniCssExtractPlugin()],
module: {
rules: [
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
],
},
};
Yes you are right. Style-loader creates javascript snippets that later in runtime creates .css rules and push them to the browser global css scope.

Combining tailwind css with sass using webpack

I'm struggling a bit getting Tailwind CSS to work with SASS and Webpack. It seems like the postcss configuration for tailwind doesn't really do anything in terms of processing #tailwind preflight, #tailwind components and #tailwind utilities
My set up is as follows:
layout.scss
#import "~tailwindcss/preflight.css";
#import "~tailwindcss/components.css";
.my-class {
#apply text-blue;
#apply border-red;
}
#import "~tailwindcss/utilities.css";
entry.js
import '../css/src/layout.scss';
postcss.config.js
const tailwindcss = require('tailwindcss');
const purgecss = require('#fullhuman/postcss-purgecss');
const cssnano = require('cssnano');
const autoprefixer = require('autoprefixer');
module.exports = {
plugins: [
tailwindcss('./tailwind.js'),
cssnano({
preset: 'default',
}),
purgecss({
content: ['./views/**/*.cshtml']
}),
autoprefixer
]
}
webpack.config.js
// NPM plugins
const autoprefixer = require('autoprefixer');
const WebpackNotifierPlugin = require('webpack-notifier');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
module.exports = {
entry: {
main: './scripts/entry.js'
},
output: {
filename: '[name].bundle.js',
publicPath: './'
},
watch: false,
externals: {
jquery: 'jQuery'
},
mode: 'development',
plugins: [
// Notify when build succeeds
new WebpackNotifierPlugin({ alwaysNotify: true }),
// Extract any CSS from any javascript file to process it as LESS/SASS using a loader
new MiniCssExtractPlugin({
fileame: "[name].bundle.css"
}),
// Minify CSS assets
new OptimizeCSSAssetsPlugin({}),
// Use BrowserSync plugin for file changes. I.e. if a CSS/SASS/LESS file changes, the changes will be injected directly in the browser with no page load
new BrowserSyncPlugin({
proxy: 'mysite.local',
open: 'external',
host: 'mysite.local',
port: 3000,
files: ['./dist/main.css', './views', './tailwind.js']
},
{
// disable reload from the webpack plugin since browser-sync will handle CSS injections and JS reloads
reload: false
})
],
module: {
rules: [
{
// Transpile ES6 scripts for browser support
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
},
{
test: /\.(png|jpg|gif|svg|eot|ttf|woff)$/,
use: [
{
loader: 'file-loader'
}
]
},
{
// Extract any SCSS content and minimize
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader' },
{
loader: 'postcss-loader',
options: {
plugins: () => [autoprefixer()]
}
},
{
loader: 'sass-loader',
options: {
plugins: () => [autoprefixer()]
}
}
]
},
{
// Extract any CSS content and minimize
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { importLoaders: 1 } },
{ loader: 'postcss-loader' }
]
}
]
}
};
When I run Webpack, everything runs just fine, but the content of /dist/main.css is:
#tailwind preflight;#tailwind components;#tailwind utilities;.my-class{#apply text-blue;#apply border-red}
I suspect it's related to the (order of) loaders, but I can't seem to figure out why it's not getting processed correctly.
Does anyone know what I'm doing wrong here? :-)
Thanks in advance.
Wow, so after fiddling around with the loaders even more, I made it work :-) For future reference:
I added options: { importLoaders: 1 } to the css-loader for SCSS files and removed: plugins: () => [autoprefixer()] from the postcss-loader in my webpack.config.js file.
Full, updated webpack.config.js file:
// NPM plugins
const autoprefixer = require('autoprefixer');
const WebpackNotifierPlugin = require('webpack-notifier');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
module.exports = {
entry: {
main: './scripts/entry.js'
},
output: {
filename: '[name].bundle.js',
publicPath: './'
},
watch: false,
externals: {
jquery: 'jQuery'
},
mode: 'development',
plugins: [
// Notify when build succeeds
new WebpackNotifierPlugin({ alwaysNotify: true }),
// Extract any CSS from any javascript file to process it as LESS/SASS using a loader
new MiniCssExtractPlugin({
fileame: "[name].bundle.css"
}),
// Minify CSS assets
new OptimizeCSSAssetsPlugin({}),
// Use BrowserSync plugin for file changes. I.e. if a CSS/SASS/LESS file changes, the changes will be injected directly in the browser with no page load
new BrowserSyncPlugin({
proxy: 'mysite.local',
open: 'external',
host: 'mysite.local',
port: 3000,
files: ['./dist/main.css', './views', './tailwind.js']
},
{
// disable reload from the webpack plugin since browser-sync will handle CSS injections and JS reloads
reload: false
})
],
module: {
rules: [
{
// Transpile ES6 scripts for browser support
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
},
{
test: /\.(png|jpg|gif|svg|eot|ttf|woff)$/,
use: [
{
loader: 'file-loader'
}
]
},
{
// Extract any SCSS content and minimize
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { importLoaders: 1 } },
{
loader: 'postcss-loader'
},
{
loader: 'sass-loader',
options: {
plugins: () => [autoprefixer()]
}
}
]
},
{
// Extract any CSS content and minimize
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { importLoaders: 1 } },
{ loader: 'postcss-loader' }
]
}
]
}
};
There is an extension called tailwindcss-transpiler which compiles your layout.tailwind.scss files into pure CSS files.It also optimizing features.I hope it will be useful.
For VS Code
https://marketplace.visualstudio.com/items?itemName=sudoaugustin.tailwindcss-transpiler
For Atom
https://atom.io/packages/tailwindcss-transpiler

Webpack Automatically import css from node modules

TL;DR: Is there any way to automatically scan or automatically import all stylesheets used by our web packages, from our node_modules folder?
I am working on a project that uses angularJS as its MVC framework, and we are currently in the process of migrating to use webpack. I currently wrote the following configuration:
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const bbProperties = require('./webpack.properties');
const appRoot = __dirname + "/../../..";
console.log('__dirname:', __dirname);
module.exports = {
entry: {
app: appRoot + bbProperties.app.entryScript,
login: appRoot + bbProperties.login.entryScript
},
output: {
path: appRoot + bbProperties.distFolder,
publicPath: "dist",
filename: "js/[name].bundle.js",
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
presets: ['es2017']
}
},
{
test: /\.css$/,
//exclude: /node_modules/,
use: [
{loader: 'style-loader'},
{loader: 'css-loader', query: {importLoaders: 1}},
{loader: 'postcss-loader', query: {config: {path: __dirname+'/postcss.config.js'}}}
]
},
{
test: /\.(png|jpg|jpeg|gif)$/,
exclude: /node_modules/,
use: [
{
loader: 'file-loader',
options: {
name:'[name].[ext]',
outputPath:"/assets/images/"
}
}
]
},
{
test: /\.(ttf|eot|woff|woff2|svg)($|\?.*$)/,
exclude: /node_modules/,
use: [
{
loader: 'file-loader',
options: {
name:'[name].[ext]',
outputPath:"/assets/fonts/"
}
}
]
},
]
},
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery",
moment: "moment"/*,
_: "lodash",
"window._": "lodash",
angular: "angular"*/
}),
//Creates an html file with all bundles injected as <script> tags
new HtmlWebpackPlugin({
chunks: ['app'],
filename: '../angular-app/menu/' + bbProperties.app.outputFile,
hash: "true", //cache busting
template: appRoot + bbProperties.app.location + "/" + bbProperties.app.inputFile
}),
//Second html bundle for login page
new HtmlWebpackPlugin({
chunks: ['login'],
filename: '../'+bbProperties.login.outputFile,
hash: "true", //cache busting
template: appRoot + bbProperties.login.location + "/" + bbProperties.login.inputFile
})
],
stats: {colors: true},
devtool: 'source-map',
node: {
fs: "empty"
}
};
As far as I understand, webpack will start creating a bundle from each entry file, adding any file it encounters in import statements recursively.
I also understand that I can require a package from my node_modules directory, and that in such a case webpack will search for a package.json file and import the file specified in the 'main' property of that file.
Lets say we are dependant on several packages, components and frameworks (for example: angular-ui-grid, angular-material etc...), I know I can manually import their CSS files using css loaders by importing them into my JS code.
Is there any way to automatically scan or automatically import all stylesheets used by those packages, from our node_modules folder?

How to import css file for into Component .jsx file

I am trying to use the react-day-pickers component like so but I can't figure out how to import the .css file and I keep getting the error:
Module parse failed:
/Users/qliu/Documents/workspace/AppNexus/pricing_ui/contract-ui/app_contract-ui/node_modules/react-day-picker/lib/style.css Unexpected token (3:0)
You may need an appropriate loader to handle this file type.
SyntaxError: Unexpected token (3:0)
I have "css-loader": "^0.19.0", in my package.json installed and
here is my Calender.jsx file:
import React from "react";
import DayPicker, { DateUtils } from "react-day-picker";
import "../../../node_modules/react-day-picker/lib/style.css"; // <==== ERROR ON THIS LINE
export default class Calender extends React.Component {
state = {
selectedDay: null
};
handleDayClick(e, day, modifiers) {
if (modifiers.indexOf("disabled") > -1) {
console.log("User clicked a disabled day.");
return;
}
this.setState({
selectedDay: day
});
}
render() {
// Add the `selected` modifier to the cell of the clicked day
const modifiers = {
disabled: DateUtils.isPastDay,
selected: day => DateUtils.isSameDay(this.state.selectedDay, day)
};
return <DayPicker enableOutsideDays modifiers={ modifiers } onDayClick={ this.handleDayClick.bind(this) } />;
}
}
and this is my webpack.config.js:
var path = require('path');
var webpack = require('webpack');
var settings = require('./src/server/config/settings');
var LessPluginAutoPrefix = require('less-plugin-autoprefix');
module.exports = {
devtool: '#source-map',
resolve: {
extensions: ['', '.jsx', '.js']
},
entry: [
'webpack-hot-middleware/client',
'./src/client/index.jsx'
],
externals: {
'react': 'React',
'react-dom': 'ReactDOM',
// to avoid duplicate import of React with react-addons-css-transition-group
'./React': 'React',
'./ReactDOM': 'ReactDOM',
config: 'config'
},
output: {
path: path.join(__dirname, 'public'),
filename: 'bundle.js',
publicPath: '/'
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
],
lessLoader: {
lessPlugins: [
new LessPluginAutoPrefix({ browsers: ['last 2 versions'] })
]
},
module: {
loaders: [{
test: /\.(js|jsx)$/,
loaders: ['babel'],
exclude: /node_modules/
},
{
test: /\.less$/,
loader: 'style!css!less'
},
{
test: /\.json$/,
loader: 'json-loader'
}]
}
};
How are you compiling this? If it's webpack, you'd probably need to bring in the style-loader and include something like this in the module.loaders array in your webpack config:
{
test: /\.css$/,
loader: "style!css"
}
Update: With the webpack.config.js now provided, we can confirm it needed a change in the module.loaders array. OP was using a less loader and only checking for .less files, so the exact loader object should read:
{
test: /\.(less|css)$/,
loader: 'style!css!less'
}
As suggested by #Q Liu. Leaving the original bit as if someone comes here with an error importing a .css file, it's likely what they need.
I used the following in my webpack config and it worked:
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
},
},
],
JSX is not CSS. In your main HTML file, just add a <link> element to the CSS file, and the DOM generated by React will use it.

Resources