lintSource.js 3.9 KB

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