How to build a less compilation chain (gulp -style) in Webpack v3? - css

For a new project I am bound to keep things webpack-only, and thus need to find a way to efficiently compile our stylesheets. Basically in a very gulh-ish way:
gather all less-files including glob-patterns like src/**/*.less (css order may be arbitrary)
also allow import of css files like, say ../node_modules/vendor/**/*.css or 3rdparty/master.less
(If I have to setup a bogus.js entry point for that, fine...)
And with all of that, a typical gulp workflow:
transpile less to css
merge (concat) less and css
have cssnano do its optimization job, with specific css nano options like e.g. orderedValues: false, normalizeWhitespace: true ...
write styles.css as final output
And of course:
have source-maps survive that chain for a styles.css.map
efficient watching and the usual lazy/incremental compilation (as gulp and webpack have it)
Something I do not need is css modules (css imported into node modules for 'scoped' use, coming out as hash-scoped selectors...)
How can a 'typical' less|css-processing toolchain be done in Webpack?
This SO question has my first attempt where I got stuck in config hell right in the middle after combining...
considerations so far (helpful or not)
I know, to webpack, any ressource including css or even font and images is a "module"... Rather than merging my css 'modules' with with actual js (only to later painstakingly separate them again later again), it might be wiser, to have an entry point cssstub.js in parallel – also known as multi-compiler mode.
But that's really, where my wisdom ends... doing a sequence of $things on a set of files in webpack seems really hard (unless it's a connected javascript module graph). I did find something on globbing, but that's not enough (merge css, cssnano,...) and mostly I simply can't glue the pieces together...

I have used gulp to build less and create corresponding maps like below:
First step compiles less and generated css in tmp folder
gulp.task('compile-less', function () {
gulp.src('./*.less') // path to your file
.pipe(less().on('error', function(err) {
console.log(err);
}))
.pipe(gulp.dest('./tmp/'));
});
Second step minifies generated css and create map files
gulp.task('build-css', ['clean'], function() {
return gulp.src('./tmp/**/*.css')
.pipe(sourcemaps.init())
.pipe(cachebust.resources())
.pipe(sourcemaps.write('./maps'))
.pipe(gulp.dest('./compiled/css'));
});
If you want you can add additional step to conact generated css.

Related

mini-css-extract-plugin WARNING in chunk chunkName [mini-css-extract-plugin] Conflicting order between:

