| 'use strict'; |
| |
| const LazyResult = require('postcss/lib/lazy-result').default; |
| const path = require('path'); |
| const { default: postcss } = require('postcss'); |
| const { promises: fs } = require('fs'); |
| |
| /** @typedef {import('postcss').Result} Result */ |
| /** @typedef {import('postcss').Syntax} Syntax */ |
| /** @typedef {import('stylelint').CustomSyntax} CustomSyntax */ |
| /** @typedef {import('stylelint').GetPostcssOptions} GetPostcssOptions */ |
| /** @typedef {import('stylelint').InternalApi} StylelintInternalApi */ |
| |
| const postcssProcessor = postcss(); |
| |
| /** |
| * @param {StylelintInternalApi} stylelint |
| * @param {GetPostcssOptions} options |
| * |
| * @returns {Promise<Result>} |
| */ |
| module.exports = async function getPostcssResult(stylelint, options = {}) { |
| const cached = options.filePath ? stylelint._postcssResultCache.get(options.filePath) : undefined; |
| |
| if (cached) { |
| return cached; |
| } |
| |
| if (stylelint._options.syntax) { |
| let error = 'The "syntax" option is no longer available. '; |
| |
| error += |
| stylelint._options.syntax === 'css' |
| ? 'You can remove the "--syntax" CLI flag as stylelint will now parse files as CSS by default' |
| : `You should install an appropriate syntax, e.g. postcss-scss, and use the "customSyntax" option`; |
| |
| return Promise.reject(new Error(error)); |
| } |
| |
| const syntax = options.customSyntax |
| ? getCustomSyntax(options.customSyntax) |
| : cssSyntax(stylelint, options.filePath); |
| |
| const postcssOptions = { |
| from: options.filePath, |
| syntax, |
| }; |
| |
| /** @type {string | undefined} */ |
| let getCode; |
| |
| if (options.code !== undefined) { |
| getCode = options.code; |
| } else if (options.filePath) { |
| getCode = await fs.readFile(options.filePath, 'utf8'); |
| } |
| |
| if (getCode === undefined) { |
| 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) { |
| stylelint._postcssResultCache.set(options.filePath, postcssResult); |
| } |
| |
| return postcssResult; |
| }; |
| |
| /** |
| * @param {CustomSyntax} customSyntax |
| * @returns {Syntax} |
| */ |
| function getCustomSyntax(customSyntax) { |
| let resolved; |
| |
| if (typeof customSyntax === 'string') { |
| try { |
| resolved = require(customSyntax); |
| } catch (error) { |
| if ( |
| error && |
| typeof error === 'object' && |
| // @ts-expect-error -- TS2571: Object is of type 'unknown'. |
| error.code === 'MODULE_NOT_FOUND' && |
| // @ts-expect-error -- TS2571: Object is of type 'unknown'. |
| error.message.includes(customSyntax) |
| ) { |
| throw new Error( |
| `Cannot resolve custom syntax module "${customSyntax}". Check that module "${customSyntax}" is available and spelled correctly.\n\nCaused by: ${error}`, |
| ); |
| } |
| |
| throw error; |
| } |
| |
| /* |
| * PostCSS allows for syntaxes that only contain a parser, however, |
| * it then expects the syntax to be set as the `parse` option. |
| */ |
| if (!resolved.parse) { |
| resolved = { |
| parse: resolved, |
| stringify: postcss.stringify, |
| }; |
| } |
| |
| return resolved; |
| } |
| |
| if (typeof customSyntax === 'object') { |
| if (typeof customSyntax.parse === 'function') { |
| resolved = { ...customSyntax }; |
| } else { |
| throw new TypeError( |
| `An object provided to the "customSyntax" option must have a "parse" property. Ensure the "parse" property exists and its value is a function.`, |
| ); |
| } |
| |
| return resolved; |
| } |
| |
| throw new Error(`Custom syntax must be a string or a Syntax object`); |
| } |
| |
| /** @type {{ [key: string]: string }} */ |
| const previouslyInferredExtensions = { |
| html: 'postcss-html', |
| js: '@stylelint/postcss-css-in-js', |
| jsx: '@stylelint/postcss-css-in-js', |
| less: 'postcss-less', |
| md: 'postcss-markdown', |
| sass: 'postcss-sass', |
| sss: 'sugarss', |
| scss: 'postcss-scss', |
| svelte: 'postcss-html', |
| ts: '@stylelint/postcss-css-in-js', |
| tsx: '@stylelint/postcss-css-in-js', |
| vue: 'postcss-html', |
| xml: 'postcss-html', |
| xst: 'postcss-html', |
| }; |
| |
| /** |
| * @param {StylelintInternalApi} stylelint |
| * @param {string|undefined} filePath |
| * @returns {Syntax} |
| */ |
| function cssSyntax(stylelint, filePath) { |
| const fileExtension = filePath ? path.extname(filePath).slice(1).toLowerCase() : ''; |
| const extensions = ['css', 'pcss', 'postcss']; |
| |
| if (previouslyInferredExtensions[fileExtension]) { |
| console.warn( |
| `${filePath}: When linting something other than CSS, you should install an appropriate syntax, e.g. "${previouslyInferredExtensions[fileExtension]}", and use the "customSyntax" option`, |
| ); |
| } |
| |
| return { |
| parse: |
| stylelint._options.fix && extensions.includes(fileExtension) |
| ? require('postcss-safe-parser') |
| : postcss.parse, |
| stringify: postcss.stringify, |
| }; |
| } |