Skip to content

Commit

Permalink
Migrate to use the new Sass JS API by default (#846)
Browse files Browse the repository at this point in the history
* Migrate to use the new Sass JS API by default

Keep the legacy API around, since its removal is still probably a
while from now.

Fixes #837

* Update README for the next major version

Add a migration section, link to relevant Sass documentation, and
add a section about still using the legacy API.

* Update tests to support the new API, sass-embedded

* Prepare changelog entry for the next major version

* Update CHANGELOG.md

---------

Co-authored-by: Michael Mifsud <[email protected]>
  • Loading branch information
wkillerud and xzyfer authored Nov 27, 2024
1 parent c04bb67 commit 510c1c8
Show file tree
Hide file tree
Showing 7 changed files with 829 additions and 75 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# gulp-sass Changelog

## v6.0.0

**November 27, 2024**

<https://github.com/dlmanning/gulp-sass/releases/tag/v6.0.0>

## v5.0.0

**June 25, 2021**
Expand Down
64 changes: 54 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ Sass plugin for [Gulp](https://github.com/gulpjs/gulp).

**_Before filing an issue, please make sure you have [updated to the latest version of `gulp-sass`](https://github.com/dlmanning/gulp-sass/wiki/Update-to-the-latest-Gulp-Sass) and have gone through our [Common Issues and Their Fixes](https://github.com/dlmanning/gulp-sass/wiki/Common-Issues-and-Their-Fixes) section._**

**Migrating your existing project to version 5? Please read our (short!) [migration guide](#migrating-to-version-5).**
**Migrating your existing project to version 5 or 6? Please read our (short!) [migration guides](#migrating-to-version-6).**

## Support

Only [Active LTS and Current releases](https://github.com/nodejs/Release#release-schedule) are supported.

## Installation

To use `gulp-sass`, you must install both `gulp-sass` itself *and* a Sass compiler. `gulp-sass` supports both [Dart Sass][] and [Node Sass][], although Node Sass is [deprecated](https://sass-lang.com/blog/libsass-is-deprecated). We recommend that you use Dart Sass for new projects, and migrate Node Sass projects to Dart Sass when possible.
To use `gulp-sass`, you must install both `gulp-sass` itself *and* a Sass compiler. `gulp-sass` supports both [Embedded Sass][], [Dart Sass][] and [Node Sass][], although Node Sass is [deprecated](https://sass-lang.com/blog/libsass-is-deprecated). We recommend that you use Dart Sass for new projects, and migrate Node Sass projects to Dart Sass or Embedded Sass when possible.

Whichever compiler you choose, it's best to install these as dev dependencies:

Expand Down Expand Up @@ -42,7 +42,7 @@ const sass = gulpSass(dartSass);

`gulp-sass` must be used in a Gulp task. Your task can call `sass()` (to asynchronously render your CSS), or `sass.sync()` (to synchronously render your CSS). Then, export your task with the `export` keyword. We'll show some examples of how to do that.

**⚠️ Note:** When using Dart Sass, **synchronous rendering is twice as fast as asynchronous rendering**. The Sass team is exploring ways to improve asynchronous rendering with Dart Sass, but for now, you will get the best performance from `sass.sync()`. If performance is critical, you can use `node-sass` instead, but bear in mind that `node-sass` may not support modern Sass features you rely on.
**⚠️ Note:** When using Dart Sass, **synchronous rendering is twice as fast as asynchronous rendering**. The Sass team is exploring ways to improve asynchronous rendering with Dart Sass, but for now, you will get the best performance from `sass.sync()`. If performance is critical, you can use `sass-embedded` instead.

### Render your CSS

Expand Down Expand Up @@ -78,17 +78,17 @@ function buildStyles() {

### Render with options

To change the final output of your CSS, you can pass an options object to your renderer. `gulp-sass` supports [Node Sass's render options](https://github.com/sass/node-sass#options), with two unsupported exceptions:
To change the final output of your CSS, you can pass an options object to your renderer. `gulp-sass` supports [Sass's JS API compile options](https://sass-lang.com/documentation/js-api/modules#compileString), with a few usage notes:

- The `data` option, which is used by `gulp-sass` internally.
- The `file` option, which has undefined behavior that may change without notice.
- The `syntax` option is set to `indented` automatically for files with the `.sass` extension
- The `sourceMap` and `sourceMapIncludeSources` options are set for you when using `gulp-sourcemaps`

For example, to compress your CSS, you can call `sass({outputStyle: 'compressed'}`. In the context of a Gulp task, that looks like this:
For example, to compress your CSS, you can call `sass({style: 'compressed'}`. In the context of a Gulp task, that looks like this:

```js
function buildStyles() {
return gulp.src('./sass/**/*.scss')
.pipe(sass({outputStyle: 'compressed'}).on('error', sass.logError))
.pipe(sass({style: 'compressed'}).on('error', sass.logError))
.pipe(gulp.dest('./css'));
};

Expand All @@ -100,7 +100,7 @@ Or this for synchronous rendering:
```js
function buildStyles() {
return gulp.src('./sass/**/*.scss')
.pipe(sass.sync({outputStyle: 'compressed'}).on('error', sass.logError))
.pipe(sass.sync({style: 'compressed'}).on('error', sass.logError))
.pipe(gulp.dest('./css'));
};

Expand Down Expand Up @@ -141,13 +141,34 @@ function buildStyles() {
exports.buildStyles = buildStyles;
```

<h2 id="migrating-to-version-6">Migrating to version 6</h2>

`gulp-sass` version 6 uses the new [compile](https://sass-lang.com/documentation/js-api/modules#compileString) function internally by default. If you use any options, for instance custom importers, please compare the [new options](https://sass-lang.com/documentation/js-api/modules#compileString) with the [legacy options](https://sass-lang.com/documentation/js-api/modules#render) in order to migrate. For instance, the `outputStyle` option is now called `style`.

```diff
function buildStyles() {
return gulp.src('./sass/**/*.scss')
- .pipe(sass({outputStyle: 'compressed'}).on('error', sass.logError))
+ .pipe(sass({style: 'compressed'}).on('error', sass.logError))
.pipe(gulp.dest('./css'));
};
```

If you want to keep using the legacy API while it's available, you can.

```js
const sass = require('gulp-sass/legacy')(require('sass'));
```

If you use source maps, you may see the result change somewhat. The result will typically be absolute `file:` URLs, rather than relative ones. The result may also be the source itself, URL encoded. You can [optionally add custom importers](https://sass-lang.com/documentation/js-api/interfaces/CompileResult#sourceMap) to adjust the source maps according to your own needs.

<h2 id="migrating-to-version-5">Migrating to version 5</h2>

`gulp-sass` version 5 requires Node.js 12 or later, and introduces some breaking changes. Additionally, changes in Node.js itself mean that Node fibers can no longer be used to speed up Dart Sass in Node.js 16.

### Setting a Sass compiler

As of version 5, `gulp-sass` _does not include a default Sass compiler_, so you must install one (either `node-sass` or `sass`) along with `gulp-sass`.
As of version 5, `gulp-sass` _does not include a default Sass compiler_, so you must install one (either `sass`, `sass-embedded`, or `node-sass`) along with `gulp-sass`.

```sh
npm install sass gulp-sass --save-dev
Expand Down Expand Up @@ -176,6 +197,28 @@ import dartSass from 'sass';
+ const sass = gulpSass(dartSass);
```

### Using the legacy Sass API

If you need to use the deprecated `render` Sass API, `gulp-sass` still includes legacy support.

```js
'use strict';

const gulp = require('gulp');
const sass = require('gulp-sass/legacy')(require('sass'));

function buildStyles() {
return gulp.src('./sass/**/*.scss')
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest('./css'));
};

exports.buildStyles = buildStyles;
exports.watch = function () {
gulp.watch('./sass/**/*.scss', ['sass']);
};
````

### What about fibers?

We used to recommend Node fibers as a way to speed up asynchronous rendering with Dart Sass. Unfortunately, [Node fibers are discontinued](https://sass-lang.com/blog/node-fibers-discontinued) and will not work in Node.js 16. The Sass team is exploring its options for future performance improvements, but for now, you will get the best performance from `sass.sync()`.
Expand All @@ -190,6 +233,7 @@ If you're having problems with the options you're passing in, it's likely a Dart

We may, in the course of resolving issues, direct you to one of these other projects. If we do so, please follow up by searching that project's issue queue (both open and closed) for your problem and, if it doesn't exist, filing an issue with them.

[Embedded Sass]: https://github.com/sass/embedded-host-node
[Dart Sass]: https://sass-lang.com/dart-sass
[LibSass]: https://sass-lang.com/libsass
[Node Sass]: https://github.com/sass/node-sass
Expand Down
69 changes: 34 additions & 35 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,17 @@ const transfob = (transform) => new Transform({ transform, objectMode: true });
/**
* Handles returning the file to the stream
*/
const filePush = (file, sassObject, callback) => {
const filePush = (file, compileResult, callback) => {
file.contents = Buffer.from(compileResult.css);
file.path = replaceExtension(file.path, '.css');

// Build Source Maps!
if (sassObject.map) {
// Transform map into JSON
const sassMap = JSON.parse(sassObject.map.toString());
if (compileResult.sourceMap) {
const sassMap = compileResult.sourceMap;
if (!sassMap.file) {
sassMap.file = file.path;
}

// Grab the stdout and transform it into stdin
const sassMapFile = sassMap.file.replace(/^stdout$/, 'stdin');
// Grab the base filename that's being worked on
Expand All @@ -55,9 +61,6 @@ const filePush = (file, sassObject, callback) => {
applySourceMap(file, sassMap);
}

file.contents = sassObject.css;
file.path = replaceExtension(file.path, '.css');

if (file.stat) {
file.stat.atime = file.stat.mtime = file.stat.ctime = new Date();
}
Expand All @@ -71,7 +74,7 @@ const filePush = (file, sassObject, callback) => {
const handleError = (error, file, callback) => {
const filePath = (error.file === 'stdin' ? file.path : error.file) || file.path;
const relativePath = path.relative(process.cwd(), filePath);
const message = `${picocolors.underline(relativePath)}\n${error.formatted}`;
const message = `${picocolors.underline(relativePath)}\n${error.message}`;

error.messageFormatted = message;
error.messageOriginal = error.message;
Expand Down Expand Up @@ -110,52 +113,48 @@ const gulpSass = (options, sync) => {
}

const opts = clonedeep(options || {});
opts.data = file.contents.toString();

// We set the file path here so that libsass can correctly resolve import paths
opts.file = file.path;

// Ensure `indentedSyntax` is true if a `.sass` file
// Ensure `indented` if a `.sass` file
if (path.extname(file.path) === '.sass') {
opts.indentedSyntax = true;
opts.syntax = 'indented';
}

// Ensure file's parent directory in the include path
if (opts.includePaths) {
if (typeof opts.includePaths === 'string') {
opts.includePaths = [opts.includePaths];
if (opts.loadPaths) {
if (typeof opts.loadPaths === 'string') {
opts.loadPaths = [opts.loadPaths];
}
} else {
opts.includePaths = [];
opts.loadPaths = [];
}

opts.includePaths.unshift(path.dirname(file.path));
opts.loadPaths.unshift(path.dirname(file.path));

// Generate Source Maps if the source-map plugin is present
if (file.sourceMap) {
opts.sourceMap = file.path;
opts.omitSourceMapUrl = true;
opts.sourceMapContents = true;
opts.sourceMap = true;
opts.sourceMapIncludeSources = true;
}

const fileContents = file.contents.toString();
if (sync !== true) {
/**
* Async Sass render
* Async Sass compile
*/
gulpSass.compiler.render(opts, (error, obj) => {
if (error) {
gulpSass.compiler
.compileStringAsync(fileContents, opts)
.then((compileResult) => {
filePush(file, compileResult, callback);
})
.catch((error) => {
handleError(error, file, callback);
return;
}

filePush(file, obj, callback);
});
});
} else {
/**
* Sync Sass render
* Sync Sass compile
*/
try {
filePush(file, gulpSass.compiler.renderSync(opts), callback);
filePush(file, gulpSass.compiler.compileString(fileContents, opts), callback);
} catch (error) {
handleError(error, file, callback);
}
Expand All @@ -164,21 +163,21 @@ const gulpSass = (options, sync) => {
};

/**
* Sync Sass render
* Sync Sass compile
*/
gulpSass.sync = (options) => gulpSass(options, true);

/**
* Log errors nicely
*/
gulpSass.logError = function logError(error) {
const message = new PluginError('sass', error.messageFormatted).toString();
const message = new PluginError('sass', error).toString();
process.stderr.write(`${message}\n`);
this.emit('end');
};

module.exports = (compiler) => {
if (!compiler || !compiler.render) {
if (!compiler || !compiler.compile) {
const message = new PluginError(
PLUGIN_NAME,
MISSING_COMPILER_MESSAGE,
Expand Down
Loading

0 comments on commit 510c1c8

Please sign in to comment.