processor-service.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. /**
  2. * @fileoverview ESLint Processor Service
  3. * @author Nicholas C. Zakas
  4. */
  5. /* eslint class-methods-use-this: off -- Anticipate future constructor arguments. */
  6. "use strict";
  7. //-----------------------------------------------------------------------------
  8. // Requirements
  9. //-----------------------------------------------------------------------------
  10. const path = require("node:path");
  11. const { VFile } = require("../linter/vfile.js");
  12. //-----------------------------------------------------------------------------
  13. // Types
  14. //-----------------------------------------------------------------------------
  15. /** @typedef {import("../types").Linter.LintMessage} LintMessage */
  16. /** @typedef {import("../linter/vfile.js").VFile} VFile */
  17. /** @typedef {import("@eslint/core").Language} Language */
  18. /** @typedef {import("eslint").Linter.Processor} Processor */
  19. //-----------------------------------------------------------------------------
  20. // Exports
  21. //-----------------------------------------------------------------------------
  22. /**
  23. * The service that applies processors to files.
  24. */
  25. class ProcessorService {
  26. /**
  27. * Preprocesses the given file synchronously.
  28. * @param {VFile} file The file to preprocess.
  29. * @param {{processor:Processor}} config The configuration to use.
  30. * @returns {{ok:boolean, files?: Array<VFile>, errors?: Array<LintMessage>}} An array of preprocessed files or errors.
  31. * @throws {Error} If the preprocessor returns a promise.
  32. */
  33. preprocessSync(file, config) {
  34. const { processor } = config;
  35. let blocks;
  36. try {
  37. blocks = processor.preprocess(file.rawBody, file.path);
  38. } catch (ex) {
  39. // If the message includes a leading line number, strip it:
  40. const message = `Preprocessing error: ${ex.message.replace(/^line \d+:/iu, "").trim()}`;
  41. return {
  42. ok: false,
  43. errors: [
  44. {
  45. ruleId: null,
  46. fatal: true,
  47. severity: 2,
  48. message,
  49. line: ex.lineNumber,
  50. column: ex.column,
  51. nodeType: null,
  52. },
  53. ],
  54. };
  55. }
  56. if (typeof blocks.then === "function") {
  57. throw new Error("Unsupported: Preprocessor returned a promise.");
  58. }
  59. return {
  60. ok: true,
  61. files: blocks.map((block, i) => {
  62. // Legacy behavior: return the block as a string
  63. if (typeof block === "string") {
  64. return block;
  65. }
  66. const filePath = path.join(file.path, `${i}_${block.filename}`);
  67. return new VFile(filePath, block.text, {
  68. physicalPath: file.physicalPath,
  69. });
  70. }),
  71. };
  72. }
  73. /**
  74. * Postprocesses the given messages synchronously.
  75. * @param {VFile} file The file to postprocess.
  76. * @param {LintMessage[][]} messages The messages to postprocess.
  77. * @param {{processor:Processor}} config The configuration to use.
  78. * @returns {LintMessage[]} The postprocessed messages.
  79. */
  80. postprocessSync(file, messages, config) {
  81. const { processor } = config;
  82. return processor.postprocess(messages, file.path);
  83. }
  84. }
  85. module.exports = { ProcessorService };