UNPKG

textlint

Version:

The pluggable linting tool for text and markdown.

271 lines 9.38 kB
// LICENSE : MIT "use strict"; import { TextLintCore } from "../textlint-core"; import { RuleMap } from "../../engine/rule-map"; import { PluginMap } from "../../engine/processor-map"; import { Config } from "../config"; import { findFiles, pathsToGlobPatterns } from "../../util/old-find-util"; import { TextLintModuleLoader } from "./textlint-module-loader"; import { ExecuteFileBackerManager } from "../../engine/execute-file-backer-manager"; import { CacheBacker } from "../../engine/execute-file-backers/cache-backer"; import { SeverityLevel } from "../../shared/type/SeverityLevel"; import debug0 from "debug"; import path from "path"; import { separateByAvailability } from "../../util/separate-by-availability"; const debug = debug0("textlint:engine-core"); /** * Core of TextLintEngine. * It is internal user. * * Hackable adaptor * * - executeOnFiles * - executeOnText * - formatResults * * There are hackable by `executor` option. */ export class AbstractTextLintEngine { /** * Process files are wanted to lint. * TextLintEngine is a wrapper of textlint.js. * Aim to be called from cli with cli options. * @param {Config|Object} [options] the options is command line options or Config object. * @constructor */ constructor(options) { /** * @type {Config} */ if (options instanceof Config) { // Almost internal use-case this.config = options; } else { this.config = Config.initWithAutoLoading(options); } /** * @type {TextLintCore} * @private */ this.textlint = new TextLintCore(this.config); /** * @type {ExecuteFileBackerManager} * @private */ this.executeFileBackerManger = new ExecuteFileBackerManager(); const cacheBaker = new CacheBacker(this.config); if (this.config.cache) { this.executeFileBackerManger.add(cacheBaker); } else { cacheBaker.destroyCache(); } /** * @type {RuleMap} ruleMap is used for linting/fixer * @private */ this.ruleMap = new RuleMap(); /** * @type {RuleMap} filerRuleMap is used for filtering * @private */ this.filterRuleMap = new RuleMap(); /** * @type {PluginMap} * @private */ this.pluginMap = new PluginMap(); /** * @type {TextLintModuleLoader} * @private */ this.moduleLoader = new TextLintModuleLoader(this.config); this.moduleLoader.on(TextLintModuleLoader.Event.rule, ([ruleName, ruleCreator]) => { this.ruleMap.defineRule(ruleName, ruleCreator); }); this.moduleLoader.on(TextLintModuleLoader.Event.filterRule, ([ruleName, ruleCreator]) => { this.filterRuleMap.defineRule(ruleName, ruleCreator); }); this.moduleLoader.on(TextLintModuleLoader.Event.plugin, ([pluginName, plugin]) => { this.pluginMap.set(pluginName, plugin); }); // load rule/plugin/processor this.moduleLoader.loadFromConfig(this.config); // set settings to textlint core this._setupRules(); } /** * @deprecated remove this method */ setRulesBaseDirectory() { throw new Error(`Should not use setRulesBaseDirectory(), insteadof use new TextLintEngine({ rulesBaseDirectory: directory }) `); } /** * load plugin manually * Note: it high cost, please use config * @param {string} pluginName * @deprecated use Constructor(config) insteadof it */ loadPlugin(pluginName) { this.moduleLoader.loadPlugin(pluginName); this._setupRules(); } /** * load plugin manually * Note: it high cost, please use config * @param {string} presetName * @deprecated use Constructor(config) insteadof it */ loadPreset(presetName) { this.moduleLoader.loadPreset(presetName); this._setupRules(); } /** * load rule manually * Note: it high cost, please use config * @param {string} ruleName * @deprecated use Constructor(config) insteadof it */ loadRule(ruleName) { this.moduleLoader.loadRule(ruleName); this._setupRules(); } /** * load filter rule manually * Note: it high cost, please use config * @param {string} ruleName * @deprecated use Constructor(config) insteadof it */ loadFilerRule(ruleName) { this.moduleLoader.loadFilterRule(ruleName); this._setupRules(); } /** * Update rules from current config * @private */ _setupRules() { // set Rules const textlintConfig = this.config ? this.config.toJSON() : {}; this.textlint.setupRules(this.ruleMap.getAllRules(), textlintConfig.rulesConfig); this.textlint.setupFilterRules(this.filterRuleMap.getAllRules(), textlintConfig.filterRulesConfig); // set Processor this.textlint.setupPlugins(this.pluginMap.toJSON(), textlintConfig.pluginsConfig); } /** * Remove all registered rule and clear messages. * @private */ resetRules() { this.textlint.resetRules(); this.ruleMap.resetRules(); this.filterRuleMap.resetRules(); } /** * Return available extensions of plugins that include built-in plugins * @example * ``` * engine.availableExtensions; // => [".txt", ".md"] * ``` */ get availableExtensions() { return this.textlint.textlintKernelDescriptor.availableExtensions; } /** * Return meta descriptor object for this engine * * WARNING: This is experimental getter method. * It will be renamed. */ get textlintrcDescriptor() { return this.textlint.textlintKernelDescriptor; } /** * Executes the current configuration on an array of file and directory names. * @param {String[]} files An array of file and directory names. * @returns {Promise<TextlintResult[]>} The results for all files that were linted. */ executeOnFiles(files) { const execFile = this.onFile(this.textlint); const patterns = pathsToGlobPatterns(files, { extensions: this.textlintrcDescriptor.availableExtensions }); const targetFiles = findFiles(patterns, { ignoreFilePath: this.config.ignoreFile }); // Maybe, unAvailableFilePath should be warning. // But, The user can use glob pattern like `src/**/*` as arguments. // pathsToGlobPatterns not modified that pattern. // So, unAvailableFilePath should be ignored silently. const { availableFiles, unAvailableFiles } = separateByAvailability(targetFiles, { extensions: this.textlintrcDescriptor.availableExtensions }); debug("Process files", availableFiles); debug("No Process files that are un-support extensions:", unAvailableFiles); // FIXME: remove cast return this.executeFileBackerManger.process(availableFiles, execFile); } /** * If want to lint a text, use it. * But, if you have a target file, use {@link executeOnFiles} instead of it. * @param {string} text linting text content * @param {string} ext ext is a type for linting. default: ".txt" * @returns {Promise<TextlintResult[]>} */ executeOnText(text, ext = ".txt") { const textlint = this.textlint; const execText = this.onText(textlint); // filePath or ext const actualExt = ext[0] === "." ? ext : path.extname(ext); if (actualExt.length === 0) { throw new Error("should specify the extension.\nex) .md"); } return execText(text, actualExt).then((result) => { return [result]; }); } /** * format {@link results} and return output text. * @param {TextlintResult[]} results the collection of result * @returns {string} formatted output text * @example * console.log(formatResults(results)); */ formatResults(results) { // default formatter: "stylish" const formatter = this.onFormat({ formatterName: this.config.formatterName || "stylish", color: this.config.color }); return formatter(results); } /** * Checks if the given message is an error message. * @param {TextlintMessage} message The message to check. * @returns {boolean} Whether or not the message is an error message. */ isErrorMessage(message) { return message.severity === SeverityLevel.error; } /** * Checks if the given results contain error message. * If there is even one error then return true. * @param {TextlintResult[]} results Linting result collection * @returns {Boolean} Whether or not the results contain error message. */ isErrorResults(results) { return results.some((result) => { return result.messages.some(this.isErrorMessage); }); } /** * @returns {boolean} */ hasRuleAtLeastOne() { return this.ruleMap.hasRuleAtLeastOne(); } } //# sourceMappingURL=textlint-engine-core.js.map