Running gulp postcss in parallel with webpack - css

I'm working on a project with a pre-configured webpack setup, that I cannot change. I do have access to the package.json though.
I'd like to use nesting in the css files. Every component in src/component has its own folder with a styles.css file. There is auto-reloading for the browser, when I change and save a .css file while the webpack based dev process is running.
I found a workaround hack to get nested css working with postcss by running a gulp watch process in parallel. The gulpfile.js looks like this:
const { src, dest, watch, series, parallel } = require('gulp')
const postcss = require('gulp-postcss')
const rename = require('gulp-rename')
const watchPcss = () => {
watch(['src/**/*.pcss'], { followSymlinks: false }, series(pcss))
}
const pcss = () => {
return src('src/**/*.pcss')
.pipe(postcss([require('autoprefixer'), require('postcss-nested')]))
.pipe(
rename((path) => {
path.extname = '.css'
}),
)
.pipe(dest('./src'))
}
exports.watch = series(watchPcss)
exports.default = series(pcss)
I have additional style.pcss files in each folder, in which I can use nesting. The gulp watch task looks for changes in .pcss files and then runs postcss to convert it to a normal css styles.css file.
This does work. The styles.css files get correctly generated. And saving changes in the .pcss files, do not trigger the webpack watch process.
My plan was that the generated/updated styles.css would trigger webpack and therefore update site in the browser. However, this only works sometimes. Most of the time, all the styles in the .css of the corresponding changed .pcss are not there at all (also not the styles that were in the file before the changes). I then have to save the .css file manually to make the styles show.
Is it possible that the gulp process does not "update" the file, but deletes it and creates a new file? And the webpack watch process notices the "deleting" of the file, but the creating of the new file happens too soon for the watch process to be triggered again?
Is there a way to fix this? Maybe just "touch" the .css file again after xxx ms?

This is probably not the best or "correct" way to make it work, but it seems to do the trick:
const { src, dest, watch, series, parallel } = require('gulp')
const postcss = require('gulp-postcss')
const rename = require('gulp-rename')
const touch = require('gulp-touch-cmd')
const watchPcss = () => {
watch(['src/**/*.pcss'], { followSymlinks: false }, series(pcss, css))
}
const css = () => {
return src('src/**/*.css').pipe(touch())
}
const pcss = () => {
return src('src/**/*.pcss')
.pipe(postcss([require('autoprefixer'), require('postcss-nested')]))
.pipe(
rename((path) => {
path.extname = '.css'
}),
)
.pipe(dest('./src'))
}
exports.watch = parallel(watchPcss)
exports.default = series(pcss)

Related

How do I import a scss npm module into Ember

How do I correctly import a non-plugin npm module into Ember?
I'm trying to use the sass version of flag-icon-css with ember-cli so that the sass is being built during deploy with the rest of ember-cli-sass, but I can't figure out how to do it in an automated fashion (e.g. without manually copying files over to public).
Using ember-auto-import seems like a good place to start but it is more tailored towards javascript imports.
I have tried this configuration in ember-cli-build.js:
'sassOptions': {
includePaths: [
'node_modules/flag-icon-css/sass' // flag-icon.scss
]
},
It will add the styles, but it doesn't include the images used in the styles.
I have read this documentation, but it doesn't specify something more complicated than a single file.
Just use ember-cli-sass:
first add it to includePaths in your ember-cli-build.js
new EmberApp({
sassOptions: {
includePaths: [
'node_modules/flag-icon-css/sass'
]
}
});
use it with #import "flag-icon";
Have a look in the readme.
now while this will sucessfully add the compiled sass output to your /assets/app-name.js there is no automated way to add any kind of assets to your dist folder.
In the case of flag-icon-css it will just add background-image: url(../flags/4x3/gr.svg); to your dist/assets/app-name.css, but not add the svg itself. You can manually do this with broccolis Funnel and MergeTrees:
install broccoli-funnnel and broccoli-merge-trees
import them in ember-cli-build.js:
const Funnel = require('broccoli-funnel');
const MergeTrees = require('broccoli-merge-trees');
use them by replacing return app.toTree() in your ember-cli-build.js with
const flagIcons = Funnel('node_modules/flag-icon-css', { include: ['flags/**/*'] });
return new MergeTrees([app.toTree(), flagIcons]);
so your entire ember-cli-build.js could look like this:
'use strict';
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
const Funnel = require('broccoli-funnel');
const MergeTrees = require('broccoli-merge-trees');
module.exports = function(defaults) {
let app = new EmberApp(defaults, {
// Add options here
sassOptions: {
includePaths: [
'node_modules/flag-icon-css/sass'
]
}
});
const flagIcons = Funnel('node_modules/flag-icon-css', { include: ['flags/**/*'] });
return new MergeTrees([app.toTree(), flagIcons]);
};
a short sidenote: I would usually recommend to put assets into the assets folder of your output, but in this case this wont work because the flag-icon-css expects the flags folder to be in the parent directory of the .css.
I figured this out, but I'm not sure it's the best or easiest way. It has some drawbacks.
const EmberApp = require('ember-cli/lib/broccoli/ember-app')
const Funnel = require('broccoli-funnel')
module.exports = function(defaults) {
const app = new EmberApp(defaults, {
'sassOptions': {
includePaths: [
'node_modules/flag-icon-css/sass'
]
}
})
const flags = new Funnel('node_modules/flag-icon-css/', {
srcDir: 'flags',
destDir: '/flags',
})
return app.toTree([flags])
}
The drawback is that the css image urls are not processed, and hardlinked to ../flags, so I have to funnel them into /flags, which is not the convention, as these assets should be compiled into public/assets/images.
This is a two-step implementation (or more steps if the npm module would be more complex). It would be preferred to include just the scss and have (an) Ember (plugin) automatically fetch the dependent resources.

