Use the Dart Sass JavaScript Implementation to Compile SASS with Node.js

This post is an updated version of a previous post containing instructions on how to compile sass with the node-sass npm package, which is now deprecated.

The SASS team now recommends using Dart Sass in favor of LibSass for new development projects. This means that the sass npm package should be used instead of the node-sass npm package, which is built on top of LibSass, to compile sass with Node.js. The sass npm package is a pure JavaScript implementation of Dart Sass. The Dart Sass JavaScript API strives to be compatible with the existing node-sass API, so that it can be integrated into existing workflows with minimal changes. This post will show how to install the Dart Sass Javascript implementation with npm and use it via the supported JavaScript API and the command line. Before proceeding make sure to have Node.js and npm installed.

npm install sass

We will also be using ES Module format for this example so the package.json requires an additional setting after generating. Add the “type” property to the package.json with the value set to “module”, so that Node.js will use ES Modules rather than CommonJS modules. Here is some additional information about how to import and export ES Modules in Node.js, which explains why this setting is necessary.

Your package.json file should now look like this:

{
"name": "npmsass",
"type": "module",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"sass": "^1.32.8"
}
}

SCSS

/* This CSS will print because %message-shared is extended. */
%message-shared {
border: 1px solid #ccc;
padding: 10px;
color: #333;
}
// This CSS won't print because %equal-heights is never extended.
%equal-heights {
display: flex;
flex-wrap: wrap;
}
.message {
@extend %message-shared;
}
.success {
@extend %message-shared;
border-color: green;
}
.error {
@extend %message-shared;
border-color: red;
}
.warning {
@extend %message-shared;
border-color: yellow;
}

The above SCSS code is borrowed from the Sass Basics guide and demonstrates one of the most useful features of Sass which is the @extend at-rule, to share a set of CSS properties among different selectors. Now that we have a SCSS file we can compile it to CSS using the sass npm package.

Compile Sass with Dart Sass JavaScript API

import sass from "sass";
import { promisify } from "util";
const sassRenderPromise = promisify(sass.render);
async function main() {
const styleResult = await sassRenderPromise({
file: `${process.cwd()}/styles.scss`,
});
console.log(styleResult.css.toString());
}
main();

This code imports the sass package along with the util.promisify module and converts the sass render function to use promises instead of the default callback implementation. This makes working with the asynchronous API of the sass npm package more manageable because it allows for the use of async/await syntax.

After importing and “promisifying” the sass npm package, the main function contains the code to compile the styles.scss file into CSS. In order to run this code add the following the scripts property in the package.json file:

{
"start": "node index.js"
}

We can then execute the main function by running the command npm run start, and the css output will be logged to the console.

Instead of logging directly to the console it is much more useful to write the CSS output to a file. The sass npm package does not expose a JavaScript API to write a file directly, however it does support a configuration property to indicate which file the CSS output will be written to. By adding this configuration and using the fs.writeFile module the CSS can be written to an output to a file like this:

import sass from "sass";
import { promisify } from "util";
import { writeFile } from "fs";
const sassRenderPromise = promisify(sass.render);
const writeFilePromise = promisify(writeFile);
async function main() {
const styleResult = await sassRenderPromise({
file: `${process.cwd()}/styles.scss`,
outFile: `${process.cwd()}/styles.css`,
});
console.log(styleResult.css.toString()); await writeFilePromise("styles.css", styleResult.css, "utf8");
}
main();

After running the npm run start command again, you should now see a styles.css file in the same project folder, that contains the compiled CSS output:

/* This CSS will print because %message-shared is extended. */
.warning,
.error,
.success,
.message {
border: 1px solid #ccc;
padding: 10px;
color: #333;
}
.success {
border-color: green;
}
.error {
border-color: red;
}
.warning {
border-color: yellow;
}

SASS Render Configuration Options

  • sourceMap
  • sourceMapContents
  • outputStyle

These can be added by modifying the options object passed into the sass render function. When including a source map file, a separate file needs to be written to the project folder containing the sourcemap information. To add these options make the following changes to the index.js:

import sass from "sass";
import { promisify } from "util";
import { writeFile } from "fs";
const sassRenderPromise = promisify(sass.render);
const writeFilePromise = promisify(writeFile);
async function main() {
const styleResult = await sassRenderPromise({
file: `${process.cwd()}/styles.scss`,
outFile: `${process.cwd()}/styles.css`,
sourceMap: true,
sourceMapContents: true,
outputStyle: "compressed",
});
console.log(styleResult.css.toString()); await writeFilePromise("styles.css", styleResult.css, "utf8"); await writeFilePromise("styles.css.map", styleResult.map, "utf8");
}
main();

Then run the npm run start command again and you should see the “styles.css” and “styles.css.map” files have both been updated.

The styles.css should now output with the blank spaces removed, and it will include a comment at the bottom to indicate the corresponding sourcemap file, which will look like this:

{
"version": 3,
"sourceRoot": "",
"sources": ["styles.scss"],
"names": [],
"mappings": "AACA,kCACE,sBACA,aACA,WAaF,SAEE,mBAGF,OAEE,iBAGF,SAEE",
"file": "styles.css",
"sourcesContent": [
"/* This CSS will print because %message-shared is extended. */\r\n%message-shared {\r\n border: 1px solid #ccc;\r\n padding: 10px;\r\n color: #333;\r\n}\r\n\r\n// This CSS won't print because %equal-heights is never extended.\r\n%equal-heights {\r\n display: flex;\r\n flex-wrap: wrap;\r\n}\r\n\r\n.message {\r\n @extend %message-shared;\r\n}\r\n\r\n.success {\r\n @extend %message-shared;\r\n border-color: green;\r\n}\r\n\r\n.error {\r\n @extend %message-shared;\r\n border-color: red;\r\n}\r\n\r\n.warning {\r\n @extend %message-shared;\r\n border-color: yellow;\r\n}\r\n"
]
}

The sourcemap will allow for easier debugging and the browser will now load both files. In the debug inspector the browser will show the line in the SCSS source code that corresponds to the CSS output being inspected.

Compile SASS using Dart Sass CLI

{
"scripts": {
"compileSass": "sass styles.scss styles.css --style=compressed --embed-sources"
}
}

This will add a package.json script to run the SASS compiler, by running the command npm run compileSass. To make sure it is working as expected you might want to delete the previously generated styles.css and styles.css.map files, before running the npm run compileSass command.

Using the sass npm package JavaScript API or command line interface, should result in the same output consisting of both the css and css.map files, as both methods rely on the JavaScript implementation of Dart Sass. The main difference is that when using the CLI option the files will automatically be written based on the input and output specified, but when using the JavaScript API we must use the fs.writeFile module to write these files to the project folder.

Edit this post on GitHub.

Originally published at https://www.devextent.com.

Freelance Web Developer | www.jamesedwards.net | www.devextent.com

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store