UNPKG

eyeglass

Version:
210 lines 8.88 kB
"use strict"; var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; result["default"] = mod; return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const fs_1 = require("fs"); const perf_1 = require("../util/perf"); const path = __importStar(require("path")); const NameExpander_1 = require("../util/NameExpander"); const ImportUtilities_1 = __importDefault(require("./ImportUtilities")); const assertions_1 = require("../util/assertions"); const typescriptUtils_1 = require("../util/typescriptUtils"); const errorFor_1 = __importDefault(require("../util/errorFor")); const MODULE_PARSER = /^((?:@[^/]+\/[^/]+)|(?:[^/]+))\/?(.*)/; /* * Walks the file list until a match is found. If * no matches are found, calls the callback with an error */ function readFirstFile(buildCache, uri, possibleFiles, callback) { for (let nextFile of possibleFiles) { try { // We only read from the cache here, we do not set it. // the set will occur for common imports by the calling context let data = buildCache.get(`fs.readFileSync(${nextFile})`); data = data || fs_1.readFileSync(nextFile, "utf8"); // if it didn't fail, we found the first file so return it callback(null, { contents: data.toString(), file: nextFile }); return; } catch (_a) { // pass } } let errorMsg = [ `\`${uri}\` was not found in any of the following locations:` ].concat(...possibleFiles).join("\n "); callback(new Error(errorMsg)); return; } /** * The goal of this cache method is to cache the files that are commonly * imported. If we see the same import lookups more than once we can * assume the import is part of a commonly accessed library and put it into cache. * In many cases, this cache ignores the entry point file to a library from * outside the library because the first files looked for are relative to the * current file (the exception would be if several fils in the same directory * import the shared import). */ function readFirstFileCached(buildCache, uri, files, callback) { let readCacheKey = files.join(";"); let countKey = `readAbstractFile(${readCacheKey})-count`; let invocationCount = buildCache.get(countKey); if (typeof invocationCount === "undefined") { invocationCount = 0; } invocationCount += 1; buildCache.set(countKey, invocationCount); if (invocationCount === 1) { readFirstFile(buildCache, uri, files, callback); } else { let fileKey = `readAbstractFile(${readCacheKey})-file`; let file = buildCache.get(fileKey); let contents; let contentsKey; if (file) { contentsKey = `fs.readFileSync(${file})`; contents = buildCache.get(contentsKey); } if (file && contents) { callback(null, { file, contents }); } else { readFirstFile(buildCache, uri, files, (err, data) => { if (data && !err) { buildCache.set(fileKey, data.file); contentsKey = `fs.readFileSync(${data.file})`; buildCache.set(contentsKey, data.contents); } callback(err, data); }); } } } // This is a bootstrap function for calling readFirstFile. function readAbstractFile(originalUri, uri, location, includePaths, moduleName, buildCache, callback) { // start a name expander to get the names of possible file locations let nameExpander = new NameExpander_1.NameExpander(uri); // add the current location to the name expander nameExpander.addLocation(location); // if we have a module name, add it as an additional location if (moduleName) { nameExpander.addLocation(path.join(location, moduleName)); } // if we have includePaths... if (includePaths) { // add each of includePaths to the name expander includePaths.forEach(function (includePath) { nameExpander.addLocation(path.resolve(location, includePath)); }); } let files = new Array(...nameExpander.files); readFirstFileCached(buildCache, originalUri, files, callback); } /* * Returns an importer suitable for passing to node-sass. * options are the eyeglass/node-sass options. * fallback importer is the importer that was specified * in the node-sass options if one was there. */ const ModuleImporter = function (eyeglass, sass, options, fallbackImporter) { let includePaths = options.includePaths; let root = options.eyeglass.root; let buildCache = options.eyeglass.buildCache; return ImportUtilities_1.default.createImporter("module", function (uri, prev, done) { let importUtils = new ImportUtilities_1.default(eyeglass, sass, options, fallbackImporter, this); let isRealFile = perf_1.existsSync(prev); // pattern to match moduleName/relativePath // $1 = moduleName (foo or @scope/foo) // $2 = relativePath let match = MODULE_PARSER.exec(uri); if (!match) { throw new Error("invalid uri: " + uri); } let moduleName = match[1]; let relativePath = match[2]; let mod = eyeglass.modules.access(moduleName, isRealFile ? prev : root); // for back-compat with previous suggestion @see // https://github.com/sass-eyeglass/eyeglass/issues/131#issuecomment-210728946 // if the module was not found and the name starts with `@`... if (!mod && moduleName[0] === "@") { // reconstruct the moduleName and relativePath the way we would have previously let pieces = moduleName.split("/"); relativePath = pieces[1] + "/" + relativePath; moduleName = pieces[0]; // and try to find it again mod = eyeglass.modules.access(moduleName, isRealFile ? prev : root); } let sassDir; if (mod) { sassDir = mod.sassDir; if (!sassDir && !isRealFile) { // No sass directory, give an error importUtils.fallback(uri, prev, done, () => { if (!mod) { return assertions_1.unreachable(); } let missingMessage = "sassDir is not specified in " + mod.name + "'s package.json"; if (mod.mainPath) { missingMessage += " or " + mod.mainPath; } return done(new Error(missingMessage)); }); return; } } function createHandler(errorHandler) { let errHandler = errorHandler || defaultErrorHandler(done); return function (err, data) { if (err || !typescriptUtils_1.isPresent(data)) { importUtils.fallback(uri, prev, done, function () { errHandler(err || "[internal error] No data returned."); }); } else { importUtils.importOnce(data, done); } }; } function handleRelativeImports(includePaths = null) { if (isRealFile) { // relative file import, potentially relative to the previous import readAbstractFile(uri, uri, path.dirname(prev), includePaths, null, buildCache, createHandler()); } else { readAbstractFile(uri, uri, root, includePaths, null, buildCache, createHandler(function (err) { done(errorFor_1.default(err, "Could not import " + uri + " from " + prev)); })); } } if (sassDir) { // read uri from location. pass no includePaths as this is an eyeglass module readAbstractFile(uri, relativePath, sassDir, null, moduleName, buildCache, createHandler( // if it fails to find a module import, // try to import relative to the current location // this handles #37 handleRelativeImports.bind(null, null))); } else { handleRelativeImports(includePaths); } }); }; function defaultErrorHandler(done) { return function (err) { done(errorFor_1.default(err)); }; } exports.default = ModuleImporter; //# sourceMappingURL=ModuleImporter.js.map