Gulp watch all .scss files and compile to css as siblings?

I've got a site with a handful of modules that need their css files to exist separate from one-another, but I'd like to write the styles using scss as well as leverage gulp for autoprefixing. Is it possible to watch for changes in any scss files under a given directory tree and then write the css for the updated file as a sibling? The structure would essentially be something like this (although it could have directories nested to greater depths):
Gulpfile.js
module1
- styles1.scss
- styles1.css
- dir
-- styles2.scss
-- styles2.css
module2
- dir
-- subdir
--- styles3.scss
--- styles3.css
I've got the following Gulpfile setup elsewhere to handle my general scss compilation but I'm not sure how I might modify it to instead handle the above scenario.
// Requirements
var postcss = require('gulp-postcss');
var gulp = require('gulp');
var sass = require('gulp-sass');
var sassGlob = require('gulp-sass-glob');
var sourcemaps = require('gulp-sourcemaps');
var autoprefixer = require('gulp-autoprefixer');
var sassOptions = {
errLogToConsole: true,
outputStyle: 'expanded'
};
gulp.task('scss', function () {
return gulp
.src('scss/style.scss')
.pipe(sourcemaps.init())
.pipe(sassGlob())
.pipe(sass(sassOptions).on('error', sass.logError))
.pipe(postcss([require('postcss-flexibility')]))
.pipe(autoprefixer())
.pipe(sourcemaps.write(''))
.pipe(gulp.dest('css'))
.resume();
});
// Create 'watch' task
gulp.task('watch', function () {
return gulp
// Watch the input folder for change,
// and run `sass` task when something happens
.watch('scss/**/*.scss', gulp.series('scss'))
// When there is a change,
// log a message in the console
.on('change', function (event) {
console.log('File ' + event + ' was updated' + ', running tasks...');
})
;
});
In your scss task, change the gulp.src glob to '**/*.scss', and the gulp.dest glob to '.'.
In your watch task, change the glob to '**/*.scss' as well.

How to generate correct sourcemaps for sass files with autoprefix and minification

