UNPKG

@textlint/kernel

Version:
181 lines 7.3 kB
// LICENSE : MIT "use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const TextlintRuleErrorImpl_1 = require("../context/TextlintRuleErrorImpl"); const promise_event_emitter_1 = require("./promise-event-emitter"); const source_location_1 = require("../core/source-location"); const timing_1 = __importDefault(require("../util/timing")); const invariant_1 = require("../util/invariant"); const MessageType_1 = __importDefault(require("../shared/type/MessageType")); const utils_1 = require("@textlint/utils"); const TextlintRuleContextImpl_1 = require("../context/TextlintRuleContextImpl"); const debug_1 = __importDefault(require("debug")); const ast_traverse_1 = require("@textlint/ast-traverse"); const traverseController = new ast_traverse_1.Controller(); const debug = (0, debug_1.default)("textlint:core-task"); class RuleTypeEmitter extends promise_event_emitter_1.PromiseEventEmitter { } /** * CoreTask receive AST and prepare, traverse AST, emit nodeType event! * You can observe task and receive "message" event that is TextLintMessage. */ class TextLintCoreTask extends promise_event_emitter_1.EventEmitter { static get events() { return { // receive start event start: "start", // receive message from each rules message: "message", // receive complete event complete: "complete", // receive error event error: "error" }; } constructor() { super(); this.ruleTypeEmitter = new RuleTypeEmitter(); } createShouldIgnore() { const shouldIgnore = (args) => { const { ruleId, range, optional } = args; (0, invariant_1.invariant)(typeof range[0] !== "undefined" && typeof range[1] !== "undefined" && range[0] >= 0 && range[1] >= 0, "ignoreRange should have actual range: " + range); // FIXME: should have index, loc // should be compatible with LintReportedMessage? const message = { type: MessageType_1.default.ignore, ruleId: ruleId, range: range, // ignoring target ruleId - default: filter all messages // This ruleId should be normalized, because the user can report any value ignoringRuleId: optional.ruleId ? (0, utils_1.normalizeTextlintKeyPath)(optional.ruleId) : "*" }; this.emit(TextLintCoreTask.events.message, message); }; return shouldIgnore; } createReporter(sourceCode) { /** * push new RuleError to results * @param {ReportMessage} reportArgs */ const reportFunction = (reportArgs) => { const { ruleId, node, severity, ruleError } = reportArgs; const { loc, range } = (0, source_location_1.resolveLocation)({ source: sourceCode, ruleId, node, ruleError }); const { fix } = (0, source_location_1.resolveFixCommandLocation)({ node, ruleError }); debug("%s report %s", ruleId, ruleError); // add TextLintMessage const message = { type: MessageType_1.default.lint, ruleId: ruleId, message: ruleError.message, index: range[0], line: loc.start.line, column: loc.start.column, range, loc, severity: severity, // it's for compatible ESLint formatter fix: fix !== undefined ? fix : undefined }; if (!(ruleError instanceof TextlintRuleErrorImpl_1.TextlintRuleErrorImpl)) { // FIXME: RuleReportedObject should be removed // `error` is a any data. const data = ruleError; message.data = data; } this.emit(TextLintCoreTask.events.message, message); }; return reportFunction; } /** * start process and emitting events. * You can listen message by `task.on("message", message => {})` * @param {SourceCode} sourceCode */ startTraverser(sourceCode) { this.emit(TextLintCoreTask.events.start); const promiseQueue = []; const ruleTypeEmitter = this.ruleTypeEmitter; traverseController.traverse(sourceCode.ast, { enter(node, parent) { const type = node.type; Object.defineProperty(node, "parent", { value: parent }); if (ruleTypeEmitter.listenerCount(type) > 0) { const promise = ruleTypeEmitter.emit(type, node); promiseQueue.push(promise); } }, leave(node) { const type = `${node.type}:exit`; if (ruleTypeEmitter.listenerCount(type) > 0) { const promise = ruleTypeEmitter.emit(type, node); promiseQueue.push(promise); } } }); Promise.all(promiseQueue) .then(() => { this.emit(TextLintCoreTask.events.complete); }) .catch((error) => { this.emit(TextLintCoreTask.events.error, error); }); } /** * try to get rule object */ tryToGetRuleObject(ruleCreator, ruleContext, ruleOptions) { try { return ruleCreator(ruleContext, ruleOptions); } catch (error) { if (error instanceof Error) { error.message = `Error while loading rule '${ruleContext.id}': ${error.message}`; } throw error; } } /** * try to get filter rule object */ tryToGetFilterRuleObject(ruleCreator, ruleContext, ruleOptions) { try { return ruleCreator(ruleContext, ruleOptions); } catch (error) { if (error instanceof Error) { error.message = `Error while loading filter rule '${ruleContext.id}': ${error.message}`; } throw error; } } /** * add all the node types as listeners of the rule * @param {Function} ruleCreator * @param {Readonly<RuleContext>|Readonly<FilterRuleContext>} ruleContext * @param {Object|boolean|undefined} ruleOptions * @returns {Object} */ tryToAddListenRule(ruleCreator, ruleContext, ruleOptions) { const ruleObject = ruleContext instanceof TextlintRuleContextImpl_1.TextlintRuleContextImpl ? this.tryToGetRuleObject(ruleCreator, ruleContext, ruleOptions) : this.tryToGetFilterRuleObject(ruleCreator, ruleContext, ruleOptions); const types = Object.keys(ruleObject); types.forEach((nodeType) => { this.ruleTypeEmitter.on(nodeType, timing_1.default.enabled ? timing_1.default.time(ruleContext.id, ruleObject[nodeType]) : ruleObject[nodeType]); }); } } exports.default = TextLintCoreTask; //# sourceMappingURL=textlint-core-task.js.map