nrwl / nx

Smart Monorepos · Fast CI
https://nx.dev
MIT License
23.75k stars 2.37k forks source link

Using SCSS files (sass etc) in NX Workspace. #1542

Closed tcoz closed 5 years ago

tcoz commented 5 years ago

I've done some looking around on this one, apologies if an answer is published and I missed it.

The issue: there doesn't appear to be an established best practice for working with .scss files and so on in an NX workspace.

It seems you can put them all in a directory somewhere and just do the usual long-winded imports "../../../etc/etc/variables.scss".

I'm digging into the stylePreprocessorOptions.includePaths, which as far as I know is the standard way of going about it, but I'm not having any luck, and it also seems like even if it worked, you'd have to add the frag to every project in the angular.json by hand. Not horrible but not ideal.

That's all guessing though. What's the established practice used by the NRWL team?

Thanks for any info here, I'm working with a team on adopting the extensions, want to make sure I get this one right.

peterblazejewicz commented 5 years ago

You could also evaluate option to share .scss assets with shared ui library. I've created a sample PR some time ago in related repo, maybe that could be an option too: https://github.com/nrwl/workshop-nx-starter/pull/4

tcoz commented 5 years ago

This was my preferred option, but I wasn't exactly sure how to implement (I'm definitely not an scss master). I'll check out your example, grazie mille.

bcabanes commented 5 years ago

Hi @tcoz!

We usually like to recommend to create a library specifically to handle the styles.

You usually already have a UI library (containing only presentational components) that are shared between your apps and used by multiple feature libraries. You can create a library next to this one, handling only the styles. For example:

libs/common/ui/my-ui-library1
libs/common/ui/my-styles-library

Inside the library you can have all your relevant .scss files that will be used by other libraries / apps.

Then, concerning the import, you could do it in two ways, like you exposed.