WARNING in chunk AccessRights~Groups~Navigator [mini-css-extract-plugin]
Conflicting order between:
css ../node_modules/css-loader??ref--7-1!../node_modules/postcss-loader/lib!./components/Icon/_circle/Icon_circle.scss
css ../node_modules/css-loader??ref--7-1!../node_modules/postcss-loader/lib!./components/Counter/Counter.scss
css ../node_modules/css-loader??ref--7-1!../node_modules/postcss-loader/lib!./components/NavigatorToolbar/NavigatorToolbar.scss
what does this mean and how to fix it? Thank you in advance!
"assets-webpack-plugin": "3.9.5",
"autoprefixer": "9.1.0",
"css-loader": "1.0.0",
"file-loader": "1.1.11",
"image-webpack-loader": "4.3.1",
"mini-css-extract-plugin": "0.4.2",
"postcss-advanced-variables": "2.3.3",
"postcss-clearfix": "2.0.1",
"postcss-conditionals": "2.1.0",
"postcss-extend": "1.0.5",
"postcss-functions": "3.0.0",
"postcss-hexrgba": "1.0.1",
"postcss-import": "12.0.0",
"postcss-loader": "2.1.6",
"postcss-media-minmax": "3.0.0",
"postcss-nested": "3.0.0",
"postcss-sassy-mixins": "2.1.0",
"postcss-simple-vars": "4.1.0",
"postcss-size": "2.0.0",
"postcss-urlrewrite": "0.2.2",
"source-map-loader": "0.2.3",
"string-replace-loader": "2.1.1",
"style-loader": "0.22.0",
"url-loader": "1.0.1",
"webpack": "4.16.5",
"webpack-cli": "3.1.0",
"webpack-dev-server": "3.1.5",
It can easily become an annoying bug! I can see it being reported in every framework -- e.g. in issue #5372 in create-react-app, issue #250 in the mini-css-extract-plugin itself, etc.
I spent 6 hours debugging it (including putting console.log in the source code of mini-css-extract-plugin where it omits the Warning) and here are my findings.
What is this plugin?
The mini-css-extract-plugin of webpack is a CSS-bundler. It is there to gather CSS pieces and put them into .css chunks for you. Just like what the whole Webpack build is doing for .js files.
Why is it happening to me?
You are running into it because all of these conditions apply to you:
You have Vue SFC or CSS-in-JS, that results in your CSS content being in different files (which are called modules).
Your webpack configurations are set to do some sort of code-splitting optimizations, (e.g. via split-chunks-plugin) which puts your modules into chunks for lazy-loading in client-side (e.g. 1000 files, into 10 chunks, that are only downloaded by the user when the user needs them.) So, this plugin goes over how webpack has bundled your modules and tries to create its own CSS bundles out of them.
There is an "Order Conflict" in your imports!
What is "Order Conflict" now?
It's "order" + "conflict." Let's review them.
Part 1) Order
This plugin is trying to run a topological sorting algorithm (this part of the source code) to find out in which order it should put the CSS rules in its output bundles so that it doesn't cause any problem.
The problem is, unlike JavaScript that you clearly export your objects from a file/module (in no order, as they are named), in CSS it will just get appended (like an array of strings) and so the order of the imports can actually matter!
Let's say you have two modules:
// module a.js
<div>hi, I am A!</div>
// ... in CSS section of the same file:
div { color: red; }
// module b.js
<div>hi, I am B!</div>
// ... in CSS section of the same file:
div { color: blue; }
And then you have a page that includes both of them them.
// page S (for Straight)
import a from "a.js"
import b from "b.js"
So far, so good! The CSS output can be
div { color: red; }
div { color: blue; }
which means all the <div>s should have blue font color.
However, if instead of that page S, we had a page had was importing them in reverse order, it would be:
// page R (for Reverse)
import b from "b.js"
import a from "a.js"
and the output would be equal to
div { color: blue; }
div { color: red; }
which means all the <div>s should have red font color.
This is why the order of imports matters.
Part 2) Conflict
Now, what should be the output CSS if you have both page S and page R?
Note that, unlike this silly example of applying a wild rule on all <div> elements, you might actually have some sort of scoped CSS or a CSS naming convention like BEM in place that would prevent such thing to become an issue. But, this plugin doesn't go over actually parsing and understanding the content of the CSS. It just complains that "Hey dude! I don't know whether a should come before b, or b should come before a!"
Solutions
You basically have two solutions, just like any other problem! Either solve it or kill the problem it.
Solution 1: Fix it
The error message is very hard to read and sometimes it doesn't even output the proper details of modules. (for me it's like , , , , , , as for some reason my ChunkGroups don't have a .name property; so zero information.) And it can be extremely messy if you have more than ~20 files.
Anyways, if you have got time this approach is the best you can try.
Notes:
You can also import PageS in PageR (or the other way around, whatever) to explicitly tell the plugin to pick this order and stop nagging! It might be easier than going over all the places that include one or another and move the lines up and down.
IMPORTANT NOTE 1: IF YOU THINK SORTING YOUR IMPORT LINES ALPHABETICALLY CAN HELP, SEE THIS EXAMPLE and THIS COMMENT (that even a Visual Code plugin cannot help)!
IMPORTANT NOTE 2: The Order Conflict is NOT NECESSARILY IN THE SAME FILE. It can be anywhere among the ancestors of the two or more files! So, can be a huge pain to find out.
IMPORTANT NOTE 3: IT'S NOT GOING TO BE FUTURE-PROOF! So, even if you move a few import lines up and down, tomorrow it might happen to another developer in your team.
So, TL;DR, if you found yourself spending more than two hours on this, try solution #2 below.
Solution 2: Kill it
If it's not actually causing a problem in production and your final output, you can suppress this error via passing an ignoreOrder flag to the options object of the plugin in your Webpack config.
Notes:
If you are using a third-party build-wrapper on top of WebPack (like Quasar's that I am using), you can use webpack chain modify arguments technique to feed this flag into the existing configuration.
It's a good last resort! Good luck. :)
CSS cares for rule order.
Q: What does the warning mean?
A: There are some order conflicts while packaging your CSS modules.
Q: What is the cause?
A: The plugin (mini-css-extract-plugin) tries to generate a CSS file but your codebase has multiple possible orderings for your modules. From the warning you showed, it seems you have used Icon before Counter in one location and Counter before Icon in another location. The plugin needs to generate a single CSS file from these and can't decide which module's CSS should be placed first. CSS cares for rule order so this can lead to issue when CSS changes without reason.
So not defining a clear order can lead to fragile builds, that's why it displays a warning here.
Q: How to fix?
A: Sort your imports to create a consistent order. If you cannot sort for some reason, for example, you have libraries in your project beyond your control or when the order of these styles doesn't matter, you can ignore the warning by making changes as suggested in other answers.
mini-css-extract-plugin version 0.8.0 included a new option ignoreOrder. You can check https://github.com/webpack-contrib/mini-css-extract-plugin#remove-order-warnings
new MiniCssExtractPlugin({
ignoreOrder: true,
}),
Please see issue reported on Github.
const webpackConfig = {
stats: {
// warn => /Conflicting order between:/gm.test(warn)
warningsFilter: warn => warn.indexOf('Conflicting order between:') > -1 // if true will ignore
}
}
There is now an NPM package named #webkrafters/ordercss which tackles this issue at the root.
Full disclosure: I initially created it to solve this very problem in one of my apps but decided to expand it and share it with everyone.
If this package helps anyone, please share it with others.
Thanks and good luck!
NB: setting MiniCssExtractPlugin ignoreOrder property would suppress the warnings but may not resolve the underlying issues especially for those using modular css. This could result in unpredictability of the rendered view.

Task Runners (Gulp, Grunt, etc) and Bundlers (Webpack, Browserify). Why use together?

I am trying to learn about technologies including Grunt, Gulp, Webpack, Browserify, but I did not feel that there is much difference between them.
In other words, I feel Webpack can do everything that a task runner does. But still I got a huge examples where gulp and webpack are used together. What's the reason?
I might be taking things in the wrong direction. Am I missing anything? If so, what?
Grunt and Gulp are actually task runners, and they have differences like config driven tasks versus stream based transformations. Each has its own strengths and weaknesses, but at the end of the day, they pretty much help you create tasks that can be run to solve a larger build problem. Most of the time, they have nothing to do with the actual run-time of the app, but rather they transform or they put files, configs and other things in place so that the run time works as expected. Sometimes they even spawn servers or other processes that you need to run your app.
Webpack and Browserify are package bundlers. Basically, they are designed to run through all of a package's dependencies and concatenate their source into one file that (ideally) can be used in a browser. They are important to modern web development, because we use so many libraries that are designed to run with Node.js and the v8 compiler. Again, there are pros and cons and different reasons some developers prefer one or the other (or sometimes both!). Usually the output bundles of these solutions contain some sort of bootstrapping mechanisms to help you get to the right file or module in a potentially huge bundle.
The blurred line between runners and bundlers might be that bundlers can also do complex transformations or trans-pilations during their run-time, so they can do several things that task runners can do. In fact, between browserify and webpack there's probably around a hundred transformers that you can use to modify your source code. For comparison, there's at least 2000 gulp plugins listed on npm right now. So you can see that there are clear (hopefully... ;)) definitions of what works best for your application.
That being said, you might see a complex project actually using both task-runners and package bundlers at the same time or in tandem. For example at my office, we use gulp to start our project, and webpack is actually run from a specific gulp task that creates the source bundles that we need to run our app in the browser. And because our app is isomorphic, we also bundle some of the server code.
In my humble opinion, you might want to get familiar with all of these technologies because chances are you will see (use) all them in the course of your career.
I've just created my own task runner/bundler.
It's simpler to use than gulp and probably webpack (although I've never used webpack).
It's very simple and has babel, browserify, uglify, minify, and handlebars out of the box.
The syntax looks like this:
const Autumn = require("autumn-wizard");
const w = new Autumn();
//----------------------------------------
// CSS
//----------------------------------------
var cssFiles = [
'./lib/pluginABC/src/css/**/*.{css,scss}',
];
w.forEach(cssFiles, srcPath => {
var dstPath = w.replace('/src/', '/dist/', srcPath);
dstPath = w.replace('.scss', '.css', dstPath);
dstPath = w.replace('.css', '.min.css', dstPath);
w.minify(srcPath, dstPath, {
sourceMap: useSourceMap,
});
});
//----------------------------------------
// BUNDLE THE JS MODULE
//----------------------------------------
var srcPath = "./lib/pluginABC/src/main.js";
var dstPath = "./lib/pluginABC/dist/bundled.min.js";
w.bundle(srcPath, dstPath, {
debug: useSourceMap,
});
//----------------------------------------
// CREATE THE HANDLEBARS TEMPLATES
//----------------------------------------
var tplPaths = [
"./lib/pluginABC/src/templates/**/*.hbs",
];
dstPath = "./lib/pluginABC/dist/templates/bundled.js";
w.precompile(tplPaths, dstPath);
And the doc is here: https://github.com/lingtalfi/Autumn
Hopefully it helps.

