Combining tailwind css with sass using webpack - css

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

Related

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.

Font-awesome 5 doesn't work with webpack when installing with npm

I want to install Font-awesome with npm so that it will work with webpack and babel.
I installed font-awesome with npm via
npm install --save-dev #fortawesome/fontawesome-free
And added this to the top of my index.js
import '#fortawesome/fontawesome-free/js/fontawesome'
import '#fortawesome/fontawesome-free/js/solid'
import '#fortawesome/fontawesome-free/js/regular'
import '#fortawesome/fontawesome-free/js/brands'
When I'm trying to add icons to my html e.g.
<i class="fas fa-like"></i>
It just shows a circle with a question mark
My webpack config
const path = require('path');
const webpack = require('webpack');
const HtmlWebPackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
main: ['webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000', './src/index.js']
},
output: {
path: path.join(__dirname, 'dist'),
publicPath: '/',
filename: '[name].js'
},
mode: 'development',
target: 'web',
devtool: 'source-map',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
},
{
// Loads the javacript into html template provided.
// Entry point is set below in HtmlWebPackPlugin in Plugins
test: /\.html$/,
use: [
{
loader: 'html-loader',
//options: { minimize: true }
}
]
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(scss)$/,
use: [{
loader: 'style-loader', // inject CSS to page
}, {
loader: 'css-loader', // translates CSS into CommonJS modules
}, {
loader: 'postcss-loader', // Run post css actions
options: {
plugins: function () { // post css plugins, can be exported to postcss.config.js
return [
require('precss'),
require('autoprefixer')
];
}
}
}, {
loader: 'sass-loader' // compiles Sass to CSS
}]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: ['file-loader']
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: './src/html/index.html',
filename: './index.html',
excludeChunks: ['server']
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()
]
};
My .babelrc
{
"presets" : ["#babel/preset-env"]
}
P.s. Everything works fine, it is just that my font-awesome icons aren't displaying properly.
You are using an invalid icon because there is no fa-like, you are probably looking for fa-thumbs-up instead:
<script src="https://use.fontawesome.com/releases/v5.8.1/js/all.js"></script>
<i class="fas fa-like"></i>
<i class="fas fa-thumbs-up"></i>
You can easily seach for icons here: https://fontawesome.com/icons?d=gallery&q=like

I'm having trouble setting up Autoprefixer in my webpack config

I've tried copying numerous webpack setups, but I can't seem to get the postcss-loader Autoprefixer to work. I use Flexbox heavily in my projects, and I really want to have webpack add the prefixes for older browsers on yarn build. Right now, the SCSS is compiled into CSS, but no prefixes are added. Here is what my webpack config currently looks like:
webpack.js
const webpack = require('webpack');
const merge = require('webpack-merge');
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
var autoprefixer = require('autoprefixer');
const baseConfig = require('./base.config.js');
module.exports = merge(baseConfig, {
output: {
filename: 'app.bundle.min.js',
path: path.join(__dirname, '../../assets')
},
module: {
rules: [
{
test: /\.(scss|css)$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: { sourceMap: true, minimize: true }
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: [require('autoprefixer')]
}
},
{
loader: 'sass-loader',
options: { sourceMap: true, minimize: true }
}
],
fallback: 'style-loader'
})
}
]
},
plugins: [
new ExtractTextPlugin('app.bundle.min.css'),
new webpack.LoaderOptionsPlugin({
options: {
postcss: [autoprefixer()]
}
}),
// Minimize JS
new UglifyJsPlugin({ sourceMap: true, compress: true })
// Minify CSS
/*new webpack.LoaderOptionsPlugin({
minimize: true,
}),*/
]
});
I believe you need to call its constructor i.e require('autoprefixer')()
I'm seeing this in the PostCss Loader README.
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: [
require('autoprefixer')({...options}), // calls constructor with optional options
...,
]
}
}
]
}

Window is not defined (Server-side rendering with Asp.Net SPA Services)

