Skip to content

Commit

Permalink
refactor: code (#742)
Browse files Browse the repository at this point in the history
  • Loading branch information
evilebottnawi committed Aug 23, 2019
1 parent 716de3c commit 33a23f8
Show file tree
Hide file tree
Showing 11 changed files with 196 additions and 258 deletions.
30 changes: 30 additions & 0 deletions src/SassError.js
@@ -0,0 +1,30 @@
class SassError extends Error {
constructor(sassError, resourcePath) {
super();

this.name = 'SassError';
this.originalSassError = sassError;
this.loc = {
line: sassError.line,
column: sassError.column,
};

// Keep original error if `sassError.formatted` is unavailable
this.message = `${this.name}: ${this.originalSassError.message}`;

if (this.originalSassError.formatted) {
this.message = `${this.name}: ${this.originalSassError.formatted
.replace(/^Error: /, '')
.replace(/(\s*)stdin(\s*)/, `$1${resourcePath}$2`)}`;

// 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
this.hideStack = true;

Error.captureStackTrace(this, this.constructor);
}
}
}

export default SassError;
78 changes: 0 additions & 78 deletions src/formatSassError.js

This file was deleted.

19 changes: 19 additions & 0 deletions src/getDefaultSassImplementation.js
@@ -0,0 +1,19 @@
function getDefaultSassImplementation() {
let sassImplPkg = 'node-sass';

try {
require.resolve('node-sass');
} catch (error) {
try {
require.resolve('sass');
sassImplPkg = 'sass';
} catch (ignoreError) {
sassImplPkg = 'node-sass';
}
}

// eslint-disable-next-line import/no-dynamic-require, global-require
return require(sassImplPkg);
}

export default getDefaultSassImplementation;
64 changes: 64 additions & 0 deletions src/getRenderFunctionFromSassImplementation.js
@@ -0,0 +1,64 @@
import semver from 'semver';
import async from 'neo-async';

let nodeSassJobQueue = null;

/**
* Verifies that the implementation and version of Sass is supported by this loader.
*
* @param {Object} module
* @returns {Function}
*/
function getRenderFunctionFromSassImplementation(module) {
const { info } = module;

if (!info) {
throw new Error('Unknown Sass implementation.');
}

const components = info.split('\t');

if (components.length < 2) {
throw new Error(`Unknown Sass implementation "${info}".`);
}

const [implementation, version] = components;

if (!semver.valid(version)) {
throw new Error(`Invalid Sass version "${version}".`);
}

if (implementation === 'dart-sass') {
if (!semver.satisfies(version, '^1.3.0')) {
throw new Error(
`Dart Sass version ${version} is incompatible with ^1.3.0.`
);
}

return module.render.bind(module);
} else if (implementation === 'node-sass') {
if (!semver.satisfies(version, '^4.0.0')) {
throw new Error(
`Node Sass version ${version} is incompatible with ^4.0.0.`
);
}

// There is an issue with node-sass when async custom importers are used
// See https://github.com/sass/node-sass/issues/857#issuecomment-93594360
// We need to use a job queue to make sure that one thread is always available to the UV lib
if (nodeSassJobQueue === null) {
const threadPoolSize = Number(process.env.UV_THREADPOOL_SIZE || 4);

nodeSassJobQueue = async.queue(
module.render.bind(module),
threadPoolSize - 1
);
}

return nodeSassJobQueue.push.bind(nodeSassJobQueue);
}

throw new Error(`Unknown Sass implementation "${implementation}".`);
}

export default getRenderFunctionFromSassImplementation;
6 changes: 3 additions & 3 deletions src/getSassOptions.js
Expand Up @@ -89,9 +89,9 @@ function getSassOptions(loaderContext, loaderOptions, content) {
? proxyCustomImporters(options.importer, resourcePath)
: [];

// `node-sass` uses `includePaths` to resolve `@import` paths. Append the currently processed file.
options.includePaths = options.includePaths || [];
options.includePaths.push(path.dirname(resourcePath));
options.includePaths = (options.includePaths || []).concat(
path.dirname(resourcePath)
);

return options;
}
Expand Down
2 changes: 1 addition & 1 deletion src/importsToResolve.js
Expand Up @@ -24,7 +24,7 @@ function importsToResolve(url) {
const request = utils.urlToRequest(url);
// Keep in mind: ext can also be something like '.datepicker' when the true extension is omitted and the filename contains a dot.
// @see https://github.com/webpack-contrib/sass-loader/issues/167
const ext = path.extname(request);
const ext = path.extname(request).toLowerCase();

// In case there is module request, send this to webpack resolver
if (matchModuleImport.test(url)) {
Expand Down
95 changes: 8 additions & 87 deletions src/index.js
@@ -1,16 +1,14 @@
import path from 'path';

import validateOptions from 'schema-utils';
import async from 'neo-async';
import semver from 'semver';
import { getOptions } from 'loader-utils';

import schema from './options.json';
import formatSassError from './formatSassError';
import webpackImporter from './webpackImporter';
import getSassOptions from './getSassOptions';

let nodeSassJobQueue = null;
import webpackImporter from './webpackImporter';
import getDefaultSassImplementation from './getDefaultSassImplementation';
import getRenderFunctionFromSassImplementation from './getRenderFunctionFromSassImplementation';
import SassError from './SassError';

/**
* The sass-loader makes node-sass and dart-sass available to webpack modules.
Expand Down Expand Up @@ -57,19 +55,18 @@ function loader(content) {
return;
}

const render = getRenderFuncFromSassImpl(
options.implementation || getDefaultSassImpl()
const render = getRenderFunctionFromSassImplementation(
options.implementation || getDefaultSassImplementation()
);

render(sassOptions, (error, result) => {
if (error) {
formatSassError(error, this.resourcePath);

if (error.file) {
this.dependency(error.file);
}

callback(error);
callback(new SassError(error, this.resourcePath));

return;
}

Expand Down Expand Up @@ -117,80 +114,4 @@ function loader(content) {
});
}

/**
* Verifies that the implementation and version of Sass is supported by this loader.
*
* @param {Object} module
* @returns {Function}
*/
function getRenderFuncFromSassImpl(module) {
const { info } = module;

if (!info) {
throw new Error('Unknown Sass implementation.');
}

const components = info.split('\t');

if (components.length < 2) {
throw new Error(`Unknown Sass implementation "${info}".`);
}

const [implementation, version] = components;

if (!semver.valid(version)) {
throw new Error(`Invalid Sass version "${version}".`);
}

if (implementation === 'dart-sass') {
if (!semver.satisfies(version, '^1.3.0')) {
throw new Error(
`Dart Sass version ${version} is incompatible with ^1.3.0.`
);
}

return module.render.bind(module);
} else if (implementation === 'node-sass') {
if (!semver.satisfies(version, '^4.0.0')) {
throw new Error(
`Node Sass version ${version} is incompatible with ^4.0.0.`
);
}

// There is an issue with node-sass when async custom importers are used
// See https://github.com/sass/node-sass/issues/857#issuecomment-93594360
// We need to use a job queue to make sure that one thread is always available to the UV lib
if (nodeSassJobQueue === null) {
const threadPoolSize = Number(process.env.UV_THREADPOOL_SIZE || 4);

nodeSassJobQueue = async.queue(
module.render.bind(module),
threadPoolSize - 1
);
}

return nodeSassJobQueue.push.bind(nodeSassJobQueue);
}

throw new Error(`Unknown Sass implementation "${implementation}".`);
}

function getDefaultSassImpl() {
let sassImplPkg = 'node-sass';

try {
require.resolve('node-sass');
} catch (error) {
try {
require.resolve('sass');
sassImplPkg = 'sass';
} catch (ignoreError) {
sassImplPkg = 'node-sass';
}
}

// eslint-disable-next-line import/no-dynamic-require, global-require
return require(sassImplPkg);
}

export default loader;
2 changes: 1 addition & 1 deletion src/webpackImporter.js
Expand Up @@ -18,7 +18,7 @@ import path from 'path';

import importsToResolve from './importsToResolve';

const matchCss = /\.css$/;
const matchCss = /\.css$/i;

/**
* Returns an importer that uses webpack's resolving algorithm.
Expand Down

0 comments on commit 33a23f8

Please sign in to comment.