I am trying to configure gulp to convert SASS (*.scss) files into CSS, autoprefix and also would like to generate a sourcemap file for the compiled CSS file. I am trying to create two css files one normal and other minified version of it. Both of these CSS files needs to have sourcemaps. I have the following gulp config file however the sourcemaps generated by this is incorrect. When I run the gulp task without autoprefixer then everything is fine however with autoprefixer the sourcemaps is messed up i.e it points to the incorrect line number when opened up in a chorme dev tools.
I have tried multiple configurations like inline sourcemaps and separate sourcemaps. I even tried loading the sourcemaps before autoprefixing and then writing it to a file after autoprefixing is done.
const { src, dest } = require('gulp');
const sass = require('gulp-sass');
const postcss = require('gulp-postcss');
const clone = require('gulp-clone');
const merge = require('merge-stream');
const rename = require('gulp-rename');
const sourcemaps = require('gulp-sourcemaps');
const sassUnicode = require('gulp-sass-unicode');
const prefix = require('autoprefixer');
const minify = require('cssnano');
const {paths} = require('./config.js');
/*
* Node Sass will be used by defualt, but it is set explicitly here for -
* forwards-compatibility in case the default ever changes
*/
sass.compiler = require('node-sass');
sass_options = {
// outputStyle: 'compressed',
outputStyle: 'compact',
// outputStyle: 'nested',
// outputStyle: 'expanded'
sourceComments: false,
precision: 10
};
prefix_options = {
browsers: ['last 2 versions', '> 5%', 'Firefox ESR'],
cascade: false
};
minify_options = {
discardComments: {
removeAll: true
}
}
// Process, compile, lint and minify Sass files
const buildStyles = function (done) {
var source = src(paths.styles.input)
.pipe(sourcemaps.init())
.pipe(sass(sass_options).on('error', sass.logError))
// .pipe(sassUnicode())
.pipe(sourcemaps.write({includeContent: false}))
.pipe(sourcemaps.init({loadMaps: true}))
.pipe(postcss([prefix(prefix_options)]));
// Create non-minified css file and its sourcemaps
var pipe1 = source.pipe(clone())
.pipe(sourcemaps.write('.'))
.pipe(dest(paths.styles.output));
// Create minified css file and its sourcemaps
var pipe2 = source.pipe(clone())
.pipe(rename({ suffix: '.min' }))
.pipe(postcss([minify(minify_options)]))
.pipe(sourcemaps.write('.'))
.pipe(dest(paths.styles.output));
return merge(pipe1, pipe2);
};
I expect correct sourcemaps even after autoprefixing however with the current setup I am getting incorrect line numbers. (for all the styles of a child element in a nested element in the source .scss file the sourcemap points to the root element).
For instance, in the below example, when I inspect h2 element the sourcemaps points to the root element .shopping-cart (line#445) instead of (line#459)
enter image description here
Is there a reason why you call sourcemaps.init twice within buildStyles -> source?
sourcemaps.init is supposed to come before sourcemaps.write. In your code, you have it the other way around forsourceinbuildFiles`.
Honestly, to me it just looks like your build is more complicated than it needs to be, and that is causing problems.
See docs for reference
Also, why do you need the non-minified code if you have sourcemaps?

Angular Universal: serving critical CSS and delaying non-critical

This is basically the code I am using and lighthouse says my (almost empty!) css bundle is delaying my initial load.
So how do I put a link to critical.scss in the
<head><style>DONT_WANT_TO_WRITE_STUFF_INLINED</style>...</head>
Is there a better solution than https://www.npmjs.com/package/critical or writing everything inlined?
And how do I delay the load of the main styles.scss until the prerendered Universal content is loaded in the browser? The server-app's config in angular-cli.json does not contain styles or assets, so I don't understand why styles.scss is loaded initially
Regarding delaying the load of the main styles.scss, I rely on two things:
First of all, I build the application with the --extract-css=false flag. That ensures that the styles bundle will be a JavaScript file.
Second, I defer the loading of that file. To do so I rely on a small script that I called defer.styles.js and that runs at the end of the build process. That script simply looks for the script tag that loads the styles, and adds a defer to it.
const path = require('path');
const htmlPath = path.join(__dirname, 'dist', 'browser', 'index.html');
fs.readFile(htmlPath, 'utf8', (err, data) => {
if (err) {
console.log(err);
return;
}
const index = data.search(`src="styles.`);
if (index > -1) {
const output = [data.slice(0, index), 'defer ', data.slice(index)].join('');
console.log(output);
fs.writeFile(htmlPath, output, 'utf8', () => {
console.log('Styles succesfully deferred');
})
}
});
Regarding your other question - how to load the critical css -. I still didn't find a way of comfortably doing it.

Import from parent directory node-sass

I am using node-sass to mock my CDN builds and I am converting my CSS to
a modular Sass design. Basically my setup involves brand sites overwriting
the common styles.
I want to do something like this in the brand folder
#import "../common/global-config";
#import "brand-config";
#import "../common/common";
// brand specific styles here
this file would live at /css/brand-name/brand.scss
the other files would live in /css/common/_common.scss
my node-sass setup looks something like this
function compileSassFile(includes, path, callback) {
if (!fs.existsSync(path)) {
return null;
}
var css = sass.renderSync({
file: path,
success: callback,
outputStyle: 'expanded',
includePaths: includes
});
return css;
}
// bundlePath = 'css', dirName = '/brand-name'
compileSassFile([path.join(bundlePath), path.join(bundlePath, 'common'), path.join(bundlePath, dirName)], path.join.apply(bundlePath, [dirName, 'brand.scss']));

Resources