question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Embroider-native support for Sass

See original GitHub issue

I played a bit trying to make an Embroider app work with Sass natively, i.e. without using ember-cli-sass, as that seems to be the new way to integrate new tools to the build-pipelines (see https://github.com/embroider-build/embroider/issues/916#issuecomment-903175051). However that turned out to be not that trivial as I hoped.

I tried the following three approaches, no one worked really perfectly. Wonder what the “right” way would be, and how to fix its issues?

For all approaches, I installed the related webpack plugins, and added their config to ember-cli-build.js. So this is sass-loader (and sass itself), mini-css-extract-plugin and css-loader. The latter two are already used by @embroider/webpack, but I made them also dependencies of the app itself. I chose to use mini-css-extract-plugin instead the usually proposed style-loader also in development, as I wanted the app to also work in FastBoot (which seems to not work with style-loader, as it uses document APIs at runtime).

I have reproductions for all these approaches at https://github.com/simonihmig/embroider-sass-app in different branches.

1. Rename app.css to app.scss

The obvious and naive approach was to just rename styles/app.css to styles/app.scss and hope that it would just work. It didn’t.

It seems it doesn’t even try to compile the sass. app.scss is found in dist/assets/ in its original form. The stylesheet reference in index.html points to a now nonexisting file (/assets/embroider-sass-app.css)

Reproduction

2. Import app.scss from app.js

Similar to what the sass-loader itself suggests and what you mostly see in “webpack-native” projects AFAIK, I imported app.scss (moved from /app/styles/app.scss to /app/app.scss, as otherwise it would not find it somehow) from app.js.

This does work in general, the Sass is transformed, and you see the compiled CSS linked and used. However it is not quite ideal:

  • you seem to need an empty /app/styles/app.css, which is a bit confusing. Otherwise the build crashes with Could not find app css: "/assets/embroider-sass-app.css" in index.html. Found the following instead: - /assets/vendor.css
  • ~the <link> tag is added at the end of the index.html (alongside the scripts). For a “regular” SPA this is probably ok-ish, but for a SSR app (FastBoot) this causes a FOUC.~ Fixed by #1045

Reproduction

3. Use index.html as the entrypoint

AFAIU Embroider itself follows he approach of using HTML as the source of truth, the root of its dependency tree. So I tried to follow that idea, by adding a separate <link integrity="" rel="stylesheet" href="{{rootURL}}app.scss"> to index.html.

For this to (somehwat) work I had to patch @embroider/webpack to also add the HTML’s styles as entrypoints to its webpack config.

Don’t know for sure if this reasonable at all. But it does work for the Sass part at least. However it also messes things up related to the static CSS. This is what the built index.html part looks like then:

<script rel="stylesheet" href="/assets/vendor.css" src="/assets/chunk.4cf6812ef5b6b3308bec.js"></script>
   
<script rel="stylesheet" href="/assets/embroider-sass-app.css" src="/assets/chunk.eac6d09440173884bc29.js"></script>

<link href="/assets/chunk.3dc0c8a50b82bb3a4c50.css" rel="stylesheet">
<script integrity="" rel="stylesheet" href="/app.scss" src="/assets/chunk.3dc0c8a50b82bb3a4c50.js"></script>

You see here:

  • the (static) CSS files don’t have a <link> anymore, but a script containing webpack loader code (including style-loader that again breaks in FastBoot!)
  • which means vendor.css is not even loaded in the browser
  • the /assets/chunk.3dc0c8a50b82bb3a4c50.css is the compiled Sass, and it gets applied correctly!
  • there is a accompanying script file to that Sass file, which seems to contain only useless empty webpack module definitions

Reproduction

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:6
  • Comments:19 (5 by maintainers)

github_iconTop GitHub Comments

2reactions
ef4commented, Dec 22, 2021

After digging deeper into the state of sass, making it really nice is a bigger pain than I recognized. We can definitely maintain compatibility with how things work in ember-cli-sass, but we can’t easily do a lot better, in terms of build performance and code splitting.

The problem is that sass isn’t just a transpiler. It’s a bundler in its own right. That was necessary in the era when Sass was conceived. But I would argue that it has become a liability, and it means Sass doesn’t really play well with whole-app optimization that involves your JS and styles together.

Components are the bedrock primitive of modern Ember apps. I believe that styles should be oriented around components, such that there’s an obvious and automatic one-to-one mapping between components and their stylesheets, and that if we’ve optimized away loading a component we should obviously optimize away loading its styles too. Sass is really not equipped to play nice in that scenario.

The most relevant discussion on the Sass language repo I’ve found is https://github.com/sass/sass/pull/2873.

1reaction
ef4commented, Nov 30, 2021

Thanks for working on this.

I think the main feature we will need is the ability to disable the classic CSS support for the app (while keeping it for any addons that are relying on it). That is what is forcing app/styles/app.css to exist. I think this is also why you couldn’t keep your scss in app/styles – that path gets enough legacy handling that it doesn’t just flow through normally to where webpack can see it.

I think both option 2 and option 3 are valid strategies that we should support.

We deliberately said in the embroider spec that we support importing CSS into JS as a way for a JS module to express a dependency on the CSS. It’s more precise than importing all your CSS at once because it allows the CSS to split and lazy-load along with the JS. Adding sass loader to that should necessarily also make importing SCSS into JS work.

And using a CSS entry point from the HTML also makes a lot of sense, for things controlled by the app that it always wants to load. Especially things like CSS resets that should come before anything else. Within the module graph you don’t get very strong guarantees of evaluation order, so the HTML is a better place for that.

the <link> tag is added at the end of the index.html (alongside the scripts). For a “regular” SPA this is probably ok-ish, but for a SSR app (FastBoot) this causes a FOUC.

This seems like a thing we should fix, even without SCSS when you import CSS you can get style chunks and they will get the same treatment. We can probably always insert them at the end of <head> instead. We’d probably want to maintain the same relative order between all style chunks though.

The latter two are already used by @embroider/webpack, but I made them also dependencies of the app itself.

We should consider making embroider’s built-in style rules extensible. ember-auto-import has styleLoaderOptions and cssLoaderOptions here. We could do a similar thing for @embroider/webpack.

A bigger-picture change I’m considering is to actually have a webpack.config.js in the app, which imports utilities from Embroider to setup the standard things we need. Stylistically it would be similar to the way that v2 addons can use a rollup config where we’ve provided standard utilities that take care of most of the complexity, without preventing you from adding other things. This would help with the extensibility if we did it well.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Switch from Sass imports to Sass Module System · Issue #29853
The event horizon for node-sass supporting the new module system seems ... Embroider-native support for Sass embroider-build/embroider#1033.
Read more >
Sass: @use
The @use rule loads mixins, functions, and variables from other Sass stylesheets, and combines CSS from multiple stylesheets together. Stylesheets loaded by @ ......
Read more >
Dart Sass
When installed via npm, Dart Sass supports a JavaScript API that aims to be compatible with Node Sass. Full compatibility is a work...
Read more >
CSS At-Rules - Sass
Sass supports all the at-rules that are part of CSS proper. To stay flexible and forwards-compatible with future versions of CSS, Sass has...
Read more >
Sass: @import
(Note that only Dart Sass currently supports @use . Users of other implementations must use the @import rule instead.) What's Wrong With @import...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found