ServiceStack Bundler and sub-directory Less files

Until recentlly I was using the rather awkward dotless. I'm restricted to what I can use somewhat because I'm using VS2010, however I came across ServiceStack Bundler which seems to work great... apart from one thing.
My Less files are split using a directory structure - to simplify things, like this:
scaffolding/my-scaffolding.less
utilities/my-utilities.less
ui/my-ui.less
Each directories has many files, particularly with mixins etc.
In order to link them together, I've been using the #import function.
My main less file - main.less contains the following:
#import "utilities/all-utilities.less"
All utilities are listed here as it enables me to plug and play various file-sets. This code previously worked ok.
However I find ServiceStack won't pick up changes unless I reference all the less files individually and directly through the bundle file, which is a bit of a pain.
So, I can't use: main.less and import all sub files.
Instead, I have to use:
utilities/util1.less
utilities/util2.less
utilities/util3.less
and so on.
I'm using the ms build function to compile.
Any ideas?
/* UPDATE */
Tried the recommended answer below without success.
This is what I've tried so far:
Call a single less file that contains all #import declarations;
Does not trigger updates on compile.
Call all less files from bundle file and add #import statements to less files where necessary (note bundler won't compile without these);
Duplicates the #imported code as many times as the #import statement is used - even when (reference) directive is used, resulting in bloated/incorrect code.
You should be able to modify Bundler's bundler.js file to specify additional paths that less should search for when processing #import directives:
function compileLess(lessCss, lessPath, cb) {
var lessDir = path.dirname(lessPath),
fileName = path.basename(lessPath),
options = {
paths: ['.', lessDir], // Specify search paths for #import directives
filename: fileName
};
less.render(lessCss, options, function (err, css) {
if (err) throw err;
cb(css);
});
}
A mistake on my part, but one which wasn't easily spottable, so I'll post the reason for my problems so that others don't spend hours as I did chasing a solution to an unecessary problem.
I was using ServiceStack Bundler - I believe this issue would also have occured on on any solution using npm's less library.
My main issue was that none of my changes were triggered on compile. I use lots of #imports and numerous sub-directories for my less files so my first thoughts were the problem was due to subdirectories, and later, due to #import statements. However neither was correct.
I defined a bundle: main.css.bundle
Within the bundle I called my main less file that contained all the other #imported files: main.less
The issue was that less would normally reserve main.css, but the bundle also gives its output the same name based on the bundle name. So both were conflicting.
Change the bundle name or the main less file name and all should work.

