webpackImporter.js 2.33 KB
"use strict";

/**
 * @name PromisedResolve
 * @type {Function}
 * @param {string} dir
 * @param {string} request
 * @returns Promise
 */

/**
 * @name Importer
 * @type {Function}
 * @param {string} url
 * @param {string} prev
 * @param {Function<Error, string>} done
 */

const path = require("path");
const tail = require("lodash.tail");
const importsToResolve = require("./importsToResolve");

const matchCss = /\.css$/;

/**
 * Returns an importer that uses webpack's resolving algorithm.
 *
 * It's important that the returned function has the correct number of arguments
 * (based on whether the call is sync or async) because otherwise node-sass doesn't exit.
 *
 * @param {string} resourcePath
 * @param {PromisedResolve} resolve
 * @param {Function<string>} addNormalizedDependency
 * @returns {Importer}
 */
function webpackImporter(resourcePath, resolve, addNormalizedDependency) {
    function dirContextFrom(fileContext) {
        return path.dirname(
            // The first file is 'stdin' when we're using the data option
            fileContext === "stdin" ? resourcePath : fileContext
        );
    }

    function startResolving(dir, importsToResolve) {
        return importsToResolve.length === 0 ?
            Promise.reject() :
            resolve(dir, importsToResolve[0])
                .then(resolvedFile => {
                    // Add the resolvedFilename as dependency. Although we're also using stats.includedFiles, this might come
                    // in handy when an error occurs. In this case, we don't get stats.includedFiles from node-sass.
                    addNormalizedDependency(resolvedFile);
                    return {
                        // By removing the CSS file extension, we trigger node-sass to include the CSS file instead of just linking it.
                        file: resolvedFile.replace(matchCss, "")
                    };
                }, () => startResolving(
                    dir,
                    tail(importsToResolve)
                ));
    }

    return (url, prev, done) => {
        startResolving(
            dirContextFrom(prev),
            importsToResolve(url)
        ) // Catch all resolving errors, return the original file and pass responsibility back to other custom importers
            .catch(() => ({ file: url }))
            .then(done);
    };
}

module.exports = webpackImporter;