仓库源文站点原文


layout: "../layouts/BlogPost.astro" title: "Node-Sass troubleshooting" slug: node-sass-troubleshooting description: "" added: "Nov 23 2022" tags: [web]

updatedDate: "Feb 13 2023"

Node-Sass installation

Node-sass is a library that allows binding for Node.js to LibSass, the C version of Sass's stylesheet preprocessor. It compiles .scss files to CSS with speed and automatically through connected middleware.

LibSass is written in C++, it’s easy to embed LibSass within other programming languages and provide a native-feeling API. Calling out to LibSass via the C++ API is very fast, which means LibSass is substantially faster in JavaScript than Dart Sass-compiled-to-JS.

node-sass 的安装过程大致是先下载 node-sass 包,安装时根据 node 版本和 node-sass 版本拉取对应的 binding.node 编译器(sass 的编译比较特殊,需要下载对应版本的编译器才能编译)。如果能拉下 binding.node 就算安装成功,如果找不到对应的 binding.node 就算失败了,然后就会尝试本地 build,需要 python 环境。

node-sass 的 install 和 postinstall 会分别执行 scripts/install.jsscripts/build.js,在脚本里面可以找到安装过程会失败的原因:

node-gyp ("gyp" is short for "generate your projects") is a tool which compiles Node.js Addons. Node.js Addons are native Node.js Modules, written in C or C++, which therefore need to be compiled on your machine. After they are compiled, their functionality can be accessed via require(), just as any other Node.js Module. node-gyp expects Python ≥v3.6, not Python v2.x.

The filename extension of the compiled addon binary is .node. The require() function is written to look for files with the .node file extension and initialize those as dynamically-linked libraries. You should also be aware that these are binary modules, so loading them is a lot like just running a standard executable file (Think .exe file if you are familiar with Windows). Like native executables they are a lot more dependent on the particulars of your system and also potentially a security risk.

Migrate from Node-Sass to Sass

Note that LibSass is Deprecated. It’s time to officially declare that LibSass and the packages built on top of it, including Node Sass, are deprecated. We no longer recommend LibSass for new Sass projects. Use Dart Sass instead. If you’re a user of Node Sass, migrating to Dart Sass is straightforward: just replace node-sass in your package.json file with sass. Both packages expose the same JavaScript API.

Run Node with the --trace-warnings flag. Check the stacktrace for hints of packages you're using. For example, NODE_OPTIONS="--trace-warnings" npm run build. Once you identify a package, check if the error has been fixed upstream, and after updating, you may no longer see the error or warnings.

Webpack and sass-loader

Sass is a popular choice for styling websites and it has two syntaxes. The older syntax is known as SASS (with .sass extention). Instead of brackets and semicolons, it uses the indentation of lines to specify blocks. The most commonly used is SCSS (with .scss extention). SCSS is a superset of CSS syntax, so every valid CSS is a valid SCSS as well.

sass-loader is a loader for Webpack for compiling SCSS/Sass files. It requires you to install either Dart Sass or Node Sass, which allows you to choose which Sass implementation to use. By default the loader resolve the implementation based on your dependencies. Just add required implementation to package.json (sass or node-sass package) and install dependencies. From sass-loader 9.x, it firstly uses sass and you don't need any configuration.

// sass-loader source code
function getDefaultSassImplementation() {
  let sassImplPkg = "sass";

  try {
    require.resolve("sass");
  } catch (error) {
    try {
      require.resolve("node-sass");

      sassImplPkg = "node-sass";
    } catch (ignoreError) {
      sassImplPkg = "sass";
    }
  }

  return require(sassImplPkg);
}

require.resolve uses the internal require() machinery to look up the location of a module, but rather than loading the module, just returns the resolved filename.

Beware the situation when node-sass and sass were installed. In order to avoid this situation you can use the implementation option. It either accepts sass (Dart Sass) or node-sass as a module.

// In sass-loader source code, 
// `options.implementation` from webpack config has the higher priority
// than the above function `getDefaultSassImplementation()`
{
  test: /\.s[ac]ss$/i,
  use: [
    "style-loader",
    "css-loader",
    {
      loader: "sass-loader",
      options: {
        // Prefer `dart-sass`
        implementation: require("sass"),
      },
    },
  ],
}