SASSLint.js

  1. /* @flow */
  2. import type {LintCallback} from './typedef';
  3. import {join} from 'path';
  4. import forEach from 'lodash/forEach';
  5. import replace from 'lodash/replace';
  6. import {lint} from 'stylelint';
  7. import {logError} from './logger';
  8. /* eslint-disable lodash/prefer-map */
  9. const defaultConfigFile = join(__dirname, '..', '.stylelintrc.yaml');
  10. /**
  11. * A SASS linter
  12. *
  13. * @class SASSLint
  14. * @param {string} [configFile="webcompiler/.stylelintrc.yaml"] - path to the stylelint configuration file
  15. * @see {@link http://stylelint.io/ stylelint}
  16. * @example
  17. * import {SASSLint} from 'webcompiler';
  18. * // or - import {SASSLint} from 'webcompiler/lib/SASSLint';
  19. * // or - var SASSLint = require('webcompiler').SASSLint;
  20. * // or - var SASSLint = require('webcompiler/lib/SASSLint').SASSLint;
  21. *
  22. * const linter = new SASSLint();
  23. */
  24. export class SASSLint {
  25. /**
  26. * path to the stylelint configuration file
  27. *
  28. * @member {string} configFile
  29. * @memberof SASSLint
  30. * @private
  31. * @instance
  32. */
  33. configFile: string;
  34. // eslint-disable-next-line require-jsdoc
  35. constructor(configFile: string = defaultConfigFile) {
  36. this.configFile = configFile;
  37. }
  38. /**
  39. * Execute the linter
  40. *
  41. * @memberof SASSLint
  42. * @instance
  43. * @method run
  44. * @param {Array<string>} paths - an array of file globs. Ultimately passed to
  45. * [node-glob](https://github.com/isaacs/node-glob) to figure out what files you
  46. * want to lint.
  47. * @param {LintCallback} callback - a callback function
  48. * @example
  49. * import {join} from 'path';
  50. * import {logLintingErrors} from 'webcompiler';
  51. *
  52. * // lint "style.scss" as well as the entire contents of the "sass" directory
  53. * linter.run([join(__dirname, 'style.scss'), join(__dirname, 'sass', '**', '*.scss')], errors => {
  54. * if (errors) {
  55. * return logLintingErrors(errors);
  56. * }
  57. * // there were no linting errors
  58. * });
  59. */
  60. run(paths: string[], callback: LintCallback) {
  61. lint({configFile: this.configFile, files: paths}).then(({results}) => {
  62. const errors = [];
  63. forEach(results, ({source: file, warnings}) => {
  64. forEach(warnings, ({line, column, text, rule}) => {
  65. errors.push({file, line, column, message: replace(text, new RegExp(`\\s*\\(${rule}\\)$`), ''), rule});
  66. });
  67. });
  68. callback(errors.length ? errors : null);
  69. }).catch(logError);
  70. }
  71. }