Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate to use the new Sass JS API by default #846

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# gulp-sass Changelog

## v6.0.0

* **Change** Migrate to use the [new Sass JS API](https://sass-lang.com/documentation/js-api/modules#compile) internally by default

## 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