Asset management - maintaining reference to relative assets after concatenation and versioning

I know that L5 and Elixir are still under development, but I'm excited to start thinking about ways to reorganize my code. I think my question has more to do with asset management, in the context of L5 and Elixir.
Want to clarify how concatenation and versioning should be handled (in my case I'm using Elixir's styles() and version()). The issue I'm having is that the new file after concat/version will be located in a new folder, breaking any references to assets from the original css or js files.
For example, an original CSS file that has background-image: url('../img.png') will no longer work. I've tried a couple of things, but both are not ideal especially in the case of vendor plugins:
Move required assets over one-by-one (using mix.copy() for each folder of assets), to the new build path (ie. the build path used by Elixir's versioning).
Manually edit the paths in each asset file to refer to an absolute path
Although both of these options will make things work, I feel as though I may be missing something. It also becomes quite impractical when working with javascript plugins (ex. ones that come with their own images, fonts, stylesheets, etc).
Is there a more practical way of managing relative paths when concatenating and versioning?
Here is the solution for Laravel Elixir after you build for versioning.
For copy command you need reference it as full path.
var elixir = require('laravel-elixir');
/*
|--------------------------------------------------------------------------
| Elixir Asset Management
|--------------------------------------------------------------------------
|
| Elixir provides a clean, fluent API for defining some basic Gulp tasks
| for your Laravel application. By default, we are compiling the Less
| file for our application, as well as publishing vendor resources.
|
*/
elixir(function(mix) {
mix.version('themes/default/assets/css/styles.css')
.copy('public/themes/default/assets/img/', 'public/build/themes/default/assets/img/');
});
EDIT:
I just submitted a pull request to Elixir, so you can just do:
mix.version(
['css/style.css', 'css/vendor/style.css'], //files to be versioned
['fonts', 'css/vendor/icons'] //dependent files/dirs to be copied
);
OLD ANSWER:
Actually, if you use mix.copy(...) alone, you just can't use gulp watch and you'll need to recompile your entire stack in order to get this working.
You can achieve the same results with the solution below and don't need to recompile everything, because it'll just work when you change a versioned file:
var shell = require('gulp-shell');
gulp.task('cp', shell.task(['cp -R public/fonts public/build/',
'cp -R path/to/vendor/dir public/build/vendor/',
'... etc ...']));
elixir(function(mix) {
...
//register a watcher to run 'cp' when you rebuild
mix.task('cp','public/build/**/*.(js|css)');
}
They are relative paths - so keep the relative relationship.
Just move the images over to the public/build/ directory as part of the gulp command, after the visioning.

Build-time CSS combiner that respects relative url references?

Looking for a build-time CSS combiner/minifier that respects relative URL references.
That is, if one of the files I am combining is at
/path/to/style.css
and contains
background-image: url(images/my-image.png)
the resulting file should contain
background-image: url(/path/to/images/my-image.png).
Should work cross-platform Mac and PC, so either .NET via Mono or Node seem like obvious choices.
Check out WebAssets / Github
Asset management application for Python web development - use it to
merge and compress your JavaScript and CSS files.
It includes filters/ precompiles for cssmin, cssutils, yui_css, less, sass, clevercss, compass, scss, coffeescript, gzip, etc.
Specific to your question:
cssrewrite Source filter that rewrites relative urls in CSS files.
CSS allows you to specify urls relative to the location of the CSS
file. However, you may want to store your compressed assets in a
different place than source files, or merge source files from
different locations. This would then break these relative CSS
references, since the base URL changed.
This filter transparently rewrites CSS url() instructions in the
source files to make them relative to the location of the output path.
It works as a source filter, i.e. it is applied individually to each
source file before they are merged.
No configuration is necessary.
The filter also supports a manual mode:
get_filter('cssrewrite', replace={'old_directory', '/custom/path/'})
This will rewrite all urls that point to files within old_directory to use /custom/path as a prefix instead.
General Usage:
from webassets import Environment
my_env = Environment('../static/media', '/media')
""""As you can see, the environment requires two arguments,
the path in which your media files are located, as well as
the url prefix under which the media directory is available.
This prefix will be used when generating output urls. Next,
you need to define your assets, in the form of so called
bundles, and register them with the environment. The easiest
way to do it is directly in code:""""
from webassets import Bundle
js = Bundle('common/jquery.js', 'site/base.js', 'site/widgets.js', filters='jsmin', output='gen/packed.js')
my_env.register('js_all', js)
In this case you'll replace your js src with output.
Here is an alternate notation:
directory: ../static
url: /media
debug: True
updater: timestamp
bundles:
bundle-name:
filters: sass,cssutils
output: cache/default.css
contents:
- css/jquery.ui.calendar.css
- css/jquery.ui.slider.css
Also has special hooks for Django, Flask, Jinja2, Werkzeug..
Documentation is here. Hope this helps!
Here's a basic Python script that will combine all CSS files in a directory and replace references to your images folder:
import os
import fnmatch
output_text = ''
for filename in os.listdir('.'):
if fnmatch.fnmatch(filename, '*.css'):
output_text += open(filename, 'r').read()
output_text = output_text.replace('url(images', 'url(/path/to/images'))
f = open('combined.css', 'w')
f.write(output_text)
f.close()
This is off the top of my head and hasn't been tested, so it might contain errors.
In response to your comment:
Alternatively, you could use server-side CSS, like SASS/Compass or LESS.
With SASS/Compass you can dynamically change the path to assets (images) using the config.rb file, or from the command-line. You can toggle between relative and absolute paths easily in the same way. Your stylesheets are automatically compressed. To make sure that your files are combined, you can just create a master.scss file and #import each file. Most of my experience is with SASS but I believe LESS has similar features.
This is probably not ideal, however. It would be much simpler, portable, and more efficient to take care of this in a Python/Ruby script. It wouldn't take much effort to expand on the Python script above to make it compress the output file and match all relative paths. Then you can make it automatically run by having something like Foreman watching the build directory for changes.

Resources