How to manage SCSS stylesheets across a monorepo with different libraries sharing variables?
Asked Answered
P

1

33

I have an angular monorepo set up via Nrwl and am using Angular Material's theming, which uses SASS.

I want the SCSS source files to import at the project level where I'll override default color variables for theming.

The problem I'm running into is that I want to import SCSS source files into other apps/libraries within my monorepo, and to projects outside of this monorepo.

I can write my imports like:

@import "../other-lib/style.scss";

...which will work for anything inside of the monorepo.

Or I can write it like this:

@import "~@my-organization/other-lib/style.scss";

...which won't work inside of my monorepo, AFAIK.

How do I get it to work in both contexts?

It's setup something like the diagram below.

enter image description here

Phylloxera answered 19/5, 2018 at 18:22 Comment(2)
I have exactly the same question. I can stuff them all in a top-level directory somewhere and just do the usual long winded imports. Setting up "paths" does not seem to work (you set up the path but the import statement in the stylesheet doesn't recognize it). I figured I'd try importing them into a styles.scss in a library and trying to export that, no luck here yet (though I haven't tried everything I don't think). Definitely an irksome one, the nature of sass etc. seems intuitively to be very monorepo, but the importing is extremely verbose. What's the established practice here?Bor
For now, we're going with css variables on our project. We're also exploring css-in-js solutions for the future.Phylloxera
B
20

I think I might have something here, this is the solution I've adopted after some back 'n forth with the NRWL team.

I create an NX workspace by first creating a project with the angular CLI, and then adding the extensions, like so:

  • ng new myproject (cd into myproject root)
  • ng add @nrwl/workspace

In addition, as of today (July 10, 2019) I've reported a bug that, even though the workspace is already an angular type and the @nrwl install recognizes that and installs @nrwl/angular, it does not correctly configure the default schematics collection, which means that "ng" commands will not run without appending "@nrwl/angular:" before the command (e.g. "ng @nrwl/angular:g module mymod"). So you have to run the install (select scss and whatever e2e runner you want):

  • ng add @nrwl/angular

It'll tell you @nrwl/angular is already installed, but will alter the config files to recognize the angular as the default schematic collection, and your ng commands will run as expected again.

That's that for the workspace. Now create a lib:

  • ng g lib scss --directory=stylesheets

This will put a lib named scss in libs->stylesheets. In the "lib" directory of that library, dump all your scss files. We will assume you put a file "variables.scss" in that lib directory.

What this does is a couple of things:

  • When you created the library, a "paths" entry is added to your repo config. This allows you to use assets in the library by using that path instead of a long relative import. Note that just adding this entry and trying to import scss files into other scss files without the library wrapper, does not work. Evidently, you need that library to get that path to resolve.

If you look, you'll see the "paths" entry is something like this:

"@nameofrepo-nameoflib"

In order to use the scss assets in the library, in angular.json, you have to manually (not ideal but there it is) add the below fragment to the "build.options" section of EACH AND EVERY PROJECT YOU WANT TO USE IT IN.

So yes, if you have ten projects, each will have a project entry in angular.json, each of those will have a build.options block (typically ending with "scripts []", at least in my vanilla install), and you have to add this to each of those options sections (this info is out there, but I wanted to confirm, this is straight from the NRWL team):

"stylePreprocessorOptions": {
    "includePaths": ["libs/stylesheets/scss/src/lib"]
},
"extractCss": true

Now, say you've created an app, and added the above config to the entry in angular.json:

ng g app myapp

And that within this app you created a feature module and component:

(cd into apps/myapp/src)

ng g module myfeature ng g component myfeature

That will create the module and component folder and assets, etc.

NOTE: As of same date as above, there's an issue that creating a component directly this way will create a .css file, even though .scss is the selected type for the project. Make sure you rename that file and change the component's pointer to it.

Within myfeature.component.scss, you would import "variables.scss" from your lib this way:

@import "variables"

Note there is no "~" (that resolves to node_modules). And, if you have subdirectories in the "lib" directory (e.g. utils), just path to it as you'd expect:

@import "utils/somefile"

Again...you MUST configure the preproccesor options as indicated in EACH project entry in angular.json!

Another gotcha: these paths may or may not appear to resolve in your IDE. Strangely, it seems some do and some don't. Not exactly sure of the pattern here, but keep in mind that your IDE may show an error where there technically isn't one.

This is working nicely for my projects.

Bor answered 10/7, 2019 at 12:41 Comment(12)
How can we use those variables from another library?Constituency
Not sure I understand, do you mean how to use style variables from one style library, in another style library?Bor
Yes. I've got a library containing all my scss variables and I want to import them from a different library but it doesn't seem to be workingConstituency
I'm betting it's your config. How did you create the libraries, do you see path entries for them in angular.json? Would definitely need more info on your setup.Bor
I thought I had to use stylePreprocessorOptions for the libs but actually just putting that on the app itself worked! Thanks a lotConstituency
I'd do but... I'm not the one who asked!Constituency
If you are already running serve before making this change, the build kicked off by the watcher will fail. Just kill the serve process and restart it and it will work. Took me a few minutes to figure that one out. I still haven't got IDE support on variables names though in vscode.Richrichara
As general practice, whenever making changes at the meta config level, you should restart any running processes to load those changes into the runtime. I don't think angular.json changes of any kind hot watch.Bor
would you happen to know how to handle this same situation with plain CSS?Archiearchiepiscopacy
Did you try the same routine but with just css? Off the top of my head I don’t see a reason it wouldn’t work.Bor
How to use this lib(scss) in another angular app that is not part of monorepo and created by angular cli?Suasion
You have to build it, and distribute (as a file or to a registry) as you normally would. If you are using nx workspace, install ng-packagr and use it to build your lib. Then npm pack it and you have the tarball you can distribute. Note that for development, you can npm link to the built (but unpacked) library. This will put a pointer in your node_modules to the built bits, which has the benefit of allowing you to work on the lib and an app at the same time, and even see --watch updates when you make changes to either one.Bor

© 2022 - 2024 — McMap. All rights reserved.