Ignore Angular2 component stylesheets using ExtractTextPlugin in webpack

I use the html-webpack-plugin plugin to insert <script> tags into the template file for the pieces that webpack generates. I also have some global style sheets that I want to dynamically add to the <head> section of my HTML template. For this, I also use html-webpack-plugin as follows (in this case, the app entry / chunk is relevant).

 module.exports = { entry: { 'polyfills': './ts/polyfills.ts', 'vendor': './ts/vendor.ts', 'app': './ts/app.ts' }, module: { loaders: [ { test: /\.ts$/, loaders: ['ts', 'angular2-template-loader'] }, { test: /\.html$/, loader: 'html-loader' }, { test: /\.css$/, loaders: ['to-string-loader', ExtractTextPlugin.extract('style-loader', 'css-loader')] } ] }, plugins: [ new ExtractTextPlugin('css/[name].[contenthash].css'), new webpack.ProvidePlugin({ bootstrap: 'bootstrap' }), new webpack.optimize.CommonsChunkPlugin({ name: 'app', filename: 'js/[name].[chunkhash].min.js', chunks: ['app'] }), new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: 'js/[name].[chunkhash].min.js', chunks: ['vendor'] }), new webpack.optimize.CommonsChunkPlugin({ name: 'polyfills', filename: 'js/[name].[chunkhash].min.js', chunks: ['polyfills'] }), new HtmlWebpackPlugin({ template: 'my-template.html', filename: 'my-template.html', inject: 'body', hash: false }), ] }; 

Below is the app.ts file, where I added a require statement for each stylesheet that I would like to include in my template (they are combined together into one stylesheet).

 require('main.css'); require('misc.css'); import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app.module'; platformBrowserDynamic().bootstrapModule(AppModule); 

Essentially, this works well, but the trick is that I use this configuration for an Angular2 application, which consists of components that may have related stylesheets, as in this example.

 @Component({ styleUrls: [ 'my-stylesheet.css' ] }) export class MyComponent { ... } 

Building an application will usually result in a ton of HTTP calls for these stylesheets for the components, so to solve this problem I pulled the stylesheets directly into the components using angular2-template-loader . This bootloader works as follows.

angular2 -template-loader looks for url templates and styleUrls declarations inside the metadata of the Angular 2 component and replaces the paths with the corresponding requirement.

The problem is that in addition to the two stylesheets added to the app.ts entry app.ts , webpack also includes all the stylesheets that my Angular2 components reference. As it goes through the application dependencies and finds the require statements added by angular2 -template-loader. This is undesirable because I want these style sheets to be local to the components to which they belong, i.e. Be embedded in JS.

Therefore, I only need to include the stylesheets that I require directly in my entry points, possibly somehow excluding component stylesheets. The problem is that in both cases the file has a .css extension, so they both pass the test for extract-text-plugin . I looked at the bootloader / plugin documentation, but I could not find anything that could solve my problem.

So I want to add the app.[contenthash].css file to my template file (this part works), but excluding the stylesheets referenced by my Angular2 components (included because of angular2 -template-loader).

How can I prevent these Angular2 stylesheets from being included in the stylesheet that is added to my HTML template?

+5
source share
3 answers

Let's say you have the following folder structure:

enter image description here

So, all my components are inside the application folder.

main.ts

 require('main.css'); require('misc.css'); import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app.module'; platformBrowserDynamic().bootstrapModule(AppModule); 

/app/app.component.ts

 @Component({ selector: 'my-app', template: `<h1>My Angular 2 App</h1>`, styleUrls: ['my-stylesheet.css'] }) export class AppComponent {} 

Then your solution might look like this:

webpack.config.js

 { test: /\.css$/, exclude: /app\\.+\.css$/, <== add this (exclude all styles from app folder) loaders: ['to-string-loader', ExtractTextPlugin.extract('style-loader', 'css-loader')] }, { test: /app\\.+\.css$/, loader: 'raw' <== use raw loader for these files } 
+5
source

You can give webpack a hint to distinguish between CSS files using two separate name templates. Let's say you name all the files that angular2 -template-loader should include with the suffix .tl (short for the template loader, for example mycss.tl.css ), and others with the suffix .pt (short for the platform, for example main.pt.css ), you can write the webpack configuration as follows:

 module: { loaders: [ { test: /\.ts$/, loaders: ['ts', 'angular2-template-loader'] }, { test: /\.pt\.css$/, loaders: ['to-string-loader', ExtractTextPlugin.extract('style-loader', 'css-loader')] }, { test: /\.tl\.css$/, loaders: ['style-loader', 'css-loader'] } ] } 

Then the definition of your component looks like

 @Component({ styleUrls: [ 'my-stylesheet.tl.css' ] }) export class MyComponent { ... } 

whereas in app.ts you put

 require('main.pt.css'); require('misc.pt.css'); import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app.module'; platformBrowserDynamic().bootstrapModule(AppModule); 

angular2 -template-loader then still wraps css paths with require calls, but based on your configuration, you might have a different set of loaders for css files.

+1
source

For anyone interested, I managed to solve a problem inspired by @yurzui's answer.

 module: { loaders: [ { test: /\.ts$/, loaders: ['ts', 'angular2-template-loader'] }, { test: /\.css$/, exclude: [helpers.root('public', 'css', 'components')], loaders: ['to-string-loader', ExtractTextPlugin.extract('style-loader', 'css-loader')] }, { test: /\.css$/, include: [helpers.root('public', 'css', 'components')], loaders: ['raw-loader'] } ] } 

In the above example, I use my own helper to build the directory path. You could have done it somehow differently, but in any case, my assistant (taken from the Angular2 documentation).

 var path = require('path'); var _root = path.resolve(__dirname, '..'); // This file is stored within a webpack subdirectory in my case function root(args) { args = Array.prototype.slice.call(arguments, 0); return path.join.apply(path, [_root].concat(args)); } exports.root = root; 

To use it, just try it in the configuration of your web package.

 var helpers = require('./helpers'); 

I hope this helps someone.

0
source

Source: https://habr.com/ru/post/1257819/


All Articles