UNPKG

stylelint

Version:
309 lines (250 loc) 11 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.augmentConfigFull = exports.augmentConfigExtended = undefined; var _lodash = require("lodash"); var _lodash2 = _interopRequireDefault(_lodash); var _utils = require("./utils"); var _fs = require("fs"); var _fs2 = _interopRequireDefault(_fs); var _globjoin = require("globjoin"); var _globjoin2 = _interopRequireDefault(_globjoin); var _normalizeRuleSettings = require("./normalizeRuleSettings"); var _normalizeRuleSettings2 = _interopRequireDefault(_normalizeRuleSettings); var _path = require("path"); var _path2 = _interopRequireDefault(_path); var _resolveFrom = require("resolve-from"); var _resolveFrom2 = _interopRequireDefault(_resolveFrom); var _rules = require("./rules"); var _rules2 = _interopRequireDefault(_rules); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var DEFAULT_IGNORE_FILENAME = ".stylelintignore"; var FILE_NOT_FOUND_ERROR_CODE = "ENOENT"; // - Makes all paths absolute // - Merges extends function augmentConfigBasic(stylelint, config, configDir) { return Promise.resolve().then(function () { return absolutizePaths(config, configDir); }).then(function (augmentedConfig) { return extendConfig(stylelint, augmentedConfig, configDir); }); } // Extended configs need to be run through augmentConfigBasic // but do not need the full treatment. Things like pluginFunctions // will be resolved and added by the parent config. function augmentConfigExtended(stylelint, cosmiconfigResult) { if (!cosmiconfigResult) return Promise.resolve(null); var configDir = _path2.default.dirname(cosmiconfigResult.filepath || ""); var cleanedConfig = _lodash2.default.omit(cosmiconfigResult.config, "ignoreFiles"); return augmentConfigBasic(stylelint, cleanedConfig, configDir).then(function (augmentedConfig) { return { config: augmentedConfig, filepath: cosmiconfigResult.filepath }; }); } function augmentConfigFull(stylelint, cosmiconfigResult) { if (!cosmiconfigResult) return Promise.resolve(null); var config = cosmiconfigResult.config; var filepath = cosmiconfigResult.filepath; var configDir = stylelint._options.configBasedir || _path2.default.dirname(filepath || ""); return addIgnorePatterns(stylelint, config, configDir).then(function (augmentedConfig) { return augmentConfigBasic(stylelint, augmentedConfig, configDir); }).then(function (augmentedConfig) { return addPluginFunctions(augmentedConfig); }).then(function (augmentedConfig) { return addProcessorFunctions(augmentedConfig); }).then(function (augmentedConfig) { var configWithOverrides = _lodash2.default.merge(augmentedConfig, stylelint._options.configOverrides); if (!configWithOverrides.rules) { throw (0, _utils.configurationError)("No rules found within configuration. Have you provided a \"rules\" property?"); } return configWithOverrides; }).then(function (augmentedConfig) { return normalizeAllRuleSettings(augmentedConfig); }).then(function (augmentedConfig) { return { config: augmentedConfig, filepath: cosmiconfigResult.filepath }; }); } // Load a file ignore ignore patterns, if there is one; // then add them to the config as an ignorePatterns property function addIgnorePatterns(stylelint, config) { var ignoreFilePath = stylelint._options.ignorePath || DEFAULT_IGNORE_FILENAME; var absoluteIgnoreFilePath = _path2.default.isAbsolute(ignoreFilePath) ? ignoreFilePath : _path2.default.resolve(process.cwd(), ignoreFilePath); return new Promise(function (resolve, reject) { _fs2.default.readFile(absoluteIgnoreFilePath, "utf8", function (err, data) { if (err) { // If the file's not found, fine, we'll just // consider it an empty array of globs if (err.code === FILE_NOT_FOUND_ERROR_CODE) { return resolve(config); } return reject(err); } // Add an ignorePatterns property to the config, containing the // .gitignore-patterned globs loaded from .stylelintignore config.ignorePatterns = data; resolve(config); }); }); } // Make all paths in the config absolute: // - ignoreFiles // - plugins // - processors // (extends handled elsewhere) function absolutizePaths(config, configDir) { if (config.ignoreFiles) { config.ignoreFiles = [].concat(config.ignoreFiles).map(function (glob) { if (_path2.default.isAbsolute(glob.replace(/^!/, ""))) return glob; return (0, _globjoin2.default)(configDir, glob); }); } if (config.plugins) { config.plugins = [].concat(config.plugins).map(function (lookup) { return getModulePath(configDir, lookup); }); } if (config.processors) { config.processors = absolutizeProcessors(config.processors, configDir); } return config; } // First try to resolve from the provided directory, // then try to resolve from process.cwd. function getModulePath(basedir, lookup) { var path = (0, _resolveFrom2.default)(basedir, lookup); if (!path) { path = (0, _resolveFrom2.default)(process.cwd(), lookup); } if (!path) { throw (0, _utils.configurationError)("Could not find \"" + lookup + "\". Do you need a `configBasedir`?"); } return path; } // Processors are absolutized in their own way because // they can be and return a string or an array function absolutizeProcessors(processors, configDir) { return [].concat(processors).map(function (item) { if (typeof item === "string") { return getModulePath(configDir, item); } return [getModulePath(configDir, item[0]), item[1]]; }); } function extendConfig(stylelint, config, configDir) { if (!config.extends) return Promise.resolve(config); var originalWithoutExtends = _lodash2.default.omit(config, "extends"); var loadExtends = [].concat(config.extends).reduce(function (resultPromise, extendLookup) { return resultPromise.then(function (resultConfig) { return loadExtendedConfig(stylelint, resultConfig, configDir, extendLookup).then(function (extendResult) { return mergeConfigs(resultConfig, extendResult.config); }); }); }, Promise.resolve(originalWithoutExtends)); return loadExtends.then(function (resultConfig) { return mergeConfigs(resultConfig, originalWithoutExtends); }); } function loadExtendedConfig(stylelint, config, configDir, extendLookup) { var extendPath = getModulePath(configDir, extendLookup); return stylelint._extendExplorer.load(null, extendPath); } // When merging configs (via extends) // - plugin and processor arrays are joined // - rules are merged via Object.assign, so there is no attempt made to // merge any given rule's settings. If b contains the same rule as a, // b's rule settings will override a's rule settings entirely. // - Everything else is merged via Object.assign function mergeConfigs(a, b) { var pluginMerger = {}; if (a.plugins || b.plugins) { pluginMerger.plugins = _lodash2.default.union(a.plugins, b.plugins); } var processorMerger = {}; if (a.processors || b.processors) { processorMerger.processors = _lodash2.default.union(a.processors, b.processors); } var rulesMerger = {}; if (a.rules || b.rules) { rulesMerger.rules = Object.assign({}, a.rules, b.rules); } return Object.assign({}, b, a, pluginMerger, rulesMerger); } function addPluginFunctions(config) { if (!config.plugins) return config; var pluginFunctions = config.plugins.reduce(function (result, pluginLookup) { var pluginImport = require(pluginLookup); // Handle either ES6 or CommonJS modules pluginImport = pluginImport.default || pluginImport;[].concat(pluginImport).forEach(function (plugin) { if (!plugin.ruleName) { throw (0, _utils.configurationError)("stylelint v3+ requires plugins to expose a ruleName. " + ("The plugin \"" + pluginLookup + "\" is not doing this, so will not work ") + "with stylelint v3+. Please file an issue with the plugin."); } if (!_lodash2.default.includes(plugin.ruleName, "/")) { throw (0, _utils.configurationError)("stylelint v7+ requires plugin rules to be namspaced, " + "i.e. only `plugin-namespace/plugin-rule-name` plugin rule names are supported. " + ("The plugin rule \"" + plugin.ruleName + "\" does not do this, so will not work. ") + "Please file an issue with the plugin."); } result[plugin.ruleName] = plugin.rule; }); return result; }, {}); config.pluginFunctions = pluginFunctions; return config; } function normalizeAllRuleSettings(config) { var normalizedRules = {}; Object.keys(config.rules).forEach(function (ruleName) { var rawRuleSettings = config.rules[ruleName]; var rule = _rules2.default[ruleName] || _lodash2.default.get(config, ["pluginFunctions", ruleName]); if (!rule) { throw (0, _utils.configurationError)("Undefined rule " + ruleName); } normalizedRules[ruleName] = (0, _normalizeRuleSettings2.default)(rawRuleSettings, ruleName, _lodash2.default.get(rule, "primaryOptionArray")); }); config.rules = normalizedRules; return config; } // Given an array of processors strings, we want to add two // properties to the augmented config: // - codeProcessors: functions that will run on code as it comes in // - resultProcessors: functions that will run on results as they go out // // To create these properties, we need to: // - Find the processor module // - Intialize the processor module by calling its functions with any // provided options // - Push the processor's code and result processors to their respective arrays var processorCache = new Map(); function addProcessorFunctions(config) { if (!config.processors) return config; var codeProcessors = []; var resultProcessors = [];[].concat(config.processors).forEach(function (processorConfig) { var processorKey = JSON.stringify(processorConfig); var initializedProcessor = void 0; if (processorCache.has(processorKey)) { initializedProcessor = processorCache.get(processorKey); } else { processorConfig = [].concat(processorConfig); var processorLookup = processorConfig[0]; var processorOptions = processorConfig[1]; var processor = require(processorLookup); processor = processor.default || processor; initializedProcessor = processor(processorOptions); processorCache.set(processorKey, initializedProcessor); } if (initializedProcessor && initializedProcessor.code) { codeProcessors.push(initializedProcessor.code); } if (initializedProcessor && initializedProcessor.result) { resultProcessors.push(initializedProcessor.result); } }); config.codeProcessors = codeProcessors; config.resultProcessors = resultProcessors; return config; } exports.augmentConfigExtended = augmentConfigExtended; exports.augmentConfigFull = augmentConfigFull;