"projects": {
    "shop-client": {
      "root": "apps/my-app",
      "sourceRoot": "apps/my-app/src",
      "projectType": "application",
      "prefix": "app",
      "schematics": {},
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/apps/my-app",
            "deployUrl": "static/",
            "index": "apps/my-app/src/index.html",
            "main": "apps/my-app/src/main.ts",
            "polyfills": "apps/my-app/src/polyfills.ts",
            "tsConfig": "apps/my-app/tsconfig.app.json",
            "stylePreprocessorOptions": {
              "includePaths": [
                "libs/common/ui/my-styles-library/src/lib"
              ]
            },
            "extractCss": true
          },
bcabanes commented 5 years ago

Closing the issue for now.

tcoz commented 5 years ago

@bcabanes Want to thank you for the responsiveness, this is exactly the way I was heading, two things though:

"Inside the library you can have all your relevant .scss files that will be used by other libraries / apps."

How exactly do I do that? I'm assuming I don't just drop them in, is there some config or something?

bcabanes commented 5 years ago

@tcoz No there is no configuration for it since this library will not contain anything JavaScript related (unless you want to test your styles with components).

The way you arrange your scss files is up to you, or more precisely the architecture / code organisation of your styles (think BEM, Atomic design, SMACSS etc...).

The only configuration needed is if you want to use the import "variables"; version, your will need to configure the stylePreprocessorOptions property for the app that needs it.

tcoz commented 5 years ago

Yep I tried the preprocessor option again, it's not working here.

My setup very vanilla:

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

In myapp.component.scss:

@import "~font-icons/variables";

Shows as unresolved, doesn't compile. Tried a variety of combinations, no go.

This is the solution I'd really like to get working, but even in the most straightforward way it doesn't seem to take. I figure I'm missing something obvious. This will all take with an "ng serve myapp"? No other build step or anything?

bcabanes commented 5 years ago

@tcoz Okay i will reopen the ticket, but we will need a repo where we can reproduce the error. This is very tied to the setup and the configuration of your workspace.

tcoz commented 5 years ago

Alright, will create a git repo tomorrow. I'm definitely not doing anything fancy, the workspace has three small apps in it and nothing else, latest and greatest CLI and NX installed via the "add nx extensions to existing cli workspace" docs.

tcoz commented 5 years ago

Repo posted (link at bottom), steps:

Angular CLI 8.0.4 node v10.15.3

(cd into preprocessor, all commands now run from workspace root unless otherwise indicated)

Even though it is an angular project that I added the Nrwl extensions to (as opposed to creating a straight nx workspace with no project type), and the @nrwl/angular package is installed, the "ng" commands won't run without appending "@nrwl/angular:" unless you do this:

(Select Jest and Cypress from prompts, Cypress download/install takes a bit, it is not already installed)

Now the default schematics/collection is properly configured and the ng commands will run as expected. I filed an issue on this a while back, but would be surprised if it's related, figured I'd point it out though.

Moving on:

Select SASS, no routing. NOTE: a warning appears the @nrwl/workspace package is now installed as both a dependency and dev dependency.

App serves on 4200 as expected. I CD into apps/subdir/testapp/src, and create a module and component:

I import the feature module into the app.module of testapp, remove the default kruft from app.component template, and add the selector for myfeature. I serve it (ng serve subdir-testapp), all is well.

NOTE: (There's another issue file on this), even though testapp was created with SASS selected, the feature module "myfeature" component is created with a .css file, not an .scss one. I rename the file to "myfeature.component.scss" and change myfeature.component.ts to point at the scss file (instead of the configured css one). In angular.json, the project configuration does indicate style is scss, and the correct "styles" .scss file is indicated:

"subdir-testapp": {
      "projectType": "application",
      "schematics": {
        "@nrwl/workspace:component": {
          "style": "scss"
        }
      },

....

"styles": ["apps/subdir/testapp/src/styles.scss"],

CD back to the workspace root, create the library:

I put a file "variables.scss", which contains a bunch of variables but no other imports, into the lib directory of that library.

Configure the preprocessor options for subdir-testapp in angular.json:

         "options": {
            "outputPath": "dist/apps/subdir/testapp",
            "index": "apps/subdir/testapp/src/index.html",
            "main": "apps/subdir/testapp/src/main.ts",
            "polyfills": "apps/subdir/testapp/src/polyfills.ts",
            "tsConfig": "apps/subdir/testapp/tsconfig.app.json",
            "aot": false,
            "assets": [
              "apps/subdir/testapp/src/favicon.ico",
              "apps/subdir/testapp/src/assets"
            ],
            "styles": ["apps/subdir/testapp/src/styles.scss"],
            "scripts": [],
            "stylePreprocessorOptions": {
              "includePaths": [
                "libs/sasslib/src/lib"
              ]
            },
            "extractCss": true
          },

Do an "ng serve subdir-testapp" everything is fine, no console errors etc.

Go to myfeature.component.scss, add this line:

@import "~variables"

The import does not resolve (WebStorm), and the app does not compile:

ERROR in ./src/myfeature/myfeature.component.scss
Module build failed (from /Users/Consolazio/Documents/GitHub/preprocessor/node_modules/sass-loader/lib/loader.js):

@import "~variables";
       ^
      Can't find stylesheet to import.
  ╷
1 │ @import "~variables";
  │         ^^^^^^^^^^^^
  ╵
  stdin 1:9  root stylesheet
      in /Users/Consolazio/Documents/GitHub/preprocessor/apps/subdir/testapp/src/myfeature/myfeature.component.scss (line 1, column 9)

It also doesn't work in testapp/src/styles.scss.

As far as I can tell, based on your direction, this should work very straightforwardly. I suspect I'm missing something obvious but at this point (I've been told more than once, "there's nothing to configure or import it should just work") I'm at a loss. I'm tinkering with it now, will be watching the thread carefully (I really need to get this working for a large project).

REPRO REPO (public):

https://github.com/tcoz/preprocessor

bcabanes commented 5 years ago

Changing @import "~variables"; by @import "variables"; makes it working. Sorry for not pointing it before, my bad.

Why?

The syntax @import "~variables"; makes the builder (Webpack) looking into the node_modules resolutions, which will fail because the style library is not part of the node_modules dependencies.

The use of stylePreprocessorOptions option is used to tell the builder (Webpack) to include the path provided in its path resolutions. It will look to your files as if they were in the same directory (sort of). This is why, you don't need the ~.

IDE not able to resolve the imported file

This is comprehensible. Since we are configuring a custom property of a builder in a custom file. The IDE can't make any assumption about that change. This import cannot be resolved because it is looking to resolve it in the same folder of the import. The only way to use the IDE resolution is to use the deep import ../../../libs/my-styles/path-to-file.

tcoz commented 5 years ago

Right, I figured that, the previous post indicated the "~". I thought maybe it added it to the path resolutions or some such. I also figured the IDE might not understand the implied path, that's fine.

Checking it out now.

(After checking in repro) so far so good, the app compiles. Rolling over to the larger repo to make the change and see if all is well...

bcabanes commented 5 years ago

@tcoz I created a PR with the change if it can be useful.

https://github.com/tcoz/preprocessor/pull/1

tcoz commented 5 years ago

Everything seems to be working in the larger project now, nice tidy style libraries, thanks for the help here.

rienheuver commented 4 years ago

How would one go about fixing the dependency graph in this situation?

milo526 commented 4 years ago

How would one go about fixing the dependency graph in this situation?

In your nx.json you will find a section for your application i.e. webapp assuming your lib with the styling is called styling you can set:

"projects": {
    "webapp": {
      "tags": [],
      "implicitDependencies": ["styling"]
    },
}
JanineAmelie commented 4 years ago

Hi @tcoz!

We usually like to recommend to create a library specifically to handle the styles.

You usually already have a UI library (containing only presentational components) that are shared between your apps and used by multiple feature libraries. You can create a library next to this one, handling only the styles. For example:

libs/common/ui/my-ui-library1
libs/common/ui/my-styles-library

Inside the library you can have all your relevant .scss files that will be used by other libraries / apps.

Then, concerning the import, you could do it in two ways, like you exposed.

  • The first one is the deep imports ../../etc/etc/variables.scss.
  • The second one is to use the stylePreprocessorOptions like you mentioned. With this option you will be able to write only import "variables"; which is neat. To achieve that you will need to setup the stylePreprocessorOptions for every apps that need those styles. You can do it by updating the angular.json file, here an example:
"projects": {
    "shop-client": {
      "root": "apps/my-app",
      "sourceRoot": "apps/my-app/src",
      "projectType": "application",
      "prefix": "app",
      "schematics": {},
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/apps/my-app",
            "deployUrl": "static/",
            "index": "apps/my-app/src/index.html",
            "main": "apps/my-app/src/main.ts",
            "polyfills": "apps/my-app/src/polyfills.ts",
            "tsConfig": "apps/my-app/tsconfig.app.json",
            "stylePreprocessorOptions": {
              "includePaths": [
                "libs/common/ui/my-styles-library/src/lib"
              ]
            },
            "extractCss": true
          },

hey @bcabanes , I was wondering how would this work in a project without angular, e.g. a NX-next.js react based series of apps? I've been stuck with this for 3 days now trying to find a solution for this exact same problem and only have workspace.json

where adding

        "stylePreprocessorOptions": {
          "includePaths": [
            "libs/common/ui/my-styles-library/src/lib"
          ]

doesn't work.

I was wondering if you have any ideas with this or know a guy who created a style library

fbaba-nibtravel commented 4 years ago

Hi @JanineLourens did you have any luck creating this structure? I was heading the same way and came across this thread. On my current tests, I was only able to achieve this import with deep imports. So far I couldn't make it work with the include paths.

On thing to note is that the includePaths works for Apps. But if a library is consuming the other library, it doesn't work. Even if adding the path to ng-package.json like the following:

{
  "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
  "dest": "../../../dist/libs/shared/ui-components",
  "lib": {
    "entryFile": "src/index.ts",
    "styleIncludePaths": ["/libs/shared/smacss/src"]
  }
}
ghost commented 4 years ago

I would also be interested in any proposed solutions/examples. I am currently working through this myself.

My scenario is a shared styles library, which also imports bootstrap.

This all works beautifully, except when I want to use a mixin such as theme-color-level within a specific component. I can reimport bootstrap but then I'd also need to import all my variable overrides each time (which currently would be a deep import as mentioned).

dmolinero commented 4 years ago

The proposed solution using the "stylePreprocessorOptions" option did not work for me as shown above.

However, if I include .scss in the filename when components are importing a file (@import "variables.scss" instead of @import "variables"), it does work.

I have not determined why yet. If anyone can enlighten me, please do.

adgoncal commented 4 years ago

Hi @JanineLourens did you have any luck creating this structure? I was heading the same way and came across this thread. On my current tests, I was only able to achieve this import with deep imports. So far I couldn't make it work with the include paths.

On thing to note is that the includePaths works for Apps. But if a library is consuming the other library, it doesn't work. Even if adding the path to ng-package.json like the following:

{
  "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
  "dest": "../../../dist/libs/shared/ui-components",
  "lib": {
    "entryFile": "src/index.ts",
    "styleIncludePaths": ["/libs/shared/smacss/src"]
  }
}

I ran into the same issue as @fbaba-nibtravel in a Nrwl workspace. I want to import a scss file from libs/shared/styles in lib/shared/ui. stylePreprocessorOptions is not available in the shared-ui lib builder ("@nrwl/angular:package"), and adding styleIncludePaths to shared-styles lib's ng-package.json throws the following error when building shared-ui lib:

ERROR: Configuration doesn't match the required schema.
Data path ".lib" should NOT have additional properties (styleInlcludePaths).
vespertilian commented 4 years ago

So I just found this thread.

Am I crazy but is just having a root level folder for styles reasonable?

So

my-nx-app
  apps
  libs
  styles

My style imports now look like:

@import "styles/variables";

h1 {
  background-color: $color;
}

No big long imports .. styles variables kinda feel like a top-level concern for me, I am not picking up any real code smells, am I missing something?

You can still seem to import styles from node-modules into this top level folder, something like:

@import "~@angular/material/theming";
$typographyConfig: mat-typography-config();

$color: blue

So yeah I feel like this bypasses some of the above-mentioned issues and is a reasonable overall solution. Let me know what you think.

adgoncal commented 4 years ago

@vespertilian I think you lose a bit of control on the affected checks if you put the styles outside the libs folder. I don't think your styles/ folder will show up on the affected graph, so any changes to those files may not cause apps to re-build.

vespertilian commented 4 years ago

@adgoncal great point.

fbaba-nibtravel commented 4 years ago

Hi all, I was able to sorta make it work. But I haven't done much tests on it so I thought you guys may want to give it a go.

On package.json

  "dependencies": {
    ...
    "@yourWorkspace/styles": "file:libs/shared/ui/styles/src",
    ...
  },

This creates a sort of symlink of the style package. With that I was able to do @import "~@yourWorkspace/styles/components/autocomplete"; within the libs.

I'll repeat, need more tests around it.

gubo commented 4 years ago

i use: @import '~libs/style/src/lib/variables'

diegovilar commented 4 years ago

Hi all, I was able to sorta make it work. But I haven't done much tests on it so I thought you guys may want to give it a go.

On package.json

  "dependencies": {
  ...
    "@yourWorkspace/styles": "file:libs/shared/ui/styles/src",
  ...
  },

This creates a sort of symlink of the style package. With that I was able to do @import "~@yourWorkspace/styles/components/autocomplete"; within the libs.

I'll repeat, need more tests around it.

So far, IMHO, this seems to be the only way that works for both apps and libs, and it is just clean.

stylePreprocessorOptions does not work in libs, only apps. And even though stylePreprocessorOptions works for apps, it doesn't allow you to create path aliases, so, in your imports, you end up referencing "global" files that may not be clear at a glance where they are coming from (the app? libraries?).

The package.json symlink approach allows you to alias your styles paths as "virtual" libraries. You still keep their code in their owning library, where they belong (let's say @corp/lib), but you can import their styles with @import "~@corp/lib-styles/..." from all across the monorepo, even from withing @corp/lib itself, without the need to create another real library.

Neat, @fbaba-nibtravel 🙌

fbaba-nibtravel commented 4 years ago

Hi all, I was able to sorta make it work. But I haven't done much tests on it so I thought you guys may want to give it a go. On package.json

  "dependencies": {
    ...
    "@yourWorkspace/styles": "file:libs/shared/ui/styles/src",
    ...
  },

This creates a sort of symlink of the style package. With that I was able to do @import "~@yourWorkspace/styles/components/autocomplete"; within the libs. I'll repeat, need more tests around it.

So far, IMHO, this seems to be the only way that works for both apps and libs, and it is just clean.

stylePreprocessorOptions does not work in libs, only apps. And even though stylePreprocessorOptions works for apps, it doesn't allow you to create path aliases, so, in your imports, you end up referencing "global" files that may not be clear at a glance where they are coming from (the app? libraries?).

The package.json symlink approach allows you to alias your styles paths as "virtual" libraries. You still keep their code in their owning library, where they belong (let's say @corp/lib), but you can import their styles with @import "~@corp/lib-styles/..." from all across the monorepo, even from withing @corp/lib itself, without the need to create another real library.

Neat, @fbaba-nibtravel 🙌

The only issue I'm having at the moment is that sometimes the HMR doesn't account for changes on the css and not sure why, but I do have to rebuild so it reflects the current changes on the css. Usually is fine tho.

nhhockeyplayer commented 3 years ago

Its nice to see this being addressed im using 10.4.4 nrwl nx

this is the main breadNbutter standing feature of nx sharing libraries across enterprise of all code to save corporations

  1. are there clear instructions posted somewhere
  2. Is there a repo that demonstrates appropriate modeling?

im dealing with a pre-generated project monorepo this is my second day being blocked I updated migrated and that failed with various errors

after attempting to import interface class files in code I keep getting this ESLint: Libraries cannot be imported by a relative or absolute path, and must begin with a npm scope(@nrwl/nx/enforce-module-boundaries)

this issue is a ten headed dragon and hasnt been solved for the public yet

Im happy to put any effort in testing but Im still flattened

this was building...

import IRouter from '../../../libs/isomorphic/src/lib/server/IRouter';
import IHitcount from '../../../libs/isomorphic/src/lib/IHitcount';

all i want to do is import

npm said to do this

import { IHitcount } from '../../../libs/isomorphic/src/lib/IHitcount'
import { IRouter } from '../../../libs/isomorphic/src/lib/server/IRouter'

when this SHOULD be able to articulate this

import { IHitcount } from '@workspace/isomorphic/src/lib/IHitcount'
import { IRouter } from '@workpsace/isomorphic/src/lib/server/IRouter'

can someone set me on a path and I will validate

Im working on this fulltime ?

thank you

FYI from my perspective as of yesterday and I just started using Nx 3 weeks ago

after opening up all tsconfig files to find everything

    • my app cannot import shared scss library within sccs apps code and I been thru the tag business and workspace.json including adding a "file: npm package to no avail
    • I cannot do a simple import from a hared interfaces lib... well I can but it cant specify "@workspace/interface" its all relative and the webstorm IDE barks with lint as shown above... it builds but the lint is annoying and I jimmied up webstorm prefs to no avail

> nx run e2-ask:build 

chunk {main} main.js, main.js.map (main) 633 bytes [initial] [rendered]
chunk {polyfills} polyfills.js, polyfills.js.map (polyfills) 688 bytes [initial] [rendered]
chunk {runtime} runtime.js, runtime.js.map (runtime) 6.15 kB [entry] [rendered]
chunk {styles} styles.css, styles.css.map (styles) 535 kB [initial] [rendered]
Date: 2020-11-28T23:57:20.753Z - Hash: 0b315edbfa2f7ce72b80 - Time: 36163ms

ERROR in Module build failed (from /Users/meanstack/new/NX/energy/node_modules/sass-loader/dist/cjs.js):
SassError: Can't find stylesheet to import.
  ╷
1 │ @import "styles-variables";
  │         ^^^^^^^^^^^^^^^^^^
  ╵
  /Users/meanstack/new/NX/energy/apps/e2-ask/src/app/biz/landing.component.scss 1:9  root stylesheet

>  NX   CLOUD  See run details at https://nx.app/runs/s2WUybZhnQN
fbaba-nibtravel commented 3 years ago

@nhhockeyplayer, I have the impression you're mixing a couple of issues as if they are one.

Firstly, regarding the ESLint and NX Enforce Boundaries. For ESLint IDE to understand the workspace path, they should be properly formed within tsconfig.json on similar to:

{
  ...
  path: {
    "@workspace/lib": "libs/lib1/src/index.ts"
  }
}

This should be defined on the root tsconfig on which all tsconfig.lib.json are extending. In addition, your rules about the boundaries should be defined within nx.json

{ 
    "projects": {
        "app": {
          "tags": ["scope:app"]
        },
        "lib1: {
          "tags": ["scope:shared", "type:ui"]
        }
}

Finally, on your .eslintrc.json

{
  "root": true,
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaVersion": 2015,
    "sourceType": "module",
    "project": "./tsconfig.*?.json"
  },
  "ignorePatterns": [],
  "plugins": ["@typescript-eslint", "@nrwl/nx"],
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/eslint-recommended",
    "plugin:@typescript-eslint/recommended",
    "prettier",
    "prettier/@typescript-eslint"
  ],
  "rules": {
    ...
    "@nrwl/nx/enforce-module-boundaries": [
      "error",
      {
        "enforceBuildableLibDependency": true,
        "allow": [],
        "depConstraints": [
          {
            "sourceTag": "scope:app",
            "onlyDependOnLibsWithTags": ["scope:app", "scope:shared"]
          },
          {
            "sourceTag": "scope:shared",
            "onlyDependOnLibsWithTags": ["scope:shared"]
          },
          {
            "sourceTag": "type:ui",
            "onlyDependOnLibsWithTags": ["type:ui", "type:util"]
          },
        ]
      }
    ]
  }
}

As for the scss import, it's not ESlint throwing this. With the trick I've previously shared, it creates a sort of symbolic link to the node_modules folder so your import should be something like @import '~/@workspace/styles/style_variables';

nhhockeyplayer commented 3 years ago

Actually this is what the IDE liked

@import '~@workspace/scss/lib/styles-variables';

where my lib is named scss

now Im trying to figure out how to get the cross-app cross-lib bidirectional imports to play the IDE is barking about those too

I appreciate the help my build is almost there

Im wetting my pants over this framework its very elite

nhhockeyplayer commented 3 years ago

actually I spoke too soon

the IDE was happy doing the code completion thing until to form

@import '~@workspace/scss/lib/styles-variables';

and the red highlight went away

but when I nx build project , the build fails on that line

so Im still shaking this out

will have an update

nhhockeyplayer commented 3 years ago

I updated my lint files having two hasnt hurt anythiing

eslintrc.json

{
    "root": true,
    "parser": "@typescript-eslint/parser",
    "parserOptions": {
        "ecmaVersion": 2015,
        "sourceType": "module",
        "project": "./tsconfig.*?.json"
    },
    "ignorePatterns": [],
    "plugins": ["@typescript-eslint", "@nrwl/nx"],
    "extends": [
        "eslint:recommended",
        "plugin:@typescript-eslint/eslint-recommended",
        "plugin:@typescript-eslint/recommended",
        "prettier",
        "prettier/@typescript-eslint"
    ],
    "rules": {
        "@nrwl/nx/enforce-module-boundaries": [
            "error",
            {
                "enforceBuildableLibDependency": true,
                "allow": [],
                "depConstraints": [
                    {
                        "sourceTag": "scope:app",
                        "onlyDependOnLibsWithTags": ["scope:app", "scope:shared"]
                    },
                    {
                        "sourceTag": "scope:shared",
                        "onlyDependOnLibsWithTags": ["scope:shared"]
                    },
                    {
                        "sourceTag": "type:ui",
                        "onlyDependOnLibsWithTags": ["type:ui", "type:util"]
                    }
                ]
            }
        ]
    }
}

AND. tslint.json

{
    "rulesDirectory": [
        "node_modules/@nrwl/workspace/src/tslint",
        "node_modules/codelyzer"
    ],
    "linterOptions": {
        "exclude": [
            "**/*"
        ]
    },
    "rules": {
        "arrow-return-shorthand": true,
        "callable-types": true,
        "class-name": true,
        "deprecation": {
            "severity": "warn"
        },
        "forin": true,
        "import-blacklist": [
            true,
            "rxjs/Rx"
        ],
        "interface-over-type-literal": true,
        "member-access": false,
        "member-ordering": [
            true,
            {
                "order": [
                    "static-field",
                    "instance-field",
                    "static-method",
                    "instance-method"
                ]
            }
        ],
        "no-arg": true,
        "no-bitwise": true,
        "no-console": [
            true,
            "debug",
            "info",
            "time",
            "timeEnd",
            "trace"
        ],
        "no-construct": true,
        "no-debugger": true,
        "no-duplicate-super": true,
        "no-empty": false,
        "no-empty-interface": true,
        "no-eval": true,
        "no-inferrable-types": [
            true,
            "ignore-params"
        ],
        "no-misused-new": true,
        "no-non-null-assertion": true,
        "no-shadowed-variable": true,
        "no-string-literal": false,
        "no-string-throw": true,
        "no-switch-case-fall-through": true,
        "no-unnecessary-initializer": true,
        "no-unused-expression": true,
        "no-var-keyword": true,
        "object-literal-sort-keys": false,
        "prefer-const": true,
        "radix": true,
        "triple-equals": [
            true,
            "allow-null-check"
        ],
        "unified-signatures": true,
        "variable-name": false,
        "@nrwl/nx/enforce-module-boundaries": [
            "error",
            {
                "enforceBuildableLibDependency": true,
                "allow": [],
                "depConstraints": [
                    {
                        "sourceTag": "scope:app",
                        "onlyDependOnLibsWithTags": [
                            "scope:app",
                            "scope:shared"
                        ]
                    },
                    {
                        "sourceTag": "scope:shared",
                        "onlyDependOnLibsWithTags": [
                            "scope:shared"
                        ]
                    },
                    {
                        "sourceTag": "type:ui",
                        "onlyDependOnLibsWithTags": [
                            "type:ui",
                            "type:util"
                        ]
                    }
                ]
            }
        ],
        "directive-selector": [
            true,
            "attribute",
            "app",
            "camelCase"
        ],
        "component-selector": [
            true,
            "element",
            "app",
            "kebab-case"
        ],
        "no-conflicting-lifecycle": true,
        "no-host-metadata-property": true,
        "no-input-rename": true,
        "no-inputs-metadata-property": true,
        "no-output-native": true,
        "no-output-on-prefix": true,
        "no-output-rename": true,
        "no-outputs-metadata-property": true,
        "template-banana-in-box": true,
        "template-no-negated-async": true,
        "use-lifecycle-interface": true,
        "use-pipe-transform-interface": true
    }
}
fbaba-nibtravel commented 3 years ago

@nhhockeyplayer, if you do have tslint and eslint enabled on VSCode, you might be getting compilation issues from both. It's recommended to use only one of them.

So my point was, the import shouldn't be an ESLint issue. If you run ESLint on the solution, you should not get those errors.

As for the compilation issue, you are importing like this:

@import '~@workspace/scss/lib/styles-variables';

Assuming you used my trick above, this should work. You can manually go to node_modules folder and see if your styles are within the expected folder. You might need to run yarn or npm install to place the files in there. This import is basically saying: within node_modules folder, find @workspace/scss/lib/styles-variables.scss file.

duard commented 3 years ago

Hi @tcoz!

We usually like to recommend to create a library specifically to handle the styles.

You usually already have a UI library (containing only presentational components) that are shared between your apps and used by multiple feature libraries. You can create a library next to this one, handling only the styles. For example:

libs/common/ui/my-ui-library1
libs/common/ui/my-styles-library

Inside the library you can have all your relevant .scss files that will be used by other libraries / apps.

Then, concerning the import, you could do it in two ways, like you exposed.

  • The first one is the deep imports ../../etc/etc/variables.scss.
  • The second one is to use the stylePreprocessorOptions like you mentioned. With this option you will be able to write only import "variables"; which is neat. To achieve that you will need to setup the stylePreprocessorOptions for every apps that need those styles. You can do it by updating the angular.json file, here an example:
"projects": {
    "shop-client": {
      "root": "apps/my-app",
      "sourceRoot": "apps/my-app/src",
      "projectType": "application",
      "prefix": "app",
      "schematics": {},
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/apps/my-app",
            "deployUrl": "static/",
            "index": "apps/my-app/src/index.html",
            "main": "apps/my-app/src/main.ts",
            "polyfills": "apps/my-app/src/polyfills.ts",
            "tsConfig": "apps/my-app/tsconfig.app.json",
            "stylePreprocessorOptions": {
              "includePaths": [
                "libs/common/ui/my-styles-library/src/lib"
              ]
            },
            "extractCss": true
          },

it it doesn't work work when using custom webpack (tailwind)

dzena commented 3 years ago

Hi all, I was able to sorta make it work. But I haven't done much tests on it so I thought you guys may want to give it a go.

On package.json

  "dependencies": {
  ...
    "@yourWorkspace/styles": "file:libs/shared/ui/styles/src",
  ...
  },

This creates a sort of symlink of the style package. With that I was able to do @import "~@yourWorkspace/styles/components/autocomplete"; within the libs.

I'll repeat, need more tests around it.

Unfortunately I can't get it work. When I run npm install I get the error npm ERR! Could not install from "libs/utils/shared/styles/src" as it does not contain a package.json file. . Which makes sense, there is no package.json on the provided path. If I put the path to the lib root libs/utils/shared/styles then I get npm ERR! Invalid name: "@generate-cms/utils/shared/styles".

Any ideas?

Here is the screen shot sou you can see the folder structure:

Screenshot 2020-12-27 at 13 27 53
nhhockeyplayer commented 3 years ago

I appreciate the help

making it work will be if it works on production like github pages

Im trying now without success

somewhere something wants to insert a "./assets/icon.png"

instead of the angular standard "assets/icon.png" whereby environment.ts gets swapped to change the appRootPrefix to all assets

for production

Im seeing this during my build and Im not coding anything like this background: url("./assets/icon.png");

when my actual scss code is this background: url('assets/icon.png');

and I know this wont pass on a production host and this seems deeper than making libraries accessible in nx workspaces

Im securing my workspace.json right now

NonErrorEmittedError: (Emitted value instead of an instance of Error) CssSyntaxError: /Users/me/new/NX/energy/libs/feature/src/lib/splash/splash.component.scss:28:20: Can't resolve './assets/icon.png' in '/Users/me/new/NX/energy/libs/feature/src/lib/splash'

  26 |   padding: 0;
  27 |   margin: 0;
> 28 |   background: url("./assets/icon.png");

the problem is the app root prefix is being omitted for assets used inside Nx libraries

and it should be adhering to standard angular convention for dev and production

which requires some jimmying in workspace.json for output property on assets

Im not seeing an avenue as of yet

manually referencing a path somehow might work for dev

but I need production now

nhhockeyplayer commented 3 years ago

as of now... this is not possible

I created the npm library locally usinf 'file:/...' for the Nx assets library in my monorepo

    "dependencies": {
        "@energy/shared-assets": "file:libs/shared-assets/src",

that successfully compiles and builds with this reference in my source code *.scss background: url('~@energy/shared-assets/assets/vignette-light.png');

I built prod and deployed to github pages production host and as you can see my assets DIR continues to fail to be animated properly https://nhhockeyplayer.github.io/assets/vignette-light.png

Im sure a local dev build ill work somehow

but for production all bets are off

so what performs this animating or appending of the app root prefix ? the configuration within workspace.json

so it looks like I need to specify the assets glob and hardwire the output to do the append of the app root prefix myself

nhhockeyplayer commented 3 years ago

I would have liked this to be automated somehow, but we've been left with enough rope to hang ourselves...happy hanging

but it works now all of the above applies I noted less a few sporadic png files missed in the bundles

and the solution lies in the workspace.json where we hardwire the production app root prefix manually

                            {
                                "input": "libs/shared-assets/src/assets",
                                "glob": "**/*",
                                "output": "energy/assets/"
                            },

if this is intended please validate

tomwhite007 commented 3 years ago

I see further up this thread a couple of people have tried styleIncludePaths and it didn't work for them. But, it does for me. This is the official method for supporting distributed scss folders. Here's the help page. Here's how I got there:

  1. Nx Workspace v10.x, and later upgraded to v11.x
  2. ng generate @nrwl/angular:library schematic used to create both the styles lib and the consuming UI lib.
  3. The UI lib was scaffolded with extra options: --buildable --publishable
  4. I added styleIncludePaths to the ng-package.json file. Note the use of relative path (../) instead of absolute described by others above (this may be the reason for my success)
{
  "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
  "dest": "../../../dist/libs/shared/ui-xxxxxxxx",
  "lib": {
    "entryFile": "src/index.ts",
    "styleIncludePaths": ["../ui-styles/scss"]
  }
}

I've done this in multiple repos all with the same success, from Nx 10.x +

Hope that helps

aalencar commented 3 years ago

@tomwhite007 Thanks for sharing your progress!

I managed to build the library in isolation following the same steps but I can't build an app that depends on this library. Isn't it likely that when building the app the compiler can't resolve styles relative to the library?

Scenario:

nx build my-components works nx build my-app fails

tomwhite007 commented 3 years ago

Hello @aalencar

I've checked, and can confirm this should have no impact on your app build.

I have two publishable libs that consume a 'ui-styles' lib via styleIncludePaths, and both of those libs are directly imported (as well as being published for external projects) via their aliases into two apps within the same Nx Workspace. And both of those apps build successfully.

I strongly suspect your build issue is not related to styleIncludePaths if your implementation is the same as mine.

Additional note: styleIncludePaths will not help an app apply styles in a publishable lib if it is directly imported within the same Nx Workspace. To apply styles to your lib, you must still use something like:

"stylePreprocessorOptions": {
      "includePaths": ["libs/shared/ui-styles/scss"]
  },

in your angular.json file for your app project node. Then the styles will be applied to the lib in the build. To put it another way, styleIncludePaths is only used by the 'publish to npm' build, not the app build process.

nhhockeyplayer commented 3 years ago

I prefer deploying the lib to node_modules by specifying a package.json entry using the file protocol

all imports thereafter work like a breeze

nhhockeyplayer commented 3 years ago

But this is now a show stopper

I cannot use the file protocol anymore due to changes in the frameworks

therefore my build broken now. And I do not want a package.json living inside my library within my libs folder for any given library monorepo is suppose to only have one package.json at the root dir not beneath as it will ruin the build and confuse compilers

new build error on angular 12 nrwl 12

Could not install from "libs/shared-assets/src/lib" as it does not contain a package.json file.

does anyone know if a workaround?

the includePaths solution feels too detached

tomwhite007 commented 3 years ago

@nhhockeyplayer, you probably need to change your paradigm.

To apply styles from your lib to your app build, you must use something like:

"stylePreprocessorOptions":
 { "includePaths": ["libs/shared/ui-styles/scss"] }, 

...in your angular.json file for your app project node. Then the styles will be extracted from the folder to your app build.

No need for npm file protocol.

The styleIncludePaths in the ng-package.json config is only for published lib builds to include an external bit of sccs (and it is ignored by apps).

marcellkiss commented 3 years ago

Hi all, I was able to sorta make it work. But I haven't done much tests on it so I thought you guys may want to give it a go.

On package.json

  "dependencies": {
  ...
    "@yourWorkspace/styles": "file:libs/shared/ui/styles/src",
  ...
  },

This creates a sort of symlink of the style package. With that I was able to do @import "~@yourWorkspace/styles/components/autocomplete"; within the libs.

I'll repeat, need more tests around it.

Works like a charm, thanks! Note: npm i is required to create the symlink in your node_modules folder

ibrahimAboelsuod commented 2 years ago

For those asking about how to do this with react apps, in next.config.js add:

sassOptions: {
    includePaths: [path.join(__dirname, 'path_to_scss_folder')]
}

and then you just import directly like: @import 'variables';

mmanista-bynd commented 2 years ago

I see further up this thread a couple of people have tried styleIncludePaths and it didn't work for them. But, it does for me. This is the official method for supporting distributed scss folders. Here's the help page. Here's how I got there:

  1. Nx Workspace v10.x, and later upgraded to v11.x
  2. ng generate @nrwl/angular:library schematic used to create both the styles lib and the consuming UI lib.
  3. The UI lib was scaffolded with extra options: --buildable --publishable
  4. I added styleIncludePaths to the ng-package.json file. Note the use of relative path (../) instead of absolute described by others above (this may be the reason for my success)
{
  "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
  "dest": "../../../dist/libs/shared/ui-xxxxxxxx",
  "lib": {
    "entryFile": "src/index.ts",
    "styleIncludePaths": ["../ui-styles/scss"]
  }
}

I've done this in multiple repos all with the same success, from Nx 10.x +

Hope that helps

Using Angular 14 setup with nx 15.

The solution for my issue (being "building a buildable nx library with a path alias scss import") was adding the styleIncludePaths line to ng-package.json of a library that the build is failing for (where ../styles is the path to the library with style definitions, where alias "begins":


  "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
  "dest": "../../../dist/libs/shared/ui",
  "lib": {
    "entryFile": "src/index.ts",
    "styleIncludePaths": ["../styles"]
  }
}```
kekel87 commented 1 year ago

Hello, we are looking at how to migrate several of our repo's to NX.

In this sense, we have a style library (SCSS) shared between two Angular applications and one React. It contains direct SCSS code (variable, mixing, ...), compiled CSS and assets.

I'm quite surprised that there are no official plugins to do this, because this case seems to me common.

The idea is to be able to :

If it wasn't planned, would someone have some ideas to bring me to achieve this kind of result?

Thanks.

josipfrljic commented 1 year ago

Repo posted (link at bottom), steps:

Angular CLI 8.0.4 node v10.15.3

  • ng new preprocessor

(cd into preprocessor, all commands now run from workspace root unless otherwise indicated)

  • ng add @nrwl/workspace

Even though it is an angular project that I added the Nrwl extensions to (as opposed to creating a straight nx workspace with no project type), and the @nrwl/angular package is installed, the "ng" commands won't run without appending "@nrwl/angular:" unless you do this:

  • ng add @nrwl/angular

(Select Jest and Cypress from prompts, Cypress download/install takes a bit, it is not already installed)

Now the default schematics/collection is properly configured and the ng commands will run as expected. I filed an issue on this a while back, but would be surprised if it's related, figured I'd point it out though.

Moving on:

  • ng g app testapp --directory=subdir

Select SASS, no routing. NOTE: a warning appears the @nrwl/workspace package is now installed as both a dependency and dev dependency.

  • ng serve subdir-testapp

App serves on 4200 as expected. I CD into apps/subdir/testapp/src, and create a module and component:

  • ng g module myfeature
  • ng g component myfeature

I import the feature module into the app.module of testapp, remove the default kruft from app.component template, and add the selector for myfeature. I serve it (ng serve subdir-testapp), all is well.

NOTE: (There's another issue file on this), even though testapp was created with SASS selected, the feature module "myfeature" component is created with a .css file, not an .scss one. I rename the file to "myfeature.component.scss" and change myfeature.component.ts to point at the scss file (instead of the configured css one). In angular.json, the project configuration does indicate style is scss, and the correct "styles" .scss file is indicated:

"subdir-testapp": {
      "projectType": "application",
      "schematics": {
        "@nrwl/workspace:component": {
          "style": "scss"
        }
      },

....

"styles": ["apps/subdir/testapp/src/styles.scss"],

CD back to the workspace root, create the library:

  • ng g lib sasslib

Selected nothing for directory (puts sasslib in lib directory), and SASS from style selections. All is well.

I put a file "variables.scss", which contains a bunch of variables but no other imports, into the lib directory of that library.

Configure the preprocessor options for subdir-testapp in angular.json:

         "options": {
            "outputPath": "dist/apps/subdir/testapp",
            "index": "apps/subdir/testapp/src/index.html",
            "main": "apps/subdir/testapp/src/main.ts",
            "polyfills": "apps/subdir/testapp/src/polyfills.ts",
            "tsConfig": "apps/subdir/testapp/tsconfig.app.json",
            "aot": false,
            "assets": [
              "apps/subdir/testapp/src/favicon.ico",
              "apps/subdir/testapp/src/assets"
            ],
            "styles": ["apps/subdir/testapp/src/styles.scss"],
            "scripts": [],
            "stylePreprocessorOptions": {
              "includePaths": [
                "libs/sasslib/src/lib"
              ]
            },
            "extractCss": true
          },

Do an "ng serve subdir-testapp" everything is fine, no console errors etc.

Go to myfeature.component.scss, add this line:

@import "~variables"

The import does not resolve (WebStorm), and the app does not compile:

ERROR in ./src/myfeature/myfeature.component.scss
Module build failed (from /Users/Consolazio/Documents/GitHub/preprocessor/node_modules/sass-loader/lib/loader.js):

@import "~variables";
       ^
      Can't find stylesheet to import.
  ╷
1 │ @import "~variables";
  │         ^^^^^^^^^^^^
  ╵
  stdin 1:9  root stylesheet
      in /Users/Consolazio/Documents/GitHub/preprocessor/apps/subdir/testapp/src/myfeature/myfeature.component.scss (line 1, column 9)

It also doesn't work in testapp/src/styles.scss.

As far as I can tell, based on your direction, this should work very straightforwardly. I suspect I'm missing something obvious but at this point (I've been told more than once, "there's nothing to configure or import it should just work") I'm at a loss. I'm tinkering with it now, will be watching the thread carefully (I really need to get this working for a large project).

REPRO REPO (public):

https://github.com/tcoz/preprocessor

I you use webpack, you can add alias for variables and use import like that