Skip to content

Compatibility with AngularCompilerPlugin #186

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
sv-uml opened this issue Jun 21, 2018 · 33 comments
Closed

Compatibility with AngularCompilerPlugin #186

sv-uml opened this issue Jun 21, 2018 · 33 comments
Labels

Comments

@sv-uml
Copy link

sv-uml commented Jun 21, 2018

I have an Angular 6 project where I want to use the mini-css-extract-plugin. Here is the relevant part of the webpack configuration:

module: {
    rules: [
        {
            test: /(?:\.ngfactory\.js|\.ngstyle\.js|\.ts)$/,
            loader: "@ngtools/webpack"
        },
        { test: /\.html$/, loader: "raw-loader" },
        {
            test: /\.(png|gif|jpe?g|woff|woff2|eot|ttf|svg)$/,
            loader: "url-loader?limit=100000"
        },
        {
            test: /\.css$/,
            use: [
                MiniCssExtractPlugin.loader,
                "css-loader"
            ]
        }
    ]
},
plugins: [
    new HtmlWebpackPlugin({
        template: __dirname + "/src/index.html",
        output: __dirname + "/dist",
        inject: "head",
        hash: true
    }),
    new MiniCssExtractPlugin({
        filename: "[name].css"
    }),
    new OptimizeCSSAssetsPlugin({
        assetNameRegExp: /\.optimize\.css$/g,
        cssProcessor: require("cssnano"),
        cssProcessorOptions: { discardComments: { removeAll: true } },
        canPrint: true
    }),
    new UglifyJsPlugin({
        parallel: 4,
        sourceMap: true
    }),
    new AngularCompilerPlugin({
        tsConfigPath: "./tsconfig.json",
        entryModule: "src/app/app.module#AppModule",
        skipCodeGeneration: false
    })
]

Running webpack will fail with the following message:

ERROR in Module build failed (from ./node_modules/mini-css-extract-plugin/dist/loader.js):
TypeError: Cannot read property 'replace' of undefined
at normalizeBackSlashDirection (C:\node_modules\webpack\lib\RequestShortener.js:16:16)
at new RequestShortener (C:\node_modules\webpack\lib\RequestShortener.js:26:15)
at Compiler (C:\node_modules\webpack\lib\Compiler.js:137:27)
at Compiler.createChildCompiler (C:\node_modules\webpack\lib\Compiler.js:378:25)
at Compilation.createChildCompiler (C:\node_modules\webpack\lib\Compilation.js:1892:24)
at Object.pitch (C:\node_modules\mini-css-extract-plugin\dist\loader.js:77:43)

However, if I change the "skipCodeGeneration" property to true, no error is thrown and everything works normally.

Is this a bug?

@sv-uml
Copy link
Author

sv-uml commented Jun 21, 2018

It seems that this issue occurs specifically when "skipCodeGeneration" is set to false and an Angular component has its "styleUrls" property set to an array of style sheet URLs.

@alexander-akait
Copy link
Member

@sv-uml what version webpack your use?

@sv-uml
Copy link
Author

sv-uml commented Jun 21, 2018

It's webpack 4.12.0. All packages are currently at the latest version.

@alexander-akait
Copy link
Member

@sv-uml can you create minimum reproducible test repo?

@sv-uml
Copy link
Author

sv-uml commented Jun 21, 2018

@evilebottnawi I am unable to create a repository right now, but I have put test code here: https://gist.github.com/sv-uml/dec79e2402e467e027abcceba9e675a6

@sv-uml
Copy link
Author

sv-uml commented Jun 22, 2018

@evilebottnawi Any luck with the issue?

@alexander-akait
Copy link
Member

@sv-uml in todo list, feel free to investigate

@dlabrecq
Copy link

dlabrecq commented Jun 26, 2018

I'm not able to use mini-css-extract-plugin with AngularCompilerPlugin (i.e., I'm not able to set skipCodeGeneration to true). I have to skip AOT compilation by omitting AngularCompilerPlugin.

dlabrecq added a commit to dlabrecq/patternfly-ng that referenced this issue Jun 26, 2018
…ct-plugin

Note: This restores the demo watch feature; however, AOT is temporarily disabled because mini-css-extract-plugin has an issue with AngularCompilerPlugin
See: webpack-contrib/mini-css-extract-plugin#186 (+1 squashed commit)
Squashed commits:
[ee51577] chore(package): update angular and webpack versions
dlabrecq added a commit to dlabrecq/patternfly-next-ng that referenced this issue Jun 26, 2018
…ct-plugin

Note: This restores the demo watch feature; however, AOT is temporarily disabled because mini-css-extract-plugin has an issue with AngularCompilerPlugin
See: webpack-contrib/mini-css-extract-plugin#186
dlabrecq added a commit to dlabrecq/patternfly-ng that referenced this issue Jun 28, 2018
…ct-plugin

Note: This restores the demo watch feature; however, AOT is temporarily disabled because mini-css-extract-plugin has an issue with AngularCompilerPlugin
See: webpack-contrib/mini-css-extract-plugin#186 (+1 squashed commit)
Squashed commits:
[ee51577] chore(package): update angular and webpack versions
@pshurygin
Copy link

