| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517 | 'use strict';const configurationError = require('./utils/configurationError');const getModulePath = require('./utils/getModulePath');const globjoin = require('globjoin');const micromatch = require('micromatch');const normalizeAllRuleSettings = require('./normalizeAllRuleSettings');const normalizePath = require('normalize-path');const path = require('path');/** @typedef {import('stylelint').ConfigPlugins} StylelintConfigPlugins *//** @typedef {import('stylelint').ConfigProcessor} StylelintConfigProcessor *//** @typedef {import('stylelint').ConfigProcessors} StylelintConfigProcessors *//** @typedef {import('stylelint').ConfigRules} StylelintConfigRules *//** @typedef {import('stylelint').ConfigOverride} StylelintConfigOverride *//** @typedef {import('stylelint').InternalApi} StylelintInternalApi *//** @typedef {import('stylelint').Config} StylelintConfig *//** @typedef {import('stylelint').CosmiconfigResult} StylelintCosmiconfigResult *//** @typedef {import('stylelint').CodeProcessor} StylelintCodeProcessor *//** @typedef {import('stylelint').ResultProcessor} StylelintResultProcessor *//** * - Merges config and stylelint options * - Makes all paths absolute * - Merges extends * @param {StylelintInternalApi} stylelint * @param {StylelintConfig} config * @param {string} configDir * @param {boolean} allowOverrides * @param {string} rootConfigDir * @param {string} [filePath] * @returns {Promise<StylelintConfig>} */async function augmentConfigBasic(	stylelint,	config,	configDir,	allowOverrides,	rootConfigDir,	filePath,) {	let augmentedConfig = config;	if (allowOverrides) {		augmentedConfig = addOptions(stylelint, augmentedConfig);	}	if (filePath) {		augmentedConfig = applyOverrides(augmentedConfig, rootConfigDir, filePath);	}	augmentedConfig = await extendConfig(		stylelint,		augmentedConfig,		configDir,		rootConfigDir,		filePath,	);	const cwd = stylelint._options.cwd;	return absolutizePaths(augmentedConfig, configDir, cwd);}/** * 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. * @param {string} cwd * @returns {(cosmiconfigResult?: StylelintCosmiconfigResult) => Promise<StylelintCosmiconfigResult>} */function augmentConfigExtended(cwd) {	return async (cosmiconfigResult) => {		if (!cosmiconfigResult) {			return null;		}		const configDir = path.dirname(cosmiconfigResult.filepath || '');		const { config } = cosmiconfigResult;		const augmentedConfig = absolutizePaths(config, configDir, cwd);		return {			config: augmentedConfig,			filepath: cosmiconfigResult.filepath,		};	};}/** * @param {StylelintInternalApi} stylelint * @param {string} [filePath] * @param {StylelintCosmiconfigResult} [cosmiconfigResult] * @returns {Promise<StylelintCosmiconfigResult>} */async function augmentConfigFull(stylelint, filePath, cosmiconfigResult) {	if (!cosmiconfigResult) {		return null;	}	const config = cosmiconfigResult.config;	const filepath = cosmiconfigResult.filepath;	const configDir = stylelint._options.configBasedir || path.dirname(filepath || '');	let augmentedConfig = await augmentConfigBasic(		stylelint,		config,		configDir,		true,		configDir,		filePath,	);	augmentedConfig = addPluginFunctions(augmentedConfig);	augmentedConfig = addProcessorFunctions(augmentedConfig);	if (!augmentedConfig.rules) {		throw configurationError(			'No rules found within configuration. Have you provided a "rules" property?',		);	}	augmentedConfig = normalizeAllRuleSettings(augmentedConfig);	return {		config: augmentedConfig,		filepath: cosmiconfigResult.filepath,	};}/** * Make all paths in the config absolute: * - ignoreFiles * - plugins * - processors * (extends handled elsewhere) * @param {StylelintConfig} config * @param {string} configDir * @param {string} cwd * @returns {StylelintConfig} */function absolutizePaths(config, configDir, cwd) {	if (config.ignoreFiles) {		config.ignoreFiles = [config.ignoreFiles].flat().map((glob) => {			if (path.isAbsolute(glob.replace(/^!/, ''))) return glob;			return globjoin(configDir, glob);		});	}	if (config.plugins) {		config.plugins = [config.plugins].flat().map((lookup) => {			if (typeof lookup === 'string') {				return getModulePath(configDir, lookup, cwd);			}			return lookup;		});	}	if (config.processors) {		config.processors = absolutizeProcessors(config.processors, configDir);	}	return config;}/** * Processors are absolutized in their own way because * they can be and return a string or an array * @param {StylelintConfigProcessors} processors * @param {string} configDir * @return {StylelintConfigProcessors} */function absolutizeProcessors(processors, configDir) {	const normalizedProcessors = Array.isArray(processors) ? processors : [processors];	return normalizedProcessors.map((item) => {		if (typeof item === 'string') {			return getModulePath(configDir, item);		}		return [getModulePath(configDir, item[0]), item[1]];	});}/** * @param {StylelintInternalApi} stylelint * @param {StylelintConfig} config * @param {string} configDir * @param {string} rootConfigDir * @param {string} [filePath] * @return {Promise<StylelintConfig>} */async function extendConfig(stylelint, config, configDir, rootConfigDir, filePath) {	if (config.extends === undefined) {		return config;	}	const { extends: configExtends, ...originalWithoutExtends } = config;	const normalizedExtends = [configExtends].flat();	let resultConfig = originalWithoutExtends;	for (const extendLookup of normalizedExtends) {		const extendResult = await loadExtendedConfig(stylelint, configDir, extendLookup);		if (extendResult) {			let extendResultConfig = extendResult.config;			const extendConfigDir = path.dirname(extendResult.filepath || '');			extendResultConfig = await augmentConfigBasic(				stylelint,				extendResultConfig,				extendConfigDir,				false,				rootConfigDir,				filePath,			);			resultConfig = mergeConfigs(resultConfig, extendResultConfig);		}	}	return mergeConfigs(resultConfig, originalWithoutExtends);}/** * @param {StylelintInternalApi} stylelint * @param {string} configDir * @param {string} extendLookup * @return {Promise<StylelintCosmiconfigResult>} */function loadExtendedConfig(stylelint, configDir, extendLookup) {	const extendPath = getModulePath(configDir, extendLookup, stylelint._options.cwd);	return stylelint._extendExplorer.load(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 * @param {StylelintConfig} a * @param {StylelintConfig} b * @returns {StylelintConfig} */function mergeConfigs(a, b) {	/** @type {{plugins: StylelintConfigPlugins}} */	const pluginMerger = {};	if (a.plugins || b.plugins) {		pluginMerger.plugins = [];		if (a.plugins) {			pluginMerger.plugins = pluginMerger.plugins.concat(a.plugins);		}		if (b.plugins) {			pluginMerger.plugins = [...new Set(pluginMerger.plugins.concat(b.plugins))];		}	}	/** @type {{processors: StylelintConfigProcessors}} */	const processorMerger = {};	if (a.processors || b.processors) {		processorMerger.processors = [];		if (a.processors) {			processorMerger.processors = processorMerger.processors.concat(a.processors);		}		if (b.processors) {			processorMerger.processors = [...new Set(processorMerger.processors.concat(b.processors))];		}	}	/** @type {{overrides: StylelintConfigOverride[]}} */	const overridesMerger = {};	if (a.overrides || b.overrides) {		overridesMerger.overrides = [];		if (a.overrides) {			overridesMerger.overrides = overridesMerger.overrides.concat(a.overrides);		}		if (b.overrides) {			overridesMerger.overrides = [...new Set(overridesMerger.overrides.concat(b.overrides))];		}	}	const rulesMerger = {};	if (a.rules || b.rules) {		rulesMerger.rules = { ...a.rules, ...b.rules };	}	const result = {		...a,		...b,		...processorMerger,		...pluginMerger,		...overridesMerger,		...rulesMerger,	};	return result;}/** * @param {StylelintConfig} config * @returns {StylelintConfig} */function addPluginFunctions(config) {	if (!config.plugins) {		return config;	}	const normalizedPlugins = [config.plugins].flat();	/** @type {StylelintConfig['pluginFunctions']} */	const pluginFunctions = {};	for (const pluginLookup of normalizedPlugins) {		let pluginImport;		if (typeof pluginLookup === 'string') {			pluginImport = require(pluginLookup);		} else {			pluginImport = pluginLookup;		}		// Handle either ES6 or CommonJS modules		pluginImport = pluginImport.default || pluginImport;		// A plugin can export either a single rule definition		// or an array of them		const normalizedPluginImport = [pluginImport].flat();		for (const pluginRuleDefinition of normalizedPluginImport) {			if (!pluginRuleDefinition.ruleName) {				throw configurationError(					`stylelint requires plugins to expose a ruleName. The plugin "${pluginLookup}" is not doing this, so will not work with stylelint. Please file an issue with the plugin.`,				);			}			if (!pluginRuleDefinition.ruleName.includes('/')) {				throw configurationError(					`stylelint requires plugin rules to be namespaced, i.e. only \`plugin-namespace/plugin-rule-name\` plugin rule names are supported. The plugin rule "${pluginRuleDefinition.ruleName}" does not do this, so will not work. Please file an issue with the plugin.`,				);			}			pluginFunctions[pluginRuleDefinition.ruleName] = pluginRuleDefinition.rule;		}	}	config.pluginFunctions = pluginFunctions;	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 * - Initialize the processor module by calling its functions with any *   provided options * - Push the processor's code and result processors to their respective arrays * @type {Map<string, string | Object>} */const processorCache = new Map();/** * @param {StylelintConfig} config * @return {StylelintConfig} */function addProcessorFunctions(config) {	if (!config.processors) return config;	/** @type {StylelintCodeProcessor[]} */	const codeProcessors = [];	/** @type {StylelintResultProcessor[]} */	const resultProcessors = [];	for (const processorConfig of [config.processors].flat()) {		const processorKey = JSON.stringify(processorConfig);		let initializedProcessor;		if (processorCache.has(processorKey)) {			initializedProcessor = processorCache.get(processorKey);		} else {			const processorLookup =				typeof processorConfig === 'string' ? processorConfig : processorConfig[0];			const processorOptions = typeof processorConfig === 'string' ? undefined : processorConfig[1];			let 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;}/** * @param {StylelintConfig} fullConfig * @param {string} rootConfigDir * @param {string} filePath * @return {StylelintConfig} */function applyOverrides(fullConfig, rootConfigDir, filePath) {	let { overrides, ...config } = fullConfig;	if (!overrides) {		return config;	}	if (!Array.isArray(overrides)) {		throw new TypeError(			'The `overrides` configuration property should be an array, e.g. { "overrides": [{ "files": "*.css", "rules": {} }] }.',		);	}	for (const override of overrides) {		const { files, ...configOverrides } = override;		if (!files) {			throw new Error(				'Every object in the `overrides` configuration property should have a `files` property with globs, e.g. { "overrides": [{ "files": "*.css", "rules": {} }] }.',			);		}		const filesGlobs = [files]			.flat()			.map((glob) => {				if (path.isAbsolute(glob.replace(/^!/, ''))) {					return glob;				}				return globjoin(rootConfigDir, glob);			})			// Glob patterns for micromatch should be in POSIX-style			.map((s) => normalizePath(s));		if (micromatch.isMatch(filePath, filesGlobs, { dot: true })) {			config = mergeConfigs(config, configOverrides);		}	}	return config;}/** * Add options to the config * * @param {StylelintInternalApi} stylelint * @param {StylelintConfig} config * * @returns {StylelintConfig} */function addOptions(stylelint, config) {	const augmentedConfig = {		...config,	};	if (stylelint._options.ignoreDisables) {		augmentedConfig.ignoreDisables = stylelint._options.ignoreDisables;	}	if (stylelint._options.quiet) {		augmentedConfig.quiet = stylelint._options.quiet;	}	if (stylelint._options.reportNeedlessDisables) {		augmentedConfig.reportNeedlessDisables = stylelint._options.reportNeedlessDisables;	}	if (stylelint._options.reportInvalidScopeDisables) {		augmentedConfig.reportInvalidScopeDisables = stylelint._options.reportInvalidScopeDisables;	}	if (stylelint._options.reportDescriptionlessDisables) {		augmentedConfig.reportDescriptionlessDisables =			stylelint._options.reportDescriptionlessDisables;	}	if (stylelint._options.customSyntax) {		augmentedConfig.customSyntax = stylelint._options.customSyntax;	}	return augmentedConfig;}module.exports = { augmentConfigExtended, augmentConfigFull, applyOverrides };
 |