123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- '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,
- };
- }
|