Adding a postcss plugin using custom-webpack does not apply the plugin to scss partial files imported into the styles.
A minimal reproduction and instructions is provided here: https://github.com/abierbaum/postcss-custom-webpack-global-issue
This repository demonstrates a bug where postcssPresetEnv is added using the @angular-builders/custom-webpack custom builders. The issue is that the postcss plugin extension settings added are not being applied when the build processes scss partial files imported into the styles of the application.
Reproduction steps:
ng run build
Open the dist/postcss-custom-webpack-global-issue/styles.css file. Note that the block of style that came from src/styles.scss directly has it's logical css properties transformed correctly. The ones that are imported by src/styles.scss from shared/styles/_shared.style.scss are not transformed and stay in their original form.
The content end ups looking like:
/* You can add global styles to this file, and also import other style files */
div.container {
border: 5px solid black;
-webkit-margin-start: 20px;
margin-inline-start: 20px;
-webkit-margin-end: 50px;
margin-inline-end: 50px;
}
div.container {
border-left-color: orange;
border-right-color: orange;
}
/*# sourceMappingURL=styles.css.map*/
For some reason the styles from shared/styles/_shared.style.scss are not being transformed with the added plugin settings.
The expected output would be:
/* You can add global styles to this file, and also import other style files */
div.container {
border: 5px solid black;
margin-left: 20px;
margin-right: 50px;
}
div.container {
border-left-color: orange;
border-right-color: orange;
}
/*# sourceMappingURL=styles.css.map*/
Angular CLI: 8.3.20
Node: 12.13.0
OS: linux x64
Angular: 8.2.14
... animations, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router
Package Version
-----------------------------------------------------------
@angular-devkit/architect 0.803.20
@angular-devkit/build-angular 0.803.20
@angular-devkit/build-optimizer 0.803.20
@angular-devkit/build-webpack 0.803.20
@angular-devkit/core 8.3.20
@angular-devkit/schematics 8.3.20
@angular/cli 8.3.20
@ngtools/webpack 8.3.20
@schematics/angular 8.3.20
@schematics/update 0.803.20
rxjs 6.4.0
typescript 3.5.3
webpack 4.39.2
A few things to point out to and maybe give some direction (didn't get a chance to play with it myself yet):
sass-loaderpostcss-loader with these optionsraw-loaderFor global styles the loaders order would be:
sass-loaderpostcss-loader with different options.RawCssLoaderMiniCssExtractPlugin.loader or style-loader (based on extractCss property).As you can see the flow is pretty much different even before your changes.
Now, to understand how your changes affect each one of these flows you should check out webpack-merge documentation, especially the section that speaks about loaders. FYI custom-webpack builder uses smartStrategy.
Hope it gives you some direction. I'd start with checking if your config applies to the global style and what output it gives (maybe by using a custom loader which just logs the data instead of postcss-loader).
If it applies and output is fine then it probably gets screwed in one of the next steps (3 or 4).
@just-jeb Thanks for the pointers. I tried to debug it by stepping through the code yesterday and was unable to find a point where I could "see" the loaders and plugins configured for the files.
One working theory I have is maybe the loader ordering. Since the plugins are working for global files except for the partials I was wondering if maybe the loaders are only processing the primary files and not the partials. So a working theory was that maybe the postcss loader needs to run _after_ sass has transformed the code in css and has pulled in all the partials for consideration.
I will try to find some time today to debug this further. Thanks for all the pointers in the code. That should give me some points to breakpoint in a debug run and see what is happening.
Starting to look into this today. One pointer I found of someone having a similar problem with webpack , postcss, and sass partials is here: https://stackoverflow.com/questions/46246188/webpack-and-postcss-autoprefixer-not-compiling-sass-partials
In this case the issue was the order of the webpack. They had to adjust the order to ensure postcss-loader was applied after the sass-loader.
The issue ended up being that after the merge, the postcss-loader was actually added as a _new_ rule instead of being merged into the existing saas rules. After some experimentation and debugging I found that the easiest way to make everything work was simply to use the custom webpack-config function form of overriding.
See below for what I did. The function loops over all the rules looking for the angular-cli rules that match scss files and then it modifies the loader chains for those rules to add a new postcss-loader in the chain to execute right after the saas-loader completes. Thus I am not overriding or extending the existing angular-cli postcss loaders, I am just adding an additional loader to each chain to do the transform that I want to execute.
This can probably be cleaned up more, but it gives me a working example to use for now.
import { CustomWebpackBrowserSchema } from '@angular-builders/custom-webpack';
import * as webpack from 'webpack';
const postcssPresetEnv = require('postcss-preset-env');
module.exports = (config: webpack.Configuration, options: CustomWebpackBrowserSchema) => {
console.log('my config override');
const saas_rules = config.module.rules.filter((r) => r.test.test('.scss'));
console.log('sass rule: ', saas_rules);
// Add new post-css loader as second loader
// chains all end in []..., 'postcss-loader', 'sass-loader']
// and we want to be between the existing postcss-loader and the saas-loader
// so we are run _after_ saas
for (const r of saas_rules) {
const custom_postcss_loader = {
loader: 'postcss-loader',
options: {
ident: 'postcss-custom',
plugins: () => [
postcssPresetEnv({
// disable all support by default
stage: false,
features: {
// Convert logical properties to hard coded direction equivalents
'logical-properties-and-values': {
dir: 'ltr',
},
},
}),
],
sourceMap: true
}
};
r.use.splice(-1, 0, custom_postcss_loader);
}
return config;
};
@just-jeb Thanks for all your help. I think we can close this bug for now unless there is anything you see in what I did that would not be the recommended way to do this.
Most helpful comment
A few things to point out to and maybe give some direction (didn't get a chance to play with it myself yet):
So for component styles the loaders order would be:
sass-loaderpostcss-loaderwith these optionsraw-loaderFor global styles the loaders order would be:
sass-loaderpostcss-loaderwith different options.RawCssLoaderMiniCssExtractPlugin.loaderorstyle-loader(based onextractCssproperty).As you can see the flow is pretty much different even before your changes.
Now, to understand how your changes affect each one of these flows you should check out
webpack-mergedocumentation, especially the section that speaks about loaders. FYIcustom-webpackbuilder usessmartStrategy.Hope it gives you some direction. I'd start with checking if your config applies to the global style and what output it gives (maybe by using a custom loader which just logs the data instead of
postcss-loader).If it applies and output is fine then it probably gets screwed in one of the next steps (3 or 4).