lintSource.mjs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. import { isAbsolute } from 'node:path';
  2. import postcss from 'postcss';
  3. import descriptionlessDisables from './descriptionlessDisables.mjs';
  4. import getConfigForFile from './getConfigForFile.mjs';
  5. import getPostcssResult from './getPostcssResult.mjs';
  6. import invalidScopeDisables from './invalidScopeDisables.mjs';
  7. import isPathIgnored from './isPathIgnored.mjs';
  8. import isPathNotFoundError from './utils/isPathNotFoundError.mjs';
  9. import lintPostcssResult from './lintPostcssResult.mjs';
  10. import needlessDisables from './needlessDisables.mjs';
  11. import reportDisables from './reportDisables.mjs';
  12. import reportUnscopedDisables from './unscopedDisables.mjs';
  13. /** @import {Result} from 'postcss' */
  14. /** @import {GetLintSourceOptions as Options, InternalApi as StylelintInternalApi, PostcssResult, StylelintPostcssResult} from 'stylelint' */
  15. /**
  16. * Run stylelint on a PostCSS Result, either one that is provided
  17. * or one that we create
  18. * @param {StylelintInternalApi} stylelint
  19. * @param {Options} options
  20. * @returns {Promise<PostcssResult>}
  21. */
  22. export default async function lintSource(stylelint, options = {}) {
  23. if (!options.filePath && options.code === undefined && !options.existingPostcssResult) {
  24. return Promise.reject(new Error('You must provide filePath, code, or existingPostcssResult'));
  25. }
  26. const isCodeNotFile = options.code !== undefined;
  27. const inputFilePath = isCodeNotFile ? options.codeFilename : options.filePath;
  28. if (inputFilePath !== undefined && !isAbsolute(inputFilePath)) {
  29. if (isCodeNotFile) {
  30. return Promise.reject(new Error('codeFilename must be an absolute path'));
  31. }
  32. return Promise.reject(new Error('filePath must be an absolute path'));
  33. }
  34. const isIgnored = await isPathIgnored(stylelint, inputFilePath).catch((err) => {
  35. if (isCodeNotFile && isPathNotFoundError(err)) return false;
  36. throw err;
  37. });
  38. if (isIgnored) {
  39. return createEmptyPostcssResult(inputFilePath, options.existingPostcssResult);
  40. }
  41. const configForFile = await getConfigForFile({
  42. stylelint,
  43. searchPath: inputFilePath,
  44. filePath: inputFilePath,
  45. });
  46. if (!configForFile) {
  47. return Promise.reject(new Error('Config file not found'));
  48. }
  49. const config = configForFile.config;
  50. const existingPostcssResult = options.existingPostcssResult;
  51. if (options.cache) {
  52. stylelint._fileCache.calcHashOfConfig(config);
  53. if (options.filePath && !stylelint._fileCache.hasFileChanged(options.filePath)) {
  54. return createEmptyPostcssResult(inputFilePath, existingPostcssResult);
  55. }
  56. }
  57. const postcssResult =
  58. existingPostcssResult ||
  59. (await getPostcssResult(stylelint, {
  60. code: options.code,
  61. codeFilename: options.codeFilename,
  62. filePath: inputFilePath,
  63. customSyntax: config.customSyntax,
  64. }));
  65. const stylelintPostcssResult = Object.assign(postcssResult, {
  66. stylelint: {
  67. ruleSeverities: {},
  68. customMessages: {},
  69. customUrls: {},
  70. ruleMetadata: {},
  71. fixersData: {},
  72. rangesOfComputedEditInfos: [],
  73. disabledRanges: {},
  74. },
  75. });
  76. await lintPostcssResult(stylelint._options, stylelintPostcssResult, config);
  77. reportDisables(stylelintPostcssResult);
  78. needlessDisables(stylelintPostcssResult);
  79. invalidScopeDisables(stylelintPostcssResult);
  80. descriptionlessDisables(stylelintPostcssResult);
  81. reportUnscopedDisables(stylelintPostcssResult);
  82. return stylelintPostcssResult;
  83. }
  84. /**
  85. * @returns {StylelintPostcssResult}
  86. */
  87. function createEmptyStylelintPostcssResult() {
  88. return {
  89. ruleSeverities: {},
  90. customMessages: {},
  91. customUrls: {},
  92. ruleMetadata: {},
  93. fixersData: {},
  94. rangesOfComputedEditInfos: [],
  95. disabledRanges: {},
  96. ignored: true,
  97. stylelintError: false,
  98. stylelintWarning: false,
  99. };
  100. }
  101. /**
  102. * @param {string | undefined} filePath
  103. * @param {Options['existingPostcssResult']} existingPostcssResult
  104. * @returns {PostcssResult}
  105. */
  106. function createEmptyPostcssResult(filePath, existingPostcssResult) {
  107. return Object.assign(existingPostcssResult ?? postcss().process('', { from: filePath }).sync(), {
  108. stylelint: createEmptyStylelintPostcssResult(),
  109. });
  110. }