blueprint icon indicating copy to clipboard operation
blueprint copied to clipboard

Unable to compile BluePrint 4 SCSS with webpack sass-loader

Open ahaganDEV opened this issue 2 years ago • 23 comments

Environment

  • Package version(s): @blueprintjs/core: 4.17.8, sass: 1.60.0, sass-loader: 13.2.2, webpack: 5.73.0, node 16.16.0
  • Operating System: Ubuntu 22.04
  • Browser name and version: Chrome: 111.0.5563.110

Steps to reproduce

  1. Install @blueprintjs/core using npm: npm install @blueprintjs/[email protected]
  2. Create an index.scss file that imports the blueprint scss file:
@import '@blueprintjs/core/src/blueprint';
  1. Add webpack configuration using sass-loader e.g.
module: {
      rules: [
        {
          test: /\.(css|scss|sass)/,
          use: [
            {
              loader: 'sass-loader',
            },
          ],
        }
},

Actual behavior

ERROR in ./style/index.scss (./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js!./style/index.scss)
Module build failed (from ./node_modules/sass-loader/dist/cjs.js):
SassError: (path: (fill: hsl(213.75, 10.8108108108%, 50%))) isn't a valid CSS value.
   ╷
39 │       background: svg-icon("16px/chevron-right.svg", (path: (fill: $pt-icon-color)));
   │                                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ value
   │                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ unknown function treated as plain CSS
   ╵
  node_modules/@blueprintjs/core/src/components/breadcrumbs/_breadcrumbs.scss 39:54  @import
  node_modules/@blueprintjs/core/src/components/_index.scss 5:9                      @import
  node_modules/@blueprintjs/core/src/blueprint.scss 18:9                             @import
  style/index.scss 5:9                                                               root stylesheet
SassError: SassError: (path: (fill: hsl(213.75, 10.8108108108%, 50%))) isn't a valid CSS value.
   ╷
39 │       background: svg-icon("16px/chevron-right.svg", (path: (fill: $pt-icon-color)));
   │                                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ value
   │                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ unknown function treated as plain CSS
   ╵
  node_modules/@blueprintjs/core/src/components/breadcrumbs/_breadcrumbs.scss 39:54  @import
  node_modules/@blueprintjs/core/src/components/_index.scss 5:9                      @import
  node_modules/@blueprintjs/core/src/blueprint.scss 18:9                             @import
  style/index.scss 5:9                                                               root stylesheet
    at Object.loader (/home/adam/Documents/source/frontend/node_modules/sass-loader/dist/index.js:56:14)
 @ ./style/index.scss 8:6-183 22:17-24 26:7-21 58:25-39 59:36-47 59:50-64 63:6-73:7 64:54-65 64:68-82 70:42-53 70:56-70 72:21-28 83:0-153 83:0-153 84:22-29 84:33-47 84:50-64 61:4-74:5
 @ ./src/index.ts 4:0-29

Expected behavior

I would expect all of the blueprint internal SCSS files (e.g. the breadcrumb component scss file) to compile out of the box.

I have looked at all the similar issues raised but I am unable to follow them or to get this SCSS compiling. Are there any other steps that need to be done after installing this version of Blueprint? Previously working on Blueprint v3 had no compilation issues.

ahaganDEV avatar Apr 03 '23 12:04 ahaganDEV

I tried to follow the webpack suggestion as well https://github.com/palantir/blueprint/issues/5334#issuecomment-1444223876 however this doesn't work and I would preferably not want to store a local copy of blueprint's icons.

ahaganDEV avatar Apr 03 '23 12:04 ahaganDEV

I tried to follow the webpack suggestion as well https://github.com/palantir/blueprint/issues/5334#issuecomment-1444223876 however this doesn't work

Sorry for the trouble here, could you share some of the issues you encountered with that approach? I'm open to making bugfixes which make it possible to continue compiling Blueprint's Sass code when it's consumed as an NPM package.

I don't have a great alternative solution right now that doesn't involve advanced Blueprint consumers storing a copy of the icons SVG folder. We inline icons in a few places across the code base and there's a bit of work to untangle that which I don't have the bandwidth to do right now. So the inlining has to stay, at least for now.

Note that the svg-icon() function is only used for a few specific icons, so you really only need to ensure these files exist (I don't have plans to add any more usage of svg-icon() to the code base):

  • 16px/chevron-right.svg
  • 16px/more.svg
  • 16px/chevron-right.svg
  • 16px/more.svg
  • 16px/small-tick.svg
  • 16px/small-minus.svg

adidahiya avatar Apr 03 '23 14:04 adidahiya

Thanks @adidahiya I will try storing those icons locally and see if that works.

  1. I was originally using React 16 and upgraded to React 18 via NPM.
  2. I also then upgraded blueprint from v3 to v4 with NPM and upgraded to use the latest version of the core package: @blueprintjs/core 4.17.8
  3. I remembered that there was a migration from node-sass to the dart-based sass. I upgraded the webpack sass-loader package to the latest version, uninstalled node-sass and then installed sass.

Once this was done, I ran npm start to run the dev server version of webpack where I then get the error above. If I run npm run build to create a production build, I also get the same error.

ahaganDEV avatar Apr 03 '23 14:04 ahaganDEV

@ahaganDEV so it sounds like you did not try using the new sassSvgInlinerFactory as suggested in https://github.com/palantir/blueprint/issues/5334#issuecomment-1444223876 and documented here in the docs, can you please try that? It requires adding a dependency on the @blueprintjs/node-build-scripts NPM package and using some of the utilities available there.

adidahiya avatar Apr 03 '23 14:04 adidahiya

@adidahiya I didn't see that in the docs! I'll give that a go. I am currently using node 16 am I right that to use the node-build-scripts library I need to use node 18?

ahaganDEV avatar Apr 04 '23 08:04 ahaganDEV

am I right that to use the node-build-scripts library I need to use node 18?

@ahaganDEV Yes, this is enforced by the "engines" constraint defined for the @blueprintjs/node-build-scripts package.

adidahiya avatar Apr 04 '23 12:04 adidahiya

@adidahiya I am trying to import the node-build scripts into my webpack.config.js file like in the example. However, I get the error:

[webpack-cli] Failed to load '/my-project/webpack.config.js' config [webpack-cli] Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main defined in /my-project/node_modules/@blueprintjs/node-build-scripts/package.json

My project is using node 18.13.0, the compilerOptions in tsconfig.json are set totarget: es6, module: commonjs and we use babel.

I tried importing using require('@blueprintjs/node-build-scripts/import') as the package.json for @blueprintjs/node-build-scripts' has that in its exports section but that doesn't work either.

Do you know how I can get this importing correctly in the project. Thanks.

Here are some snippets of the webpack config:

webpack.config.js

// node build scripts import
const { sassSvgInlinerFactory, sassNodeModulesLoadPaths} = require('@blueprintjs/node-build-scripts');
const sass = require('sass');

// sass function
const sassFunctions = {
  /**
   * Sass function to inline a UI icon svg and change its path color.
   *
   * Usage:
   * svg-icon("16px/icon-name.svg", (path: (fill: $color)) )
   */
  'svg-icon($path, $selectors: null)': sassSvgInlinerFactory('style/blueprint/local-icons', {
    optimize: true,
    encodingFormat: 'uri',
  }),
};

// sass-loader config

{
   loader: 'sass-loader',
   options: {
      sassOptions: {
         loadPaths: sassNodeModulesLoadPaths,
         functions: sassFunctions,
     }, 
   },
},

ahaganDEV avatar Apr 04 '23 15:04 ahaganDEV

@adidahiya I got the above to start running by turning my webpack config into es6 format.

However, I get an error when running webpack with the sassSvgInliner is this something that has been seen before or could something be configured wrong?

@blueprintjs/node-build-scripts/src/sass/sassSvgInliner.mjs:42
        const resolvedPath = resolve(base, path.text);
                                                ^
TypeError: Cannot read properties of undefined (reading 'text')
    at Object.<anonymous> (file:///home/source/node_modules/@blueprintjs/node-build-scripts/src/sass/sassSvgInliner.mjs:42:49)

When I add console logs to sassSvgInliner function, I get this output for the args paramter: sass.types.String { dartValue: "16px/chevron-right.svg" }

The sass in _breadcrumbs.scss is: background: svg-icon("16px/chevron-right.svg", (path: (fill: $pt-icon-color)));

and later on in the file background: svg-icon("16px/chevron-right.svg", (path: (fill: $pt-dark-icon-color)));

ahaganDEV avatar Apr 06 '23 14:04 ahaganDEV

Hello @ahaganDEV I am having the exact same problem that you described, have you found a solution to this yet?@adidahiya Do you have any idea how to resolve this?

DaivsP avatar Apr 21 '23 18:04 DaivsP

Hi @adidahiya, I am also facing same issue. Can you please help us.

prachibansal01 avatar Apr 25 '23 07:04 prachibansal01

Just wanted to add my +1 to the issue @ahaganDEV brought up above. I feel like I've got everything set up right, and yet, this custom function doesn't compile.

My feeling is like, I don't know what the heck is going on, but I'm pretty sure the fault is with sass-loader. I made an issue there asking for help.

https://github.com/webpack-contrib/sass-loader/issues/1137

eastside avatar May 06 '23 04:05 eastside

There is an answer https://github.com/webpack-contrib/sass-loader/issues/1137#issuecomment-1537240206.

Shortly - sass-loader (and other sass loader/transformers for other bundlers, like vite/rollup plugins/esbuild plugins/etc) are on the old API, i.e. use render/renderSync, instead compileString/compileStringAsync due to lack abilities to implement resolving (due to lack ability to get current file where we found @import/@use), that is why it is not working, sass team recentry merge RFC to support it, so I hope we will fix it soon, maybe blueprint should provide different versions of functions for the old and the new API

feel free to feedback

alexander-akait avatar May 06 '23 23:05 alexander-akait

@alexander-akait thanks for the clarification, I didn't realize that sass-loader only supports the legacy Sass render APIs.

maybe blueprint should provide different versions of functions for the old and the new API

There is an SVG inliner function available for the legacy Sass API. Blueprint was using a third-party library for this before we migrated to the newer API. It's called @vgrid/sass-inline-svg, but it's not compatible with Node 18+. You can see how it was used here: https://github.com/palantir/blueprint/blob/f88e8184940d1de1ae4251a1d0c082bd08c7e328/packages/core/scripts/sass-custom-functions.js. Maybe this same setup could still be used with Blueprint v4 and sass-loader? You would need to downgrade to Node 16.x, though.

adidahiya avatar May 08 '23 13:05 adidahiya

Thanks all!

For anyone else who needs a workaround literally asap, I made a quick gist here: https://gist.github.com/eastside/adcdf0d189c7470be241a851c5add350

Instead of the normal factory, you'd use legacySassSvgInlinerFactory. It works almost the same as the original factory, (except optimize doesn't work), at least through the current version of Blueprint.js.

You'd just make a new file, maybe call it sassSvgInliner.mjs, and then of course import it:

import { legacySassSvgInlinerFactory } from './path-to/sassSvgInliner.mjs';

...<snip>...

          {
            loader: "sass-loader",
            options: {
              implementation: "sass",
              sassOptions: {
                "functions": {
                  // /**
                  //  * Sass function to inline a UI icon svg and change its path color.
                  //  *
                  //  * Usage:
                  //  * svg-icon("16px/icon-name.svg", (path: (fill: $color)) )
                  //  */
                  "svg-icon($path, $selectors: null)": legacySassSvgInlinerFactory("blueprint-icons/icons"),
                }
              }
            }
          }

eastside avatar May 08 '23 22:05 eastside

Hi

We tried your approach, but started getting new error.

image

Please help.

prachibansal01 avatar May 09 '23 05:05 prachibansal01

@eastside Thank you for the gist, I managed to make it work with Vite 4. There were 2 errors in your code which required adjustments:

  1. SVG wasn't properly encoded to base64. Creating new buffer was required instead of encoding string directly
  2. Closing " was missing at the end of url("...) in base64 case
function encode(content, opts) {
    let buff = new Buffer(content)

    if (opts.encodingFormat === "uri") {
        return new sass.SassString(
            `url("${svgToDataUri(buff.toString("UTF-8"))}")`,
            {quotes: false}
        )
    }

    if (opts.encodingFormat === "base64") {
        return new sass.SassString(
            `url("data:image/svg+xml;base64,${buff.toString("base64")}")`,
            {quotes: false}
        )
    }

    throw new Error(
        `[node-build-scripts] encodingFormat ${opts.encodingFormat} is not supported`
    )
}

Additionally, it was required to install postcss-scss and create postcss.config.js config file to support scss imports:

module.exports = {
    syntax: 'postcss-scss',
};

That's all I had to adjust

FYI @prachibansal01

bioc-druzgami avatar Jul 04 '23 07:07 bioc-druzgami

Hi, wondering if there is any chance of an update on resolving this issue within the library? There are a number of things that make the source issue a pain, including:

  1. The svg-loader issue for Dart-SASS
  2. For Node-SASS (workaround), many other SCSS functions in the BlueprintJS library aren't supported.

Given it is a few functions, wondering if it would be possible to take another look?

Thanks

pbower avatar Nov 09 '23 11:11 pbower

@pbower yes I'm interested in taking another look at the tooling issues here but a lot of things have changed since the original post so I think we should start a new thread and only concern ourselves with Blueprint v5.x, Node v18+, and dart-sass. Can you please file a new issue and describe your environment and the problems you're facing in more detail?

adidahiya avatar Nov 09 '23 23:11 adidahiya

Hi Adi,

Sure thing, I will do this later today and provide further details.

In summary - the underlying need mostly relates to overriding the Blueprint CSS _files with non-default values, to implement custom styles (particularly for the light blue colour, which is quite light against a white background), with more of a navy-like colour, as well as application-specific fonts.

This then results in the need to compile the SCSS files when building the application, which causes issues in the build process as describes.

Will comment with the issue number here once logged.

Thanks for your help with this!!

Regards,

Peter

Get Outlook for iOShttps://aka.ms/o0ukef


From: Adi Dahiya @.> Sent: Friday, November 10, 2023 10:33:26 AM To: palantir/blueprint @.> Cc: Peter @.>; Mention @.> Subject: Re: [palantir/blueprint] Unable to compile BluePrint 4 SCSS with webpack sass-loader (Issue #6051)

@pbowerhttps://github.com/pbower yes I'm interested in taking another look at the tooling issues here but a lot of things have changed since the original post so I think we should start a new thread and only concern ourselves with Blueprint v5.x, Node v18+, and dart-sass. Can you please file a new issue and describe your environment and the problems you're facing in more detail?

— Reply to this email directly, view it on GitHubhttps://github.com/palantir/blueprint/issues/6051#issuecomment-1804843279, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AI27BYRORI5MXPGAA35RI53YDVR4NAVCNFSM6AAAAAAWRG5LUCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQMBUHA2DGMRXHE. You are receiving this because you were mentioned.Message ID: @.***>

pbower avatar Nov 10 '23 01:11 pbower

@pbower Did you ever create that new issue? I'm encountering the same thing and can't seem to find it if you have.

JoeDuncko avatar Mar 05 '24 19:03 JoeDuncko

Looks like sass-loader fixed the underlying issue (https://github.com/webpack-contrib/sass-loader/issues/774) but SassSvgInlinerFactory still fails with exactly the same problem - anyone have any insight?

vwillyams avatar Jun 28 '24 17:06 vwillyams

Looks like sass-loader fixed the underlying issue (webpack-contrib/sass-loader#774) but SassSvgInlinerFactory still fails with exactly the same problem - anyone have any insight?

No insights, except that it's makes it very difficult to use Blueprint 5, given that it breaks the project from compiling, including Storybook, without workarounds that rely on aging legacy plugins.

Failed to compile. SassError: (path: #5f6b7c)) isn't a valid CSS value. node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[5].oneOf[7].use[1]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[5].oneOf[7].use[2]!./node_modules/resolve-url-loader/index.js??ruleSet[1].rules[5].oneOf[7].use[3]!./node_modules/react-scripts/node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[5].oneOf[7].use[4]!./src/styles/initialise-theme-palette.scss undefined ╷ 39 │ background: svg-icon("16px/chevron-right.svg", (path: (fill: $pt-icon-color))); │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ value │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ unknown function treated as plain CSS ╵ node_modules/@blueprintjs/core/src/components/breadcrumbs/_breadcrumbs.scss 39:54 @import node_modules/@blueprintjs/core/src/components/_index.scss 5:9 @import node_modules/@blueprintjs/core/src/blueprint.scss 18:9 @import src/styles/scss-overrides/blueprint-js/custom-overrides.scss 8:9 @import src/styles/initialise-theme-palette.scss 1:9 root stylesheet

Which is a shame as it's such a great set of components.

The below fix got me through the last 6 months by craco-ing around webpack, but now that dependencies for various project are forced to update, it and storybook has broken:

/* eslint-disable */

const CracoEsbuildPlugin = require("craco-esbuild"); const inliner = require("@vgrid/sass-inline-svg");

module.exports = { plugins: [{ plugin: CracoEsbuildPlugin }], style: { sass: { loaderOptions: { // functions from blueprint : https://github.com/palantir/blueprint/blob/develop/packages/core/scripts/sass-custom-functions.js sassOptions: { functions: { "svg-icon($path, $selectors: null)": inliner("./resources/icons", { optimize: true, encodingFormat: "uri", }), }, }, }, }, }, babel: { plugins: ["@emotion","@emotion/babel-plugin"], }, };

It relied on this plugin which now appears to be dead and incompatible- [email protected] │ repo: https://github.com/artisanofcode/storybook-preset-craco

If there's any way you guys can fix this stuff will be much appreciated.

pbower avatar Jul 03 '24 13:07 pbower

I wonder if it couldn't be better to simply deprecate svg-icon function because it's only there for solely 2-3 components in the main base. :)

bppdddqqqq avatar Jul 17 '24 08:07 bppdddqqqq