UNPKG

textlint

Version:

The pluggable linting tool for text and markdown.

351 lines 16.2 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 }); exports.Config = void 0; const crypto_1 = __importDefault(require("crypto")); const config_loader_1 = require("./config/config-loader"); const preset_loader_1 = require("./config/preset-loader"); const plugin_loader_1 = require("./config/plugin-loader"); const textlint_module_resolver_1 = require("./engine/textlint-module-resolver"); const separate_by_config_option_1 = require("./config/separate-by-config-option"); const utils_1 = require("@textlint/utils"); const logger_1 = require("../util/logger"); // @ts-expect-error no types. it will be removed const md5_1 = __importDefault(require("md5")); const fs_1 = __importDefault(require("fs")); const assert_1 = __importDefault(require("assert")); // @ts-expect-error no types. it will be removed const unique_concat_1 = __importDefault(require("unique-concat")); const path_1 = __importDefault(require("path")); const read_pkg_up_1 = __importDefault(require("read-pkg-up")); function applyNormalizerToList(normalizer, names) { return names.map((name) => { return normalizer(name); }); } function applyNormalizerToConfig(normalizer, config) { return Object.keys(config).reduce((results, key) => { const shortPluginName = normalizer(key); results[shortPluginName] = config[key]; return results; }, {}); } /** * @type {TextlintConfig} */ const defaultOptions = Object.freeze({ // rule package names rules: [], // disabled rule package names // always should start with empty disabledRules: [], // rules config object rulesConfig: {}, // filter rule package names filterRules: [], disabledFilterRules: [], // rules config object filterRulesConfig: {}, // preset package names // e.g.) ["preset-foo"] presets: [], // plugin package names plugins: [], // plugin config pluginsConfig: {}, // base directory for loading {rule, config, plugin} modules rulesBaseDirectory: undefined, // ".textlint" file path configFile: undefined, // rule directories rulePaths: [], // formatter file name // e.g.) stylish.js => set "stylish" // NOTE: default formatter is defined in Engine, // because There is difference between TextLintEngine and TextFixEngine. formatterName: undefined, // --quiet quiet: false, // --no-color color: true, // --no-textlintrc textlintrc: true, // --cache : enable or disable cache: false, // --cache-location: cache file path cacheLocation: path_1.default.resolve(process.cwd(), ".textlintcache"), // --ignore-path: ".textlintignore" file path ignoreFile: path_1.default.resolve(process.cwd(), ".textlintignore") }); // Priority: CLI > Code options > config file class Config { /** * @return {string} rc config filename * it's name use as `.<name>rc` */ static get CONFIG_FILE_NAME() { return "textlint"; } /** * Create config object form command line options * See options.js * @param {object} cliOptions the options is command line option object. @see options.js * @returns {Config} */ static initWithCLIOptions(cliOptions) { const options = {}; options.rules = cliOptions.rule ? cliOptions.rule : defaultOptions.rules; // TODO: CLI --filter <rule>? options.filterRules = defaultOptions.filterRules; options.disabledFilterRules = defaultOptions.disabledFilterRules; // TODO: CLI --disable <rule>? options.disabledRules = defaultOptions.disabledRules; options.presets = cliOptions.preset ? cliOptions.preset : defaultOptions.presets; options.plugins = cliOptions.plugin ? cliOptions.plugin : defaultOptions.plugins; options.configFile = cliOptions.config ? cliOptions.config : defaultOptions.configFile; options.rulePaths = cliOptions.rulesdir ? cliOptions.rulesdir : defaultOptions.rulePaths; options.formatterName = cliOptions.format ? cliOptions.format : defaultOptions.formatterName; options.quiet = cliOptions.quiet !== undefined ? cliOptions.quiet : defaultOptions.quiet; options.color = cliOptions.color !== undefined ? cliOptions.color : defaultOptions.color; // --no-textlintrc: disable textlint options.textlintrc = cliOptions.textlintrc !== undefined ? cliOptions.textlintrc : defaultOptions.textlintrc; // --cache options.cache = cliOptions.cache !== undefined ? cliOptions.cache : defaultOptions.cache; // --cache-location="path/to/file" options.cacheLocation = cliOptions.cacheLocation !== undefined ? path_1.default.resolve(process.cwd(), cliOptions.cacheLocation) : defaultOptions.cacheLocation; // --rules-base-directory "other/node_modules" options.rulesBaseDirectory = cliOptions.rulesBaseDirectory || defaultOptions.rulesBaseDirectory; // --ignore-path="path/to/file" options.ignoreFile = cliOptions.ignorePath !== undefined ? path_1.default.resolve(process.cwd(), cliOptions.ignorePath) : defaultOptions.ignoreFile; return this.initWithAutoLoading(options); } /* eslint-disable complexity */ /** * load config and merge options. * These config is user defined options. * These config is prefer than preset packages's config that is defined by package author. * @param options */ static initWithAutoLoading(options = {}) { // Base directory const rulesBaseDirectory = options.rulesBaseDirectory ? options.rulesBaseDirectory : defaultOptions.rulesBaseDirectory; // Create resolver const moduleResolver = new textlint_module_resolver_1.TextLintModuleResolver({ rulesBaseDirectory }); // => ConfigFile // configFile is optional // => load .textlintrc const loadedResult = typeof options.textlintrc === "undefined" || options.textlintrc ? (0, config_loader_1.loadConfig)({ configFileName: this.CONFIG_FILE_NAME, configFilePath: options.configFile, moduleResolver, cwd: options.cwd }) : { config: {}, filePath: undefined }; const configFileRaw = loadedResult.config; const configFilePath = loadedResult.filePath; // => Load options from .textlintrc const configRuleNamesObject = (0, separate_by_config_option_1.separateEnabledOrDisabled)(configFileRaw.rules); const configFilterRuleNamesObject = (0, separate_by_config_option_1.separateEnabledOrDisabled)(configFileRaw.filters); const configPresetNames = configRuleNamesObject.presetNames; const configFilePlugins = (0, plugin_loader_1.getPluginNames)(configFileRaw); const configFilePluginConfig = (0, plugin_loader_1.getPluginConfig)(configFileRaw); // Notes: vs. loadRulesConfigFromPresets // loadRulesConfigFromPresets load rules config from **preset package**. (It is not user defined config. It is defined by package author) // In other hands, this line load rules config from .textlintrc. (It is user defined config) const configFileRulesConfig = (0, preset_loader_1.createFlatRulesConfigFromRawRulesConfig)(configFileRaw.rules); const configFileFilterRulesConfig = (0, preset_loader_1.createFlatRulesConfigFromRawRulesConfig)(configFileRaw.filters); // => User specified Options const optionRules = options.rules || []; const optionFilterRules = options.filterRules || []; const optionDisabledRules = options.disabledRules || []; const optionDisabledFilterRules = options.disabledFilterRules || []; const optionRulesConfig = options.rulesConfig || {}; const optionFilterRulesConfig = options.filterRulesConfig || {}; const optionPlugins = options.plugins || []; const optionPresets = options.presets || []; const optionPluginsConfig = options.pluginsConfig || {}; // => Merge options and configFileOptions // Priority options > configFile const rules = (0, unique_concat_1.default)(optionRules, configRuleNamesObject.enabledRuleNames); const disabledRules = (0, unique_concat_1.default)(optionDisabledRules, configRuleNamesObject.disabledRuleNames); const filterRules = (0, unique_concat_1.default)(optionFilterRules, configFilterRuleNamesObject.enabledRuleNames); const disabledFilterRules = (0, unique_concat_1.default)(optionDisabledFilterRules, configFilterRuleNamesObject.disabledRuleNames); const rulesConfig = Object.assign({}, configFileRulesConfig, optionRulesConfig); const filterRulesConfig = Object.assign({}, configFileFilterRulesConfig, optionFilterRulesConfig); const plugins = (0, unique_concat_1.default)(optionPlugins, configFilePlugins); const pluginsConfig = Object.assign({}, configFilePluginConfig, optionPluginsConfig); const presets = (0, unique_concat_1.default)(optionPresets, configPresetNames); const mergedOptions = Object.assign({}, options, { rules, disabledRules, rulesConfig, filterRules, disabledFilterRules, filterRulesConfig, plugins, pluginsConfig, presets, configFile: configFilePath }); return new this(mergedOptions); } /** * Return hash string of the config and textlint version * @returns {string} */ get hash() { try { const version = read_pkg_up_1.default.sync({ cwd: __dirname }).pkg.version; const toString = JSON.stringify(this.toJSON()); return (0, md5_1.default)(`${version}-${toString}`); } catch (error) { // Fallback for some env // https://github.com/textlint/textlint/issues/597 logger_1.Logger.warn("Use random value as hash because calculating hash value throw error", error); return crypto_1.default.randomBytes(20).toString("hex"); } } /** * initialize with options. * @param {TextlintConfig} options the option object is defined as TextlintConfig. * @returns {Config} * @constructor */ constructor(options = {}) { /** * @type {string|undefined} absolute path to .textlintrc file. * - If using .textlintrc, return path to .textlintrc * - If using npm config module, return path to main file of the module * - If not using config file, return undefined */ this.configFile = options.configFile; if (this.configFile) { assert_1.default.ok(path_1.default.isAbsolute(this.configFile), `configFile should be absolute path: ${this.configFile}`); } this.rulesBaseDirectory = options.rulesBaseDirectory ? options.rulesBaseDirectory : defaultOptions.rulesBaseDirectory; // rule names that are defined in ,textlintrc const moduleResolver = new textlint_module_resolver_1.TextLintModuleResolver({ rulesBaseDirectory: this.rulesBaseDirectory }); /** * @type {string[]} rule key list * These rule is set `false` to options */ this.disabledRules = applyNormalizerToList(utils_1.normalizeTextlintRuleKey, options.disabledRules ? options.disabledRules : defaultOptions.disabledRules); /** * @type {string[]} rule key list * rules does not includes disabledRules */ this.rules = applyNormalizerToList(utils_1.normalizeTextlintRuleKey, options.rules ? options.rules : defaultOptions.rules).filter((ruleName) => { return !this.disabledRules.includes(ruleName); }); /** * @type {string[]} rule key list * These rule is set `false` to options */ this.disabledFilterRules = applyNormalizerToList(utils_1.normalizeTextlintFilterRuleKey, options.disabledFilterRules ? options.disabledFilterRules : defaultOptions.disabledFilterRules); /** * @type {string[]} filter rule key list */ this.filterRules = applyNormalizerToList(utils_1.normalizeTextlintFilterRuleKey, options.filterRules ? options.filterRules : defaultOptions.filterRules).filter((ruleName) => { return !this.disabledFilterRules.includes(ruleName); }); /** * @type {string[]} preset key list */ this.presets = applyNormalizerToList(utils_1.normalizeTextlintRulePresetKey, options.presets ? options.presets : defaultOptions.presets); // => load plugins // this.rules has not contain plugin rules // ===================== this.plugins = applyNormalizerToList(utils_1.normalizeTextlintPluginKey, options.plugins ? options.plugins : defaultOptions.plugins); this.pluginsConfig = applyNormalizerToConfig(utils_1.normalizeTextlintPluginKey, options.pluginsConfig ? options.pluginsConfig : defaultOptions.pluginsConfig); // rulesConfig // load preset package's config and merge it to user defined rules config // user config > default preset config const presetRulesConfig = (0, preset_loader_1.loadRulesConfigFromPresets)(this.presets, moduleResolver); this.rulesConfig = applyNormalizerToConfig(utils_1.normalizeTextlintRuleKey, Object.assign({}, presetRulesConfig, options.rulesConfig)); // filterRulesConfig this.filterRulesConfig = applyNormalizerToConfig(utils_1.normalizeTextlintFilterRuleKey, options.filterRulesConfig || defaultOptions.filterRulesConfig); /** * @type {string[]} */ this.rulePaths = options.rulePaths ? options.rulePaths : defaultOptions.rulePaths; /** * @type {string} */ this.formatterName = options.formatterName ? options.formatterName : defaultOptions.formatterName; /** * @type {boolean} */ this.quiet = options.quiet !== undefined ? options.quiet : defaultOptions.quiet; /** * @type {boolean} */ this.color = options.color !== undefined ? options.color : defaultOptions.color; /** * @type {boolean} */ this.cache = options.cache !== undefined ? options.cache : defaultOptions.cache; /** * @type {string} */ this.cacheLocation = options.cacheLocation !== undefined ? options.cacheLocation : defaultOptions.cacheLocation; this._assertCacheLocation(this.cacheLocation); /** * @type {string} */ this.ignoreFile = options.ignoreFile !== undefined ? options.ignoreFile : defaultOptions.ignoreFile; } _assertCacheLocation(locationPath) { let fileStats; try { fileStats = fs_1.default.lstatSync(locationPath); } catch (ex) { fileStats = null; } if (!fileStats) { return; } // TODO: --cache-location does not support directory // We should defined what is default name. assert_1.default.ok(!fileStats.isDirectory(), "--cache-location doesn't support directory"); } /* eslint-enable complexity */ toJSON() { const r = Object.create(null); Object.keys(this).forEach((key) => { if (!this.hasOwnProperty(key)) { return; } const value = this[key]; if (value == null) { return; } r[key] = typeof value.toJSON !== "undefined" ? value.toJSON() : value; }); return r; } } exports.Config = Config; //# sourceMappingURL=config.js.map