UNPKG

node-sass-magic-importer

Version:

Custom node-sass importer for selector specific imports, node importing, module importing, globbing support and importing files only once

120 lines 5.46 kB
"use strict"; const cssNodeExtract = require("css-node-extract"); const cssSelectorExtract = require("css-selector-extract"); const findupSync = require("findup-sync"); const fs = require("fs"); const hash = require("object-hash"); const path = require("path"); const postcssSyntax = require("postcss-scss"); const default_options_1 = require("./default-options"); const toolbox_1 = require("./toolbox"); const EMPTY_IMPORT = { file: ``, contents: ``, }; const DIRECTORY_SEPARATOR = `/`; function getStoreId(resolvedUrl, selectorFilters = null, nodeFilters = null) { return hash({ resolvedUrl, selectorFilters, nodeFilters, }, { unorderedArrays: true }); } module.exports = function magicImporter(userOptions) { const options = Object.assign({}, default_options_1.defaultOptions, userOptions); if (options.hasOwnProperty(`prefix`)) { process.emitWarning('Using the `prefix` option is not supported anymore, use `packagePrefix` instead.'); } if (options.hasOwnProperty(`inlcudePaths`)) { process.emitWarning('Using the `inlcudePaths` option is not supported anymore, Use the node-sass `includePaths` option instead.'); } const escapedPackagePrefix = options.packagePrefix.replace(/[-/\\^$*+?.()|[\]{}]/g, `\\$&`); const matchPackageUrl = new RegExp(`^${escapedPackagePrefix}(?!/)`); return function importer(url, prev) { const nodeSassOptions = this.options; // Create a context for the current importer run. // An importer run is different from an importer instance, // one importer instance can spawn infinite importer runs. if (!this.nodeSassOnceImporterContext) { this.nodeSassOnceImporterContext = { store: new Set() }; } // Each importer run has it's own new store, otherwise // files already imported in a previous importer run // would be detected as multiple imports of the same file. const store = this.nodeSassOnceImporterContext.store; const includePaths = toolbox_1.buildIncludePaths(nodeSassOptions.includePaths, prev); let data = null; let filterPrefix = ``; let filteredContents = null; let cleanedUrl = toolbox_1.cleanImportUrl(url); let resolvedUrl = null; const isPackageUrl = cleanedUrl.match(matchPackageUrl); if (isPackageUrl) { cleanedUrl = cleanedUrl.replace(matchPackageUrl, ``); const packageName = cleanedUrl.charAt(0) === `@` ? cleanedUrl.split(DIRECTORY_SEPARATOR).slice(0, 2).join(DIRECTORY_SEPARATOR) : cleanedUrl.split(DIRECTORY_SEPARATOR)[0]; const normalizedPackageName = path.normalize(packageName); const packageSearchPath = path.join('node_modules', normalizedPackageName, `package.json`); const packagePath = path.dirname(findupSync(packageSearchPath, { cwd: options.cwd })); const escapedNormalizedPackageName = normalizedPackageName.replace(`\\`, `\\\\`); cleanedUrl = path.resolve(packagePath.replace(new RegExp(`${escapedNormalizedPackageName}$`), ``), path.normalize(cleanedUrl)); resolvedUrl = toolbox_1.resolvePackageUrl(cleanedUrl, options.extensions, options.cwd, options.packageKeys); if (resolvedUrl) { data = { file: resolvedUrl.replace(/\.css$/, ``) }; } } else { resolvedUrl = toolbox_1.resolveUrl(cleanedUrl, includePaths); } const nodeFilters = toolbox_1.parseNodeFilters(url); const selectorFilters = toolbox_1.parseSelectorFilters(url); const hasFilters = nodeFilters.length || selectorFilters.length; const globFilePaths = toolbox_1.resolveGlobUrl(cleanedUrl, includePaths); const storeId = getStoreId(resolvedUrl, selectorFilters, nodeFilters); if (hasFilters) { filterPrefix = `${url.split(` from `)[0]} from `; } if (globFilePaths) { const contents = globFilePaths .filter((x) => !store.has(getStoreId(x, selectorFilters, nodeFilters))) .map((x) => `@import '${filterPrefix}${x}';`) .join(`\n`); return { contents }; } if (store.has(storeId)) { return EMPTY_IMPORT; } if (resolvedUrl && hasFilters) { filteredContents = fs.readFileSync(resolvedUrl, { encoding: `utf8` }); if (selectorFilters.length) { filteredContents = cssSelectorExtract.processSync({ css: filteredContents, filters: selectorFilters, postcssSyntax, preserveLines: true, }); } if (nodeFilters.length) { filteredContents = cssNodeExtract.processSync({ css: filteredContents, filters: nodeFilters, customFilters: options.customFilters, postcssSyntax, preserveLines: true, }); } } if (!options.disableImportOnce) { store.add(storeId); } if (filteredContents) { data = { file: resolvedUrl, contents: filteredContents, }; } return data; }; }; //# sourceMappingURL=index.js.map