Describe the bug
When running @angular-builders/custom-webpack with a custom loader pointed to a single file, the loader's output appears to be ignored (it does get called and executes) when the merge strategy is set to "append" or when left blank.
To Reproduce
Minimal repo: https://github.com/mralbobo/angular-webpack-builder-repro
Freshly cli generated, with the following notable modifications/ additions:
The two "real" files have been extremely cut down to having the loader just replace the file with essentially nonsense. There are two test cases:
Expected behavior
The expectation is that the loader will run, replace the contents of the file with the string of f's and the app should throw an error when it tries to run the created function. In actuality, the function runs successfully.
Builder:
Libraries
angular-devkit/build-angular: 0.802.2Extra
I don't think it's a bug. It's a matter of how you define your rule and how Angular define theirs.
You should look into documentation of https://github.com/survivejs/webpack-merge, in particular smart merge. You'll notice that loader strings override each other (unlike loaders) array.
Which explains your case. Replacing loader with loaders array (with your loader being a single entry) causes your loader to be invoked (I see the fffffffffff being printed to the console) but for some reason the output is still the correct file. Not sure why it happens, but I'm convinced it's not a bug in angular-builders. I'd guess it's the behavior of Angular build.
In any case, why can't you just use prepend?
I'm not sure if loader vs loaders actually matters in this case, ie, my loader is invoked either way. I'd imagine if the string was overridden it wouldn't be invoked at all. Maybe the smart merge treats a path differently than a loader configuration string? Dunno.
The loader is effectively a big scary regex find and replace, it needs to run first, or at least before the file gets mangled by decorator pre-processing and minification. Prepend gets me a pretty ugly mess to work through and more importantly it's formatting isn't really reliable.
Don't suppose you could provide any insights on where I can start when it comes to figuring out what the angular build is doing?
Sure, here are a few URLs:
Now, I think the real issue here is that they don't really use loaders for Typescript. The entity that processes TS is AngularCompilerPlugin. I have no idea why it works with prepend, and why is that different from append when the real TS compilation happens in AngularCompilerPlugin but I'd say this is the way you should take.
You can hook into transformers by pushing your own transformer.
To add your transformer into _transformers array you'll probably have to do something like this in your webpack config:
const index = config.plugins.findIndex(p => p instanceof
AngularCompilerPlugin.AngularCompilerPlugin);
config.plugins[index]._transformers.push(YourTrasnformerHere);
return config;
In this example I pushed it at the end but here is where you choose when this transformer is applied. You can squeeze it in between or run before all the transformers, whatever you prefer. I think it should work.
To learn more about how this stuff works look into the code of AngularCompilerPlugin.
Update:
Guys from Angular team say that the right way is to use platformTransformers. It's actually all the same in terms of execution (they are just added to the _transformers array). I think the reason it is considered the right approach is that platformTransformers are provided in options while _transformers are private property.
In case you're using platformTransformers you should be able to use non-functional custom config:
module.exports = {
module: {
plugins: [
new AngularCompilerPlugin({platformTransformers: [YourTrasnformerHere]})
]
}
};
Notice though these are pushed after certain transformers.
Also, Angular is still using a single loader for TS files which works in conjunction with compiler plugin (this is most probably the reason for the mess with append and prepend).
So the gist here is basically, don't use webpack loaders for typescipt files in the cli. Use a typescript transformer instead? That's fine I guess for my case.
Just curious though, is that likely to apply to _any_ webpack loader (or plugin for that matter) that wants to transform .ts files? Or is it just stuff that wants to run first/ early in the process?
Also, found this confusingly related issue of that as a feature request. https://github.com/angular/angular/issues/22434
I believe it's right for every webpack loader/plugin that alters ts files.
Do you have any results to share? Can I close the issue?
Fiddled a bit with it, didn't really get anywhere. Ended up deciding it wasn't worth the minor boilerplate reduction the old script provided and did a work around. So... nothing really to share on getting that to work.
Thanks for the detailed description of the problem @just-jeb. This helped me greatly.
The pleasure is mine @Blackbaud-SteveBrush.
Most helpful comment
Sure, here are a few URLs:
Now, I think the real issue here is that they don't really use
loadersfor Typescript. The entity that processes TS isAngularCompilerPlugin. I have no idea why it works withprepend, and why is that different fromappendwhen the real TS compilation happens inAngularCompilerPluginbut I'd say this is the way you should take.You can hook into
transformersby pushing your own transformer.To add your transformer into
_transformersarray you'll probably have to do something like this in your webpack config:In this example I pushed it at the end but here is where you choose when this transformer is applied. You can squeeze it in between or run before all the transformers, whatever you prefer. I think it should work.
To learn more about how this stuff works look into the code of
AngularCompilerPlugin.