8a14419856
This documents the option to use Dart Sass, and encourages users to explicitly choose their implementation, but it doesn't change the existing behavior.
165 lines
5.2 KiB
JavaScript
165 lines
5.2 KiB
JavaScript
const chalk = require('chalk');
|
|
const PluginError = require('plugin-error');
|
|
const replaceExtension = require('replace-ext');
|
|
const stripAnsi = require('strip-ansi');
|
|
const through = require('through2');
|
|
const clonedeep = require('lodash.clonedeep');
|
|
const path = require('path');
|
|
const applySourceMap = require('vinyl-sourcemaps-apply');
|
|
|
|
const PLUGIN_NAME = 'gulp-sass';
|
|
|
|
//////////////////////////////
|
|
// Main Gulp Sass function
|
|
//////////////////////////////
|
|
const gulpSass = (options, sync) => through.obj((file, enc, cb) => { // eslint-disable-line consistent-return
|
|
if (file.isNull()) {
|
|
return cb(null, file);
|
|
}
|
|
|
|
if (file.isStream()) {
|
|
return cb(new PluginError(PLUGIN_NAME, 'Streaming not supported'));
|
|
}
|
|
|
|
if (path.basename(file.path).indexOf('_') === 0) {
|
|
return cb();
|
|
}
|
|
|
|
if (!file.contents.length) {
|
|
file.path = replaceExtension(file.path, '.css'); // eslint-disable-line no-param-reassign
|
|
return cb(null, file);
|
|
}
|
|
|
|
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
|
|
if (path.extname(file.path) === '.sass') {
|
|
opts.indentedSyntax = true;
|
|
}
|
|
|
|
// Ensure file's parent directory in the include path
|
|
if (opts.includePaths) {
|
|
if (typeof opts.includePaths === 'string') {
|
|
opts.includePaths = [opts.includePaths];
|
|
}
|
|
} else {
|
|
opts.includePaths = [];
|
|
}
|
|
|
|
opts.includePaths.unshift(path.dirname(file.path));
|
|
|
|
// Generate Source Maps if plugin source-map present
|
|
if (file.sourceMap) {
|
|
opts.sourceMap = file.path;
|
|
opts.omitSourceMapUrl = true;
|
|
opts.sourceMapContents = true;
|
|
}
|
|
|
|
//////////////////////////////
|
|
// Handles returning the file to the stream
|
|
//////////////////////////////
|
|
const filePush = (sassObj) => {
|
|
let sassMap;
|
|
let sassMapFile;
|
|
let sassFileSrc;
|
|
let sassFileSrcPath;
|
|
let sourceFileIndex;
|
|
|
|
// Build Source Maps!
|
|
if (sassObj.map) {
|
|
// Transform map into JSON
|
|
sassMap = JSON.parse(sassObj.map.toString());
|
|
// Grab the stdout and transform it into stdin
|
|
sassMapFile = sassMap.file.replace(/^stdout$/, 'stdin');
|
|
// Grab the base file name that's being worked on
|
|
sassFileSrc = file.relative;
|
|
// Grab the path portion of the file that's being worked on
|
|
sassFileSrcPath = path.dirname(sassFileSrc);
|
|
if (sassFileSrcPath) {
|
|
// Prepend the path to all files in the sources array except the file that's being worked on
|
|
sourceFileIndex = sassMap.sources.indexOf(sassMapFile);
|
|
sassMap.sources = sassMap.sources.map((source, index) => { // eslint-disable-line arrow-body-style
|
|
return index === sourceFileIndex ? source : path.join(sassFileSrcPath, source);
|
|
});
|
|
}
|
|
|
|
// Remove 'stdin' from souces and replace with filenames!
|
|
sassMap.sources = sassMap.sources.filter(src => src !== 'stdin' && src);
|
|
|
|
// Replace the map file with the original file name (but new extension)
|
|
sassMap.file = replaceExtension(sassFileSrc, '.css');
|
|
// Apply the map
|
|
applySourceMap(file, sassMap);
|
|
}
|
|
|
|
file.contents = sassObj.css; // eslint-disable-line no-param-reassign
|
|
file.path = replaceExtension(file.path, '.css'); // eslint-disable-line no-param-reassign
|
|
|
|
cb(null, file);
|
|
};
|
|
|
|
//////////////////////////////
|
|
// Handles error message
|
|
//////////////////////////////
|
|
const errorM = (error) => {
|
|
const filePath = (error.file === 'stdin' ? file.path : error.file) || file.path;
|
|
const relativePath = path.relative(process.cwd(), filePath);
|
|
const message = [chalk.underline(relativePath), error.formatted].join('\n');
|
|
|
|
error.messageFormatted = message; // eslint-disable-line no-param-reassign
|
|
error.messageOriginal = error.message; // eslint-disable-line no-param-reassign
|
|
error.message = stripAnsi(message); // eslint-disable-line no-param-reassign
|
|
error.relativePath = relativePath; // eslint-disable-line no-param-reassign
|
|
|
|
return cb(new PluginError(PLUGIN_NAME, error));
|
|
};
|
|
|
|
if (sync !== true) {
|
|
//////////////////////////////
|
|
// Async Sass render
|
|
//////////////////////////////
|
|
const callback = (error, obj) => { // eslint-disable-line consistent-return
|
|
if (error) {
|
|
return errorM(error);
|
|
}
|
|
filePush(obj);
|
|
};
|
|
|
|
gulpSass.compiler.render(opts, callback);
|
|
} else {
|
|
//////////////////////////////
|
|
// Sync Sass render
|
|
//////////////////////////////
|
|
try {
|
|
filePush(gulpSass.compiler.renderSync(opts));
|
|
} catch (error) {
|
|
return errorM(error);
|
|
}
|
|
}
|
|
});
|
|
|
|
//////////////////////////////
|
|
// Sync Sass render
|
|
//////////////////////////////
|
|
gulpSass.sync = options => gulpSass(options, true);
|
|
|
|
//////////////////////////////
|
|
// Log errors nicely
|
|
//////////////////////////////
|
|
gulpSass.logError = function logError(error) {
|
|
const message = new PluginError('sass', error.messageFormatted).toString();
|
|
process.stderr.write(`${message}\n`);
|
|
this.emit('end');
|
|
};
|
|
|
|
//////////////////////////////
|
|
// Store compiler in a prop
|
|
//////////////////////////////
|
|
gulpSass.compiler = require('node-sass');
|
|
|
|
module.exports = gulpSass;
|