UNPKG

@textlint/kernel

Version:
226 lines (220 loc) 8.83 kB
// MIT © 2017- azu // sequence import FixerProcessor from "./fixer/fixer-processor"; // parallel import LinterProcessor from "./linter/linter-processor"; // message process manager import MessageProcessManager from "./messages/MessageProcessManager"; import filterIgnoredProcess from "./messages/filter-ignored-process"; import filterDuplicatedProcess from "./messages/filter-duplicated-process"; import filterSeverityProcess from "./messages/filter-severity-process"; import sortMessageProcess from "./messages/sort-messages-process"; import { TextlintKernelDescriptor } from "./descriptor"; import { TextlintSourceCodeImpl } from "./context/TextlintSourceCodeImpl"; import { isPluginParsedObject } from "./util/isPluginParsedObject"; import { invariant } from "./util/invariant"; import { coreFlags } from "@textlint/feature-flag"; import { isTxtAST } from "@textlint/ast-tester"; import _debug from "debug"; import { parseByPlugin } from "./util/parse-by-plugin"; import { createDummyTextLintResult } from "./util/createDummyTextLintResult"; const debug = _debug("textlint:kernel"); /** * add fileName to trailing of error message * @param {string|undefined} fileName * @param {string} message * @returns {string} */ function addingAtFileNameToError(fileName, message) { if (!fileName) { return message; } return `${message} at ${fileName}`; } /** * TextlintKernel is core logic written by pure JavaScript. * * Pass * * - config * - plugins * - rules * - filterRules * - messageProcessor * */ export class TextlintKernel { /** * @param config */ constructor(config = {}) { // this.config often is undefined. this.config = config; // Initialize Message Processor // Now, It is built-in process only // filter `shouldIgnore()` results this.messageProcessManager = new MessageProcessManager([filterIgnoredProcess]); // filter duplicated messages this.messageProcessManager.add(filterDuplicatedProcess); // filter by severity this.messageProcessManager.add(filterSeverityProcess(this.config)); this.messageProcessManager.add(sortMessageProcess); } /** * lint text by registered rules. * The result contains target filePath and error messages. * @param {string} text * @param {Object} options linting options * @returns {Promise.<TextlintResult>} */ lintText(text, options) { return Promise.resolve().then(() => { const descriptor = new TextlintKernelDescriptor({ rules: options.rules || [], filterRules: options.filterRules || [], plugins: options.plugins || [] }); return this._parallelProcess({ descriptor, text, options }); }); } /** * fix texts and return fix result object * @param {string} text * @param {Object} options lint options * @returns {Promise.<TextlintFixResult>} */ fixText(text, options) { return Promise.resolve().then(() => { const descriptor = new TextlintKernelDescriptor({ rules: options.rules || [], filterRules: options.filterRules || [], plugins: options.plugins || [] }); return this._sequenceProcess({ descriptor, options, text }); }); } /** * process text in parallel for Rules and return {Promise.<TextLintResult>} * In other word, parallel flow process. * @param {*} processor * @param {string} text * @param {Object} options * @returns {Promise.<TextlintResult>} * @private */ async _parallelProcess({ descriptor, text, options }) { const { ext, filePath, configBaseDir } = options; const plugin = descriptor.findPluginDescriptorWithExt(ext); if (plugin === undefined) { throw new Error(`Not found available plugin for ${ext}`); } debug("used plugin %j", plugin.id); const processor = plugin.processor; const { preProcess, postProcess } = processor.processor(ext); invariant(typeof preProcess === "function" && typeof postProcess === "function", `${plugin.id} processor should implements {preProcess, postProcess}`); const preProcessResult = await parseByPlugin({ preProcess, sourceText: text, filePath }); if (preProcessResult instanceof Error) { return createDummyTextLintResult(`Failed to parse text by plugin: ${plugin.id} Please report this error with the content to plugin author. ${preProcessResult.stack} `, filePath); } // { text, ast } or ast const isParsedObject = isPluginParsedObject(preProcessResult); const textForAST = isParsedObject ? preProcessResult.text : text; const ast = isParsedObject ? preProcessResult.ast : preProcessResult; invariant(typeof textForAST === "string", `${plugin.id} processor should return correct text`); invariant(typeof ast === "object", `${plugin.id} processor should return correct AST object`); if (coreFlags.runningTester) { invariant(isTxtAST(ast), `${plugin.id} processor return invalid AST object. Please check out @textlint/ast-tester. You can check the validation result with "DEBUG=textlint*" env See https://textlint.github.io/docs/plugin.html`); } const sourceCode = new TextlintSourceCodeImpl({ text: textForAST, ast, ext, filePath }); debug("process file %s", filePath); const linterProcessor = new LinterProcessor(processor, this.messageProcessManager); return await linterProcessor .process({ config: this.config, ruleDescriptors: descriptor.rule, filterRuleDescriptors: descriptor.filterRule, sourceCode, configBaseDir }) .catch((error) => { error.message = addingAtFileNameToError(filePath, error.message); return Promise.reject(error); }); } /** * process text in series for Rules and return {Promise.<TextlintFixResult>} * In other word, sequence flow process. * @param {*} processor * @param {string} text * @param {TextlintKernelOptions} options * @returns {Promise.<TextlintFixResult>} * @private */ async _sequenceProcess({ descriptor, text, options }) { const { ext, filePath, configBaseDir } = options; const plugin = descriptor.findPluginDescriptorWithExt(ext); if (plugin === undefined) { throw new Error(`Not found available plugin for ${ext}`); } debug("used plugin %j", plugin.id); const processor = plugin.processor; const { preProcess, postProcess } = processor.processor(ext); invariant(typeof preProcess === "function" && typeof postProcess === "function", `${plugin.id} processor should implements {preProcess, postProcess}`); const preProcessResult = await Promise.resolve(preProcess(text, filePath)); // { text, ast } or ast const isParsedObject = isPluginParsedObject(preProcessResult); const textForAST = isParsedObject ? preProcessResult.text : text; const ast = isParsedObject ? preProcessResult.ast : preProcessResult; invariant(typeof textForAST === "string", `${plugin.id} processor should return correct text`); invariant(typeof ast === "object", `${plugin.id} processor should return correct AST object`); if (coreFlags.runningTester) { invariant(isTxtAST(ast), `${plugin.id} processor return invalid AST object. Please check out @textlint/ast-tester. You can check the validation result with "DEBUG=textlint*" env See https://textlint.github.io/docs/plugin.html`); } const sourceCode = new TextlintSourceCodeImpl({ text: textForAST, ast, ext, filePath }); debug("process file %s", filePath); const fixerProcessor = new FixerProcessor(processor, this.messageProcessManager); return await fixerProcessor .process({ config: this.config, ruleDescriptors: descriptor.rule, filterRules: descriptor.filterRule, sourceCode, configBaseDir }) .catch((error) => { error.message = addingAtFileNameToError(filePath, error.message); return Promise.reject(error); }); } } //# sourceMappingURL=textlint-kernel.js.map