As a workaround you can simply import './component.css' at the top of your component class and omit styleUrls option altogether. This way you loose ViewEncapsulation feature, but i beleive you can replace it with your custom css-modules configuration using post-css if you really need it.

But of course it would be great if this issue got fixed so we could use this plugin with AngularCompilerPlugin the recommended way.

@thescientist13
Copy link

thescientist13 commented Jul 17, 2018

Compatibility with AngularCompilerPlugin would be great!

@thescientist13
Copy link

thescientist13 commented Jul 26, 2018

@pshurygin
Thanks, that suggestion has been working well for me in the meantime. 👍

MattJeanes pushed a commit to MattJeanes/SystemChecker that referenced this issue Aug 1, 2018
MattJeanes pushed a commit to MattJeanes/SystemChecker that referenced this issue Aug 1, 2018
@thepian
Copy link

thepian commented Aug 16, 2018

So there is no way to extract CSS when using Angular 6 + Webpack 4 (required) at this point?

Not using styleUrls isn't going to be acceptable for any serious Angular project.

@devpreview
Copy link

@pshurygin your suggestion now conflict with "conflicting order" warnings (see issue #250).

@pshurygin
Copy link

Well I have a pretty large project(100K LOC) using this approach and there are no warnings with mini-css-extract plugin 0.4.2. I guess it is a matter of your code organization and imports structure.

@asidelnik
Copy link

asidelnik commented Sep 3, 2018

@pshurygin Could you give a code example of this sentence of yours:

import './component.css' at the top of your component class and omit styleUrls

And an organization/structure example of this sentence:

your code organization and imports structure

@pshurygin
Copy link

We import css file on top of the component it belongs to and nowhere else. Also, we import components only once: either in shared module, or a feature module. So i guess with this pattern you cant get into that issue.

@thescientist13
Copy link

@asidelnik
In practice (as I was able to get it working)

Before

import { Component } from '@angular/core';

@Component({
  selector: 'my-component',
  templateUrl: './my.component.html',
  styleUrls: ['./my.component.scss']
})

export class AppComponent { }

After

import { Component, ViewEncapsulation } from '@angular/core';
import './app.component.scss';

@Component({
  selector: 'my-component',
  templateUrl: './my.component.html',
  encapsulation: ViewEncapsulation.None
})

export class AppComponent { }

@thw0rted
Copy link

I think I have this working. The way I look at it is, Angular's view encapsulation means that your component styles are part of the component definition, not part of the page style. This means that you don't want any Webpack plugins (other than AngularCompiler) to touch it. So, I did this:

rules: {
  ...,
  {
    test: /\.css$/,
    include: resolve(__dirname, "src", "app"),
    use: "raw-loader"
  }, {
    test: /\.css$/,
    exclude: resolve(__dirname, "src", "app"),
    use : [CssExtractPlugin.loader, "css-loader"]
  },
  ...,
}

My page styles all live in src but not under app, while all my components are defined in directories under app. You could of course set your include/exclude rules up differently. I don't have a sample repo demo to share, but this is more or less how my project has worked since I first switched to Webpack 4, and it seems to work well.

@thescientist13
Copy link

thescientist13 commented Sep 21, 2018

Thanks for sharing @thw0rted.

The way I look at it is, Angular's view encapsulation means that your component styles are part of the component definition, not part of the page style.

Yup. I understood ViewEncapsulation to be Angular's API for exposing Shadow DOM to developers for use with their components, and so in this way, the styles are part of the component's Shadow DOM, which actually lives in the DOM itself (inline CSS) and not loaded via a <script> tag. (This just my understanding mind you)

My only concern around using Shadow DOM is what are the long term caching implications (no file, no caching), and what if the same <table-cell> component is used 1000x on a page? Is that 1000x inline styles? As opposed to one bundled / optimized CSS file that contains the single table-cell class definition and needed styles (code)? 🤔

Anyway, I would like to use ViewEncapsulation, but haven't had time to compare against the more traditional approaches and how to best manage CSS in a web component world.

@thw0rted
Copy link

thw0rted commented Sep 24, 2018

I also don't have a super firm grip of how encapsulation works, but my understanding was that under the hood, component styles have an additional attribute attached to them, which uses a generated ID to uniquely identify component instances and limit application. That is, the rules come out looking like #mainMenuButton[_ngcontent-c0] { ... }. At runtime (maybe at template-compile time?), the element in my component with id=mainMenuButton has an attribute _ngcontent-c0="" attached in the DOM. This way, the style rule only exists in one place, but is applied in a scoped manner.

And I'm pretty sure the rule does exist in one place, for what it's worth. I have a "debug" Webpack build that generally tries to leave the Angular compiler's output as un-messed-with as possible, but otherwise pulls everything into a small number of files -- this makes it easy to deploy but also easy to read when the browser has problems with sourcemaps. I specify my "top level" CSS (everything outside src/app) as an entry point, then thanks to mini-css-extract-plugin, the raw-loader rule above means these CSS files get treated as "plain text", then handed off to the CSS extractor which bundles them all into main.css. Meanwhile, the raw Angular component CSS winds up in main.js as named Webpack modules (since I have named modules turned on for this build), one per component This named module is referenced in the component definition, e.g.

exports.AppComponent = AppComponent = __decorate([(0, _core.Component)({
  selector: "app-root",
  template: __webpack_require__(/*! ./app.component.html */ "./src/app/app.component.html"),
  styles: [__webpack_require__(/*! ./app.component.css */ "./src/app/app.component.css")],
  providers: [_search.SearchService],
  animations: [_animations.GROW_DOWN_ANIMATION, _animations.SLIDE_FROM_LEFT_ANIMATION, _animations.SLIDE_FROM_RIGHT_ANIMATION]
}), __metadata("design:paramtypes", [_settings.SettingsService])], AppComponent);

So, effectively the raw CSS (and apparently template HTML) strings are passed to the component decorator function and that component object is used to construct instances. This means that component CSS is not treated by webpack as CSS, but rather as a string literal -- which is exactly what I want! -- so it can't be minified, comment-stripped, etc.

@thescientist13
Copy link

thescientist13 commented Sep 24, 2018

That's a great write up, thank you @thw0rted , and now that you mention it, I do recall this being the behavior with Angular ("linked" classes). I think this is sort of like how CSS Modules work?

Definitely going to review your comment in depth, and hopefully see this supported in webpack and / or this plugin. 👍

@thescientist13
Copy link

thescientist13 commented Oct 4, 2018

@thw0rted
So In your example you are only extracting "page" level styles and just using styleUrls for your components without extraction then? Does that mean your page level styles don't get import / @import ed anywhere?

Not sure if you could share an example of each, would be curious to try and solve this problem on my end. Starting to get CSS collisions now that all styles have moved into a "global" namespace (single file) and where our specificity is loose / weak. Not sure what options there are (CSS Modules, CSS-in-JS) is worth it as a stop gap measure to get component level CSS scoping too? (with or without extract I suppose, I would just prefer whatever makes CSS more modular / portable)

@gentios
Copy link

gentios commented Oct 12, 2018

I have the same issue if I do: ng build --prod --build-optimizer it fails with the same error as described in this issue, I tried also @thescientist13 solution but it still fails

└─┬ @angular-devkit/[email protected] └── [email protected]

└─┬ @angular-devkit/[email protected] └── [email protected]

"@angular/compiler": "6.1.10", "@angular/core": "6.1.10",

@thw0rted
Copy link

@thescientist13 That's the idea, and I include main.css (with all the page-level style) as an entry point. The end result is that my vendor styles and page-level styles are extracted into one blob that gets injected into the page by html-webpack-plugin. What did you want to see an example of?

@zaikin-andrew
Copy link

I faced with the same problem. Any news?

@alexander-akait
Copy link
Member

@zaikin-andrew it is open source, PR welcome

@ViieeS
Copy link

ViieeS commented Mar 27, 2019

I solved this by excluding .ts issuer:

 {
            test: /\.(scss|sass|css)$/,
            issuer: {
                exclude: /\.ts$/
            },
            use: [{
                loader: MiniCssExtractPlugin.loader
            }, {
                loader: 'css-loader',
                options: {importLoaders: 2, sourceMap: isDev}
            }]
        }, {
            test: /\.(scss|sass|css)$/,

            use: [{
                loader: 'postcss-loader',
                options: {
                    ident: 'postcss',
                    sourceMap: isDev,
                    plugins: () => [
                        postcssPresetEnv(),
                        ...(!isDev
                            ? [cssnano({preset: ['default', {discardComments: {removeAll: true}}]})]
                            : [])
                    ]
                }
            }, {
                loader: 'sass-loader',
                options: {
                    sourceMap: isDev,
                }
            }]
        }, 

@zaikin-andrew
Copy link

My bad. I should read the comments accurately. 😀
image

@gentios
Copy link

gentios commented Jun 10, 2019

@zaikin-andrew what is the helpers.root in this case, can you share the whole code snippet please

@alexander-akait
Copy link
Member

Closed because not related to mini-css-extract-plugin, anyway if you still think we have bugs in code, please open new issue with reproducible test repo thanks

@KingDarBoja
Copy link

@ViieeS Doesn't work, I am getting the following error;

ERROR in Module parse failed: Unexpected token (1:0) You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders

@huineng
Copy link

huineng commented Nov 27, 2019

i have the same issue after installing https://github.com/swimlane/ngx-charts
and only in combination with the ngToolsWebpack.AngularCompilerPlugin for aot (is working outside aot)

TypeError: Cannot read property 'replace' of undefined

angular 8

{
                test: /\.css/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                ],
            },

all the other solutions above didn't work, it needs to be checked against the package causing it

@StNekroman
Copy link

the same bug (in mini-css-extract-plugin) while running aot build.
In dev mode (jit compiler) it works fine

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests