'use strict';
const path = require('path');
const os = require('os');
const fs = require('fs');
// A typical sass error looks like this
// const SassError = {
// message: "invalid property name",
// column: 14,
// line: 1,
// file: "stdin",
// status: 1
// };
/**
* Enhances the sass error with additional information about what actually went wrong.
*
* @param {SassError} err
* @param {string} resourcePath
*/
function formatSassError(err, resourcePath) {
// Instruct webpack to hide the JS stack from the console
// Usually you're only interested in the SASS stack in this case.
// eslint-disable-next-line no-param-reassign
err.hideStack = true;
// The file property is missing in rare cases.
// No improvement in the error is possible.
if (!err.file) {
return;
}
let msg = err.message;
if (err.file === 'stdin') {
// eslint-disable-next-line no-param-reassign
err.file = resourcePath;
}
// node-sass returns UNIX-style paths
// eslint-disable-next-line no-param-reassign
err.file = path.normalize(err.file);
// The 'Current dir' hint of node-sass does not help us, we're providing
// additional information by reading the err.file property
msg = msg.replace(/\s*Current dir:\s*/, '');
// eslint-disable-next-line no-param-reassign
err.message = `${getFileExcerptIfPossible(err) +
msg.charAt(0).toUpperCase() +
msg.slice(1) +
os.EOL} in ${err.file} (line ${err.line}, column ${err.column})`;
}
/**
* Tries to get an excerpt of the file where the error happened.
* Uses err.line and err.column.
*
* Returns an empty string if the excerpt could not be retrieved.
*
* @param {SassError} err
* @returns {string}
*/
function getFileExcerptIfPossible(err) {
try {
const content = fs.readFileSync(err.file, 'utf8');
return `${os.EOL +
content.split(os.EOL)[err.line - 1] +
os.EOL +
new Array(err.column - 1).join(' ')}^${os.EOL} `;
} catch (ignoreErr) {
// If anything goes wrong here, we don't want any errors to be reported to the user
return '';
}
}
module.exports = formatSassError;