Thank you guys for reading my question. Really hoping for a solution to this, I've been trying to find a fix for days to no avail.
Here's the rundown: My goal is to render a React application on the server-side using .NET Core. I haven't even started with the react part yet, right now I'm simply trying to render an h1 tag with the ASP.NET Javascript Services Prerendering functionality.
On my first iteration, I wrote my boot-server.js file with es5 and it worked perfectly. However, I quickly realized I was going to need to compile the file through webpack in order for it to understand my React code.
As soon as I piped that file through webpack though, I got a "window is not defined" error which I haven't been able to fix. I understand that part of my application should not be aware of the window object since it lives in the server but setting the webpack config's target field to node does not seem to fix the issue. Below are all the files involved.
Here is my boot-server.js file:
import { createServerRenderer } from 'aspnet-prerendering';
module.exports = createServerRenderer((params) => {
return new Promise((resolve, reject) => {
var html = '<h1>Hello world!</h1>';
resolve({
html: html
});
});
});
Here is my cshtml view:
#addTagHelper "*, Microsoft.AspNetCore.SpaServices"
<app id="root" asp-prerender-module="wwwroot/client/boot-server.bundle">Loading...</app>
Here is my webpack.config.js file:
const webpack = require('webpack');
const nodeExternals = require('webpack-node-externals');
const path = require("path");
const clientConfig = {
entry: './FrontEnd/index.js',
output: {
path: path.resolve(__dirname, "wwwroot/client"),
filename: "client.min.js",
publicPath: "/client/"
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["es2015", "react", "stage-0"],
plugins: ["transform-class-properties", "transform-decorators-legacy", "react-html-attrs"]
}
}
},
{
test: /\.scss$/,
use: [{
loader: "style-loader"
}, {
loader: "css-loader"
}, {
loader: "sass-loader"
}]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader?name=[name]_[hash:8].[ext]'
]
}
]
},
mode: 'development',
devServer: {
contentBase: './wwwroot/client',
hot: true
}
};
const bootServerConfig = {
stats: { modules: false },
resolve: { extensions: ['.js'] },
output: {
filename: '[name].js',
publicPath: '/wwwroot/client/', // Webpack dev middleware, if enabled, handles requests for this URL prefix
libraryTarget: 'commonjs'
},
entry: {
'main-server': './FrontEnd/boot-server.js'
},
module: {
rules: [
{
test: /\.js$/,
include: /FrontEnd/,
use: [
{
loader: "babel-loader",
options: {
presets: ["es2015", "react", "stage-0"],
plugins: ["transform-class-properties", "transform-decorators-legacy", "react-html-attrs"]
}
}
]
},
{
test: /\.svg$/,
use: {
loader: 'url-loader',
options: { limit: 25000 } //?limit=100000'
}
}
]
},
output: {
libraryTarget: 'commonjs',
path: path.join(__dirname, './wwwroot/client')
},
target: 'node'
}
module.exports = [clientConfig, bootServerConfig];
Here is a screenshot of the error page:

Webpack source-map does not resolve sass imports

I have webpack configured to transpile scss -> css, but sourcemap generated by webpack does not resolve scss #imports.
webpack.config.js:
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const outputPath = path.join(__dirname, 'dist');
module.exports = {
devtool: 'source-map',
entry: ['./src/main.scss'],
target: 'web',
output: {
filename: 'js/[name].bundle.js',
path: outputPath
},
module: {
rules: [
{ // sass / scss loader for webpack
test: /\.(sass|scss)$/,
loader: ExtractTextPlugin.extract([
{
loader: 'css-loader',
options: {
url: false,
import: true,
minimize: true,
sourceMap: true,
}
},
'sass-loader'
])
},
]
},
plugins: [
new ExtractTextPlugin({ // define where to save the file
filename: 'css/[name].bundle.css',
allChunks: true,
})
]
};
main.scss:
#import 'foo';
_foo.scss:
h1 { color: red; }
However, in Chrome dev tools, I see a reference to main.scss where I expect reference to _foo.scss - see the screenshot below:
Compiled demo: http://store.amniverse.net/webpacktest/
You should not use extractTextPlugin when you are in dev mode.
Please make extra configs for dev and production mode. In production the use of extractTextPlugin is fine but in dev mode it is not necessary and can lead to other features not working. So instead use the style-loader.
Also - I am not sure if that fixes your problem - try to use importLoaders prop on the css loader. Look here for more info:
https://github.com/webpack-contrib/css-loader#importloaders
const path = require('path');
const outputPath = path.join(__dirname, 'dist');
module.exports = {
devtool: 'source-map',
entry: ['./src/main.scss'],
target: 'web',
output: {
filename: 'js/[name].bundle.js',
path: outputPath
},
module: {
rules: [
{ // sass / scss loader for webpack
test: /\.(sass|scss)$/,
loader: [
{
loader: 'style-loader',
options: {
sourceMap: true
}
},
{
loader: 'css-loader',
options: {
url: false,
import: true,
minimize: true,
sourceMap: true,
importLoaders: 1,
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}
]
},
]
}
};
You have sass-loader there, switch it with:
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}
And that would work.
There's nothing wrong with ExtractTextPlugin in dev mode, and what #Omri Aharon posted is correct. However, what you should consider is having source-map enabled only in dev mode.
To build webpack using its default production settings (which uglifies and applies OccurrenceOrderPlugin plugin by default in webpack 2.0+), run the command webpack -p, and then in your webpack.config.js, you can determine if you're in dev mode or not by doing:
const DEBUG = !process.argv.includes('-p');
Add the function
function cssConfig(modules) {
return {
sourceMap: DEBUG,
modules,
localIdentName: DEBUG ? '[name]_[local]_[hash:base64:3]' : '[hash:base64:4]',
minimize: !DEBUG
};
}
in your webpack.config.js, making your scss loader appear as so:
test: /\.(sass|scss)$/,
loader: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: cssConfig(true)
},
{
loader: 'sass-loader',
options: { sourceMap: DEBUG }
}
]
})
},
and my plugins section has
new ExtractTextPlugin('[name].css?[contenthash]'),

Resources