standalone.cjs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. // NOTICE: This file is generated by Rollup. To modify it,
  2. // please instead edit the ESM counterpart and rebuild with Rollup (npm run build).
  3. 'use strict';
  4. const path = require('node:path');
  5. const fs = require('node:fs');
  6. const process = require('node:process');
  7. const createDebug = require('debug');
  8. const fastGlob = require('fast-glob');
  9. const globby = require('globby');
  10. const normalizePath = require('normalize-path');
  11. const writeFileAtomic = require('write-file-atomic');
  12. const errors = require('./utils/errors.cjs');
  13. const validateTypes = require('./utils/validateTypes.cjs');
  14. const emitWarning = require('./utils/emitWarning.cjs');
  15. const constants = require('./constants.cjs');
  16. const suppressionsService = require('./utils/suppressionsService.cjs');
  17. const createPartialStylelintResult = require('./createPartialStylelintResult.cjs');
  18. const createStylelint = require('./createStylelint.cjs');
  19. const filterFilePaths = require('./utils/filterFilePaths.cjs');
  20. const getConfigForFile = require('./getConfigForFile.cjs');
  21. const getFileIgnorer = require('./utils/getFileIgnorer.cjs');
  22. const getFormatter = require('./utils/getFormatter.cjs');
  23. const lintSource = require('./lintSource.cjs');
  24. const normalizeFixMode = require('./utils/normalizeFixMode.cjs');
  25. const prepareReturnValue = require('./prepareReturnValue.cjs');
  26. const resolveFilePath = require('./utils/resolveFilePath.cjs');
  27. const resolveOptionValue = require('./utils/resolveOptionValue.cjs');
  28. const debug = createDebug('stylelint:standalone');
  29. const ALWAYS_IGNORED_GLOBS = ['**/node_modules/**'];
  30. /** @import {InternalApi, LintResult} from 'stylelint' */
  31. /**
  32. * @type {import('stylelint').PublicApi['lint']}
  33. */
  34. async function standalone({
  35. allowEmptyInput,
  36. cache,
  37. cacheLocation,
  38. cacheStrategy,
  39. code,
  40. codeFilename,
  41. config,
  42. configBasedir,
  43. configFile,
  44. customSyntax,
  45. cwd = process.cwd(),
  46. disableDefaultIgnores,
  47. files,
  48. fix,
  49. computeEditInfo,
  50. formatter,
  51. _defaultFormatter,
  52. globbyOptions,
  53. ignoreDisables,
  54. ignorePath,
  55. ignorePattern,
  56. maxWarnings,
  57. quiet,
  58. quietDeprecationWarnings = false,
  59. reportDescriptionlessDisables,
  60. reportInvalidScopeDisables,
  61. reportNeedlessDisables,
  62. reportUnscopedDisables,
  63. suppressAll,
  64. suppressRule,
  65. suppressLocation,
  66. validate = true,
  67. }) {
  68. const startTime = Date.now();
  69. const useInputCode = !files && validateTypes.isString(code);
  70. const hasOneValidInput = (files && !validateTypes.isString(code)) || useInputCode;
  71. if (!hasOneValidInput) {
  72. return Promise.reject(
  73. new Error('You must pass stylelint a `files` glob or a `code` string, though not both'),
  74. );
  75. }
  76. // The ignorer will be used to filter file paths after the glob is checked,
  77. // before any files are actually read
  78. /** @type {import('ignore').Ignore} */
  79. let ignorer;
  80. try {
  81. ignorer = getFileIgnorer({ cwd, ignorePath, ignorePattern });
  82. } catch (error) {
  83. return Promise.reject(error);
  84. }
  85. const stylelint = createStylelint({
  86. allowEmptyInput,
  87. cache,
  88. cacheLocation,
  89. cacheStrategy,
  90. config,
  91. configFile,
  92. configBasedir,
  93. cwd,
  94. formatter,
  95. _defaultFormatter,
  96. ignoreDisables,
  97. ignorePath,
  98. reportNeedlessDisables,
  99. reportInvalidScopeDisables,
  100. reportDescriptionlessDisables,
  101. reportUnscopedDisables,
  102. customSyntax,
  103. fix,
  104. computeEditInfo,
  105. quiet,
  106. quietDeprecationWarnings,
  107. validate,
  108. });
  109. /** @see https://github.com/stylelint/stylelint/issues/7447 */
  110. if (!quietDeprecationWarnings && formatter === 'github') {
  111. emitWarning.emitDeprecationWarning(
  112. '"github" formatter is deprecated.',
  113. 'GITHUB_FORMATTER',
  114. 'See https://stylelint.io/awesome-stylelint#formatters for alternative formatters.',
  115. );
  116. }
  117. const formatterFunction = await getFormatter(stylelint);
  118. if (!files) {
  119. validateTypes.assertString(code);
  120. const absoluteCodeFilename =
  121. codeFilename !== undefined && !path.isAbsolute(codeFilename)
  122. ? path.join(cwd, codeFilename)
  123. : codeFilename;
  124. // if file is ignored, return nothing
  125. if (
  126. absoluteCodeFilename &&
  127. !filterFilePaths(ignorer, [path.relative(cwd, absoluteCodeFilename)]).length
  128. ) {
  129. return prepareReturnValue({
  130. results: [],
  131. maxWarnings,
  132. quietDeprecationWarnings,
  133. formatter: formatterFunction,
  134. cwd,
  135. });
  136. }
  137. let stylelintResult;
  138. try {
  139. const postcssResult = await lintSource(stylelint, {
  140. code,
  141. codeFilename: absoluteCodeFilename,
  142. });
  143. stylelintResult = createPartialStylelintResult(postcssResult);
  144. } catch (error) {
  145. stylelintResult = handleError(error);
  146. }
  147. await postProcessStylelintResult(stylelint, stylelintResult, absoluteCodeFilename);
  148. const postcssResult = stylelintResult._postcssResult;
  149. const returnValue = prepareReturnValue({
  150. results: [stylelintResult],
  151. maxWarnings,
  152. quietDeprecationWarnings,
  153. formatter: formatterFunction,
  154. cwd,
  155. });
  156. const autofix = normalizeFixMode(stylelint._options.fix) ?? config?.fix ?? false;
  157. if (autofix && postcssResult && !postcssResult.stylelint.ignored) {
  158. returnValue.code = postcssResult.opts
  159. ? // If we're fixing, the output should be the fixed code
  160. postcssResult.root.toString(postcssResult.opts.syntax)
  161. : // If the writing of the fix is disabled, the input code is returned as-is
  162. code;
  163. returnValue._output = returnValue.code; // TODO: Deprecated. Remove in the next major version.
  164. }
  165. return returnValue;
  166. }
  167. let fileList = [files].flat().map((entry) => {
  168. const globCWD = (globbyOptions && globbyOptions.cwd) || cwd;
  169. const absolutePath = !path.isAbsolute(entry) ? path.join(globCWD, entry) : path.normalize(entry);
  170. if (fs.existsSync(absolutePath)) {
  171. // This path points to a file. Return an escaped path to avoid globbing
  172. return fastGlob.escapePath(normalizePath(entry));
  173. }
  174. return entry;
  175. });
  176. if (!disableDefaultIgnores) {
  177. fileList = fileList.concat(ALWAYS_IGNORED_GLOBS.map((glob) => `!${glob}`));
  178. }
  179. const useCache = await resolveOptionValue({ stylelint, name: 'cache', default: false });
  180. if (!useCache) {
  181. stylelint._fileCache.destroy();
  182. }
  183. const effectiveGlobbyOptions = {
  184. cwd,
  185. ...(globbyOptions || {}),
  186. absolute: true,
  187. };
  188. const globCWD = effectiveGlobbyOptions.cwd;
  189. let filePaths = await globby(fileList, effectiveGlobbyOptions);
  190. // Record the length of filePaths before ignore operation
  191. // Prevent prompting "No files matching the pattern 'xx' were found." when .stylelintignore ignore all input files
  192. const filePathsLengthBeforeIgnore = filePaths.length;
  193. // The ignorer filter needs to check paths relative to cwd
  194. filePaths = filterFilePaths(
  195. ignorer,
  196. filePaths.map((p) => path.relative(globCWD, p)),
  197. );
  198. let stylelintResults;
  199. if (filePaths.length) {
  200. let absoluteFilePaths = filePaths.map((filePath) => {
  201. const absoluteFilepath = !path.isAbsolute(filePath)
  202. ? path.join(globCWD, filePath)
  203. : path.normalize(filePath);
  204. return absoluteFilepath;
  205. });
  206. const getStylelintResults = absoluteFilePaths.map(async (absoluteFilepath) => {
  207. debug(`Processing ${absoluteFilepath}`);
  208. try {
  209. const postcssResult = await lintSource(stylelint, {
  210. filePath: absoluteFilepath,
  211. cache: useCache,
  212. });
  213. if (
  214. (postcssResult.stylelint.stylelintError || postcssResult.stylelint.stylelintWarning) &&
  215. useCache
  216. ) {
  217. debug(`${absoluteFilepath} contains linting errors and will not be cached.`);
  218. stylelint._fileCache.removeEntry(absoluteFilepath);
  219. }
  220. /**
  221. * If we're fixing, save the file with changed code
  222. */
  223. if (postcssResult.root && postcssResult.opts && !postcssResult.stylelint.ignored && fix) {
  224. const fixedCss = postcssResult.root.toString(postcssResult.opts.syntax);
  225. if (
  226. postcssResult.root &&
  227. postcssResult.root.source &&
  228. postcssResult.root.source.input.css !== fixedCss
  229. ) {
  230. await writeFileAtomic(absoluteFilepath, fixedCss);
  231. }
  232. }
  233. const stylelintResult = createPartialStylelintResult(postcssResult);
  234. await postProcessStylelintResult(stylelint, stylelintResult, absoluteFilepath);
  235. return stylelintResult;
  236. } catch (error) {
  237. // On any error, we should not cache the lint result
  238. stylelint._fileCache.removeEntry(absoluteFilepath);
  239. const stylelintResult = handleError(error);
  240. await postProcessStylelintResult(stylelint, stylelintResult, absoluteFilepath);
  241. return stylelintResult;
  242. }
  243. });
  244. stylelintResults = await Promise.all(getStylelintResults);
  245. } else if (await resolveOptionValue({ stylelint, name: 'allowEmptyInput', default: false })) {
  246. stylelintResults = await Promise.all([]);
  247. } else if (filePathsLengthBeforeIgnore) {
  248. // All input files ignored
  249. stylelintResults = await Promise.reject(new errors.AllFilesIgnoredError());
  250. } else {
  251. stylelintResults = await Promise.reject(new errors.NoFilesFoundError(fileList));
  252. }
  253. if (!useInputCode) {
  254. const resolvedSuppressLocation = resolveFilePath(
  255. suppressLocation || constants.DEFAULT_SUPPRESSION_FILENAME,
  256. cwd,
  257. constants.DEFAULT_SUPPRESSION_FILENAME,
  258. );
  259. const existsSuppressionsFile = fs.existsSync(resolvedSuppressLocation);
  260. if (suppressLocation && !existsSuppressionsFile && !suppressAll && !suppressRule) {
  261. throw new errors.SuppressionFileNotFoundError();
  262. }
  263. if (suppressAll || suppressRule || existsSuppressionsFile) {
  264. emitWarning.emitExperimentalWarning(
  265. 'The suppressions feature is experimental.',
  266. 'SUPPRESSIONS',
  267. 'See https://stylelint.io/user-guide/suppressions for more information.',
  268. );
  269. const suppressions = new suppressionsService.SuppressionsService({
  270. filePath: resolvedSuppressLocation,
  271. cwd: process.cwd(),
  272. });
  273. if (suppressAll || suppressRule) {
  274. await suppressions.suppress(stylelintResults, suppressRule);
  275. }
  276. const suppressionResults = suppressions.applySuppressions(
  277. stylelintResults,
  278. await suppressions.load(),
  279. );
  280. stylelintResults = suppressionResults.results;
  281. }
  282. }
  283. if (useCache) {
  284. stylelint._fileCache.reconcile();
  285. }
  286. const result = prepareReturnValue({
  287. results: stylelintResults,
  288. maxWarnings,
  289. quietDeprecationWarnings,
  290. formatter: formatterFunction,
  291. cwd,
  292. });
  293. debug(`Linting complete in ${Date.now() - startTime}ms`);
  294. return result;
  295. }
  296. /**
  297. * @import {CssSyntaxError} from 'stylelint'
  298. *
  299. * @param {unknown} error
  300. * @returns {LintResult}
  301. */
  302. function handleError(error) {
  303. if (error instanceof Error && error.name === 'CssSyntaxError') {
  304. return createPartialStylelintResult(undefined, /** @type {CssSyntaxError} */ (error));
  305. }
  306. throw error;
  307. }
  308. /**
  309. * @param {InternalApi} stylelint
  310. * @param {LintResult} stylelintResult
  311. * @param {string} [filePath]
  312. * @returns {Promise<void>}
  313. */
  314. async function postProcessStylelintResult(stylelint, stylelintResult, filePath) {
  315. const configForFile = await getConfigForFile({ stylelint, searchPath: filePath, filePath });
  316. const config = configForFile === null ? {} : configForFile.config;
  317. if (!config._processorFunctions) {
  318. return;
  319. }
  320. for (let postprocess of config._processorFunctions.values()) {
  321. postprocess?.(stylelintResult, stylelintResult._postcssResult?.root);
  322. }
  323. }
  324. module.exports = standalone;