diff --git a/package.json b/package.json index eb50b689..7f193ef3 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,6 @@ "css-loader": "^4.2.0", "del": "^5.1.0", "del-cli": "^3.0.1", - "enhanced-resolve": "^4.3.0", "eslint": "^7.6.0", "eslint-config-prettier": "^6.11.0", "eslint-plugin-import": "^2.21.2", diff --git a/src/importer.js b/src/importer.js deleted file mode 100644 index 5312d377..00000000 --- a/src/importer.js +++ /dev/null @@ -1,73 +0,0 @@ -import { getSassImplementation, getWebpackResolver } from './utils'; - -/** - * A factory function for creating a Sass importer that uses `sass-loader`'s - * resolution rules. - * - * @see https://sass-lang.com/documentation/js-api#importer - * - * This is useful when attempting to mimic `sass-loader`'s behaviour in contexts - * that do not support Webpack. For example, it could be used to write a Jest - * transform for testing files with Sass imports. - * - * The resulting Sass importer is asynchronous, so it can only be used with - * `sass.render()` and not `renderSync()`. - * - * Example usage: - * ```js - * import sass from 'sass'; - * import resolve from 'enhanced-resolve'; - * import createImporter from 'sass-loader/dist/importer'; - * import webpackConfig = './webpack.config'; - * - * const { resolve: { alias } } = webpackConfig; - * const resolverFactory = (options) => resolve.create({ alias, ...options }); - * const importer = createImporter(resolverFactory, sass); - * - * sass.render({ - * file: 'input.scss', - * importer, - * }, (err, result) => { - * // ... - * }); - * ``` - * - * @param {Function} resolverFactory - A factory function for creating a Webpack - * resolver. The resulting `resolve` function should be compatible with the - * asynchronous resolve function supplied by [`enhanced-resolve`]{@link - * https://github.com/webpack/enhanced-resolve}. In all likelihood you'll want - * to pass `resolve.create()` from `enhanced-resolve`, or a wrapped copy of - * it. - * @param {Object} [implementation] - The imported Sass implementation, both - * `sass` (Dart Sass) and `node-sass` are supported. If no implementation is - * supplied, `sass` will be preferred if it's available. - * @param {string[]} [includePaths] - The list of include paths passed to Sass. - * - * @returns {Function} - */ -export default function createSassImporter( - resolverFactory, - implementation = null, - includePaths = [] -) { - if (!implementation) { - // eslint-disable-next-line no-param-reassign - implementation = getSassImplementation(); - } - - const resolve = getWebpackResolver( - implementation, - resolverFactory, - includePaths - ); - - return (url, prev, done) => { - resolve(prev, url) - .then((result) => { - done({ file: result }); - }) - .catch(() => { - done(null); - }); - }; -} diff --git a/src/utils.js b/src/utils.js index 920c16f9..3ec2dbda 100644 --- a/src/utils.js +++ b/src/utils.js @@ -24,6 +24,11 @@ function getDefaultSassImplementation() { return require(sassImplPkg); } +/** + * @public + * This function is not Webpack-specific and can be used by tools wishing to + * mimic `sass-loader`'s behaviour, so its signature should not be changed. + */ function getSassImplementation(implementation) { let resolvedImplementation = implementation; @@ -273,6 +278,22 @@ const isSpecialModuleImport = /^~[^/]+$/; // `[drive_letter]:\` + `\\[server]\[sharename]\` const isNativeWin32Path = /^[a-zA-Z]:[/\\]|^\\\\/i; +/** + * @public + * Create the resolve function used in the custom Sass importer. + * + * Can be used by external tools to mimic how `sass-loader` works, for example + * in a Jest transform. Such usages will want to wrap `resolve.create` from + * [`enhanced-resolve`]{@link https://github.com/webpack/enhanced-resolve} to + * pass as the `resolverFactory` argument. + * + * @param {Object} implementation - The imported Sass implementation, both + * `sass` (Dart Sass) and `node-sass` are supported. + * @param {Function} resolverFactory - A factory function for creating a Webpack + * resolver. + * @param {string[]} [includePaths] - The list of include paths passed to Sass. + * @param {boolean} [rootContext] - The configured Webpack root context. + */ function getWebpackResolver( implementation, resolverFactory, diff --git a/test/__snapshots__/importer.test.js.snap b/test/__snapshots__/importer.test.js.snap deleted file mode 100644 index 6bda69c9..00000000 --- a/test/__snapshots__/importer.test.js.snap +++ /dev/null @@ -1,95 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`importer should resolve imports when passed to \`sass\` 1`] = ` -"@charset \\"UTF-8\\"; -/* @import another/module */ -@import url(http://example.com/something/from/the/interwebs); -.another-sass-module { - background: hotpink; -} - -/* @import another/underscore */ -.underscore { - background: hotpink; -} - -/* @import another/_underscore */ -.underscore { - background: hotpink; -} - -/* @import ~sass/underscore */ -.underscore-sass { - background: hotpink; -} - -/* @import ~sass/some.module */ -.some-sass-module { - background: hotpink; -} - -/* @import url(http://example.com/something/from/the/interwebs); */ -/* scoped import @import language */ -.scoped-import body { - font: 100% Helvetica, sans-serif; - color: #333; -} -.scoped-import nav ul { - margin: 0; - padding: 0; - list-style: none; -} -.scoped-import nav li { - display: inline-block; -} -.scoped-import nav a { - display: block; - padding: 6px 12px; - text-decoration: none; -} -.scoped-import .box { - -webkit-border-radius: 10px; - -moz-border-radius: 10px; - -ms-border-radius: 10px; - border-radius: 10px; -} -.scoped-import .message, .scoped-import .warning, .scoped-import .error, .scoped-import .success { - border: 1px solid #ccc; - padding: 10px; - color: #333; -} -.scoped-import .success { - border-color: green; -} -.scoped-import .error { - border-color: red; -} -.scoped-import .warning { - border-color: yellow; -} -.scoped-import .foo:before { - content: \\"\\"; -} -.scoped-import .bar:before { - content: \\"∑\\"; -} - -/* @import util */ -.util { - color: hotpink; -} - -/* @import ~module */ -.module { - background: hotpink; -} - -/* @import ~another */ -.another-scss-module-from-node-modules { - background: hotpink; -} - -a { - color: red; -}" -`; diff --git a/test/importer.test.js b/test/importer.test.js deleted file mode 100644 index 65a13bb8..00000000 --- a/test/importer.test.js +++ /dev/null @@ -1,44 +0,0 @@ -import sass from 'sass'; -import resolve from 'enhanced-resolve'; - -import createSassImporter from '../src/importer'; - -describe('importer', () => { - it('should resolve imports when passed to `sass`', (done) => { - const importer = createSassImporter(resolve.create); - - sass.render( - { - file: 'test/sass/imports.sass', - importer, - }, - (err, result) => { - expect(result.css.toString()).toMatchSnapshot(); - - done(err); - } - ); - }); - - it('should pass `null` to `done()` when resolution fails', (done) => { - let spy; - // eslint-disable-next-line no-shadow - const importer = (url, prev, done) => { - spy = jest.fn(done); - createSassImporter(resolve.create, sass)(url, prev, spy); - }; - - sass.render( - { - file: 'test/sass/error-file-not-found.sass', - importer, - }, - (err) => { - expect(spy).toHaveBeenCalledWith(null); - expect(err.toString()).toMatch("Can't find stylesheet to import."); - - done(); - } - ); - }); -});