UNPKG

eslint-plugin-eslint-config

Version:
354 lines (280 loc) 12.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.tryGetConfigInfo = exports.getConfigInfo = exports.ESLintErrorType = void 0; var _eslint = _interopRequireDefault(require("eslint")); var _requireRelative = _interopRequireDefault(require("require-relative")); var _vm = require("vm"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } const pathToBlankFile = require.resolve('../blank.js'); const getsertCache = (cache, key, fn, _name) => { const cached = cache.get(key); if (cached) { // console.debug(`${_name}: hit`); return cached; } // console.debug(`${_name}: miss`); const fresh = fn(); cache.set(key, fresh); return fresh; }; const relativeRequire = name => (0, _requireRelative.default)(name, process.cwd()); relativeRequire.resolve = name => _requireRelative.default.resolve(name, process.cwd()); const compileConfigCodeCache = new Map(); const compileConfigCode = fileCode => { return getsertCache(compileConfigCodeCache, fileCode, () => { var _runInNewContext; return (_runInNewContext = (0, _vm.runInNewContext)(fileCode, { module: { exports: {} }, require: relativeRequire })) !== null && _runInNewContext !== void 0 ? _runInNewContext : {}; }, 'compileConfigCode'); }; const createCliEngineCache = new Map(); const createCLIEngine = config => { const extraConfig = { parserOptions: _objectSpread(_objectSpread({}, config.parserOptions), {}, { project: require.resolve('../../tsconfig.fake.json'), projectFolderIgnoreList: [] }) }; if (config.ignorePatterns) { const patterns = Array.isArray(config.ignorePatterns) ? config.ignorePatterns : [config.ignorePatterns]; extraConfig.ignorePatterns = patterns.filter(pattern => typeof pattern !== 'string'); } return getsertCache(createCliEngineCache, JSON.stringify(config), () => new _eslint.default.CLIEngine({ useEslintrc: false, ignorePath: pathToBlankFile, ignorePattern: ['!node_modules/*'], cache: false, envs: ['node'], baseConfig: _objectSpread(_objectSpread({}, config), extraConfig) }), 'createCLIEngine'); }; const mergeConfigInfo = (a, b) => ({ deprecatedRules: [...a.deprecatedRules, ...b.deprecatedRules], unknownRules: [...a.unknownRules, ...b.unknownRules], errors: [...a.errors, ...b.errors] }); const ensureArray = (v = []) => Array.isArray(v) ? v : [v]; /** * Merges two ESLint configs, favoring the second config over the first. * * Merging is done at the top level only, and based on explicit named properties, * so any additional properties on either config will be lost. */ const mergeConfigs = (a, b) => { var _b$parser, _b$processor; return { parser: (_b$parser = b.parser) !== null && _b$parser !== void 0 ? _b$parser : a.parser, parserOptions: _objectSpread(_objectSpread({}, a.parserOptions), b.parserOptions), processor: (_b$processor = b.processor) !== null && _b$processor !== void 0 ? _b$processor : a.processor, env: _objectSpread(_objectSpread({}, a.env), b.env), globals: _objectSpread(_objectSpread({}, a.globals), b.globals), plugins: [...ensureArray(a.plugins), ...(Array.isArray(b.plugins) ? b.plugins : [])], extends: [...ensureArray(a.extends), ...ensureArray(b.extends)], settings: _objectSpread(_objectSpread({}, a.settings), b.settings), overrides: [...ensureArray(a.overrides), ...ensureArray(b.overrides)], rules: _objectSpread(_objectSpread({}, a.rules), b.rules) }; }; const extractRelevantConfigs = config => { var _config$overrides; return [_objectSpread(_objectSpread({}, config), {}, { overrides: [] }), ...((_config$overrides = config.overrides) !== null && _config$overrides !== void 0 ? _config$overrides : []).map(override => extractRelevantConfigs(mergeConfigs(_objectSpread(_objectSpread({}, config), {}, { overrides: [], rules: {} }), override))).reduce((prev, curr) => prev.concat(curr), [])]; }; let ESLintErrorType; exports.ESLintErrorType = ESLintErrorType; (function (ESLintErrorType) { ESLintErrorType["FailedToLoadModule"] = "FailedToLoadModule"; ESLintErrorType["InvalidRuleConfig"] = "InvalidRuleConfig"; ESLintErrorType["ProcessorNotFound"] = "ProcessorNotFound"; ESLintErrorType["FailedToExtend"] = "FailedToExtend"; ESLintErrorType["InvalidConfig"] = "InvalidConfig"; })(ESLintErrorType || (exports.ESLintErrorType = ESLintErrorType = {})); const tryParseAsFailedToLoadModuleError = error => { var _$exec; // noinspection RegExpRedundantEscape const [, kind, name, path] = (_$exec = /Failed to load ([\0-\u{10FFFF}]+) '([\0-\u{10FFFF}]*)' declared in 'BaseConfig((?:#overrides\[[0-9]*?\])*)': Cannot find module '/iu.exec(error.message.trim())) !== null && _$exec !== void 0 ? _$exec : []; return kind // ? { type: ESLintErrorType.FailedToLoadModule, kind, name, path } : null; }; const tryParseAsInvalidRuleConfigError = error => { var _$exec2; // noinspection RegExpRedundantEscape const [, path, ruleId, reason] = (_$exec2 = /BaseConfig((?:#overrides\[[0-9]*?\])*):[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]+Configuration for rule "([\0-\u{10FFFF}]+)" is invalid:([\0-\u{10FFFF}]+)/iu.exec(error.message.trim())) !== null && _$exec2 !== void 0 ? _$exec2 : []; return ruleId // ? { type: ESLintErrorType.InvalidRuleConfig, reason, ruleId, path } : null; }; const tryParseAsFailedToExtendError = error => { var _$exec3; // noinspection RegExpRedundantEscape const [, name, path] = (_$exec3 = /Failed to load config "([\0-\u{10FFFF}]+)" to extend from\.\nReferenced from: BaseConfig((?:#overrides\[[0-9]*?\])*)/iu.exec(error.message.trim())) !== null && _$exec3 !== void 0 ? _$exec3 : []; return name // ? { type: ESLintErrorType.FailedToExtend, name, path } : null; }; const tryToParseAsProcessorNotFoundError = error => { var _$exec4; const [, name] = (_$exec4 = /ESLint configuration of processor in '(?:[\0-\u{10FFFF}]+)' is invalid: '([\0-\u{10FFFF}]+)' was not found\./iu.exec(error.message.trim())) !== null && _$exec4 !== void 0 ? _$exec4 : []; return name // ? { type: ESLintErrorType.ProcessorNotFound, name } : null; }; const tryParseAsInvalidConfigError = error => { var _$exec5; const [, reason] = (_$exec5 = /ESLint configuration (?:[\0-\u{10FFFF}]+) is invalid:([\0-\u{10FFFF}]+)/iu.exec(error.message.trim())) !== null && _$exec5 !== void 0 ? _$exec5 : []; return reason // ? { type: ESLintErrorType.InvalidConfig, reason } : null; }; const errorParsers = [tryParseAsFailedToLoadModuleError, tryParseAsInvalidRuleConfigError, tryToParseAsProcessorNotFoundError, tryParseAsFailedToExtendError, tryParseAsInvalidConfigError]; const parseESLintError = error => { for (const errorParser of errorParsers) { const parsedError = errorParser(error); if (parsedError) { return parsedError; } } error.message = `Unable to parse error from ESLint: ${error.message}`; throw error; }; const followErrorPathToConfig = (config, error) => { if (!error.path || !config.overrides) { return config; } const levels = error.path.substr(1).split('#').map(p => parseInt(p.substring(p.indexOf('[') + 1, p.length - 1))); /* istanbul ignore next */ return levels.reduce((conf, i) => { var _conf$overrides$i, _conf$overrides; return (_conf$overrides$i = (_conf$overrides = conf.overrides) === null || _conf$overrides === void 0 ? void 0 : _conf$overrides[i]) !== null && _conf$overrides$i !== void 0 ? _conf$overrides$i : {}; }, config); }; const tryRemoveErrorPointFromConfig = (config, error) => { var _configToDeleteFrom$p; const configToDeleteFrom = followErrorPathToConfig(config, error); if (error.type === ESLintErrorType.FailedToExtend) { if (typeof configToDeleteFrom.extends === 'string') { delete configToDeleteFrom.extends; return true; } configToDeleteFrom.extends = ensureArray(configToDeleteFrom.extends).filter(extend => extend !== error.name); return true; } if (error.type === ESLintErrorType.FailedToLoadModule) { switch (error.kind) { case 'plugin': configToDeleteFrom.plugins = (_configToDeleteFrom$p = configToDeleteFrom.plugins) === null || _configToDeleteFrom$p === void 0 ? void 0 : _configToDeleteFrom$p.filter(plugin => plugin !== error.name); return true; case 'parser': // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete configToDeleteFrom[error.kind]; return true; // no default } } if (error.type === ESLintErrorType.InvalidRuleConfig) { const { ruleId } = error; /* istanbul ignore if */ if (!(configToDeleteFrom.rules && ruleId in configToDeleteFrom.rules)) { throw new Error('Cannot delete InvalidRuleConfig error - please report'); } // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete configToDeleteFrom.rules[ruleId]; return true; } return false; }; const collectConfigInfoFromESLint = config => { const theConfig = _objectSpread({ rules: {} }, config); const errors = []; let counter = 0; do { /* istanbul ignore if */ if ((counter += 1) > 20) { throw new Error('inf. loop detected - please report'); } try { const results = createCLIEngine(theConfig).executeOnText('', pathToBlankFile); return { deprecatedRules: results.usedDeprecatedRules, unknownRules: results.results[0].messages.filter(({ message }) => /Definition for rule .+ was not found\./iu.test(message)).map(({ ruleId }) => ruleId).filter(ruleId => !!ruleId), errors }; } catch (error) { const eslintError = parseESLintError(error); errors.push(eslintError); if (tryRemoveErrorPointFromConfig(theConfig, eslintError)) { continue; } return { deprecatedRules: [], unknownRules: [], errors }; } // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition,no-constant-condition } while (true); }; const collectConfigInfoCache = new Map(); /** * Collects information about the given `config` using ESLint. * * Info about any `overrides` the `config` might have will be collected and * merged into the returned info object. */ const collectConfigInfo = config => { return getsertCache(collectConfigInfoCache, JSON.stringify(config), () => collectConfigInfoFromESLint(config), 'collectConfigInfo'); }; const getConfigInfoCache = new Map(); const getConfigInfo = configText => { return getsertCache(getConfigInfoCache, configText, () => { const config = compileConfigCode(configText); const topLevelInfo = collectConfigInfo(config); return extractRelevantConfigs(config).map(collectConfigInfo).map(info => _objectSpread(_objectSpread({}, info), {}, { errors: info.errors.filter(error => ![ESLintErrorType.InvalidRuleConfig, ESLintErrorType.InvalidConfig].includes(error.type)) })) // .concat(topLevelInfo).reduce(mergeConfigInfo); }, 'getConfigInfo'); }; exports.getConfigInfo = getConfigInfo; const tryGetConfigInfo = configText => { try { return getConfigInfo(configText); } catch (_unused) { return null; } }; exports.tryGetConfigInfo = tryGetConfigInfo;