diff --git a/.changeset/hot-gifts-compete.md b/.changeset/hot-gifts-compete.md new file mode 100644 index 0000000000..e40fb4dc5d --- /dev/null +++ b/.changeset/hot-gifts-compete.md @@ -0,0 +1,5 @@ +--- +"stylelint": major +--- + +Removed: support for processors diff --git a/README.md b/README.md index 39ec504710..584b536c78 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,6 @@ There are also rules for enforcing stylistic conventions, but we now recommend y - [Writing custom syntaxes](docs/developer-guide/syntaxes.md) - [Writing custom formatters](docs/developer-guide/formatters.md) - [Writing system tests](docs/developer-guide/system-tests.md) - - [Writing processors](docs/developer-guide/processors.md) - Migration guide - [Migrating to 14.0.0](docs/migration-guide/to-14.md) - Maintainer guide diff --git a/docs/developer-guide/processors.md b/docs/developer-guide/processors.md deleted file mode 100644 index e7d05bc81b..0000000000 --- a/docs/developer-guide/processors.md +++ /dev/null @@ -1,28 +0,0 @@ -# Writing processors - -Processors are functions that hook into Stylelint's pipeline, modifying code on its way into Stylelint and modifying results on their way out. - -**Their use is discouraged in favor of [custom syntaxes](syntaxes.md).** - -Processor modules are functions that accept an options object and return an object with the following the functions, which hook into the processing of each file: - -- **code**: A function that accepts two arguments, the file's code and the file's path, and returns a string for Stylelint to lint. -- **result**: A function that accepts two arguments, the file's Stylelint result object and the file's path, and either mutates the result object (returning nothing) or returns a new one. - -```js -// my-processor.js -module.exports = function myProcessor(options) { - return { - code: (input, filepath) => { - // ... - return transformedCode; - }, - result: (stylelintResult, filepath) => { - // ... - return transformedResult; - } - }; -}; -``` - -_Processor options must be JSON-friendly_ because users will need to include them in `.stylelintrc` files. diff --git a/docs/migration-guide/to-15.md b/docs/migration-guide/to-15.md index dfdfdd7948..1a8104cd58 100644 --- a/docs/migration-guide/to-15.md +++ b/docs/migration-guide/to-15.md @@ -1,5 +1,9 @@ # Migrating to 15.0.0 +## Remove support for processors + +We removed the support for processors. Instead, please use [custom syntaxes](../developer-guide/syntaxes.md). + ## Change of `overrides.extends` behavior We changed the `overrides.extends` behavior to merge rather than replace, to make it consistent with the `overrides.plugins`. diff --git a/docs/user-guide/configure.md b/docs/user-guide/configure.md index 5e72f91f06..4ed1d8dc62 100644 --- a/docs/user-guide/configure.md +++ b/docs/user-guide/configure.md @@ -388,35 +388,6 @@ If the globs are absolute paths, they are used as is. If they are relative, they _Note that this is not an efficient method for ignoring lots of files._ If you want to ignore a lot of files efficiently, use [`.stylelintignore`](ignore-code.md) or adjust your files globs. -## `processors` - -Processors are functions built by the community that hook into Stylelint's pipeline, modifying code on its way into Stylelint and modifying results on their way out. - -**We discourage their use in favor of using the [`customSyntax` option](#customsyntax) as processors are incompatible with the [autofix feature](usage/options.md#fix).** - -To use one, add a `"processors"` array to your config, containing "locaters" identifying the processors you want to use. As with `extends`, above, a "locater" can be either an npm module name, an absolute path, or a path relative to the invoking configuration file. - -```json -{ - "processors": ["stylelint-my-processor"], - "rules": {} -} -``` - -If your processor has options, make that item an array whose first item is the "locator" and second item is the options object. - -```json -{ - "processors": [ - "stylelint-my-processor", - ["some-other-processor", { "optionOne": true, "optionTwo": false }] - ], - "rules": {} -} -``` - -Processors can also only be used with the CLI and the Node.js API, not with the PostCSS plugin. (The PostCSS plugin ignores them.) - ## `report*` These `report*` properties provide extra validation for `stylelint-disable` comments. This can be helpful for enforcing useful and well-documented disables. diff --git a/lib/__tests__/processors.test.js b/lib/__tests__/processors.test.js deleted file mode 100644 index 4156bfff90..0000000000 --- a/lib/__tests__/processors.test.js +++ /dev/null @@ -1,234 +0,0 @@ -'use strict'; - -const path = require('path'); -const standalone = require('../standalone'); - -const fixturesPath = path.join(__dirname, './fixtures'); -const safeChdir = require('../testUtils/safeChdir'); - -describe('processor transforms input and output', () => { - let results; - - beforeEach(async () => { - const code = 'one\ntwo\n```start\na {}\nb { color: pink }\n```end\nthree'; - - const data = await standalone({ - code, - config: { - extends: './config-block-no-empty', - processors: './processor-fenced-blocks', - }, - configBasedir: fixturesPath, - }); - - results = data.results; - }); - - it('number of results', () => { - expect(results).toHaveLength(1); - }); - - it('number of warnings', () => { - expect(results[0].warnings).toHaveLength(1); - }); - - it('warning rule', () => { - expect(results[0].warnings[0].rule).toBe('block-no-empty'); - }); - - it('warning line', () => { - expect(results[0].warnings[0].line).toBe(2); - }); - - it('warning column', () => { - expect(results[0].warnings[0].column).toBe(3); - }); - - it('special message', () => { - expect(results[0].specialMessage).toBe('was processed'); - }); -}); - -describe('processor accepts options', () => { - let results; - - beforeEach(async () => { - const code = 'one\ntwo\n```start\na {}\nb { color: pink }\n```end\nthree'; - - const data = await standalone({ - code, - config: { - extends: './config-block-no-empty', - processors: [['./processor-fenced-blocks', { specialMessage: 'options worked' }]], - }, - configBasedir: fixturesPath, - }); - - results = data.results; - }); - - it('special message', () => { - expect(results[0].specialMessage).toBe('options worked'); - }); -}); - -describe('multiple processors', () => { - let results; - - beforeEach(async () => { - const code = - 'one\ntwo\n```start\na {}\nb { color: pink }\n```end\nthree???startc {}???end' + - '\n\n???start```start\na {}\nb { color: pink }\n```end???end'; - - const data = await standalone({ - code, - config: { - extends: './config-block-no-empty', - processors: [ - './processor-triple-question-marks', - ['./processor-fenced-blocks', { specialMessage: 'options worked' }], - ], - }, - configBasedir: fixturesPath, - }); - - results = data.results; - }); - - it('number of results', () => { - expect(results).toHaveLength(1); - }); - - it('number of warnings', () => { - expect(results[0].warnings).toHaveLength(1); - }); - - it('warning rule', () => { - expect(results[0].warnings[0].rule).toBe('block-no-empty'); - }); - - it('warning line', () => { - expect(results[0].warnings[0].line).toBe(2); - }); - - it('warning column', () => { - expect(results[0].warnings[0].column).toBe(3); - }); - - it('special message', () => { - expect(results[0].specialMessage).toBe('options worked'); - }); - - it('tripleQuestionMarkBlocksFound', () => { - expect(results[0].tripleQuestionMarkBlocksFound).toBe(true); - }); -}); - -describe('loading processors (and extend) from process.cwd', () => { - safeChdir(path.join(__dirname, '..')); - - let results; - - beforeEach(async () => { - const code = - 'one\ntwo\n```start\na {}\nb { color: pink }\n```end\nthree???startc {}???end' + - '\n\n???start```start\na {}\nb { color: pink }\n```end???end'; - - const data = await standalone({ - code, - config: { - extends: './__tests__/fixtures/config-block-no-empty', - processors: [ - './__tests__/fixtures/processor-triple-question-marks', - ['./__tests__/fixtures/processor-fenced-blocks', { specialMessage: 'options worked' }], - ], - }, - }); - - results = data.results; - }); - - it('number of results', () => { - expect(results).toHaveLength(1); - }); - - it('number of warnings', () => { - expect(results[0].warnings).toHaveLength(1); - }); - - it('special message', () => { - expect(results[0].specialMessage).toBe('options worked'); - }); - - it('tripleQuestionMarkBlocksFound', () => { - expect(results[0].tripleQuestionMarkBlocksFound).toBe(true); - }); -}); - -describe('loading processors (and extend) from options.cwd', () => { - let results; - - beforeEach(async () => { - const code = - 'one\ntwo\n```start\na {}\nb { color: pink }\n```end\nthree???startc {}???end' + - '\n\n???start```start\na {}\nb { color: pink }\n```end???end'; - - const data = await standalone({ - code, - config: { - extends: './__tests__/fixtures/config-block-no-empty', - processors: [ - './__tests__/fixtures/processor-triple-question-marks', - ['./__tests__/fixtures/processor-fenced-blocks', { specialMessage: 'options worked' }], - ], - }, - cwd: path.join(__dirname, '..'), - }); - - results = data.results; - }); - - it('number of results', () => { - expect(results).toHaveLength(1); - }); - - it('number of warnings', () => { - expect(results[0].warnings).toHaveLength(1); - }); - - it('special message', () => { - expect(results[0].specialMessage).toBe('options worked'); - }); - - it('tripleQuestionMarkBlocksFound', () => { - expect(results[0].tripleQuestionMarkBlocksFound).toBe(true); - }); -}); - -describe('processor gets to modify result on CssSyntaxError', () => { - let results; - - beforeEach(async () => { - const code = "one\ntwo\n```start\na {}\nb { color: 'pink; }\n```end\nthree"; - - const data = await standalone({ - code, - config: { - rules: { 'block-no-empty': true }, - processors: './processor-fenced-blocks', - }, - configBasedir: fixturesPath, - }); - - results = data.results; - }); - - it('CssSyntaxError occurred', () => { - expect(results[0].warnings).toHaveLength(1); - expect(results[0].warnings[0].rule).toBe('CssSyntaxError'); - }); - - it('successfully modified result', () => { - expect(results[0].specialMessage).toBe('was processed'); - }); -}); diff --git a/lib/augmentConfig.js b/lib/augmentConfig.js index 7ff45b1ee7..c9774263bf 100644 --- a/lib/augmentConfig.js +++ b/lib/augmentConfig.js @@ -10,15 +10,11 @@ const path = require('path'); /** @typedef {import('stylelint').ConfigExtends} StyleLintConfigExtends */ /** @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 @@ -114,7 +110,6 @@ async function augmentConfigFull(stylelint, filePath, cosmiconfigResult) { ); augmentedConfig = addPluginFunctions(augmentedConfig); - augmentedConfig = addProcessorFunctions(augmentedConfig); if (!augmentedConfig.rules) { throw configurationError( @@ -131,11 +126,8 @@ async function augmentConfigFull(stylelint, filePath, cosmiconfigResult) { } /** - * Make all paths in the config absolute: - * - ignoreFiles - * - plugins - * - processors - * (extends handled elsewhere) + * Make all paths in the config absolute. + * * @param {StylelintConfig} config * @param {string} configDir * @param {string} cwd @@ -154,32 +146,9 @@ function absolutizePaths(config, configDir, cwd) { config.plugins = [config.plugins].flat().map((lookup) => getModulePath(configDir, lookup, cwd)); } - 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 @@ -235,7 +204,7 @@ function loadExtendedConfig(stylelint, configDir, extendLookup) { /** * When merging configs (via extends) - * - plugin, extends, overrides, processor arrays are joined + * - plugin, extends, overrides 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. @@ -260,21 +229,6 @@ function mergeConfigs(a, b) { } } - /** @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 = {}; @@ -315,7 +269,6 @@ function mergeConfigs(a, b) { ...a, ...b, ...extendsMerger, - ...processorMerger, ...pluginMerger, ...overridesMerger, ...rulesMerger, @@ -370,66 +323,6 @@ function addPluginFunctions(config) { 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} - */ -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 diff --git a/lib/createStylelintResult.js b/lib/createStylelintResult.js deleted file mode 100644 index 2fe267ff45..0000000000 --- a/lib/createStylelintResult.js +++ /dev/null @@ -1,42 +0,0 @@ -'use strict'; - -const createPartialStylelintResult = require('./createPartialStylelintResult'); -const getConfigForFile = require('./getConfigForFile'); - -/** @typedef {import('stylelint').PostcssResult} PostcssResult */ -/** @typedef {import('stylelint').LintResult} StylelintResult */ - -/** - * @param {import('stylelint').InternalApi} stylelint - * @param {PostcssResult} [postcssResult] - * @param {string} [filePath] - * @param {import('stylelint').CssSyntaxError} [cssSyntaxError] - * @return {Promise} - */ -module.exports = async function createStylelintResult( - stylelint, - postcssResult, - filePath, - cssSyntaxError, -) { - let stylelintResult = createPartialStylelintResult(postcssResult, cssSyntaxError); - - const configForFile = await getConfigForFile(stylelint, filePath, filePath); - - const config = configForFile === null ? {} : configForFile.config; - const file = stylelintResult.source || (cssSyntaxError && cssSyntaxError.file); - - if (config.resultProcessors) { - for (const resultProcessor of config.resultProcessors) { - // Result processors might just mutate the result object, - // or might return a new one - const returned = resultProcessor(stylelintResult, file); - - if (returned) { - stylelintResult = returned; - } - } - } - - return stylelintResult; -}; diff --git a/lib/getPostcssResult.js b/lib/getPostcssResult.js index 63ce9e8959..3fa72a764a 100644 --- a/lib/getPostcssResult.js +++ b/lib/getPostcssResult.js @@ -48,21 +48,6 @@ module.exports = async function getPostcssResult(stylelint, options = {}) { return Promise.reject(new Error('code or filePath required')); } - if (options.codeProcessors && options.codeProcessors.length) { - if (stylelint._options.fix) { - console.warn( - 'Autofix is incompatible with processors and will be disabled. Are you sure you need a processor?', - ); - stylelint._options.fix = false; - } - - const sourceName = options.code ? options.codeFilename : options.filePath; - - for (const codeProcessor of options.codeProcessors) { - getCode = codeProcessor(getCode, sourceName); - } - } - const postcssResult = await new LazyResult(postcssProcessor, getCode, postcssOptions); if (options.filePath) { diff --git a/lib/lintSource.js b/lib/lintSource.js index df59f17f2a..2a92619a81 100644 --- a/lib/lintSource.js +++ b/lib/lintSource.js @@ -100,7 +100,6 @@ module.exports = async function lintSource(stylelint, options = {}) { code: options.code, codeFilename: options.codeFilename, filePath: inputFilePath, - codeProcessors: config.codeProcessors, customSyntax: config.customSyntax, })); diff --git a/lib/standalone.js b/lib/standalone.js index e365e47ba6..66fe3f6002 100644 --- a/lib/standalone.js +++ b/lib/standalone.js @@ -8,7 +8,7 @@ const normalizePath = require('normalize-path'); const path = require('path'); const createStylelint = require('./createStylelint'); -const createStylelintResult = require('./createStylelintResult'); +const createPartialStylelintResult = require('./createPartialStylelintResult'); const filterFilePaths = require('./utils/filterFilePaths'); const formatters = require('./formatters'); const getFileIgnorer = require('./utils/getFileIgnorer'); @@ -129,9 +129,9 @@ async function standalone({ codeFilename: absoluteCodeFilename, }); - stylelintResult = await createStylelintResult(stylelint, postcssResult, absoluteCodeFilename); + stylelintResult = createPartialStylelintResult(postcssResult); } catch (error) { - stylelintResult = await handleError(stylelint, error); + stylelintResult = handleError(error); } const postcssResult = stylelintResult._postcssResult; @@ -244,12 +244,12 @@ async function standalone({ } } - return createStylelintResult(stylelint, postcssResult, absoluteFilepath); + return createPartialStylelintResult(postcssResult); } catch (error) { // On any error, we should not cache the lint result stylelint._fileCache.removeEntry(absoluteFilepath); - return handleError(stylelint, error, absoluteFilepath); + return handleError(error); } }); @@ -301,14 +301,14 @@ function getFormatterFunction(selected) { } /** - * @param {import('stylelint').InternalApi} stylelint - * @param {any} error - * @param {string} [filePath] - * @return {Promise} + * @typedef {import('stylelint').CssSyntaxError} CssSyntaxError + * + * @param {unknown} error + * @return {StylelintResult} */ -function handleError(stylelint, error, filePath = undefined) { - if (error.name === 'CssSyntaxError') { - return createStylelintResult(stylelint, undefined, filePath, error); +function handleError(error) { + if (error instanceof Error && error.name === 'CssSyntaxError') { + return createPartialStylelintResult(undefined, /** @type {CssSyntaxError} */ (error)); } throw error; diff --git a/types/stylelint/index.d.ts b/types/stylelint/index.d.ts index 43eca99899..3722d7fdb3 100644 --- a/types/stylelint/index.d.ts +++ b/types/stylelint/index.d.ts @@ -8,8 +8,6 @@ declare module 'stylelint' { export type ConfigExtends = string | string[]; export type ConfigPlugins = string | string[]; - export type ConfigProcessor = string | [string, Object]; - export type ConfigProcessors = string | ConfigProcessor[]; export type ConfigIgnoreFiles = string | string[]; export type ConfigRuleSettings = | null @@ -30,21 +28,15 @@ declare module 'stylelint' { }; export type DisableSettings = ConfigRuleSettings; - export type ResultProcessor = (result: LintResult, file: string | undefined) => LintResult; - export type Config = { extends?: ConfigExtends; plugins?: ConfigPlugins; pluginFunctions?: { [pluginName: string]: Rule; }; - processors?: ConfigProcessors; - processorFunctions?: Function[]; ignoreFiles?: ConfigIgnoreFiles; ignorePatterns?: string; rules?: ConfigRules; - codeProcessors?: CodeProcessor[]; - resultProcessors?: ResultProcessor[]; quiet?: boolean; defaultSeverity?: Severity; ignoreDisables?: DisableSettings; @@ -206,13 +198,10 @@ declare module 'stylelint' { export type Plugin = RuleBase; - export type CodeProcessor = (code: string, file: string | undefined) => string; - export type GetPostcssOptions = { code?: string; codeFilename?: string; filePath?: string; - codeProcessors?: CodeProcessor[]; customSyntax?: CustomSyntax; };