50 seconds compile time with PostCSS and Tailwind
See original GitHub issue- Laravel Mix Version: 5.0.5
- Node Version: v10.15.1
- NPM Version: 6.4.1
- OS: MacOS 10.15.6
Description:
Initial compilation of my app.css
takes 50-60 seconds, changes to it need around 40-50 seconds (even if I just press Save again). Removing the Tailwind directives in the app.css
makes it fast again, but I thought that these would be incremental builds. Is there any misconfiguration on my side?
Steps To Reproduce:
app.css
:
@tailwind base;
@tailwind components;
@tailwind utilities;
And thatβs my webpack.mix.js
file (based on https://github.com/ben-rogerson/agency-webpack-mix-config, but with a few changes):
/**
* ===========================
* Agency Webpack-Mix Config
* A capable website/webapp config built for the modern web agency.
* https://github.com/ben-rogerson/agency-webpack-mix-config
* ===========================
*
* Contents
*
* ποΈ Settings
* π Templates
* π Hashing
* π¨ Styles: PostCSS
* π¨ Styles: CriticalCSS
* π¨ Styles: PurgeCSS
* π¨ Styles: Polyfills
* π Scripts
* π Scripts: Polyfills
* π Scripts: Auto import libraries
* π Scripts: Vendor
* π Scripts: Linting
* π Images
* π Icons
* ποΈ Static
* π§ Webpack-dev-server
*/
// ποΈ Base config
const config = {
// Dev domain to proxy
devProxyDomain: process.env.DEFAULT_SITE_URL || 'https://myproject.test',
// Paths to observe for changes then trigger a full page reload
devWatchPaths: ['templates'],
// Port to use with webpack-dev-server
devServerPort: 8080,
// Folders where purgeCss can look for used selectors
purgeCssGrabFolders: ['src', 'templates'],
// Build a static site from the src/template files
buildStaticSite: false,
// Urls for CriticalCss to look for "above the fold" Css
criticalCssUrls: [
// { urlPath: "/", label: "index" },
// { urlPath: "/about", label: "about" },
],
// Folder served to users
publicFolder: 'web/assets',
}
// ποΈ Imports
require('laravel-mix-react-typescript-extension')
const mix = require('laravel-mix')
const path = require('path')
const globby = require('globby')
const tailwindcss = require('tailwindcss')
const autoprefixer = require('autoprefixer')
const presetenv = require('postcss-preset-env')
const hexrgba = require('postcss-hexrgba')
// ποΈ Source folders
const source = {
icons: path.resolve('src/icons'),
images: path.resolve('src/images'),
scripts: path.resolve('src/scripts'),
styles: path.resolve('src/styles'),
static: path.resolve('src/static'),
templates: path.resolve('templates'),
}
// ποΈ Misc
mix.setPublicPath(config.publicFolder)
mix.disableNotifications()
mix.webpackConfig({ resolve: { alias: source } })
!mix.inProduction() && mix.sourceMaps()
/**
* π Hashing (for non-static sites)
* Mix has querystring hashing by default, eg: main.css?id=abcd1234
* This script converts it to filename hashing, eg: main.abcd1234.css
* https://github.com/JeffreyWay/laravel-mix/issues/1022#issuecomment-379168021
*/
if (mix.inProduction() && !config.buildStaticSite) {
// Allow versioning in production
mix.version()
// Get the manifest filepath for filehash conversion
const manifestPath = path.join(config.publicFolder, 'mix-manifest.json')
// Run after mix finishes
mix.then(() => {
const convertToFileHash = require('laravel-mix-make-file-hash')
convertToFileHash({
publicPath: config.publicFolder,
manifestFilePath: manifestPath,
})
})
}
/**
* π¨ Styles: PostCSS
* Extend Css with plugins
* https://laravel-mix.com/docs/4.0/css-preprocessors#postcss-plugins
*/
mix.postCss(path.join(source.styles, 'app.css'), 'css').options({
postCss: [
tailwindcss(),
autoprefixer({
cascade: false,
}),
presetenv({
stage: 0,
}),
hexrgba,
],
processCssUrls: false,
})
/**
* π¨ Styles: CriticalCSS
* https://github.com/addyosmani/critical#options
*/
const criticalDomain = config.devProxyDomain
if (criticalDomain && config.criticalCssUrls && config.criticalCssUrls.length) {
require('laravel-mix-critical')
const url = require('url')
mix.critical({
enabled: mix.inProduction(),
urls: config.criticalCssUrls.map((page) => ({
src: url.resolve(criticalDomain, page.urlPath),
dest: path.join(config.publicFolder, 'css', `${page.label}-critical.css`),
})),
options: {
width: 1200,
height: 1200,
},
})
}
/**
* π¨ Styles: PurgeCSS
* https://github.com/spatie/laravel-mix-purgecss#usage
*/
if (config.purgeCssGrabFolders.length) {
require('laravel-mix-purgecss')
mix.purgeCss({
enabled: mix.inProduction(),
folders: config.purgeCssGrabFolders, // Folders scanned for selectors
whitelist: ['html', 'body', 'active', 'wf-active', 'wf-inactive'],
whitelistPatterns: [],
extensions: ['php', 'twig', 'html', 'js', 'mjs', 'ts', 'tsx'],
})
}
/**
* π Scripts: Main
* Script files are transpiled to vanilla JavaScript
* https://laravel-mix.com/docs/4.0/mixjs
*/
const scriptFiles = globby.sync(`${source.scripts}/*.{js,mjs,ts,tsx}`)
scriptFiles.forEach((scriptFile) => {
mix.reactTypeScript(scriptFile, path.join(config.publicFolder, 'js'))
})
/**
* π Scripts: Polyfills
* Automatically add polyfills for target browsers with core-js@3
* See "browserslist" in package.json for your targets
* https://github.com/zloirock/core-js/blob/master/docs/2019-03-19-core-js-3-babel-and-a-look-into-the-future.md
* https://github.com/scottcharlesworth/laravel-mix-polyfill#options
*/
require('laravel-mix-polyfill')
mix.polyfill({
enabled: mix.inProduction(),
useBuiltIns: 'usage', // Only add a polyfill when a feature is used
targets: false, // "false" makes the config use browserslist targets in package.json
corejs: 3,
debug: false, // "true" to check which polyfills are being used
})
/**
* π Scripts: Vendor
* Separate the JavaScript code imported from node_modules
* https://laravel-mix.com/docs/4.0/extract
* Without mix.extract you'll see an annoying js error after
* launching the dev server - this should be fixed in webpack 5
*/
mix.extract([], path.join(config.publicFolder, 'js', 'vendor')) // Empty params = separate all node_modules
// mix.extract(['jquery']) // Specify packages to add to the vendor file
/**
* π Scripts: Linting
*/
if (!mix.inProduction()) {
require('laravel-mix-eslint')
mix.eslint()
}
/**
* π Images
* Images are optimized and copied to the build directory
* https://github.com/CupOfTea696/laravel-mix-imagemin
* https://github.com/Klathmon/imagemin-webpack-plugin#api
*
* Important: laravel-mix-imagemin is incompatible with
* copy-webpack-plugin > 5.1.1, so keep that dependency at that version.
* See: https://github.com/CupOfTea696/laravel-mix-imagemin/issues/9
*/
require('laravel-mix-imagemin')
mix.imagemin(
{
from: path.join(source.images, '**/*'),
to: config.publicFolder,
context: 'src/images',
},
{},
{
gifsicle: { interlaced: true },
mozjpeg: { progressive: true, arithmetic: false },
optipng: { optimizationLevel: 3 }, // Lower number = speedier/reduced compression
svgo: {
plugins: [
{ convertPathData: false },
{ convertColors: { currentColor: false } },
{ removeDimensions: true },
{ removeViewBox: false },
{ cleanupIDs: false },
],
},
},
)
/**
* π Icons
* Individual SVG icons are optimised then combined into a single cacheable SVG
* https://github.com/kisenka/svg-sprite-loader#configuration
*/
require('laravel-mix-svg-sprite')
mix.svgSprite(source.icons, path.join(config.publicFolder, 'sprite.svg'), {
symbolId: (filePath) => `icon-${path.parse(filePath).name}`,
extract: true,
})
// Icon options
mix.options({
imgLoaderOptions: {
svgo: {
plugins: [{ convertColors: { currentColor: true } }, { removeDimensions: false }, { removeViewBox: false }],
},
},
})
/**
* ποΈ Static
* Additional folders with no transform requirements are copied to your build folders
*/
mix.copyDirectory(source.static, path.join(config.publicFolder))
/**
* π§ Webpack-dev-server
* https://webpack.js.org/configuration/dev-server/
*/
mix.webpackConfig({
devServer: {
clientLogLevel: 'none', // Hide console feedback so eslint can take over
open: true,
overlay: true,
port: config.devServerPort,
public: `localhost:${config.devServerPort}`,
host: '0.0.0.0', // Allows access from network
https: config.devProxyDomain.includes('https://'),
contentBase: config.devWatchPaths.length ? config.devWatchPaths : undefined,
watchContentBase: config.devWatchPaths.length > 0,
watchOptions: {
aggregateTimeout: 200,
poll: 200, // Lower for faster reloads (more cpu intensive)
ignored: ['storage', 'node_modules', 'vendor'],
},
disableHostCheck: true, // Fixes "Invalid Host header error" on assets
headers: {
'Access-Control-Allow-Origin': '*',
},
proxy: {
'**': {
target: config.devProxyDomain,
changeOrigin: true,
secure: false,
},
},
publicPath: '/',
},
})
mix.options({
hmrOptions: {
host: 'localhost',
port: config.devServerPort,
},
})
Issue Analytics
- State:
- Created 3 years ago
- Reactions:1
- Comments:5
Top Results From Across the Web
50 seconds compile time with PostCSS and Tailwind #2470
Description: Initial compilation of my app.css takes 50-60 seconds, changes to it need around 40-50 seconds (even if I just press Save again)....
Read more >Just-in-Time Mode - Tailwind CSS
Tailwind CSS v2.1 introduces a new just-in-time compiler for Tailwind CSS that generates your styles on-demand as you author your templates instead ofΒ ......
Read more >Set up Tailwind and PostCSS | egghead.io
In this lesson we'll introduce Tailwind to a basic HTML project and see how it compiles into CSS with PostCSS. Tailwind can be...
Read more >Set up Tailwind CSS JIT in a Rails project to compile styles ...
Watch this video to learn more. 2.5MB of CSS takes up to 4.5 seconds to compile, which is no way to live.
Read more >Checking Tailwind Class Names at Compile Time with Rust
My editor takes a slight pause when it loads, but it's only a second or two. And jumping around the file and searching...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
By splitting Tailwind utilities from the rest of the stylesheet, i got down from 26 seconds on every file change to 0.15 seconds. You pretty much get instant feedback.
The initial build still takes 26 seconds because it needs to compile all of those thousands of lines of utility classes, but the subsequent builds a really fast and thatβs what matters.
Hereβs my setup
webpack.mix.js
app.css
tailwind-utilities.css
app-layout.blade.php
This works great. If you want just one compiled css file, you can use
mix.combine
, but I like to keep them seperate β At least during development. It makes it a little easier if you donβt have 20k lines of utility classes mixed in with your actual CSS.Itβs hard to debug such a massive mix configuration file. To help, Iβd need you to break this down to the simplest reproducible example.
Also check Mix 6 beta. That may have resolved the issue inadvertently.