Embroider-native support for Sass
See original GitHub issueI 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
)
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 withCould 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
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 (includingstyle-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
Issue Analytics
- State:
- Created 2 years ago
- Reactions:6
- Comments:19 (5 by maintainers)
Top GitHub Comments
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.
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 inapp/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.
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.We should consider making embroider’s built-in style rules extensible. ember-auto-import has
styleLoaderOptions
andcssLoaderOptions
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.