diff --git a/tools/node_modules/eslint/README.md b/tools/node_modules/eslint/README.md index fd0c14668743bc..b7cc385f0e35c7 100644 --- a/tools/node_modules/eslint/README.md +++ b/tools/node_modules/eslint/README.md @@ -206,9 +206,9 @@ Brandon Mills Toru Nagashima - -
-Gyandeep Singh +
+
+Kai Cataldo
@@ -237,9 +237,9 @@ The people who review and implement new features. The people who review and fix bugs and help triage issues.
- -
-Kai Cataldo +
+
+Gyandeep Singh
@@ -265,7 +265,7 @@ The following companies, organizations, and individuals support ESLint's ongoing

Gold Sponsors

Shopify Salesforce Badoo Airbnb Facebook Open Source

Silver Sponsors

AMP Project

Bronze Sponsors

-

UI UX Design Agencies Bugsnag Stability Monitoring MiniTool Software Ltd Codacy Mixpanel VPS Server Free Icons by Icons8 Crosswordsolver clay Discord ThemeIsle TekHattan Marfeel Fire Stick Tricks JSHeroes

+

Bugsnag Stability Monitoring Crosswordsolver Codacy Mixpanel VPS Server Free Icons by Icons8 UI UX Design Agencies clay Discord ThemeIsle TekHattan Marfeel Fire Stick Tricks JSHeroes

## Technology Sponsors diff --git a/tools/node_modules/eslint/conf/config-schema.js b/tools/node_modules/eslint/conf/config-schema.js index 164f0b4219f319..83addff8781732 100644 --- a/tools/node_modules/eslint/conf/config-schema.js +++ b/tools/node_modules/eslint/conf/config-schema.js @@ -55,6 +55,7 @@ const configSchema = { type: "object", properties: { root: { type: "boolean" }, + ignorePatterns: { $ref: "#/definitions/stringOrStrings" }, ...baseConfigProperties }, additionalProperties: false diff --git a/tools/node_modules/eslint/conf/default-cli-options.js b/tools/node_modules/eslint/conf/default-cli-options.js index abbd9184e2165c..0f7b377fb362ec 100644 --- a/tools/node_modules/eslint/conf/default-cli-options.js +++ b/tools/node_modules/eslint/conf/default-cli-options.js @@ -14,7 +14,7 @@ module.exports = { globals: [], extensions: [".js"], ignore: true, - ignorePath: null, + ignorePath: void 0, cache: false, /* diff --git a/tools/node_modules/eslint/lib/cli-engine/cascading-config-array-factory.js b/tools/node_modules/eslint/lib/cli-engine/cascading-config-array-factory.js index 6c914ea491c1b4..52703a873bb67e 100644 --- a/tools/node_modules/eslint/lib/cli-engine/cascading-config-array-factory.js +++ b/tools/node_modules/eslint/lib/cli-engine/cascading-config-array-factory.js @@ -27,7 +27,7 @@ const os = require("os"); const path = require("path"); const { validateConfigArray } = require("../shared/config-validator"); const { ConfigArrayFactory } = require("./config-array-factory"); -const { ConfigArray, ConfigDependency } = require("./config-array"); +const { ConfigArray, ConfigDependency, IgnorePattern } = require("./config-array"); const loadRules = require("./load-rules"); const debug = require("debug")("eslint:cascading-config-array-factory"); @@ -45,8 +45,9 @@ const debug = require("debug")("eslint:cascading-config-array-factory"); * @typedef {Object} CascadingConfigArrayFactoryOptions * @property {Map} [additionalPluginPool] The map for additional plugins. * @property {ConfigData} [baseConfig] The config by `baseConfig` option. - * @property {ConfigData} [cliConfig] The config by CLI options (`--env`, `--global`, `--parser`, `--parser-options`, `--plugin`, and `--rule`). CLI options overwrite the setting in config files. + * @property {ConfigData} [cliConfig] The config by CLI options (`--env`, `--global`, `--ignore-pattern`, `--parser`, `--parser-options`, `--plugin`, and `--rule`). CLI options overwrite the setting in config files. * @property {string} [cwd] The base directory to start lookup. + * @property {string} [ignorePath] The path to the alternative file of `.eslintignore`. * @property {string[]} [rulePaths] The value of `--rulesdir` option. * @property {string} [specificConfigPath] The value of `--config` option. * @property {boolean} [useEslintrc] if `false` then it doesn't load config files. @@ -62,6 +63,7 @@ const debug = require("debug")("eslint:cascading-config-array-factory"); * @property {Map} configCache The cache from directory paths to config arrays. * @property {string} cwd The base directory to start lookup. * @property {WeakMap} finalizeCache The cache from config arrays to finalized config arrays. + * @property {string} [ignorePath] The path to the alternative file of `.eslintignore`. * @property {string[]|null} rulePaths The value of `--rulesdir` option. This is used to reset `baseConfigArray`. * @property {string|null} specificConfigPath The value of `--config` option. This is used to reset `cliConfigArray`. * @property {boolean} useEslintrc if `false` then it doesn't load config files. @@ -86,14 +88,22 @@ function createBaseConfigArray({ { name: "BaseConfig" } ); + /* + * Create the config array element for the default ignore patterns. + * This element has `ignorePattern` property that ignores the default + * patterns in the current working directory. + */ + baseConfigArray.unshift(configArrayFactory.create( + { ignorePatterns: IgnorePattern.DefaultPatterns }, + { name: "DefaultIgnorePattern" } + )[0]); + + /* + * Load rules `--rulesdir` option as a pseudo plugin. + * Use a pseudo plugin to define rules of `--rulesdir`, so we can validate + * the rule's options with only information in the config array. + */ if (rulePaths && rulePaths.length > 0) { - - /* - * Load rules `--rulesdir` option as a pseudo plugin. - * Use a pseudo plugin to define rules of `--rulesdir`, so we can - * validate the rule's options with only information in the config - * array. - */ baseConfigArray.push({ name: "--rulesdir", filePath: "", @@ -128,6 +138,7 @@ function createBaseConfigArray({ function createCLIConfigArray({ cliConfigData, configArrayFactory, + ignorePath, specificConfigPath }) { const cliConfigArray = configArrayFactory.create( @@ -135,6 +146,12 @@ function createCLIConfigArray({ { name: "CLIOptions" } ); + cliConfigArray.unshift( + ...(ignorePath + ? configArrayFactory.loadESLintIgnore(ignorePath) + : configArrayFactory.loadDefaultESLintIgnore()) + ); + if (specificConfigPath) { cliConfigArray.unshift( ...configArrayFactory.loadFile( @@ -178,6 +195,7 @@ class CascadingConfigArrayFactory { baseConfig: baseConfigData = null, cliConfig: cliConfigData = null, cwd = process.cwd(), + ignorePath, resolvePluginsRelativeTo = cwd, rulePaths = [], specificConfigPath = null, @@ -200,6 +218,7 @@ class CascadingConfigArrayFactory { cliConfigArray: createCLIConfigArray({ cliConfigData, configArrayFactory, + ignorePath, specificConfigPath }), cliConfigData, @@ -207,6 +226,7 @@ class CascadingConfigArrayFactory { configCache: new Map(), cwd, finalizeCache: new WeakMap(), + ignorePath, rulePaths, specificConfigPath, useEslintrc @@ -229,9 +249,11 @@ class CascadingConfigArrayFactory { * If `filePath` was not given, it returns the config which contains only * `baseConfigData` and `cliConfigData`. * @param {string} [filePath] The file path to a file. + * @param {Object} [options] The options. + * @param {boolean} [options.ignoreNotFoundError] If `true` then it doesn't throw `ConfigurationNotFoundError`. * @returns {ConfigArray} The config array of the file. */ - getConfigArrayForFile(filePath) { + getConfigArrayForFile(filePath, { ignoreNotFoundError = false } = {}) { const { baseConfigArray, cliConfigArray, @@ -248,7 +270,8 @@ class CascadingConfigArrayFactory { return this._finalizeConfigArray( this._loadConfigInAncestors(directoryPath), - directoryPath + directoryPath, + ignoreNotFoundError ); } @@ -354,10 +377,11 @@ class CascadingConfigArrayFactory { * Concatenate `--config` and other CLI options. * @param {ConfigArray} configArray The parent config array. * @param {string} directoryPath The path to the leaf directory to find config files. + * @param {boolean} ignoreNotFoundError If `true` then it doesn't throw `ConfigurationNotFoundError`. * @returns {ConfigArray} The loaded config. * @private */ - _finalizeConfigArray(configArray, directoryPath) { + _finalizeConfigArray(configArray, directoryPath, ignoreNotFoundError) { const { cliConfigArray, configArrayFactory, @@ -403,7 +427,8 @@ class CascadingConfigArrayFactory { ); } - if (useEslintrc && finalConfigArray.length === 0) { + // At least one element (the default ignore patterns) exists. + if (!ignoreNotFoundError && useEslintrc && finalConfigArray.length <= 1) { throw new ConfigurationNotFoundError(directoryPath); } diff --git a/tools/node_modules/eslint/lib/cli-engine/cli-engine.js b/tools/node_modules/eslint/lib/cli-engine/cli-engine.js index 8afd262708fa8b..0b1c76bac6864a 100644 --- a/tools/node_modules/eslint/lib/cli-engine/cli-engine.js +++ b/tools/node_modules/eslint/lib/cli-engine/cli-engine.js @@ -25,10 +25,9 @@ const ModuleResolver = require("../shared/relative-module-resolver"); const { Linter } = require("../linter"); const builtInRules = require("../rules"); const { CascadingConfigArrayFactory } = require("./cascading-config-array-factory"); -const { getUsedExtractedConfigs } = require("./config-array"); +const { IgnorePattern, getUsedExtractedConfigs } = require("./config-array"); const { FileEnumerator } = require("./file-enumerator"); const hash = require("./hash"); -const { IgnoredPaths } = require("./ignored-paths"); const LintResultCache = require("./lint-result-cache"); const debug = require("debug")("eslint:cli-engine"); @@ -64,7 +63,7 @@ const validFixTypes = new Set(["problem", "suggestion", "layout"]); * @property {string[]} globals An array of global variables to declare. * @property {boolean} ignore False disables use of .eslintignore. * @property {string} ignorePath The ignore file to use instead of .eslintignore. - * @property {string} ignorePattern A glob pattern of files to ignore. + * @property {string|string[]} ignorePattern One or more glob patterns to ignore. * @property {boolean} useEslintrc False disables looking for .eslintrc * @property {string} parser The name of the parser to use. * @property {ParserOptions} parserOptions An object of parserOption settings to use. @@ -113,8 +112,8 @@ const validFixTypes = new Set(["problem", "suggestion", "layout"]); * @property {Map} additionalPluginPool The map for additional plugins. * @property {string} cacheFilePath The path to the cache of lint results. * @property {CascadingConfigArrayFactory} configArrayFactory The factory of configs. + * @property {(filePath: string) => boolean} defaultIgnores The default predicate function to check if a file ignored or not. * @property {FileEnumerator} fileEnumerator The file enumerator. - * @property {IgnoredPaths} ignoredPaths The ignored paths. * @property {ConfigArray[]} lastConfigArrays The list of config arrays that the last `executeOnFiles` or `executeOnText` used. * @property {LintResultCache|null} lintResultCache The cache of lint results. * @property {Linter} linter The linter instance which has loaded rules. @@ -486,13 +485,20 @@ function toBooleanMap(keys, defaultValue, displayName) { * @returns {ConfigData|null} The created config data. */ function createConfigDataFromOptions(options) { - const { parser, parserOptions, plugins, rules } = options; + const { + ignorePattern, + parser, + parserOptions, + plugins, + rules + } = options; const env = toBooleanMap(options.envs, true, "envs"); const globals = toBooleanMap(options.globals, false, "globals"); if ( env === void 0 && globals === void 0 && + (ignorePattern === void 0 || ignorePattern.length === 0) && parser === void 0 && parserOptions === void 0 && plugins === void 0 && @@ -500,7 +506,15 @@ function createConfigDataFromOptions(options) { ) { return null; } - return { env, globals, parser, parserOptions, plugins, rules }; + return { + env, + globals, + ignorePatterns: ignorePattern, + parser, + parserOptions, + plugins, + rules + }; } /** @@ -551,19 +565,18 @@ class CLIEngine { baseConfig: options.baseConfig || null, cliConfig: createConfigDataFromOptions(options), cwd: options.cwd, + ignorePath: options.ignorePath, resolvePluginsRelativeTo: options.resolvePluginsRelativeTo, rulePaths: options.rulePaths, specificConfigPath: options.configFile, useEslintrc: options.useEslintrc }); - const ignoredPaths = new IgnoredPaths(options); const fileEnumerator = new FileEnumerator({ configArrayFactory, cwd: options.cwd, extensions: options.extensions, globInputPaths: options.globInputPaths, - ignore: options.ignore, - ignoredPaths + ignore: options.ignore }); const lintResultCache = options.cache ? new LintResultCache(cacheFilePath) : null; @@ -577,8 +590,8 @@ class CLIEngine { additionalPluginPool, cacheFilePath, configArrayFactory, + defaultIgnores: IgnorePattern.createDefaultIgnore(options.cwd), fileEnumerator, - ignoredPaths, lastConfigArrays, lintResultCache, linter, @@ -835,7 +848,6 @@ class CLIEngine { const { configArrayFactory, fileEnumerator, - ignoredPaths, lastConfigArrays, linter, options: { @@ -852,7 +864,7 @@ class CLIEngine { // Clear the last used config arrays. lastConfigArrays.length = 0; - if (resolvedFilename && ignoredPaths.contains(resolvedFilename)) { + if (resolvedFilename && this.isPathIgnored(resolvedFilename)) { if (warnIgnored) { results.push(createIgnoreResult(resolvedFilename, cwd)); } @@ -926,9 +938,23 @@ class CLIEngine { * @returns {boolean} Whether or not the given path is ignored. */ isPathIgnored(filePath) { - const { ignoredPaths } = internalSlotsMap.get(this); + const { + configArrayFactory, + defaultIgnores, + options: { cwd, ignore } + } = internalSlotsMap.get(this); + const absolutePath = path.resolve(cwd, filePath); + + if (ignore) { + const config = configArrayFactory + .getConfigArrayForFile(absolutePath) + .extractConfig(absolutePath); + const ignores = config.ignores || defaultIgnores; + + return ignores(absolutePath); + } - return ignoredPaths.contains(filePath); + return defaultIgnores(absolutePath); } /** diff --git a/tools/node_modules/eslint/lib/cli-engine/config-array-factory.js b/tools/node_modules/eslint/lib/cli-engine/config-array-factory.js index cf529b6ee6313e..c444031bcb0a57 100644 --- a/tools/node_modules/eslint/lib/cli-engine/config-array-factory.js +++ b/tools/node_modules/eslint/lib/cli-engine/config-array-factory.js @@ -17,6 +17,12 @@ * Create a `ConfigArray` instance from a config file which is on a given * directory. This tries to load `.eslintrc.*` or `package.json`. If not * found, returns an empty `ConfigArray`. + * - `loadESLintIgnore(filePath)` + * Create a `ConfigArray` instance from a config file that is `.eslintignore` + * format. This is to handle `--ignore-path` option. + * - `loadDefaultESLintIgnore()` + * Create a `ConfigArray` instance from `.eslintignore` or `package.json` in + * the current working directory. * * `ConfigArrayFactory` class has the responsibility that loads configuration * files, including loading `extends`, `parser`, and `plugins`. The created @@ -40,7 +46,12 @@ const stripComments = require("strip-json-comments"); const { validateConfigSchema } = require("../shared/config-validator"); const naming = require("../shared/naming"); const ModuleResolver = require("../shared/relative-module-resolver"); -const { ConfigArray, ConfigDependency, OverrideTester } = require("./config-array"); +const { + ConfigArray, + ConfigDependency, + IgnorePattern, + OverrideTester +} = require("./config-array"); const debug = require("debug")("eslint:config-array-factory"); //------------------------------------------------------------------------------ @@ -221,6 +232,26 @@ function loadPackageJSONConfigFile(filePath) { } } +/** + * Loads a `.eslintignore` from a file. + * @param {string} filePath The filename to load. + * @returns {string[]} The ignore patterns from the file. + * @private + */ +function loadESLintIgnoreFile(filePath) { + debug(`Loading .eslintignore file: ${filePath}`); + + try { + return readFile(filePath) + .split(/\r?\n/gu) + .filter(line => line.trim() !== "" && !line.startsWith("#")); + } catch (e) { + debug(`Error reading .eslintignore file: ${filePath}`); + e.message = `Cannot read .eslintignore file: ${filePath}\nError: ${e.message}`; + throw e; + } +} + /** * Creates an error to notify about a missing config to extend from. * @param {string} configName The name of the missing config. @@ -403,6 +434,54 @@ class ConfigArrayFactory { ); } + /** + * Load `.eslintignore` file. + * @param {string} filePath The path to a `.eslintignore` file to load. + * @returns {ConfigArray} Loaded config. An empty `ConfigArray` if any config doesn't exist. + */ + loadESLintIgnore(filePath) { + const { cwd } = internalSlotsMap.get(this); + const absolutePath = path.resolve(cwd, filePath); + const name = path.relative(cwd, absolutePath); + const ignorePatterns = loadESLintIgnoreFile(absolutePath); + + return createConfigArray( + this._normalizeESLintIgnoreData(ignorePatterns, absolutePath, name) + ); + } + + /** + * Load `.eslintignore` file in the current working directory. + * @returns {ConfigArray} Loaded config. An empty `ConfigArray` if any config doesn't exist. + */ + loadDefaultESLintIgnore() { + const { cwd } = internalSlotsMap.get(this); + const eslintIgnorePath = path.resolve(cwd, ".eslintignore"); + const packageJsonPath = path.resolve(cwd, "package.json"); + + if (fs.existsSync(eslintIgnorePath)) { + return this.loadESLintIgnore(eslintIgnorePath); + } + if (fs.existsSync(packageJsonPath)) { + const data = loadJSONConfigFile(packageJsonPath); + + if (Object.hasOwnProperty.call(data, "eslintIgnore")) { + if (!Array.isArray(data.eslintIgnore)) { + throw new Error("Package.json eslintIgnore property requires an array of paths"); + } + return createConfigArray( + this._normalizeESLintIgnoreData( + data.eslintIgnore, + packageJsonPath, + "eslintIgnore in package.json" + ) + ); + } + } + + return new ConfigArray(); + } + /** * Load a given config file. * @param {string} filePath The path to a config file. @@ -451,6 +530,30 @@ class ConfigArrayFactory { return null; } + /** + * Normalize a given `.eslintignore` data to config array elements. + * @param {string[]} ignorePatterns The patterns to ignore files. + * @param {string|undefined} filePath The file path of this config. + * @param {string|undefined} name The name of this config. + * @returns {IterableIterator} The normalized config. + * @private + */ + *_normalizeESLintIgnoreData(ignorePatterns, filePath, name) { + const elements = this._normalizeObjectConfigData( + { ignorePatterns }, + filePath, + name + ); + + // Set `ignorePattern.loose` flag for backward compatibility. + for (const element of elements) { + if (element.ignorePattern) { + element.ignorePattern.loose = true; + } + yield element; + } + } + /** * Normalize a given config to an array. * @param {ConfigData} configData The config data to normalize. @@ -494,6 +597,9 @@ class ConfigArrayFactory { if (element.criteria) { element.criteria.basePath = basePath; } + if (element.ignorePattern) { + element.ignorePattern.basePath = basePath; + } /* * Merge the criteria; this is for only file extension processors in @@ -526,6 +632,7 @@ class ConfigArrayFactory { env, extends: extend, globals, + ignorePatterns, noInlineConfig, parser: parserName, parserOptions, @@ -541,6 +648,10 @@ class ConfigArrayFactory { name ) { const extendList = Array.isArray(extend) ? extend : [extend]; + const ignorePattern = ignorePatterns && new IgnorePattern( + Array.isArray(ignorePatterns) ? ignorePatterns : [ignorePatterns], + filePath ? path.dirname(filePath) : internalSlotsMap.get(this).cwd + ); // Flatten `extends`. for (const extendName of extendList.filter(Boolean)) { @@ -569,6 +680,7 @@ class ConfigArrayFactory { criteria: null, env, globals, + ignorePattern, noInlineConfig, parser, parserOptions, diff --git a/tools/node_modules/eslint/lib/cli-engine/config-array/config-array.js b/tools/node_modules/eslint/lib/cli-engine/config-array/config-array.js index 089ff305a2ba44..4fae8deaca1b69 100644 --- a/tools/node_modules/eslint/lib/cli-engine/config-array/config-array.js +++ b/tools/node_modules/eslint/lib/cli-engine/config-array/config-array.js @@ -31,6 +31,7 @@ //------------------------------------------------------------------------------ const { ExtractedConfig } = require("./extracted-config"); +const { IgnorePattern } = require("./ignore-pattern"); //------------------------------------------------------------------------------ // Helpers @@ -54,6 +55,7 @@ const { ExtractedConfig } = require("./extracted-config"); * @property {InstanceType|null} criteria The tester for the `files` and `excludedFiles` of this config element. * @property {Record|undefined} env The environment settings. * @property {Record|undefined} globals The global variable settings. + * @property {IgnorePattern|undefined} ignorePattern The ignore patterns. * @property {boolean|undefined} noInlineConfig The flag that disables directive comments. * @property {DependentParser|undefined} parser The parser loader. * @property {Object|undefined} parserOptions The parser options. @@ -231,6 +233,7 @@ function mergeRuleConfigs(target, source) { */ function createConfig(instance, indices) { const config = new ExtractedConfig(); + const ignorePatterns = []; // Merge elements. for (const index of indices) { @@ -260,6 +263,11 @@ function createConfig(instance, indices) { config.reportUnusedDisableDirectives = element.reportUnusedDisableDirectives; } + // Collect ignorePatterns + if (element.ignorePattern) { + ignorePatterns.push(element.ignorePattern); + } + // Merge others. mergeWithoutOverwrite(config.env, element.env); mergeWithoutOverwrite(config.globals, element.globals); @@ -269,6 +277,11 @@ function createConfig(instance, indices) { mergeRuleConfigs(config.rules, element.rules); } + // Create the predicate function for ignore patterns. + if (ignorePatterns.length > 0) { + config.ignores = IgnorePattern.createIgnore(ignorePatterns.reverse()); + } + return config; } diff --git a/tools/node_modules/eslint/lib/cli-engine/config-array/extracted-config.js b/tools/node_modules/eslint/lib/cli-engine/config-array/extracted-config.js index 66858313ba6220..b27d6ffb188e58 100644 --- a/tools/node_modules/eslint/lib/cli-engine/config-array/extracted-config.js +++ b/tools/node_modules/eslint/lib/cli-engine/config-array/extracted-config.js @@ -16,6 +16,8 @@ */ "use strict"; +const { IgnorePattern } = require("./ignore-pattern"); + // For VSCode intellisense /** @typedef {import("../../shared/types").ConfigData} ConfigData */ /** @typedef {import("../../shared/types").GlobalConf} GlobalConf */ @@ -23,6 +25,17 @@ /** @typedef {import("./config-dependency").DependentParser} DependentParser */ /** @typedef {import("./config-dependency").DependentPlugin} DependentPlugin */ +/** + * Check if `xs` starts with `ys`. + * @template T + * @param {T[]} xs The array to check. + * @param {T[]} ys The array that may be the first part of `xs`. + * @returns {boolean} `true` if `xs` starts with `ys`. + */ +function startsWith(xs, ys) { + return xs.length >= ys.length && ys.every((y, i) => y === xs[i]); +} + /** * The class for extracted config data. */ @@ -47,6 +60,12 @@ class ExtractedConfig { */ this.globals = {}; + /** + * The glob patterns that ignore to lint. + * @type {(((filePath:string, dot?:boolean) => boolean) & { basePath:string; patterns:string[] }) | undefined} + */ + this.ignores = void 0; + /** * The flag that disables directive comments. * @type {boolean|undefined} @@ -106,11 +125,19 @@ class ExtractedConfig { configNameOfNoInlineConfig: _ignore1, processor: _ignore2, /* eslint-enable no-unused-vars */ + ignores, ...config } = this; config.parser = config.parser && config.parser.filePath; config.plugins = Object.keys(config.plugins).filter(Boolean).reverse(); + config.ignorePatterns = ignores ? ignores.patterns : []; + + // Strip the default patterns from `ignorePatterns`. + if (startsWith(config.ignorePatterns, IgnorePattern.DefaultPatterns)) { + config.ignorePatterns = + config.ignorePatterns.slice(IgnorePattern.DefaultPatterns.length); + } return config; } diff --git a/tools/node_modules/eslint/lib/cli-engine/config-array/ignore-pattern.js b/tools/node_modules/eslint/lib/cli-engine/config-array/ignore-pattern.js new file mode 100644 index 00000000000000..6140194433e861 --- /dev/null +++ b/tools/node_modules/eslint/lib/cli-engine/config-array/ignore-pattern.js @@ -0,0 +1,231 @@ +/** + * @fileoverview `IgnorePattern` class. + * + * `IgnorePattern` class has the set of glob patterns and the base path. + * + * It provides two static methods. + * + * - `IgnorePattern.createDefaultIgnore(cwd)` + * Create the default predicate function. + * - `IgnorePattern.createIgnore(ignorePatterns)` + * Create the predicate function from multiple `IgnorePattern` objects. + * + * It provides two properties and a method. + * + * - `patterns` + * The glob patterns that ignore to lint. + * - `basePath` + * The base path of the glob patterns. If absolute paths existed in the + * glob patterns, those are handled as relative paths to the base path. + * - `getPatternsRelativeTo(basePath)` + * Get `patterns` as modified for a given base path. It modifies the + * absolute paths in the patterns as prepending the difference of two base + * paths. + * + * `ConfigArrayFactory` creates `IgnorePattern` objects when it processes + * `ignorePatterns` properties. + * + * @author Toru Nagashima + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const assert = require("assert"); +const path = require("path"); +const ignore = require("ignore"); +const debug = require("debug")("eslint:ignore-pattern"); + +/** @typedef {ReturnType} Ignore */ + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +/** + * Get the path to the common ancestor directory of given paths. + * @param {string[]} sourcePaths The paths to calculate the common ancestor. + * @returns {string} The path to the common ancestor directory. + */ +function getCommonAncestorPath(sourcePaths) { + let result = sourcePaths[0]; + + for (let i = 1; i < sourcePaths.length; ++i) { + const a = result; + const b = sourcePaths[i]; + + // Set the shorter one (it's the common ancestor if one includes the other). + result = a.length < b.length ? a : b; + + // Set the common ancestor. + for (let j = 0, lastSepPos = 0; j < a.length && j < b.length; ++j) { + if (a[j] !== b[j]) { + result = a.slice(0, lastSepPos); + break; + } + if (a[j] === path.sep) { + lastSepPos = j; + } + } + } + + return result || path.sep; +} + +/** + * Make relative path. + * @param {string} from The source path to get relative path. + * @param {string} to The destination path to get relative path. + * @returns {string} The relative path. + */ +function relative(from, to) { + const relPath = path.relative(from, to); + + if (path.sep === "/") { + return relPath; + } + return relPath.split(path.sep).join("/"); +} + +/** + * Get the trailing slash if existed. + * @param {string} filePath The path to check. + * @returns {string} The trailing slash if existed. + */ +function dirSuffix(filePath) { + const isDir = ( + filePath.endsWith(path.sep) || + (process.platform === "win32" && filePath.endsWith("/")) + ); + + return isDir ? "/" : ""; +} + +const DefaultPatterns = Object.freeze(["/node_modules/*", "/bower_components/*"]); +const DotPatterns = Object.freeze([".*", "!../"]); + +//------------------------------------------------------------------------------ +// Public +//------------------------------------------------------------------------------ + +class IgnorePattern { + + /** + * The default patterns. + * @type {string[]} + */ + static get DefaultPatterns() { + return DefaultPatterns; + } + + /** + * Create the default predicate function. + * @param {string} cwd The current working directory. + * @returns {((filePath:string, dot:boolean) => boolean) & {basePath:string; patterns:string[]}} + * The preficate function. + * The first argument is an absolute path that is checked. + * The second argument is the flag to not ignore dotfiles. + * If the predicate function returned `true`, it means the path should be ignored. + */ + static createDefaultIgnore(cwd) { + return this.createIgnore([new IgnorePattern(DefaultPatterns, cwd)]); + } + + /** + * Create the predicate function from multiple `IgnorePattern` objects. + * @param {IgnorePattern[]} ignorePatterns The list of ignore patterns. + * @returns {((filePath:string, dot?:boolean) => boolean) & {basePath:string; patterns:string[]}} + * The preficate function. + * The first argument is an absolute path that is checked. + * The second argument is the flag to not ignore dotfiles. + * If the predicate function returned `true`, it means the path should be ignored. + */ + static createIgnore(ignorePatterns) { + debug("Create with: %o", ignorePatterns); + + const basePath = getCommonAncestorPath(ignorePatterns.map(p => p.basePath)); + const patterns = [].concat( + ...ignorePatterns.map(p => p.getPatternsRelativeTo(basePath)) + ); + const ig = ignore().add([...DotPatterns, ...patterns]); + const dotIg = ignore().add(patterns); + + debug(" processed: %o", { basePath, patterns }); + + return Object.assign( + (filePath, dot = false) => { + assert(path.isAbsolute(filePath), "'filePath' should be an absolute path."); + const relPathRaw = relative(basePath, filePath); + const relPath = relPathRaw && (relPathRaw + dirSuffix(filePath)); + const adoptedIg = dot ? dotIg : ig; + const result = relPath !== "" && adoptedIg.ignores(relPath); + + debug("Check", { filePath, dot, relativePath: relPath, result }); + return result; + }, + { basePath, patterns } + ); + } + + /** + * Initialize a new `IgnorePattern` instance. + * @param {string[]} patterns The glob patterns that ignore to lint. + * @param {string} basePath The base path of `patterns`. + */ + constructor(patterns, basePath) { + assert(path.isAbsolute(basePath), "'basePath' should be an absolute path."); + + /** + * The glob patterns that ignore to lint. + * @type {string[]} + */ + this.patterns = patterns; + + /** + * The base path of `patterns`. + * @type {string} + */ + this.basePath = basePath; + + /** + * If `true` then patterns which don't start with `/` will match the paths to the outside of `basePath`. Defaults to `false`. + * + * It's set `true` for `.eslintignore`, `package.json`, and `--ignore-path` for backward compatibility. + * It's `false` as-is for `ignorePatterns` property in config files. + * @type {boolean} + */ + this.loose = false; + } + + /** + * Get `patterns` as modified for a given base path. It modifies the + * absolute paths in the patterns as prepending the difference of two base + * paths. + * @param {string} newBasePath The base path. + * @returns {string[]} Modifired patterns. + */ + getPatternsRelativeTo(newBasePath) { + assert(path.isAbsolute(newBasePath), "'newBasePath' should be an absolute path."); + const { basePath, loose, patterns } = this; + + if (newBasePath === basePath) { + return patterns; + } + const prefix = `/${relative(newBasePath, basePath)}`; + + return patterns.map(pattern => { + const negative = pattern.startsWith("!"); + const head = negative ? "!" : ""; + const body = negative ? pattern.slice(1) : pattern; + + if (body.startsWith("/") || body.startsWith("../")) { + return `${head}${prefix}${body}`; + } + return loose ? pattern : `${head}${prefix}/**/${body}`; + }); + } +} + +module.exports = { IgnorePattern }; diff --git a/tools/node_modules/eslint/lib/cli-engine/config-array/index.js b/tools/node_modules/eslint/lib/cli-engine/config-array/index.js index de8831906fd6ef..928d76c83ab1f7 100644 --- a/tools/node_modules/eslint/lib/cli-engine/config-array/index.js +++ b/tools/node_modules/eslint/lib/cli-engine/config-array/index.js @@ -7,12 +7,14 @@ const { ConfigArray, getUsedExtractedConfigs } = require("./config-array"); const { ConfigDependency } = require("./config-dependency"); const { ExtractedConfig } = require("./extracted-config"); +const { IgnorePattern } = require("./ignore-pattern"); const { OverrideTester } = require("./override-tester"); module.exports = { ConfigArray, ConfigDependency, ExtractedConfig, + IgnorePattern, OverrideTester, getUsedExtractedConfigs }; diff --git a/tools/node_modules/eslint/lib/cli-engine/file-enumerator.js b/tools/node_modules/eslint/lib/cli-engine/file-enumerator.js index 38f55de039d6d1..700f8009cf88f2 100644 --- a/tools/node_modules/eslint/lib/cli-engine/file-enumerator.js +++ b/tools/node_modules/eslint/lib/cli-engine/file-enumerator.js @@ -40,8 +40,8 @@ const getGlobParent = require("glob-parent"); const isGlob = require("is-glob"); const { escapeRegExp } = require("lodash"); const { Minimatch } = require("minimatch"); +const { IgnorePattern } = require("./config-array"); const { CascadingConfigArrayFactory } = require("./cascading-config-array-factory"); -const { IgnoredPaths } = require("./ignored-paths"); const debug = require("debug")("eslint:file-enumerator"); //------------------------------------------------------------------------------ @@ -64,7 +64,6 @@ const IGNORED = 2; * @property {string[]} [extensions] The extensions to match files for directory patterns. * @property {boolean} [globInputPaths] Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file. * @property {boolean} [ignore] The flag to check ignored files. - * @property {IgnoredPaths} [ignoredPaths] The ignored paths. * @property {string[]} [rulePaths] The value of `--rulesdir` option. */ @@ -92,8 +91,7 @@ const IGNORED = 2; * @property {RegExp} extensionRegExp The RegExp to test if a string ends with specific file extensions. * @property {boolean} globInputPaths Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file. * @property {boolean} ignoreFlag The flag to check ignored files. - * @property {IgnoredPaths} ignoredPathsWithDotfiles The ignored paths but don't include dot files. - * @property {IgnoredPaths} ignoredPaths The ignored paths. + * @property {(filePath:string, dot:boolean) => boolean} defaultIgnores The default predicate function to ignore files. */ /** @type {WeakMap} */ @@ -192,12 +190,12 @@ class FileEnumerator { configArrayFactory = new CascadingConfigArrayFactory({ cwd }), extensions = [".js"], globInputPaths = true, - ignore = true, - ignoredPaths = new IgnoredPaths({ cwd, ignore }) + ignore = true } = {}) { internalSlotsMap.set(this, { configArrayFactory, cwd, + defaultIgnores: IgnorePattern.createDefaultIgnore(cwd), extensionRegExp: new RegExp( `.\\.(?:${extensions .map(ext => escapeRegExp( @@ -210,12 +208,7 @@ class FileEnumerator { "u" ), globInputPaths, - ignoreFlag: ignore, - ignoredPaths, - ignoredPathsWithDotfiles: new IgnoredPaths({ - ...ignoredPaths.options, - dotfiles: true - }) + ignoreFlag: ignore }); } @@ -321,7 +314,7 @@ class FileEnumerator { const { configArrayFactory } = internalSlotsMap.get(this); const config = configArrayFactory.getConfigArrayForFile(filePath); - const ignored = this._isIgnoredFile(filePath, { direct: true }); + const ignored = this._isIgnoredFile(filePath, { config, direct: true }); const flag = ignored ? IGNORED : NONE; return [{ config, filePath, flag }]; @@ -353,7 +346,7 @@ class FileEnumerator { _iterateFilesWithGlob(pattern, dotfiles) { debug(`Glob: ${pattern}`); - const directoryPath = getGlobParent(pattern); + const directoryPath = path.resolve(getGlobParent(pattern)); const globPart = pattern.slice(directoryPath.length + 1); /* @@ -399,9 +392,18 @@ class FileEnumerator { // Check if the file is matched. if (stat && stat.isFile()) { if (!config) { - config = configArrayFactory.getConfigArrayForFile(filePath); + config = configArrayFactory.getConfigArrayForFile( + filePath, + + /* + * We must ignore `ConfigurationNotFoundError` at this + * point because we don't know if target files exist in + * this directory. + */ + { ignoreNotFoundError: true } + ); } - const ignored = this._isIgnoredFile(filePath, options); + const ignored = this._isIgnoredFile(filePath, { ...options, config }); const flag = ignored ? IGNORED_SILENTLY : NONE; const matched = options.selector @@ -413,7 +415,11 @@ class FileEnumerator { if (matched) { debug(`Yield: ${filename}${ignored ? " but ignored" : ""}`); - yield { config, filePath, flag }; + yield { + config: configArrayFactory.getConfigArrayForFile(filePath), + filePath, + flag + }; } else { debug(`Didn't match: ${filename}`); } @@ -431,24 +437,37 @@ class FileEnumerator { * Check if a given file should be ignored. * @param {string} filePath The path to a file to check. * @param {Object} options Options + * @param {ConfigArray} [options.config] The config for this file. * @param {boolean} [options.dotfiles] If `true` then this is not ignore dot files by default. * @param {boolean} [options.direct] If `true` then this is a direct specified file. * @returns {boolean} `true` if the file should be ignored. * @private */ - _isIgnoredFile(filePath, { dotfiles = false, direct = false }) { + _isIgnoredFile(filePath, { + config: providedConfig, + dotfiles = false, + direct = false + }) { const { - ignoreFlag, - ignoredPaths, - ignoredPathsWithDotfiles + configArrayFactory, + defaultIgnores, + ignoreFlag } = internalSlotsMap.get(this); - const adoptedIgnoredPaths = dotfiles - ? ignoredPathsWithDotfiles - : ignoredPaths; - return ignoreFlag - ? adoptedIgnoredPaths.contains(filePath) - : (!direct && adoptedIgnoredPaths.contains(filePath, "default")); + if (ignoreFlag) { + const config = + providedConfig || + configArrayFactory.getConfigArrayForFile( + filePath, + { ignoreNotFoundError: true } + ); + const ignores = + config.extractConfig(filePath).ignores || defaultIgnores; + + return ignores(filePath, dotfiles); + } + + return !direct && defaultIgnores(filePath, dotfiles); } } diff --git a/tools/node_modules/eslint/lib/cli-engine/ignored-paths.js b/tools/node_modules/eslint/lib/cli-engine/ignored-paths.js deleted file mode 100644 index dec8e1860420e2..00000000000000 --- a/tools/node_modules/eslint/lib/cli-engine/ignored-paths.js +++ /dev/null @@ -1,363 +0,0 @@ -/** - * @fileoverview Responsible for loading ignore config files and managing ignore patterns - * @author Jonathan Rajavuori - */ - -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const fs = require("fs"), - path = require("path"), - ignore = require("ignore"); - -const debug = require("debug")("eslint:ignored-paths"); - -//------------------------------------------------------------------------------ -// Constants -//------------------------------------------------------------------------------ - -const ESLINT_IGNORE_FILENAME = ".eslintignore"; - -/** - * Adds `"*"` at the end of `"node_modules/"`, - * so that subtle directories could be re-included by .gitignore patterns - * such as `"!node_modules/should_not_ignored"` - */ -const DEFAULT_IGNORE_DIRS = [ - "/node_modules/*", - "/bower_components/*" -]; -const DEFAULT_OPTIONS = { - dotfiles: false, - cwd: process.cwd() -}; - -//------------------------------------------------------------------------------ -// Helpers -//------------------------------------------------------------------------------ - -/** - * Find a file in the current directory. - * @param {string} cwd Current working directory - * @param {string} name File name - * @returns {string} Path of ignore file or an empty string. - */ -function findFile(cwd, name) { - const ignoreFilePath = path.resolve(cwd, name); - - return fs.existsSync(ignoreFilePath) && fs.statSync(ignoreFilePath).isFile() ? ignoreFilePath : ""; -} - -/** - * Find an ignore file in the current directory. - * @param {string} cwd Current working directory - * @returns {string} Path of ignore file or an empty string. - */ -function findIgnoreFile(cwd) { - return findFile(cwd, ESLINT_IGNORE_FILENAME); -} - -/** - * Find an package.json file in the current directory. - * @param {string} cwd Current working directory - * @returns {string} Path of package.json file or an empty string. - */ -function findPackageJSONFile(cwd) { - return findFile(cwd, "package.json"); -} - -/** - * Merge options with defaults - * @param {Object} options Options to merge with DEFAULT_OPTIONS constant - * @returns {Object} Merged options - */ -function mergeDefaultOptions(options) { - return Object.assign({}, DEFAULT_OPTIONS, options); -} - -/* eslint-disable jsdoc/check-param-names, jsdoc/require-param */ -/** - * Normalize the path separators in a given string. - * On Windows environment, this replaces `\` by `/`. - * Otherwrise, this does nothing. - * @param {string} str The path string to normalize. - * @returns {string} The normalized path. - */ -const normalizePathSeps = path.sep === "/" - ? (str => str) - : ((seps, str) => str.replace(seps, "/")).bind(null, new RegExp(`\\${path.sep}`, "gu")); -/* eslint-enable jsdoc/check-param-names, jsdoc/require-param */ - -/** - * Converts a glob pattern to a new glob pattern relative to a different directory - * @param {string} globPattern The glob pattern, relative the the old base directory - * @param {string} relativePathToOldBaseDir A relative path from the new base directory to the old one - * @returns {string} A glob pattern relative to the new base directory - */ -function relativize(globPattern, relativePathToOldBaseDir) { - if (relativePathToOldBaseDir === "") { - return globPattern; - } - - const prefix = globPattern.startsWith("!") ? "!" : ""; - const globWithoutPrefix = globPattern.replace(/^!/u, ""); - - if (globWithoutPrefix.startsWith("/")) { - return `${prefix}/${normalizePathSeps(relativePathToOldBaseDir)}${globWithoutPrefix}`; - } - - return globPattern; -} - -//------------------------------------------------------------------------------ -// Public Interface -//------------------------------------------------------------------------------ - -/** - * IgnoredPaths class - */ -class IgnoredPaths { - - // eslint-disable-next-line jsdoc/require-description - /** - * @param {Object} providedOptions object containing 'ignore', 'ignorePath' and 'patterns' properties - */ - constructor(providedOptions) { - const options = mergeDefaultOptions(providedOptions); - - this.cache = {}; - - this.defaultPatterns = [].concat(DEFAULT_IGNORE_DIRS, options.patterns || []); - - this.ignoreFileDir = options.ignore !== false && options.ignorePath - ? path.dirname(path.resolve(options.cwd, options.ignorePath)) - : options.cwd; - this.options = options; - this._baseDir = null; - - this.ig = { - custom: ignore(), - default: ignore() - }; - - this.defaultPatterns.forEach(pattern => this.addPatternRelativeToCwd(this.ig.default, pattern)); - if (options.dotfiles !== true) { - - /* - * ignore files beginning with a dot, but not files in a parent or - * ancestor directory (which in relative format will begin with `../`). - */ - this.addPatternRelativeToCwd(this.ig.default, ".*"); - this.addPatternRelativeToCwd(this.ig.default, "!../"); - } - - /* - * Add a way to keep track of ignored files. This was present in node-ignore - * 2.x, but dropped for now as of 3.0.10. - */ - this.ig.custom.ignoreFiles = []; - this.ig.default.ignoreFiles = []; - - if (options.ignore !== false) { - let ignorePath; - - if (options.ignorePath) { - debug("Using specific ignore file"); - - try { - const stat = fs.statSync(options.ignorePath); - - if (!stat.isFile()) { - throw new Error(`${options.ignorePath} is not a file`); - } - ignorePath = options.ignorePath; - } catch (e) { - e.message = `Cannot read ignore file: ${options.ignorePath}\nError: ${e.message}`; - throw e; - } - } else { - debug(`Looking for ignore file in ${options.cwd}`); - ignorePath = findIgnoreFile(options.cwd); - - try { - fs.statSync(ignorePath); - debug(`Loaded ignore file ${ignorePath}`); - } catch (e) { - debug("Could not find ignore file in cwd"); - } - } - - if (ignorePath) { - debug(`Adding ${ignorePath}`); - this.addIgnoreFile(this.ig.custom, ignorePath); - this.addIgnoreFile(this.ig.default, ignorePath); - } else { - try { - - // if the ignoreFile does not exist, check package.json for eslintIgnore - const packageJSONPath = findPackageJSONFile(options.cwd); - - if (packageJSONPath) { - let packageJSONOptions; - - try { - packageJSONOptions = JSON.parse(fs.readFileSync(packageJSONPath, "utf8")); - } catch (e) { - debug("Could not read package.json file to check eslintIgnore property"); - e.messageTemplate = "failed-to-read-json"; - e.messageData = { - path: packageJSONPath, - message: e.message - }; - throw e; - } - - if (packageJSONOptions.eslintIgnore) { - if (Array.isArray(packageJSONOptions.eslintIgnore)) { - packageJSONOptions.eslintIgnore.forEach(pattern => { - this.addPatternRelativeToIgnoreFile(this.ig.custom, pattern); - this.addPatternRelativeToIgnoreFile(this.ig.default, pattern); - }); - } else { - throw new TypeError("Package.json eslintIgnore property requires an array of paths"); - } - } - } - } catch (e) { - debug("Could not find package.json to check eslintIgnore property"); - throw e; - } - } - - if (options.ignorePattern) { - this.addPatternRelativeToCwd(this.ig.custom, options.ignorePattern); - this.addPatternRelativeToCwd(this.ig.default, options.ignorePattern); - } - } - } - - /* - * If `ignoreFileDir` is a subdirectory of `cwd`, all paths will be normalized to be relative to `cwd`. - * Otherwise, all paths will be normalized to be relative to `ignoreFileDir`. - * This ensures that the final normalized ignore rule will not contain `..`, which is forbidden in - * ignore rules. - */ - - addPatternRelativeToCwd(ig, pattern) { - const baseDir = this.getBaseDir(); - const cookedPattern = baseDir === this.options.cwd - ? pattern - : relativize(pattern, path.relative(baseDir, this.options.cwd)); - - ig.addPattern(cookedPattern); - debug("addPatternRelativeToCwd:\n original = %j\n cooked = %j", pattern, cookedPattern); - } - - addPatternRelativeToIgnoreFile(ig, pattern) { - const baseDir = this.getBaseDir(); - const cookedPattern = baseDir === this.ignoreFileDir - ? pattern - : relativize(pattern, path.relative(baseDir, this.ignoreFileDir)); - - ig.addPattern(cookedPattern); - debug("addPatternRelativeToIgnoreFile:\n original = %j\n cooked = %j", pattern, cookedPattern); - } - - // Detect the common ancestor - getBaseDir() { - if (!this._baseDir) { - const a = path.resolve(this.options.cwd); - const b = path.resolve(this.ignoreFileDir); - let lastSepPos = 0; - - // Set the shorter one (it's the common ancestor if one includes the other). - this._baseDir = a.length < b.length ? a : b; - - // Set the common ancestor. - for (let i = 0; i < a.length && i < b.length; ++i) { - if (a[i] !== b[i]) { - this._baseDir = a.slice(0, lastSepPos); - break; - } - if (a[i] === path.sep) { - lastSepPos = i; - } - } - - // If it's only Windows drive letter, it needs \ - if (/^[A-Z]:$/u.test(this._baseDir)) { - this._baseDir += "\\"; - } - - debug("baseDir = %j", this._baseDir); - } - return this._baseDir; - } - - /** - * read ignore filepath - * @param {string} filePath file to add to ig - * @returns {Array} raw ignore rules - */ - readIgnoreFile(filePath) { - if (typeof this.cache[filePath] === "undefined") { - this.cache[filePath] = fs.readFileSync(filePath, "utf8").split(/\r?\n/gu).filter(Boolean); - } - return this.cache[filePath]; - } - - /** - * add ignore file to node-ignore instance - * @param {Object} ig instance of node-ignore - * @param {string} filePath file to add to ig - * @returns {void} - */ - addIgnoreFile(ig, filePath) { - ig.ignoreFiles.push(filePath); - this - .readIgnoreFile(filePath) - .forEach(ignoreRule => this.addPatternRelativeToIgnoreFile(ig, ignoreRule)); - } - - /** - * Determine whether a file path is included in the default or custom ignore patterns - * @param {string} filepath Path to check - * @param {string} [category=undefined] check 'default', 'custom' or both (undefined) - * @returns {boolean} true if the file path matches one or more patterns, false otherwise - */ - contains(filepath, category) { - const isDir = filepath.endsWith(path.sep) || - (path.sep === "\\" && filepath.endsWith("/")); - let result = false; - const basePath = this.getBaseDir(); - const absolutePath = path.resolve(this.options.cwd, filepath); - let relativePath = path.relative(basePath, absolutePath); - - if (relativePath) { - if (isDir) { - relativePath += path.sep; - } - if (typeof category === "undefined") { - result = - (this.ig.default.filter([relativePath]).length === 0) || - (this.ig.custom.filter([relativePath]).length === 0); - } else { - result = - (this.ig[category].filter([relativePath]).length === 0); - } - } - debug("contains:"); - debug(" target = %j", filepath); - debug(" base = %j", basePath); - debug(" relative = %j", relativePath); - debug(" result = %j", result); - - return result; - - } -} - -module.exports = { IgnoredPaths }; diff --git a/tools/node_modules/eslint/lib/init/config-initializer.js b/tools/node_modules/eslint/lib/init/config-initializer.js index 48e56ce526131a..28dfad194a7e6f 100644 --- a/tools/node_modules/eslint/lib/init/config-initializer.js +++ b/tools/node_modules/eslint/lib/init/config-initializer.js @@ -291,6 +291,7 @@ function processAnswers(answers) { jsx: true }; config.plugins = ["react"]; + config.extends.push("plugin:react/recommended"); } else if (answers.framework === "vue") { config.plugins = ["vue"]; config.extends.push("plugin:vue/essential"); @@ -522,9 +523,9 @@ function promptUser() { name: "styleguide", message: "Which style guide do you want to follow?", choices: [ - { name: "Airbnb (https://github.com/airbnb/javascript)", value: "airbnb" }, - { name: "Standard (https://github.com/standard/standard)", value: "standard" }, - { name: "Google (https://github.com/google/eslint-config-google)", value: "google" } + { name: "Airbnb: https://github.com/airbnb/javascript", value: "airbnb" }, + { name: "Standard: https://github.com/standard/standard", value: "standard" }, + { name: "Google: https://github.com/google/eslint-config-google", value: "google" } ], when(answers) { answers.packageJsonExists = npmUtils.checkPackageJson(); diff --git a/tools/node_modules/eslint/lib/linter/report-translator.js b/tools/node_modules/eslint/lib/linter/report-translator.js index 8c9ed007a25f0d..eef5165585b21d 100644 --- a/tools/node_modules/eslint/lib/linter/report-translator.js +++ b/tools/node_modules/eslint/lib/linter/report-translator.js @@ -26,6 +26,7 @@ const interpolate = require("./interpolate"); * @property {Object} [data] Optional data to use to fill in placeholders in the * message. * @property {Function} [fix] The function to call that creates a fix command. + * @property {Array<{desc?: string, messageId?: string, fix: Function}>} suggest Suggestion descriptions and functions to create a the associated fixes. */ /** @@ -34,14 +35,15 @@ const interpolate = require("./interpolate"); * @property {string} ruleId * @property {(0|1|2)} severity * @property {(string|undefined)} message - * @property {(string|undefined)} messageId + * @property {(string|undefined)} [messageId] * @property {number} line * @property {number} column - * @property {(number|undefined)} endLine - * @property {(number|undefined)} endColumn + * @property {(number|undefined)} [endLine] + * @property {(number|undefined)} [endColumn] * @property {(string|null)} nodeType * @property {string} source - * @property {({text: string, range: (number[]|null)}|null)} fix + * @property {({text: string, range: (number[]|null)}|null)} [fix] + * @property {Array<{text: string, range: (number[]|null)}|null>} [suggestions] */ //------------------------------------------------------------------------------ @@ -182,6 +184,29 @@ function normalizeFixes(descriptor, sourceCode) { return fix; } +/** + * Gets an array of suggestion objects from the given descriptor. + * @param {MessageDescriptor} descriptor The report descriptor. + * @param {SourceCode} sourceCode The source code object to get text between fixes. + * @param {Object} messages Object of meta messages for the rule. + * @returns {Array} The suggestions for the descriptor + */ +function mapSuggestions(descriptor, sourceCode, messages) { + if (!descriptor.suggest || !Array.isArray(descriptor.suggest)) { + return []; + } + + return descriptor.suggest.map(suggestInfo => { + const computedDesc = suggestInfo.desc || messages[suggestInfo.messageId]; + + return { + ...suggestInfo, + desc: interpolate(computedDesc, suggestInfo.data), + fix: normalizeFixes(suggestInfo, sourceCode) + }; + }); +} + /** * Creates information about the report from a descriptor * @param {Object} options Information about the problem @@ -192,6 +217,7 @@ function normalizeFixes(descriptor, sourceCode) { * @param {string} [options.messageId] The error message ID. * @param {{start: SourceLocation, end: (SourceLocation|null)}} options.loc Start and end location * @param {{text: string, range: (number[]|null)}} options.fix The fix object + * @param {Array<{text: string, range: (number[]|null)}>} options.suggestions The array of suggestions objects * @returns {function(...args): ReportInfo} Function that returns information about the report */ function createProblem(options) { @@ -221,9 +247,47 @@ function createProblem(options) { problem.fix = options.fix; } + if (options.suggestions && options.suggestions.length > 0) { + problem.suggestions = options.suggestions; + } + return problem; } +/** + * Validates that suggestions are properly defined. Throws if an error is detected. + * @param {Array<{ desc?: string, messageId?: string }>} suggest The incoming suggest data. + * @param {Object} messages Object of meta messages for the rule. + * @returns {void} + */ +function validateSuggestions(suggest, messages) { + if (suggest && Array.isArray(suggest)) { + suggest.forEach(suggestion => { + if (suggestion.messageId) { + const { messageId } = suggestion; + + if (!messages) { + throw new TypeError(`context.report() called with a suggest option with a messageId '${messageId}', but no messages were present in the rule metadata.`); + } + + if (!messages[messageId]) { + throw new TypeError(`context.report() called with a suggest option with a messageId '${messageId}' which is not present in the 'messages' config: ${JSON.stringify(messages, null, 2)}`); + } + + if (suggestion.desc) { + throw new TypeError("context.report() called with a suggest option that defines both a 'messageId' and an 'desc'. Please only pass one."); + } + } else if (!suggestion.desc) { + throw new TypeError("context.report() called with a suggest option that doesn't have either a `desc` or `messageId`"); + } + + if (typeof suggestion.fix !== "function") { + throw new TypeError(`context.report() called with a suggest option without a fix function. See: ${suggestion}`); + } + }); + } +} + /** * Returns a function that converts the arguments of a `context.report` call from a rule into a reported * problem for the Node.js API. @@ -242,17 +306,17 @@ module.exports = function createReportTranslator(metadata) { */ return (...args) => { const descriptor = normalizeMultiArgReportCall(...args); + const messages = metadata.messageIds; assertValidNodeInfo(descriptor); let computedMessage; if (descriptor.messageId) { - if (!metadata.messageIds) { + if (!messages) { throw new TypeError("context.report() called with a messageId, but no messages were present in the rule metadata."); } const id = descriptor.messageId; - const messages = metadata.messageIds; if (descriptor.message) { throw new TypeError("context.report() called with a message and a messageId. Please only pass one."); @@ -267,6 +331,7 @@ module.exports = function createReportTranslator(metadata) { throw new TypeError("Missing `message` property in report() call; add a message that describes the linting problem."); } + validateSuggestions(descriptor.suggest, messages); return createProblem({ ruleId: metadata.ruleId, @@ -275,7 +340,8 @@ module.exports = function createReportTranslator(metadata) { message: interpolate(computedMessage, descriptor.data), messageId: descriptor.messageId, loc: normalizeReportLoc(descriptor), - fix: metadata.disableFixes ? null : normalizeFixes(descriptor, metadata.sourceCode) + fix: metadata.disableFixes ? null : normalizeFixes(descriptor, metadata.sourceCode), + suggestions: metadata.disableFixes ? [] : mapSuggestions(descriptor, metadata.sourceCode, messages) }); }; }; diff --git a/tools/node_modules/eslint/lib/rule-tester/rule-tester.js b/tools/node_modules/eslint/lib/rule-tester/rule-tester.js index e57cd09bd87014..b4b3b2bec86c78 100644 --- a/tools/node_modules/eslint/lib/rule-tester/rule-tester.js +++ b/tools/node_modules/eslint/lib/rule-tester/rule-tester.js @@ -349,7 +349,7 @@ class RuleTester { filename = item.filename; } - if (Object.prototype.hasOwnProperty.call(item, "options")) { + if (hasOwnProperty(item, "options")) { assert(Array.isArray(item.options), "options must be an array"); config.rules[ruleName] = [1].concat(item.options); } else { @@ -577,21 +577,55 @@ class RuleTester { assert.strictEqual(message.nodeType, error.type, `Error type should be ${error.type}, found ${message.nodeType}`); } - if (Object.prototype.hasOwnProperty.call(error, "line")) { + if (hasOwnProperty(error, "line")) { assert.strictEqual(message.line, error.line, `Error line should be ${error.line}`); } - if (Object.prototype.hasOwnProperty.call(error, "column")) { + if (hasOwnProperty(error, "column")) { assert.strictEqual(message.column, error.column, `Error column should be ${error.column}`); } - if (Object.prototype.hasOwnProperty.call(error, "endLine")) { + if (hasOwnProperty(error, "endLine")) { assert.strictEqual(message.endLine, error.endLine, `Error endLine should be ${error.endLine}`); } - if (Object.prototype.hasOwnProperty.call(error, "endColumn")) { + if (hasOwnProperty(error, "endColumn")) { assert.strictEqual(message.endColumn, error.endColumn, `Error endColumn should be ${error.endColumn}`); } + + if (hasOwnProperty(error, "suggestions")) { + + // Support asserting there are no suggestions + if (!error.suggestions) { + assert.strictEqual(message.suggestions, error.suggestions, `Error should have no suggestions on error with message: "${message.message}"`); + } else { + assert.strictEqual(Array.isArray(message.suggestions), true, `Error should have an array of suggestions. Instead received "${message.suggestions}" on error with message: "${message.message}"`); + assert.strictEqual(message.suggestions.length, error.suggestions.length, `Error should have ${error.suggestions.length} suggestions. Instead found ${message.suggestions.length} suggestions`); + + error.suggestions.forEach((expectedSuggestion, index) => { + const actualSuggestion = message.suggestions[index]; + + /** + * Tests equality of a suggestion key if that key is defined in the expected output. + * @param {string} key Key to validate from the suggestion object + * @returns {void} + */ + function assertSuggestionKeyEquals(key) { + if (hasOwnProperty(expectedSuggestion, key)) { + assert.deepStrictEqual(actualSuggestion[key], expectedSuggestion[key], `Error suggestion at index: ${index} should have desc of: "${actualSuggestion[key]}"`); + } + } + assertSuggestionKeyEquals("desc"); + assertSuggestionKeyEquals("messageId"); + + if (hasOwnProperty(expectedSuggestion, "output")) { + const codeWithAppliedSuggestion = SourceCodeFixer.applyFixes(item.code, [actualSuggestion]).output; + + assert.strictEqual(codeWithAppliedSuggestion, expectedSuggestion.output, `Expected the applied suggestion fix to match the test suggestion output for suggestion at index: ${index} on error with message: "${message.message}"`); + } + }); + } + } } else { // Message was an unexpected type @@ -600,7 +634,7 @@ class RuleTester { } } - if (Object.prototype.hasOwnProperty.call(item, "output")) { + if (hasOwnProperty(item, "output")) { if (item.output === null) { assert.strictEqual( result.output, diff --git a/tools/node_modules/eslint/lib/rules/camelcase.js b/tools/node_modules/eslint/lib/rules/camelcase.js index cdc16fab9f9460..a06c7f94042ad4 100644 --- a/tools/node_modules/eslint/lib/rules/camelcase.js +++ b/tools/node_modules/eslint/lib/rules/camelcase.js @@ -28,6 +28,10 @@ module.exports = { type: "boolean", default: false }, + ignoreImports: { + type: "boolean", + default: false + }, properties: { enum: ["always", "never"] }, @@ -56,6 +60,7 @@ module.exports = { const options = context.options[0] || {}; let properties = options.properties || ""; const ignoreDestructuring = options.ignoreDestructuring; + const ignoreImports = options.ignoreImports; const allow = options.allow || []; if (properties !== "always" && properties !== "never") { @@ -79,7 +84,7 @@ module.exports = { function isUnderscored(name) { // if there's an underscore, it might be A_CONSTANT, which is okay - return name.indexOf("_") > -1 && name !== name.toUpperCase(); + return name.includes("_") && name !== name.toUpperCase(); } /** @@ -89,9 +94,9 @@ module.exports = { * @private */ function isAllowed(name) { - return allow.findIndex( + return allow.some( entry => name === entry || name.match(new RegExp(entry, "u")) - ) !== -1; + ); } /** @@ -127,7 +132,7 @@ module.exports = { * @private */ function report(node) { - if (reported.indexOf(node) < 0) { + if (!reported.includes(node)) { reported.push(node); context.report({ node, messageId: "notCamelCase", data: { name: node.name } }); } @@ -209,10 +214,18 @@ module.exports = { } // Check if it's an import specifier - } else if (["ImportSpecifier", "ImportNamespaceSpecifier", "ImportDefaultSpecifier"].indexOf(node.parent.type) >= 0) { + } else if (["ImportSpecifier", "ImportNamespaceSpecifier", "ImportDefaultSpecifier"].includes(node.parent.type)) { + + if (node.parent.type === "ImportSpecifier" && ignoreImports) { + return; + } // Report only if the local imported identifier is underscored - if (node.parent.local && node.parent.local.name === node.name && nameIsUnderscored) { + if ( + node.parent.local && + node.parent.local.name === node.name && + nameIsUnderscored + ) { report(node); } diff --git a/tools/node_modules/eslint/lib/rules/comma-dangle.js b/tools/node_modules/eslint/lib/rules/comma-dangle.js index fb2d167c77eef9..e22b7f3551e114 100644 --- a/tools/node_modules/eslint/lib/rules/comma-dangle.js +++ b/tools/node_modules/eslint/lib/rules/comma-dangle.js @@ -231,7 +231,7 @@ module.exports = { if (astUtils.isCommaToken(trailingToken)) { context.report({ node: lastItem, - loc: trailingToken.loc.start, + loc: trailingToken.loc, messageId: "unexpected", fix(fixer) { return fixer.remove(trailingToken); @@ -267,7 +267,10 @@ module.exports = { if (trailingToken.value !== ",") { context.report({ node: lastItem, - loc: trailingToken.loc.end, + loc: { + start: trailingToken.loc.end, + end: astUtils.getNextLocation(sourceCode, trailingToken.loc.end) + }, messageId: "missing", fix(fixer) { return fixer.insertTextAfter(trailingToken, ","); diff --git a/tools/node_modules/eslint/lib/rules/computed-property-spacing.js b/tools/node_modules/eslint/lib/rules/computed-property-spacing.js index bc8be964f4ffee..a0bd7f48ced66b 100644 --- a/tools/node_modules/eslint/lib/rules/computed-property-spacing.js +++ b/tools/node_modules/eslint/lib/rules/computed-property-spacing.js @@ -153,10 +153,10 @@ module.exports = { const property = node[propertyName]; - const before = sourceCode.getTokenBefore(property), - first = sourceCode.getFirstToken(property), - last = sourceCode.getLastToken(property), - after = sourceCode.getTokenAfter(property); + const before = sourceCode.getTokenBefore(property, astUtils.isOpeningBracketToken), + first = sourceCode.getTokenAfter(before, { includeComments: true }), + after = sourceCode.getTokenAfter(property, astUtils.isClosingBracketToken), + last = sourceCode.getTokenBefore(after, { includeComments: true }); if (astUtils.isTokenOnSameLine(before, first)) { if (propertyNameMustBeSpaced) { diff --git a/tools/node_modules/eslint/lib/rules/curly.js b/tools/node_modules/eslint/lib/rules/curly.js index 93c74d11fcf35f..ee2fe4dceb82bf 100644 --- a/tools/node_modules/eslint/lib/rules/curly.js +++ b/tools/node_modules/eslint/lib/rules/curly.js @@ -97,10 +97,15 @@ module.exports = { * @private */ function isOneLiner(node) { - const first = sourceCode.getFirstToken(node), - last = sourceCode.getLastToken(node); + if (node.type === "EmptyStatement") { + return true; + } + + const first = sourceCode.getFirstToken(node); + const last = sourceCode.getLastToken(node); + const lastExcludingSemicolon = astUtils.isSemicolonToken(last) ? sourceCode.getTokenBefore(last) : last; - return first.loc.start.line === last.loc.end.line; + return first.loc.start.line === lastExcludingSemicolon.loc.end.line; } /** @@ -240,7 +245,7 @@ module.exports = { if (node.type === "IfStatement" && node.consequent === body && requiresBraceOfConsequent(node)) { expected = true; } else if (multiOnly) { - if (hasBlock && body.body.length === 1) { + if (hasBlock && body.body.length === 1 && !isLexicalDeclaration(body.body[0])) { expected = false; } } else if (multiLine) { diff --git a/tools/node_modules/eslint/lib/rules/function-call-argument-newline.js b/tools/node_modules/eslint/lib/rules/function-call-argument-newline.js index 31ebc097c46f51..b6abbe95fa98c3 100644 --- a/tools/node_modules/eslint/lib/rules/function-call-argument-newline.js +++ b/tools/node_modules/eslint/lib/rules/function-call-argument-newline.js @@ -70,6 +70,8 @@ module.exports = { { includeComments: true } ); + const hasLineCommentBefore = tokenBefore.type === "Line"; + context.report({ node, loc: { @@ -77,7 +79,7 @@ module.exports = { end: currentArgToken.loc.start }, messageId: checker.messageId, - fix: checker.createFix(currentArgToken, tokenBefore) + fix: hasLineCommentBefore ? null : checker.createFix(currentArgToken, tokenBefore) }); } } diff --git a/tools/node_modules/eslint/lib/rules/grouped-accessor-pairs.js b/tools/node_modules/eslint/lib/rules/grouped-accessor-pairs.js new file mode 100644 index 00000000000000..a790f83750b2eb --- /dev/null +++ b/tools/node_modules/eslint/lib/rules/grouped-accessor-pairs.js @@ -0,0 +1,224 @@ +/** + * @fileoverview Rule to require grouped accessor pairs in object literals and classes + * @author Milos Djermanovic + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const astUtils = require("./utils/ast-utils"); + +//------------------------------------------------------------------------------ +// Typedefs +//------------------------------------------------------------------------------ + +/** + * Property name if it can be computed statically, otherwise the list of the tokens of the key node. + * @typedef {string|Token[]} Key + */ + +/** + * Accessor nodes with the same key. + * @typedef {Object} AccessorData + * @property {Key} key Accessor's key + * @property {ASTNode[]} getters List of getter nodes. + * @property {ASTNode[]} setters List of setter nodes. + */ + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +/** + * Checks whether or not the given lists represent the equal tokens in the same order. + * Tokens are compared by their properties, not by instance. + * @param {Token[]} left First list of tokens. + * @param {Token[]} right Second list of tokens. + * @returns {boolean} `true` if the lists have same tokens. + */ +function areEqualTokenLists(left, right) { + if (left.length !== right.length) { + return false; + } + + for (let i = 0; i < left.length; i++) { + const leftToken = left[i], + rightToken = right[i]; + + if (leftToken.type !== rightToken.type || leftToken.value !== rightToken.value) { + return false; + } + } + + return true; +} + +/** + * Checks whether or not the given keys are equal. + * @param {Key} left First key. + * @param {Key} right Second key. + * @returns {boolean} `true` if the keys are equal. + */ +function areEqualKeys(left, right) { + if (typeof left === "string" && typeof right === "string") { + + // Statically computed names. + return left === right; + } + if (Array.isArray(left) && Array.isArray(right)) { + + // Token lists. + return areEqualTokenLists(left, right); + } + + return false; +} + +/** + * Checks whether or not a given node is of an accessor kind ('get' or 'set'). + * @param {ASTNode} node A node to check. + * @returns {boolean} `true` if the node is of an accessor kind. + */ +function isAccessorKind(node) { + return node.kind === "get" || node.kind === "set"; +} + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: "suggestion", + + docs: { + description: "require grouped accessor pairs in object literals and classes", + category: "Best Practices", + recommended: false, + url: "https://eslint.org/docs/rules/grouped-accessor-pairs" + }, + + schema: [ + { + enum: ["anyOrder", "getBeforeSet", "setBeforeGet"] + } + ], + + messages: { + notGrouped: "Accessor pair {{ formerName }} and {{ latterName }} should be grouped.", + invalidOrder: "Expected {{ latterName }} to be before {{ formerName }}." + } + }, + + create(context) { + const order = context.options[0] || "anyOrder"; + const sourceCode = context.getSourceCode(); + + /** + * Reports the given accessor pair. + * @param {string} messageId messageId to report. + * @param {ASTNode} formerNode getter/setter node that is defined before `latterNode`. + * @param {ASTNode} latterNode getter/setter node that is defined after `formerNode`. + * @returns {void} + * @private + */ + function report(messageId, formerNode, latterNode) { + context.report({ + node: latterNode, + messageId, + loc: astUtils.getFunctionHeadLoc(latterNode.value, sourceCode), + data: { + formerName: astUtils.getFunctionNameWithKind(formerNode.value), + latterName: astUtils.getFunctionNameWithKind(latterNode.value) + } + }); + } + + /** + * Creates a new `AccessorData` object for the given getter or setter node. + * @param {ASTNode} node A getter or setter node. + * @returns {AccessorData} New `AccessorData` object that contains the given node. + * @private + */ + function createAccessorData(node) { + const name = astUtils.getStaticPropertyName(node); + const key = (name !== null) ? name : sourceCode.getTokens(node.key); + + return { + key, + getters: node.kind === "get" ? [node] : [], + setters: node.kind === "set" ? [node] : [] + }; + } + + /** + * Merges the given `AccessorData` object into the given accessors list. + * @param {AccessorData[]} accessors The list to merge into. + * @param {AccessorData} accessorData The object to merge. + * @returns {AccessorData[]} The same instance with the merged object. + * @private + */ + function mergeAccessorData(accessors, accessorData) { + const equalKeyElement = accessors.find(a => areEqualKeys(a.key, accessorData.key)); + + if (equalKeyElement) { + equalKeyElement.getters.push(...accessorData.getters); + equalKeyElement.setters.push(...accessorData.setters); + } else { + accessors.push(accessorData); + } + + return accessors; + } + + /** + * Checks accessor pairs in the given list of nodes. + * @param {ASTNode[]} nodes The list to check. + * @param {Function} shouldCheck – Predicate that returns `true` if the node should be checked. + * @returns {void} + * @private + */ + function checkList(nodes, shouldCheck) { + const accessors = nodes + .filter(shouldCheck) + .filter(isAccessorKind) + .map(createAccessorData) + .reduce(mergeAccessorData, []); + + for (const { getters, setters } of accessors) { + + // Don't report accessor properties that have duplicate getters or setters. + if (getters.length === 1 && setters.length === 1) { + const [getter] = getters, + [setter] = setters, + getterIndex = nodes.indexOf(getter), + setterIndex = nodes.indexOf(setter), + formerNode = getterIndex < setterIndex ? getter : setter, + latterNode = getterIndex < setterIndex ? setter : getter; + + if (Math.abs(getterIndex - setterIndex) > 1) { + report("notGrouped", formerNode, latterNode); + } else if ( + (order === "getBeforeSet" && getterIndex > setterIndex) || + (order === "setBeforeGet" && getterIndex < setterIndex) + ) { + report("invalidOrder", formerNode, latterNode); + } + } + } + } + + return { + ObjectExpression(node) { + checkList(node.properties, n => n.type === "Property"); + }, + ClassBody(node) { + checkList(node.body, n => n.type === "MethodDefinition" && !n.static); + checkList(node.body, n => n.type === "MethodDefinition" && n.static); + } + }; + } +}; diff --git a/tools/node_modules/eslint/lib/rules/indent.js b/tools/node_modules/eslint/lib/rules/indent.js index 94c83692b39f0f..694cf7d9e6ed4f 100644 --- a/tools/node_modules/eslint/lib/rules/indent.js +++ b/tools/node_modules/eslint/lib/rules/indent.js @@ -1492,6 +1492,17 @@ module.exports = { ); }, + JSXSpreadAttribute(node) { + const openingCurly = sourceCode.getFirstToken(node); + const closingCurly = sourceCode.getLastToken(node); + + offsets.setDesiredOffsets( + [openingCurly.range[1], closingCurly.range[0]], + openingCurly, + 1 + ); + }, + "*"(node) { const firstToken = sourceCode.getFirstToken(node); diff --git a/tools/node_modules/eslint/lib/rules/index.js b/tools/node_modules/eslint/lib/rules/index.js index 8b0abc4ee7a8f9..d3fbe412080360 100644 --- a/tools/node_modules/eslint/lib/rules/index.js +++ b/tools/node_modules/eslint/lib/rules/index.js @@ -52,6 +52,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({ "generator-star-spacing": () => require("./generator-star-spacing"), "getter-return": () => require("./getter-return"), "global-require": () => require("./global-require"), + "grouped-accessor-pairs": () => require("./grouped-accessor-pairs"), "guard-for-in": () => require("./guard-for-in"), "handle-callback-err": () => require("./handle-callback-err"), "id-blacklist": () => require("./id-blacklist"), @@ -101,6 +102,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({ "no-console": () => require("./no-console"), "no-const-assign": () => require("./no-const-assign"), "no-constant-condition": () => require("./no-constant-condition"), + "no-constructor-return": () => require("./no-constructor-return"), "no-continue": () => require("./no-continue"), "no-control-regex": () => require("./no-control-regex"), "no-debugger": () => require("./no-debugger"), @@ -108,6 +110,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({ "no-div-regex": () => require("./no-div-regex"), "no-dupe-args": () => require("./no-dupe-args"), "no-dupe-class-members": () => require("./no-dupe-class-members"), + "no-dupe-else-if": () => require("./no-dupe-else-if"), "no-dupe-keys": () => require("./no-dupe-keys"), "no-duplicate-case": () => require("./no-duplicate-case"), "no-duplicate-imports": () => require("./no-duplicate-imports"), @@ -186,6 +189,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({ "no-self-assign": () => require("./no-self-assign"), "no-self-compare": () => require("./no-self-compare"), "no-sequences": () => require("./no-sequences"), + "no-setter-return": () => require("./no-setter-return"), "no-shadow": () => require("./no-shadow"), "no-shadow-restricted-names": () => require("./no-shadow-restricted-names"), "no-spaced-func": () => require("./no-spaced-func"), @@ -238,6 +242,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({ "prefer-arrow-callback": () => require("./prefer-arrow-callback"), "prefer-const": () => require("./prefer-const"), "prefer-destructuring": () => require("./prefer-destructuring"), + "prefer-exponentiation-operator": () => require("./prefer-exponentiation-operator"), "prefer-named-capture-group": () => require("./prefer-named-capture-group"), "prefer-numeric-literals": () => require("./prefer-numeric-literals"), "prefer-object-spread": () => require("./prefer-object-spread"), diff --git a/tools/node_modules/eslint/lib/rules/multiline-comment-style.js b/tools/node_modules/eslint/lib/rules/multiline-comment-style.js index 6578a120126bfb..fb50e1522ea11d 100644 --- a/tools/node_modules/eslint/lib/rules/multiline-comment-style.js +++ b/tools/node_modules/eslint/lib/rules/multiline-comment-style.js @@ -43,17 +43,150 @@ module.exports = { //---------------------------------------------------------------------- /** - * Gets a list of comment lines in a group - * @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment - * @returns {string[]} A list of comment lines + * Checks if a comment line is starred. + * @param {string} line A string representing a comment line. + * @returns {boolean} Whether or not the comment line is starred. + */ + function isStarredCommentLine(line) { + return /^\s*\*/u.test(line); + } + + /** + * Checks if a comment group is in starred-block form. + * @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment. + * @returns {boolean} Whether or not the comment group is in starred block form. + */ + function isStarredBlockComment([firstComment]) { + if (firstComment.type !== "Block") { + return false; + } + + const lines = firstComment.value.split(astUtils.LINEBREAK_MATCHER); + + // The first and last lines can only contain whitespace. + return lines.length > 0 && lines.every((line, i) => (i === 0 || i === lines.length - 1 ? /^\s*$/u : /^\s*\*/u).test(line)); + } + + /** + * Checks if a comment group is in JSDoc form. + * @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment. + * @returns {boolean} Whether or not the comment group is in JSDoc form. + */ + function isJSDocComment([firstComment]) { + if (firstComment.type !== "Block") { + return false; + } + + const lines = firstComment.value.split(astUtils.LINEBREAK_MATCHER); + + return /^\*\s*$/u.test(lines[0]) && + lines.slice(1, -1).every(line => /^\s* /u.test(line)) && + /^\s*$/u.test(lines[lines.length - 1]); + } + + /** + * Processes a comment group that is currently in separate-line form, calculating the offset for each line. + * @param {Token[]} commentGroup A group of comments containing multiple line comments. + * @returns {string[]} An array of the processed lines. + */ + function processSeparateLineComments(commentGroup) { + const allLinesHaveLeadingSpace = commentGroup + .map(({ value }) => value) + .filter(line => line.trim().length) + .every(line => line.startsWith(" ")); + + return commentGroup.map(({ value }) => (allLinesHaveLeadingSpace ? value.replace(/^ /u, "") : value)); + } + + /** + * Processes a comment group that is currently in starred-block form, calculating the offset for each line. + * @param {Token} comment A single block comment token in starred-block form. + * @returns {string[]} An array of the processed lines. + */ + function processStarredBlockComment(comment) { + const lines = comment.value.split(astUtils.LINEBREAK_MATCHER) + .filter((line, i, linesArr) => !(i === 0 || i === linesArr.length - 1)) + .map(line => line.replace(/^\s*$/u, "")); + const allLinesHaveLeadingSpace = lines + .map(line => line.replace(/\s*\*/u, "")) + .filter(line => line.trim().length) + .every(line => line.startsWith(" ")); + + return lines.map(line => line.replace(allLinesHaveLeadingSpace ? /\s*\* ?/u : /\s*\*/u, "")); + } + + /** + * Processes a comment group that is currently in bare-block form, calculating the offset for each line. + * @param {Token} comment A single block comment token in bare-block form. + * @returns {string[]} An array of the processed lines. + */ + function processBareBlockComment(comment) { + const lines = comment.value.split(astUtils.LINEBREAK_MATCHER).map(line => line.replace(/^\s*$/u, "")); + const leadingWhitespace = `${sourceCode.text.slice(comment.range[0] - comment.loc.start.column, comment.range[0])} `; + let offset = ""; + + /* + * Calculate the offset of the least indented line and use that as the basis for offsetting all the lines. + * The first line should not be checked because it is inline with the opening block comment delimiter. + */ + for (const [i, line] of lines.entries()) { + if (!line.trim().length || i === 0) { + continue; + } + + const [, lineOffset] = line.match(/^(\s*\*?\s*)/u); + + if (lineOffset.length < leadingWhitespace.length) { + const newOffset = leadingWhitespace.slice(lineOffset.length - leadingWhitespace.length); + + if (newOffset.length > offset.length) { + offset = newOffset; + } + } + } + + return lines.map(line => { + const match = line.match(/^(\s*\*?\s*)(.*)/u); + const [, lineOffset, lineContents] = match; + + if (lineOffset.length > leadingWhitespace.length) { + return `${lineOffset.slice(leadingWhitespace.length - (offset.length + lineOffset.length))}${lineContents}`; + } + + if (lineOffset.length < leadingWhitespace.length) { + return `${lineOffset.slice(leadingWhitespace.length)}${lineContents}`; + } + + return lineContents; + }); + } + + /** + * Gets a list of comment lines in a group, formatting leading whitespace as necessary. + * @param {Token[]} commentGroup A group of comments containing either multiple line comments or a single block comment. + * @returns {string[]} A list of comment lines. */ function getCommentLines(commentGroup) { - if (commentGroup[0].type === "Line") { - return commentGroup.map(comment => comment.value); + const [firstComment] = commentGroup; + + if (firstComment.type === "Line") { + return processSeparateLineComments(commentGroup); } - return commentGroup[0].value - .split(astUtils.LINEBREAK_MATCHER) - .map(line => line.replace(/^\s*\*?/u, "")); + + if (isStarredBlockComment(commentGroup)) { + return processStarredBlockComment(firstComment); + } + + return processBareBlockComment(firstComment); + } + + /** + * Gets the initial offset (whitespace) from the beginning of a line to a given comment token. + * @param {Token} comment The token to check. + * @returns {string} The offset from the beginning of a line to the token. + */ + function getInitialOffset(comment) { + return sourceCode.text.slice(comment.range[0] - comment.loc.start.column, comment.range[0]); } /** @@ -63,10 +196,9 @@ module.exports = { * @returns {string} A representation of the comment value in starred-block form, excluding start and end markers */ function convertToStarredBlock(firstComment, commentLinesList) { - const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]); - const starredLines = commentLinesList.map(line => `${initialOffset} *${line}`); + const initialOffset = getInitialOffset(firstComment); - return `\n${starredLines.join("\n")}\n${initialOffset} `; + return `/*\n${commentLinesList.map(line => `${initialOffset} * ${line}`).join("\n")}\n${initialOffset} */`; } /** @@ -76,10 +208,7 @@ module.exports = { * @returns {string} A representation of the comment value in separate-line form */ function convertToSeparateLines(firstComment, commentLinesList) { - const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]); - const separateLines = commentLinesList.map(line => `// ${line.trim()}`); - - return separateLines.join(`\n${initialOffset}`); + return commentLinesList.map(line => `// ${line}`).join(`\n${getInitialOffset(firstComment)}`); } /** @@ -89,24 +218,7 @@ module.exports = { * @returns {string} A representation of the comment value in bare-block form */ function convertToBlock(firstComment, commentLinesList) { - const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]); - const blockLines = commentLinesList.map(line => line.trim()); - - return `/* ${blockLines.join(`\n${initialOffset} `)} */`; - } - - /** - * Check a comment is JSDoc form - * @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment - * @returns {boolean} if commentGroup is JSDoc form, return true - */ - function isJSDoc(commentGroup) { - const lines = commentGroup[0].value.split(astUtils.LINEBREAK_MATCHER); - - return commentGroup[0].type === "Block" && - /^\*\s*$/u.test(lines[0]) && - lines.slice(1, -1).every(line => /^\s* /u.test(line)) && - /^\s*$/u.test(lines[lines.length - 1]); + return `/* ${commentLinesList.join(`\n${getInitialOffset(firstComment)} `)} */`; } /** @@ -117,6 +229,7 @@ module.exports = { */ const commentGroupCheckers = { "starred-block"(commentGroup) { + const [firstComment] = commentGroup; const commentLines = getCommentLines(commentGroup); if (commentLines.some(value => value.includes("*/"))) { @@ -126,31 +239,30 @@ module.exports = { if (commentGroup.length > 1) { context.report({ loc: { - start: commentGroup[0].loc.start, + start: firstComment.loc.start, end: commentGroup[commentGroup.length - 1].loc.end }, messageId: "expectedBlock", fix(fixer) { - const range = [commentGroup[0].range[0], commentGroup[commentGroup.length - 1].range[1]]; - const starredBlock = `/*${convertToStarredBlock(commentGroup[0], commentLines)}*/`; + const range = [firstComment.range[0], commentGroup[commentGroup.length - 1].range[1]]; return commentLines.some(value => value.startsWith("/")) ? null - : fixer.replaceTextRange(range, starredBlock); + : fixer.replaceTextRange(range, convertToStarredBlock(firstComment, commentLines)); } }); } else { - const block = commentGroup[0]; - const lines = block.value.split(astUtils.LINEBREAK_MATCHER); - const expectedLinePrefix = `${sourceCode.text.slice(block.range[0] - block.loc.start.column, block.range[0])} *`; + const lines = firstComment.value.split(astUtils.LINEBREAK_MATCHER); + const expectedLeadingWhitespace = getInitialOffset(firstComment); + const expectedLinePrefix = `${expectedLeadingWhitespace} *`; if (!/^\*?\s*$/u.test(lines[0])) { - const start = block.value.startsWith("*") ? block.range[0] + 1 : block.range[0]; + const start = firstComment.value.startsWith("*") ? firstComment.range[0] + 1 : firstComment.range[0]; context.report({ loc: { - start: block.loc.start, - end: { line: block.loc.start.line, column: block.loc.start.column + 2 } + start: firstComment.loc.start, + end: { line: firstComment.loc.start.line, column: firstComment.loc.start.column + 2 } }, messageId: "startNewline", fix: fixer => fixer.insertTextAfterRange([start, start + 2], `\n${expectedLinePrefix}`) @@ -160,36 +272,54 @@ module.exports = { if (!/^\s*$/u.test(lines[lines.length - 1])) { context.report({ loc: { - start: { line: block.loc.end.line, column: block.loc.end.column - 2 }, - end: block.loc.end + start: { line: firstComment.loc.end.line, column: firstComment.loc.end.column - 2 }, + end: firstComment.loc.end }, messageId: "endNewline", - fix: fixer => fixer.replaceTextRange([block.range[1] - 2, block.range[1]], `\n${expectedLinePrefix}/`) + fix: fixer => fixer.replaceTextRange([firstComment.range[1] - 2, firstComment.range[1]], `\n${expectedLinePrefix}/`) }); } - for (let lineNumber = block.loc.start.line + 1; lineNumber <= block.loc.end.line; lineNumber++) { + for (let lineNumber = firstComment.loc.start.line + 1; lineNumber <= firstComment.loc.end.line; lineNumber++) { const lineText = sourceCode.lines[lineNumber - 1]; + const errorType = isStarredCommentLine(lineText) + ? "alignment" + : "missingStar"; if (!lineText.startsWith(expectedLinePrefix)) { context.report({ loc: { start: { line: lineNumber, column: 0 }, - end: { line: lineNumber, column: sourceCode.lines[lineNumber - 1].length } + end: { line: lineNumber, column: lineText.length } }, - messageId: /^\s*\*/u.test(lineText) - ? "alignment" - : "missingStar", + messageId: errorType, fix(fixer) { const lineStartIndex = sourceCode.getIndexFromLoc({ line: lineNumber, column: 0 }); - const linePrefixLength = lineText.match(/^\s*\*? ?/u)[0].length; - const commentStartIndex = lineStartIndex + linePrefixLength; - const replacementText = lineNumber === block.loc.end.line || lineText.length === linePrefixLength - ? expectedLinePrefix - : `${expectedLinePrefix} `; + if (errorType === "alignment") { + const [, commentTextPrefix = ""] = lineText.match(/^(\s*\*)/u) || []; + const commentTextStartIndex = lineStartIndex + commentTextPrefix.length; + + return fixer.replaceTextRange([lineStartIndex, commentTextStartIndex], expectedLinePrefix); + } + + const [, commentTextPrefix = ""] = lineText.match(/^(\s*)/u) || []; + const commentTextStartIndex = lineStartIndex + commentTextPrefix.length; + let offset; + + for (const [idx, line] of lines.entries()) { + if (!/\S+/u.test(line)) { + continue; + } + + const lineTextToAlignWith = sourceCode.lines[firstComment.loc.start.line - 1 + idx]; + const [, prefix = "", initialOffset = ""] = lineTextToAlignWith.match(/^(\s*(?:\/?\*)?(\s*))/u) || []; - return fixer.replaceTextRange([lineStartIndex, commentStartIndex], replacementText); + offset = `${commentTextPrefix.slice(prefix.length)}${initialOffset}`; + break; + } + + return fixer.replaceTextRange([lineStartIndex, commentTextStartIndex], `${expectedLinePrefix}${offset}`); } }); } @@ -197,67 +327,68 @@ module.exports = { } }, "separate-lines"(commentGroup) { - if (!isJSDoc(commentGroup) && commentGroup[0].type === "Block") { - const commentLines = getCommentLines(commentGroup); - const block = commentGroup[0]; - const tokenAfter = sourceCode.getTokenAfter(block, { includeComments: true }); + const [firstComment] = commentGroup; + + if (firstComment.type !== "Block" || isJSDocComment(commentGroup)) { + return; + } - if (tokenAfter && block.loc.end.line === tokenAfter.loc.start.line) { - return; + const commentLines = getCommentLines(commentGroup); + const tokenAfter = sourceCode.getTokenAfter(firstComment, { includeComments: true }); + + if (tokenAfter && firstComment.loc.end.line === tokenAfter.loc.start.line) { + return; + } + + context.report({ + loc: { + start: firstComment.loc.start, + end: { line: firstComment.loc.start.line, column: firstComment.loc.start.column + 2 } + }, + messageId: "expectedLines", + fix(fixer) { + return fixer.replaceText(firstComment, convertToSeparateLines(firstComment, commentLines)); } + }); + }, + "bare-block"(commentGroup) { + if (isJSDocComment(commentGroup)) { + return; + } + const [firstComment] = commentGroup; + const commentLines = getCommentLines(commentGroup); + + // Disallows consecutive line comments in favor of using a block comment. + if (firstComment.type === "Line" && commentLines.length > 1 && + !commentLines.some(value => value.includes("*/"))) { context.report({ loc: { - start: block.loc.start, - end: { line: block.loc.start.line, column: block.loc.start.column + 2 } + start: firstComment.loc.start, + end: commentGroup[commentGroup.length - 1].loc.end }, - messageId: "expectedLines", + messageId: "expectedBlock", fix(fixer) { - return fixer.replaceText(block, convertToSeparateLines(block, commentLines.filter(line => line))); + return fixer.replaceTextRange( + [firstComment.range[0], commentGroup[commentGroup.length - 1].range[1]], + convertToBlock(firstComment, commentLines) + ); } }); } - }, - "bare-block"(commentGroup) { - if (!isJSDoc(commentGroup)) { - const commentLines = getCommentLines(commentGroup); - - // disallows consecutive line comments in favor of using a block comment. - if (commentGroup[0].type === "Line" && commentLines.length > 1 && - !commentLines.some(value => value.includes("*/"))) { - context.report({ - loc: { - start: commentGroup[0].loc.start, - end: commentGroup[commentGroup.length - 1].loc.end - }, - messageId: "expectedBlock", - fix(fixer) { - const range = [commentGroup[0].range[0], commentGroup[commentGroup.length - 1].range[1]]; - const block = convertToBlock(commentGroup[0], commentLines.filter(line => line)); - - return fixer.replaceTextRange(range, block); - } - }); - } - // prohibits block comments from having a * at the beginning of each line. - if (commentGroup[0].type === "Block") { - const block = commentGroup[0]; - const lines = block.value.split(astUtils.LINEBREAK_MATCHER).filter(line => line.trim()); - - if (lines.length > 0 && lines.every(line => /^\s*\*/u.test(line))) { - context.report({ - loc: { - start: block.loc.start, - end: { line: block.loc.start.line, column: block.loc.start.column + 2 } - }, - messageId: "expectedBareBlock", - fix(fixer) { - return fixer.replaceText(block, convertToBlock(block, commentLines.filter(line => line))); - } - }); + // Prohibits block comments from having a * at the beginning of each line. + if (isStarredBlockComment(commentGroup)) { + context.report({ + loc: { + start: firstComment.loc.start, + end: { line: firstComment.loc.start.line, column: firstComment.loc.start.column + 2 } + }, + messageId: "expectedBareBlock", + fix(fixer) { + return fixer.replaceText(firstComment, convertToBlock(firstComment, commentLines)); } - } + }); } } }; diff --git a/tools/node_modules/eslint/lib/rules/no-cond-assign.js b/tools/node_modules/eslint/lib/rules/no-cond-assign.js index 67dcd28b2087b5..3843a7ac2e3e91 100644 --- a/tools/node_modules/eslint/lib/rules/no-cond-assign.js +++ b/tools/node_modules/eslint/lib/rules/no-cond-assign.js @@ -2,10 +2,21 @@ * @fileoverview Rule to flag assignment in a conditional statement's test expression * @author Stephen Murray */ + "use strict"; +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + const astUtils = require("./utils/ast-utils"); +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +const TEST_CONDITION_PARENT_TYPES = new Set(["IfStatement", "WhileStatement", "DoWhileStatement", "ForStatement", "ConditionalExpression"]); + const NODE_DESCRIPTIONS = { DoWhileStatement: "a 'do...while' statement", ForStatement: "a 'for' statement", @@ -55,7 +66,7 @@ module.exports = { */ function isConditionalTestExpression(node) { return node.parent && - node.parent.test && + TEST_CONDITION_PARENT_TYPES.has(node.parent.type) && node === node.parent.test; } @@ -105,8 +116,7 @@ module.exports = { ) { context.report({ - node, - loc: node.test.loc.start, + node: node.test, messageId: "missing" }); } @@ -122,7 +132,7 @@ module.exports = { if (ancestor) { context.report({ - node: ancestor, + node, messageId: "unexpected", data: { type: NODE_DESCRIPTIONS[ancestor.type] || ancestor.type diff --git a/tools/node_modules/eslint/lib/rules/no-constructor-return.js b/tools/node_modules/eslint/lib/rules/no-constructor-return.js new file mode 100644 index 00000000000000..4757770b7cc8e6 --- /dev/null +++ b/tools/node_modules/eslint/lib/rules/no-constructor-return.js @@ -0,0 +1,62 @@ +/** + * @fileoverview Rule to disallow returning value from constructor. + * @author Pig Fang + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: "problem", + + docs: { + description: "disallow returning value from constructor", + category: "Best Practices", + recommended: false, + url: "https://eslint.org/docs/rules/no-constructor-return" + }, + + schema: {}, + + fixable: null, + + messages: { + unexpected: "Unexpected return statement in constructor." + } + }, + + create(context) { + const stack = []; + + return { + onCodePathStart(_, node) { + stack.push(node); + }, + onCodePathEnd() { + stack.pop(); + }, + ReturnStatement(node) { + const last = stack[stack.length - 1]; + + if (!last.parent) { + return; + } + + if ( + last.parent.type === "MethodDefinition" && + last.parent.kind === "constructor" && + (node.parent.parent === last || node.argument) + ) { + context.report({ + node, + messageId: "unexpected" + }); + } + } + }; + } +}; diff --git a/tools/node_modules/eslint/lib/rules/no-dupe-else-if.js b/tools/node_modules/eslint/lib/rules/no-dupe-else-if.js new file mode 100644 index 00000000000000..a165e16607d2f0 --- /dev/null +++ b/tools/node_modules/eslint/lib/rules/no-dupe-else-if.js @@ -0,0 +1,122 @@ +/** + * @fileoverview Rule to disallow duplicate conditions in if-else-if chains + * @author Milos Djermanovic + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const astUtils = require("./utils/ast-utils"); + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +/** + * Determines whether the first given array is a subset of the second given array. + * @param {Function} comparator A function to compare two elements, should return `true` if they are equal. + * @param {Array} arrA The array to compare from. + * @param {Array} arrB The array to compare against. + * @returns {boolean} `true` if the array `arrA` is a subset of the array `arrB`. + */ +function isSubsetByComparator(comparator, arrA, arrB) { + return arrA.every(a => arrB.some(b => comparator(a, b))); +} + +/** + * Splits the given node by the given logical operator. + * @param {string} operator Logical operator `||` or `&&`. + * @param {ASTNode} node The node to split. + * @returns {ASTNode[]} Array of conditions that makes the node when joined by the operator. + */ +function splitByLogicalOperator(operator, node) { + if (node.type === "LogicalExpression" && node.operator === operator) { + return [...splitByLogicalOperator(operator, node.left), ...splitByLogicalOperator(operator, node.right)]; + } + return [node]; +} + +const splitByOr = splitByLogicalOperator.bind(null, "||"); +const splitByAnd = splitByLogicalOperator.bind(null, "&&"); + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: "problem", + + docs: { + description: "disallow duplicate conditions in if-else-if chains", + category: "Possible Errors", + recommended: false, + url: "https://eslint.org/docs/rules/no-dupe-else-if" + }, + + schema: [], + + messages: { + unexpected: "This branch can never execute. Its condition is a duplicate or covered by previous conditions in the if-else-if chain." + } + }, + + create(context) { + const sourceCode = context.getSourceCode(); + + /** + * Determines whether the two given nodes are considered to be equal. In particular, given that the nodes + * represent expressions in a boolean context, `||` and `&&` can be considered as commutative operators. + * @param {ASTNode} a First node. + * @param {ASTNode} b Second node. + * @returns {boolean} `true` if the nodes are considered to be equal. + */ + function equal(a, b) { + if (a.type !== b.type) { + return false; + } + + if ( + a.type === "LogicalExpression" && + (a.operator === "||" || a.operator === "&&") && + a.operator === b.operator + ) { + return equal(a.left, b.left) && equal(a.right, b.right) || + equal(a.left, b.right) && equal(a.right, b.left); + } + + return astUtils.equalTokens(a, b, sourceCode); + } + + const isSubset = isSubsetByComparator.bind(null, equal); + + return { + IfStatement(node) { + const test = node.test, + conditionsToCheck = test.type === "LogicalExpression" && test.operator === "&&" + ? [test, ...splitByAnd(test)] + : [test]; + let current = node, + listToCheck = conditionsToCheck.map(c => splitByOr(c).map(splitByAnd)); + + while (current.parent && current.parent.type === "IfStatement" && current.parent.alternate === current) { + current = current.parent; + + const currentOrOperands = splitByOr(current.test).map(splitByAnd); + + listToCheck = listToCheck.map(orOperands => orOperands.filter( + orOperand => !currentOrOperands.some(currentOrOperand => isSubset(currentOrOperand, orOperand)) + )); + + if (listToCheck.some(orOperands => orOperands.length === 0)) { + context.report({ node: test, messageId: "unexpected" }); + break; + } + } + } + }; + } +}; diff --git a/tools/node_modules/eslint/lib/rules/no-implicit-globals.js b/tools/node_modules/eslint/lib/rules/no-implicit-globals.js index 2eea2b28463250..d4bfa3af82fc3b 100644 --- a/tools/node_modules/eslint/lib/rules/no-implicit-globals.js +++ b/tools/node_modules/eslint/lib/rules/no-implicit-globals.js @@ -1,5 +1,5 @@ /** - * @fileoverview Rule to check for implicit global variables and functions. + * @fileoverview Rule to check for implicit global variables, functions and classes. * @author Joshua Peek */ @@ -14,41 +14,123 @@ module.exports = { type: "suggestion", docs: { - description: "disallow variable and `function` declarations in the global scope", + description: "disallow declarations in the global scope", category: "Best Practices", recommended: false, url: "https://eslint.org/docs/rules/no-implicit-globals" }, - schema: [] + schema: [{ + type: "object", + properties: { + lexicalBindings: { + type: "boolean", + default: false + } + }, + additionalProperties: false + }], + + messages: { + globalNonLexicalBinding: "Unexpected {{kind}} declaration in the global scope, wrap in an IIFE for a local variable, assign as global property for a global variable.", + globalLexicalBinding: "Unexpected {{kind}} declaration in the global scope, wrap in a block or in an IIFE.", + globalVariableLeak: "Global variable leak, declare the variable if it is intended to be local.", + assignmentToReadonlyGlobal: "Unexpected assignment to read-only global variable.", + redeclarationOfReadonlyGlobal: "Unexpected redeclaration of read-only global variable." + } }, create(context) { + + const checkLexicalBindings = context.options[0] && context.options[0].lexicalBindings === true; + + /** + * Reports the node. + * @param {ASTNode} node Node to report. + * @param {string} messageId Id of the message to report. + * @param {string|undefined} kind Declaration kind, can be 'var', 'const', 'let', function or class. + * @returns {void} + */ + function report(node, messageId, kind) { + context.report({ + node, + messageId, + data: { + kind + } + }); + } + return { Program() { const scope = context.getScope(); scope.variables.forEach(variable => { - if (variable.writeable) { + + // Only ESLint global variables have the `writable` key. + const isReadonlyEslintGlobalVariable = variable.writeable === false; + const isWritableEslintGlobalVariable = variable.writeable === true; + + if (isWritableEslintGlobalVariable) { + + // Everything is allowed with writable ESLint global variables. return; } variable.defs.forEach(def => { + const defNode = def.node; + if (def.type === "FunctionName" || (def.type === "Variable" && def.parent.kind === "var")) { - context.report({ node: def.node, message: "Implicit global variable, assign as global property instead." }); + if (isReadonlyEslintGlobalVariable) { + report(defNode, "redeclarationOfReadonlyGlobal"); + } else { + report( + defNode, + "globalNonLexicalBinding", + def.type === "FunctionName" ? "function" : `'${def.parent.kind}'` + ); + } + } + + if (checkLexicalBindings) { + if (def.type === "ClassName" || + (def.type === "Variable" && (def.parent.kind === "let" || def.parent.kind === "const"))) { + if (isReadonlyEslintGlobalVariable) { + report(defNode, "redeclarationOfReadonlyGlobal"); + } else { + report( + defNode, + "globalLexicalBinding", + def.type === "ClassName" ? "class" : `'${def.parent.kind}'` + ); + } + } } }); }); + // Undeclared assigned variables. scope.implicit.variables.forEach(variable => { const scopeVariable = scope.set.get(variable.name); + let messageId; - if (scopeVariable && scopeVariable.writeable) { - return; + if (scopeVariable) { + + // ESLint global variable + if (scopeVariable.writeable) { + return; + } + messageId = "assignmentToReadonlyGlobal"; + + } else { + + // Reference to an unknown variable, possible global leak. + messageId = "globalVariableLeak"; } + // def.node is an AssignmentExpression, ForInStatement or ForOfStatement. variable.defs.forEach(def => { - context.report({ node: def.node, message: "Implicit global variable, assign as global property instead." }); + report(def.node, messageId); }); }); } diff --git a/tools/node_modules/eslint/lib/rules/no-inline-comments.js b/tools/node_modules/eslint/lib/rules/no-inline-comments.js index b35db114100a34..bd226ecc35fde1 100644 --- a/tools/node_modules/eslint/lib/rules/no-inline-comments.js +++ b/tools/node_modules/eslint/lib/rules/no-inline-comments.js @@ -35,22 +35,36 @@ module.exports = { */ function testCodeAroundComment(node) { - // Get the whole line and cut it off at the start of the comment - const startLine = String(sourceCode.lines[node.loc.start.line - 1]); - const endLine = String(sourceCode.lines[node.loc.end.line - 1]); + const startLine = String(sourceCode.lines[node.loc.start.line - 1]), + endLine = String(sourceCode.lines[node.loc.end.line - 1]), + preamble = startLine.slice(0, node.loc.start.column).trim(), + postamble = endLine.slice(node.loc.end.column).trim(), + isPreambleEmpty = !preamble, + isPostambleEmpty = !postamble; - const preamble = startLine.slice(0, node.loc.start.column).trim(); + // Nothing on both sides + if (isPreambleEmpty && isPostambleEmpty) { + return; + } - // Also check after the comment - const postamble = endLine.slice(node.loc.end.column).trim(); + // JSX Exception + if ( + (isPreambleEmpty || preamble === "{") && + (isPostambleEmpty || postamble === "}") + ) { + const enclosingNode = sourceCode.getNodeByRangeIndex(node.range[0]); - // Check that this comment isn't an ESLint directive - const isDirective = astUtils.isDirectiveComment(node); + if (enclosingNode && enclosingNode.type === "JSXEmptyExpression") { + return; + } + } - // Should be empty if there was only whitespace around the comment - if (!isDirective && (preamble || postamble)) { - context.report({ node, message: "Unexpected comment inline with code." }); + // Don't report ESLint directive comments + if (astUtils.isDirectiveComment(node)) { + return; } + + context.report({ node, message: "Unexpected comment inline with code." }); } //-------------------------------------------------------------------------- diff --git a/tools/node_modules/eslint/lib/rules/no-invalid-this.js b/tools/node_modules/eslint/lib/rules/no-invalid-this.js index b1dddd9319c745..5398fc3b5f842c 100644 --- a/tools/node_modules/eslint/lib/rules/no-invalid-this.js +++ b/tools/node_modules/eslint/lib/rules/no-invalid-this.js @@ -26,10 +26,23 @@ module.exports = { url: "https://eslint.org/docs/rules/no-invalid-this" }, - schema: [] + schema: [ + { + type: "object", + properties: { + capIsConstructor: { + type: "boolean", + default: true + } + }, + additionalProperties: false + } + ] }, create(context) { + const options = context.options[0] || {}; + const capIsConstructor = options.capIsConstructor !== false; const stack = [], sourceCode = context.getSourceCode(); @@ -48,7 +61,8 @@ module.exports = { current.init = true; current.valid = !astUtils.isDefaultThisBinding( current.node, - sourceCode + sourceCode, + { capIsConstructor } ); } return current; diff --git a/tools/node_modules/eslint/lib/rules/no-octal-escape.js b/tools/node_modules/eslint/lib/rules/no-octal-escape.js index 7f6845ec70f388..5b4c7b2ebb45c8 100644 --- a/tools/node_modules/eslint/lib/rules/no-octal-escape.js +++ b/tools/node_modules/eslint/lib/rules/no-octal-escape.js @@ -38,7 +38,7 @@ module.exports = { // \0 represents a valid NULL character if it isn't followed by a digit. const match = node.raw.match( - /^(?:[^\\]|\\.)*?\\([0-3][0-7]{1,2}|[4-7][0-7]|[1-7])/u + /^(?:[^\\]|\\.)*?\\([0-3][0-7]{1,2}|[4-7][0-7]|0(?=[89])|[1-7])/su ); if (match) { diff --git a/tools/node_modules/eslint/lib/rules/no-setter-return.js b/tools/node_modules/eslint/lib/rules/no-setter-return.js new file mode 100644 index 00000000000000..e0948696c347f5 --- /dev/null +++ b/tools/node_modules/eslint/lib/rules/no-setter-return.js @@ -0,0 +1,227 @@ +/** + * @fileoverview Rule to disallow returning values from setters + * @author Milos Djermanovic + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const astUtils = require("./utils/ast-utils"); +const { findVariable } = require("eslint-utils"); + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +/** + * Determines whether the given identifier node is a reference to a global variable. + * @param {ASTNode} node `Identifier` node to check. + * @param {Scope} scope Scope to which the node belongs. + * @returns {boolean} True if the identifier is a reference to a global variable. + */ +function isGlobalReference(node, scope) { + const variable = findVariable(scope, node); + + return variable !== null && variable.scope.type === "global" && variable.defs.length === 0; +} + +/** + * Determines whether the given node is an argument of the specified global method call, at the given `index` position. + * E.g., for given `index === 1`, this function checks for `objectName.methodName(foo, node)`, where objectName is a global variable. + * @param {ASTNode} node The node to check. + * @param {Scope} scope Scope to which the node belongs. + * @param {string} objectName Name of the global object. + * @param {string} methodName Name of the method. + * @param {number} index The given position. + * @returns {boolean} `true` if the node is argument at the given position. + */ +function isArgumentOfGlobalMethodCall(node, scope, objectName, methodName, index) { + const parent = node.parent; + + return parent.type === "CallExpression" && + parent.arguments[index] === node && + parent.callee.type === "MemberExpression" && + astUtils.getStaticPropertyName(parent.callee) === methodName && + parent.callee.object.type === "Identifier" && + parent.callee.object.name === objectName && + isGlobalReference(parent.callee.object, scope); +} + +/** + * Determines whether the given node is used as a property descriptor. + * @param {ASTNode} node The node to check. + * @param {Scope} scope Scope to which the node belongs. + * @returns {boolean} `true` if the node is a property descriptor. + */ +function isPropertyDescriptor(node, scope) { + if ( + isArgumentOfGlobalMethodCall(node, scope, "Object", "defineProperty", 2) || + isArgumentOfGlobalMethodCall(node, scope, "Reflect", "defineProperty", 2) + ) { + return true; + } + + const parent = node.parent; + + if ( + parent.type === "Property" && + parent.value === node + ) { + const grandparent = parent.parent; + + if ( + grandparent.type === "ObjectExpression" && + ( + isArgumentOfGlobalMethodCall(grandparent, scope, "Object", "create", 1) || + isArgumentOfGlobalMethodCall(grandparent, scope, "Object", "defineProperties", 1) + ) + ) { + return true; + } + } + + return false; +} + +/** + * Determines whether the given function node is used as a setter function. + * @param {ASTNode} node The node to check. + * @param {Scope} scope Scope to which the node belongs. + * @returns {boolean} `true` if the node is a setter. + */ +function isSetter(node, scope) { + const parent = node.parent; + + if ( + parent.kind === "set" && + parent.value === node + ) { + + // Setter in an object literal or in a class + return true; + } + + if ( + parent.type === "Property" && + parent.value === node && + astUtils.getStaticPropertyName(parent) === "set" && + parent.parent.type === "ObjectExpression" && + isPropertyDescriptor(parent.parent, scope) + ) { + + // Setter in a property descriptor + return true; + } + + return false; +} + +/** + * Finds function's outer scope. + * @param {Scope} scope Function's own scope. + * @returns {Scope} Function's outer scope. + */ +function getOuterScope(scope) { + const upper = scope.upper; + + if (upper.type === "function-expression-name") { + return upper.upper; + } + + return upper; +} + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: "problem", + + docs: { + description: "disallow returning values from setters", + category: "Possible Errors", + recommended: false, + url: "https://eslint.org/docs/rules/no-setter-return" + }, + + schema: [], + + messages: { + returnsValue: "Setter cannot return a value." + } + }, + + create(context) { + let funcInfo = null; + + /** + * Creates and pushes to the stack a function info object for the given function node. + * @param {ASTNode} node The function node. + * @returns {void} + */ + function enterFunction(node) { + const outerScope = getOuterScope(context.getScope()); + + funcInfo = { + upper: funcInfo, + isSetter: isSetter(node, outerScope) + }; + } + + /** + * Pops the current function info object from the stack. + * @returns {void} + */ + function exitFunction() { + funcInfo = funcInfo.upper; + } + + /** + * Reports the given node. + * @param {ASTNode} node Node to report. + * @returns {void} + */ + function report(node) { + context.report({ node, messageId: "returnsValue" }); + } + + return { + + /* + * Function declarations cannot be setters, but we still have to track them in the `funcInfo` stack to avoid + * false positives, because a ReturnStatement node can belong to a function declaration inside a setter. + * + * Note: A previously declared function can be referenced and actually used as a setter in a property descriptor, + * but that's out of scope for this rule. + */ + FunctionDeclaration: enterFunction, + FunctionExpression: enterFunction, + ArrowFunctionExpression(node) { + enterFunction(node); + + if (funcInfo.isSetter && node.expression) { + + // { set: foo => bar } property descriptor. Report implicit return 'bar' as the equivalent for a return statement. + report(node.body); + } + }, + + "FunctionDeclaration:exit": exitFunction, + "FunctionExpression:exit": exitFunction, + "ArrowFunctionExpression:exit": exitFunction, + + ReturnStatement(node) { + + // Global returns (e.g., at the top level of a Node module) don't have `funcInfo`. + if (funcInfo && funcInfo.isSetter && node.argument) { + report(node); + } + } + }; + } +}; diff --git a/tools/node_modules/eslint/lib/rules/no-underscore-dangle.js b/tools/node_modules/eslint/lib/rules/no-underscore-dangle.js index 3f59815b575f84..e910e2739a740a 100644 --- a/tools/node_modules/eslint/lib/rules/no-underscore-dangle.js +++ b/tools/node_modules/eslint/lib/rules/no-underscore-dangle.js @@ -38,6 +38,10 @@ module.exports = { type: "boolean", default: false }, + allowAfterThisConstructor: { + type: "boolean", + default: false + }, enforceInMethodNames: { type: "boolean", default: false @@ -54,6 +58,7 @@ module.exports = { const ALLOWED_VARIABLES = options.allow ? options.allow : []; const allowAfterThis = typeof options.allowAfterThis !== "undefined" ? options.allowAfterThis : false; const allowAfterSuper = typeof options.allowAfterSuper !== "undefined" ? options.allowAfterSuper : false; + const allowAfterThisConstructor = typeof options.allowAfterThisConstructor !== "undefined" ? options.allowAfterThisConstructor : false; const enforceInMethodNames = typeof options.enforceInMethodNames !== "undefined" ? options.enforceInMethodNames : false; //------------------------------------------------------------------------- @@ -72,7 +77,7 @@ module.exports = { /** * Check if identifier has a underscore at the end - * @param {ASTNode} identifier node to evaluate + * @param {string} identifier name of the node * @returns {boolean} true if its is present * @private */ @@ -84,7 +89,7 @@ module.exports = { /** * Check if identifier is a special case member expression - * @param {ASTNode} identifier node to evaluate + * @param {string} identifier name of the node * @returns {boolean} true if its is a special case * @private */ @@ -94,7 +99,7 @@ module.exports = { /** * Check if identifier is a special case variable expression - * @param {ASTNode} identifier node to evaluate + * @param {string} identifier name of the node * @returns {boolean} true if its is a special case * @private */ @@ -104,6 +109,18 @@ module.exports = { return identifier === "_"; } + /** + * Check if a node is a member reference of this.constructor + * @param {ASTNode} node node to evaluate + * @returns {boolean} true if it is a reference on this.constructor + * @private + */ + function isThisConstructorReference(node) { + return node.object.type === "MemberExpression" && + node.object.property.name === "constructor" && + node.object.object.type === "ThisExpression"; + } + /** * Check if function has a underscore at the end * @param {ASTNode} node node to evaluate @@ -156,11 +173,13 @@ module.exports = { function checkForTrailingUnderscoreInMemberExpression(node) { const identifier = node.property.name, isMemberOfThis = node.object.type === "ThisExpression", - isMemberOfSuper = node.object.type === "Super"; + isMemberOfSuper = node.object.type === "Super", + isMemberOfThisConstructor = isThisConstructorReference(node); if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) && !(isMemberOfThis && allowAfterThis) && !(isMemberOfSuper && allowAfterSuper) && + !(isMemberOfThisConstructor && allowAfterThisConstructor) && !isSpecialCaseIdentifierForMemberExpression(identifier) && !isAllowed(identifier)) { context.report({ node, diff --git a/tools/node_modules/eslint/lib/rules/no-useless-computed-key.js b/tools/node_modules/eslint/lib/rules/no-useless-computed-key.js index 95527832b2d7f0..b5e53174e42be7 100644 --- a/tools/node_modules/eslint/lib/rules/no-useless-computed-key.js +++ b/tools/node_modules/eslint/lib/rules/no-useless-computed-key.js @@ -8,6 +8,7 @@ // Requirements //------------------------------------------------------------------------------ +const lodash = require("lodash"); const astUtils = require("./utils/ast-utils"); //------------------------------------------------------------------------------ @@ -21,57 +22,83 @@ module.exports = { type: "suggestion", docs: { - description: "disallow unnecessary computed property keys in object literals", + description: "disallow unnecessary computed property keys in objects and classes", category: "ECMAScript 6", recommended: false, url: "https://eslint.org/docs/rules/no-useless-computed-key" }, - schema: [], + schema: [{ + type: "object", + properties: { + enforceForClassMembers: { + type: "boolean", + default: false + } + }, + additionalProperties: false + }], fixable: "code" }, create(context) { const sourceCode = context.getSourceCode(); + const enforceForClassMembers = context.options[0] && context.options[0].enforceForClassMembers; + + /** + * Reports a given node if it violated this rule. + * @param {ASTNode} node The node to check. + * @returns {void} + */ + function check(node) { + if (!node.computed) { + return; + } - return { - Property(node) { - if (!node.computed) { - return; - } - - const key = node.key, - nodeType = typeof key.value; + const key = node.key, + nodeType = typeof key.value; - if (key.type === "Literal" && (nodeType === "string" || nodeType === "number") && key.value !== "__proto__") { - context.report({ - node, - message: MESSAGE_UNNECESSARY_COMPUTED, - data: { property: sourceCode.getText(key) }, - fix(fixer) { - const leftSquareBracket = sourceCode.getFirstToken(node, astUtils.isOpeningBracketToken); - const rightSquareBracket = sourceCode.getFirstTokenBetween(node.key, node.value, astUtils.isClosingBracketToken); - const tokensBetween = sourceCode.getTokensBetween(leftSquareBracket, rightSquareBracket, 1); + let allowedKey; - if (tokensBetween.slice(0, -1).some((token, index) => - sourceCode.getText().slice(token.range[1], tokensBetween[index + 1].range[0]).trim())) { + if (node.type === "MethodDefinition") { + allowedKey = node.static ? "prototype" : "constructor"; + } else { + allowedKey = "__proto__"; + } - // If there are comments between the brackets and the property name, don't do a fix. - return null; - } + if (key.type === "Literal" && (nodeType === "string" || nodeType === "number") && key.value !== allowedKey) { + context.report({ + node, + message: MESSAGE_UNNECESSARY_COMPUTED, + data: { property: sourceCode.getText(key) }, + fix(fixer) { + const leftSquareBracket = sourceCode.getFirstToken(node, astUtils.isOpeningBracketToken); + const rightSquareBracket = sourceCode.getFirstTokenBetween(node.key, node.value, astUtils.isClosingBracketToken); + const tokensBetween = sourceCode.getTokensBetween(leftSquareBracket, rightSquareBracket, 1); + + if (tokensBetween.slice(0, -1).some((token, index) => + sourceCode.getText().slice(token.range[1], tokensBetween[index + 1].range[0]).trim())) { + + // If there are comments between the brackets and the property name, don't do a fix. + return null; + } - const tokenBeforeLeftBracket = sourceCode.getTokenBefore(leftSquareBracket); + const tokenBeforeLeftBracket = sourceCode.getTokenBefore(leftSquareBracket); - // Insert a space before the key to avoid changing identifiers, e.g. ({ get[2]() {} }) to ({ get2() {} }) - const needsSpaceBeforeKey = tokenBeforeLeftBracket.range[1] === leftSquareBracket.range[0] && - !astUtils.canTokensBeAdjacent(tokenBeforeLeftBracket, sourceCode.getFirstToken(key)); + // Insert a space before the key to avoid changing identifiers, e.g. ({ get[2]() {} }) to ({ get2() {} }) + const needsSpaceBeforeKey = tokenBeforeLeftBracket.range[1] === leftSquareBracket.range[0] && + !astUtils.canTokensBeAdjacent(tokenBeforeLeftBracket, sourceCode.getFirstToken(key)); - const replacementKey = (needsSpaceBeforeKey ? " " : "") + key.raw; + const replacementKey = (needsSpaceBeforeKey ? " " : "") + key.raw; - return fixer.replaceTextRange([leftSquareBracket.range[0], rightSquareBracket.range[1]], replacementKey); - } - }); - } + return fixer.replaceTextRange([leftSquareBracket.range[0], rightSquareBracket.range[1]], replacementKey); + } + }); } + } + + return { + Property: check, + MethodDefinition: enforceForClassMembers ? check : lodash.noop }; } }; diff --git a/tools/node_modules/eslint/lib/rules/no-useless-escape.js b/tools/node_modules/eslint/lib/rules/no-useless-escape.js index ebfe4cba38a9a7..8057e44ddab463 100644 --- a/tools/node_modules/eslint/lib/rules/no-useless-escape.js +++ b/tools/node_modules/eslint/lib/rules/no-useless-escape.js @@ -85,7 +85,14 @@ module.exports = { description: "disallow unnecessary escape characters", category: "Best Practices", recommended: true, - url: "https://eslint.org/docs/rules/no-useless-escape" + url: "https://eslint.org/docs/rules/no-useless-escape", + suggestion: true + }, + + messages: { + unnecessaryEscape: "Unnecessary escape character: \\{{character}}.", + removeEscape: "Remove the `\\`. This maintains the current functionality.", + escapeBackslash: "Replace the `\\` with `\\\\` to include the actual backslash character." }, schema: [] @@ -103,6 +110,8 @@ module.exports = { */ function report(node, startOffset, character) { const start = sourceCode.getLocFromIndex(sourceCode.getIndexFromLoc(node.loc.start) + startOffset); + const rangeStart = sourceCode.getIndexFromLoc(node.loc.start) + startOffset; + const range = [rangeStart, rangeStart + 1]; context.report({ node, @@ -110,8 +119,22 @@ module.exports = { start, end: { line: start.line, column: start.column + 1 } }, - message: "Unnecessary escape character: \\{{character}}.", - data: { character } + messageId: "unnecessaryEscape", + data: { character }, + suggest: [ + { + messageId: "removeEscape", + fix(fixer) { + return fixer.removeRange(range); + } + }, + { + messageId: "escapeBackslash", + fix(fixer) { + return fixer.insertTextBeforeRange(range, "\\"); + } + } + ] }); } diff --git a/tools/node_modules/eslint/lib/rules/object-curly-spacing.js b/tools/node_modules/eslint/lib/rules/object-curly-spacing.js index 53ddaaa24ad01e..117a7a35426103 100644 --- a/tools/node_modules/eslint/lib/rules/object-curly-spacing.js +++ b/tools/node_modules/eslint/lib/rules/object-curly-spacing.js @@ -74,16 +74,16 @@ module.exports = { * @returns {void} */ function reportNoBeginningSpace(node, token) { + const nextToken = context.getSourceCode().getTokenAfter(token, { includeComments: true }); + context.report({ node, - loc: token.loc.start, + loc: { start: token.loc.end, end: nextToken.loc.start }, message: "There should be no space after '{{token}}'.", data: { token: token.value }, fix(fixer) { - const nextToken = context.getSourceCode().getTokenAfter(token, { includeComments: true }); - return fixer.removeRange([token.range[1], nextToken.range[0]]); } }); @@ -96,16 +96,16 @@ module.exports = { * @returns {void} */ function reportNoEndingSpace(node, token) { + const previousToken = context.getSourceCode().getTokenBefore(token, { includeComments: true }); + context.report({ node, - loc: token.loc.start, + loc: { start: previousToken.loc.end, end: token.loc.start }, message: "There should be no space before '{{token}}'.", data: { token: token.value }, fix(fixer) { - const previousToken = context.getSourceCode().getTokenBefore(token, { includeComments: true }); - return fixer.removeRange([previousToken.range[1], token.range[0]]); } }); @@ -120,7 +120,7 @@ module.exports = { function reportRequiredBeginningSpace(node, token) { context.report({ node, - loc: token.loc.start, + loc: token.loc, message: "A space is required after '{{token}}'.", data: { token: token.value @@ -140,7 +140,7 @@ module.exports = { function reportRequiredEndingSpace(node, token) { context.report({ node, - loc: token.loc.start, + loc: token.loc, message: "A space is required before '{{token}}'.", data: { token: token.value diff --git a/tools/node_modules/eslint/lib/rules/operator-assignment.js b/tools/node_modules/eslint/lib/rules/operator-assignment.js index e294668b42b1ce..b19ba0d02e1a1b 100644 --- a/tools/node_modules/eslint/lib/rules/operator-assignment.js +++ b/tools/node_modules/eslint/lib/rules/operator-assignment.js @@ -71,6 +71,9 @@ function same(a, b) { */ return same(a.object, b.object) && same(a.property, b.property); + case "ThisExpression": + return true; + default: return false; } @@ -83,8 +86,14 @@ function same(a, b) { * @returns {boolean} `true` if the node can be fixed */ function canBeFixed(node) { - return node.type === "Identifier" || - node.type === "MemberExpression" && node.object.type === "Identifier" && (!node.computed || node.property.type === "Literal"); + return ( + node.type === "Identifier" || + ( + node.type === "MemberExpression" && + (node.object.type === "Identifier" || node.object.type === "ThisExpression") && + (!node.computed || node.property.type === "Literal") + ) + ); } module.exports = { diff --git a/tools/node_modules/eslint/lib/rules/prefer-const.js b/tools/node_modules/eslint/lib/rules/prefer-const.js index 87f83892126d50..439a4db3c963f6 100644 --- a/tools/node_modules/eslint/lib/rules/prefer-const.js +++ b/tools/node_modules/eslint/lib/rules/prefer-const.js @@ -356,7 +356,9 @@ module.exports = { const ignoreReadBeforeAssign = options.ignoreReadBeforeAssign === true; const variables = []; let reportCount = 0; - let name = ""; + let checkedId = null; + let checkedName = ""; + /** * Reports given identifier nodes if all of the nodes should be declared @@ -387,25 +389,30 @@ module.exports = { /* * First we check the declaration type and then depending on * if the type is a "VariableDeclarator" or its an "ObjectPattern" - * we compare the name from the first identifier, if the names are different - * we assign the new name and reset the count of reportCount and nodeCount in + * we compare the name and id from the first identifier, if the names are different + * we assign the new name, id and reset the count of reportCount and nodeCount in * order to check each block for the number of reported errors and base our fix * based on comparing nodes.length and nodesToReport.length. */ if (firstDecParent.type === "VariableDeclarator") { - if (firstDecParent.id.name !== name) { - name = firstDecParent.id.name; + if (firstDecParent.id.name !== checkedName) { + checkedName = firstDecParent.id.name; reportCount = 0; } if (firstDecParent.id.type === "ObjectPattern") { - if (firstDecParent.init.name !== name) { - name = firstDecParent.init.name; + if (firstDecParent.init.name !== checkedName) { + checkedName = firstDecParent.init.name; reportCount = 0; } } + + if (firstDecParent.id !== checkedId) { + checkedId = firstDecParent.id; + reportCount = 0; + } } } } diff --git a/tools/node_modules/eslint/lib/rules/prefer-exponentiation-operator.js b/tools/node_modules/eslint/lib/rules/prefer-exponentiation-operator.js new file mode 100644 index 00000000000000..5e75ef4724f3b7 --- /dev/null +++ b/tools/node_modules/eslint/lib/rules/prefer-exponentiation-operator.js @@ -0,0 +1,189 @@ +/** + * @fileoverview Rule to disallow Math.pow in favor of the ** operator + * @author Milos Djermanovic + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const astUtils = require("./utils/ast-utils"); +const { CALL, ReferenceTracker } = require("eslint-utils"); + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +const PRECEDENCE_OF_EXPONENTIATION_EXPR = astUtils.getPrecedence({ type: "BinaryExpression", operator: "**" }); + +/** + * Determines whether the given node needs parens if used as the base in an exponentiation binary expression. + * @param {ASTNode} base The node to check. + * @returns {boolean} `true` if the node needs to be parenthesised. + */ +function doesBaseNeedParens(base) { + return ( + + // '**' is right-associative, parens are needed when Math.pow(a ** b, c) is converted to (a ** b) ** c + astUtils.getPrecedence(base) <= PRECEDENCE_OF_EXPONENTIATION_EXPR || + + // An unary operator cannot be used immediately before an exponentiation expression + base.type === "UnaryExpression" + ); +} + +/** + * Determines whether the given node needs parens if used as the exponent in an exponentiation binary expression. + * @param {ASTNode} exponent The node to check. + * @returns {boolean} `true` if the node needs to be parenthesised. + */ +function doesExponentNeedParens(exponent) { + + // '**' is right-associative, there is no need for parens when Math.pow(a, b ** c) is converted to a ** b ** c + return astUtils.getPrecedence(exponent) < PRECEDENCE_OF_EXPONENTIATION_EXPR; +} + +/** + * Determines whether an exponentiation binary expression at the place of the given node would need parens. + * @param {ASTNode} node A node that would be replaced by an exponentiation binary expression. + * @param {SourceCode} sourceCode A SourceCode object. + * @returns {boolean} `true` if the expression needs to be parenthesised. + */ +function doesExponentiationExpressionNeedParens(node, sourceCode) { + const parent = node.parent; + + const needsParens = ( + parent.type === "ClassDeclaration" || + ( + parent.type.endsWith("Expression") && + astUtils.getPrecedence(parent) >= PRECEDENCE_OF_EXPONENTIATION_EXPR && + !(parent.type === "BinaryExpression" && parent.operator === "**" && parent.right === node) && + !((parent.type === "CallExpression" || parent.type === "NewExpression") && parent.arguments.includes(node)) && + !(parent.type === "MemberExpression" && parent.computed && parent.property === node) && + !(parent.type === "ArrayExpression") + ) + ); + + return needsParens && !astUtils.isParenthesised(sourceCode, node); +} + +/** + * Optionally parenthesizes given text. + * @param {string} text The text to parenthesize. + * @param {boolean} shouldParenthesize If `true`, the text will be parenthesised. + * @returns {string} parenthesised or unchanged text. + */ +function parenthesizeIfShould(text, shouldParenthesize) { + return shouldParenthesize ? `(${text})` : text; +} + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: "suggestion", + + docs: { + description: "disallow the use of `Math.pow` in favor of the `**` operator", + category: "Stylistic Issues", + recommended: false, + url: "https://eslint.org/docs/rules/prefer-exponentiation-operator" + }, + + schema: [], + fixable: "code", + + messages: { + useExponentiation: "Use the '**' operator instead of 'Math.pow'." + } + }, + + create(context) { + const sourceCode = context.getSourceCode(); + + /** + * Reports the given node. + * @param {ASTNode} node 'Math.pow()' node to report. + * @returns {void} + */ + function report(node) { + context.report({ + node, + messageId: "useExponentiation", + fix(fixer) { + if ( + node.arguments.length !== 2 || + node.arguments.some(arg => arg.type === "SpreadElement") || + sourceCode.getCommentsInside(node).length > 0 + ) { + return null; + } + + const base = node.arguments[0], + exponent = node.arguments[1], + baseText = sourceCode.getText(base), + exponentText = sourceCode.getText(exponent), + shouldParenthesizeBase = doesBaseNeedParens(base), + shouldParenthesizeExponent = doesExponentNeedParens(exponent), + shouldParenthesizeAll = doesExponentiationExpressionNeedParens(node, sourceCode); + + let prefix = "", + suffix = ""; + + if (!shouldParenthesizeAll) { + if (!shouldParenthesizeBase) { + const firstReplacementToken = sourceCode.getFirstToken(base), + tokenBefore = sourceCode.getTokenBefore(node); + + if ( + tokenBefore && + tokenBefore.range[1] === node.range[0] && + !astUtils.canTokensBeAdjacent(tokenBefore, firstReplacementToken) + ) { + prefix = " "; // a+Math.pow(++b, c) -> a+ ++b**c + } + } + if (!shouldParenthesizeExponent) { + const lastReplacementToken = sourceCode.getLastToken(exponent), + tokenAfter = sourceCode.getTokenAfter(node); + + if ( + tokenAfter && + node.range[1] === tokenAfter.range[0] && + !astUtils.canTokensBeAdjacent(lastReplacementToken, tokenAfter) + ) { + suffix = " "; // Math.pow(a, b)in c -> a**b in c + } + } + } + + const baseReplacement = parenthesizeIfShould(baseText, shouldParenthesizeBase), + exponentReplacement = parenthesizeIfShould(exponentText, shouldParenthesizeExponent), + replacement = parenthesizeIfShould(`${baseReplacement}**${exponentReplacement}`, shouldParenthesizeAll); + + return fixer.replaceText(node, `${prefix}${replacement}${suffix}`); + } + }); + } + + return { + Program() { + const scope = context.getScope(); + const tracker = new ReferenceTracker(scope); + const trackMap = { + Math: { + pow: { [CALL]: true } + } + }; + + for (const { node } of tracker.iterateGlobalReferences(trackMap)) { + report(node); + } + } + }; + } +}; diff --git a/tools/node_modules/eslint/lib/rules/require-await.js b/tools/node_modules/eslint/lib/rules/require-await.js index 0aa6fce7e19859..22c111b6dc852e 100644 --- a/tools/node_modules/eslint/lib/rules/require-await.js +++ b/tools/node_modules/eslint/lib/rules/require-await.js @@ -89,9 +89,17 @@ module.exports = { "ArrowFunctionExpression:exit": exitFunction, AwaitExpression() { + if (!scopeInfo) { + return; + } + scopeInfo.hasAwait = true; }, ForOfStatement(node) { + if (!scopeInfo) { + return; + } + if (node.await) { scopeInfo.hasAwait = true; } diff --git a/tools/node_modules/eslint/lib/rules/semi.js b/tools/node_modules/eslint/lib/rules/semi.js index a159e19d347dcf..22e299efe72e49 100644 --- a/tools/node_modules/eslint/lib/rules/semi.js +++ b/tools/node_modules/eslint/lib/rules/semi.js @@ -93,17 +93,20 @@ module.exports = { const lastToken = sourceCode.getLastToken(node); let message, fix, - loc = lastToken.loc; + loc; if (!missing) { message = "Missing semicolon."; - loc = loc.end; + loc = { + start: lastToken.loc.end, + end: astUtils.getNextLocation(sourceCode, lastToken.loc.end) + }; fix = function(fixer) { return fixer.insertTextAfter(lastToken, ";"); }; } else { message = "Extra semicolon."; - loc = loc.start; + loc = lastToken.loc; fix = function(fixer) { /* diff --git a/tools/node_modules/eslint/lib/rules/space-infix-ops.js b/tools/node_modules/eslint/lib/rules/space-infix-ops.js index b2fbe47b4765e6..bd2c0ae0e1d3b7 100644 --- a/tools/node_modules/eslint/lib/rules/space-infix-ops.js +++ b/tools/node_modules/eslint/lib/rules/space-infix-ops.js @@ -69,7 +69,7 @@ module.exports = { function report(mainNode, culpritToken) { context.report({ node: mainNode, - loc: culpritToken.loc.start, + loc: culpritToken.loc, message: "Operator '{{operator}}' must be spaced.", data: { operator: culpritToken.value diff --git a/tools/node_modules/eslint/lib/rules/spaced-comment.js b/tools/node_modules/eslint/lib/rules/spaced-comment.js index 958bb2c6444080..daf56cd6bb42d0 100644 --- a/tools/node_modules/eslint/lib/rules/spaced-comment.js +++ b/tools/node_modules/eslint/lib/rules/spaced-comment.js @@ -249,7 +249,8 @@ module.exports = { beginRegex: requireSpace ? createAlwaysStylePattern(markers, exceptions) : createNeverStylePattern(markers), endRegex: balanced && requireSpace ? new RegExp(`${createExceptionsPattern(exceptions)}$`, "u") : new RegExp(endNeverPattern, "u"), hasExceptions: exceptions.length > 0, - markers: new RegExp(`^(${markers.map(escape).join("|")})`, "u") + captureMarker: new RegExp(`^(${markers.map(escape).join("|")})`, "u"), + markers: new Set(markers) }; return rule; @@ -322,8 +323,8 @@ module.exports = { rule = styleRules[type], commentIdentifier = type === "block" ? "/*" : "//"; - // Ignores empty comments. - if (node.value.length === 0) { + // Ignores empty comments and comments that consist only of a marker. + if (node.value.length === 0 || rule.markers.has(node.value)) { return; } @@ -333,7 +334,7 @@ module.exports = { // Checks. if (requireSpace) { if (!beginMatch) { - const hasMarker = rule.markers.exec(node.value); + const hasMarker = rule.captureMarker.exec(node.value); const marker = hasMarker ? commentIdentifier + hasMarker[0] : commentIdentifier; if (rule.hasExceptions) { diff --git a/tools/node_modules/eslint/lib/rules/utils/ast-utils.js b/tools/node_modules/eslint/lib/rules/utils/ast-utils.js index 17e056c240c13d..01c6b47b82eefc 100644 --- a/tools/node_modules/eslint/lib/rules/utils/ast-utils.js +++ b/tools/node_modules/eslint/lib/rules/utils/ast-utils.js @@ -581,23 +581,31 @@ module.exports = { * * First, this checks the node: * - * - The function name does not start with uppercase (it's a constructor). + * - The function name does not start with uppercase. It's a convention to capitalize the names + * of constructor functions. This check is not performed if `capIsConstructor` is set to `false`. * - The function does not have a JSDoc comment that has a @this tag. * * Next, this checks the location of the node. * If the location is below, this judges `this` is valid. * * - The location is not on an object literal. - * - The location is not assigned to a variable which starts with an uppercase letter. + * - The location is not assigned to a variable which starts with an uppercase letter. Applies to anonymous + * functions only, as the name of the variable is considered to be the name of the function in this case. + * This check is not performed if `capIsConstructor` is set to `false`. * - The location is not on an ES2015 class. * - Its `bind`/`call`/`apply` method is not called directly. * - The function is not a callback of array methods (such as `.forEach()`) if `thisArg` is given. * @param {ASTNode} node A function node to check. * @param {SourceCode} sourceCode A SourceCode instance to get comments. + * @param {boolean} [capIsConstructor = true] `false` disables the assumption that functions which name starts + * with an uppercase or are assigned to a variable which name starts with an uppercase are constructors. * @returns {boolean} The function node is the default `this` binding. */ - isDefaultThisBinding(node, sourceCode) { - if (isES5Constructor(node) || hasJSDocThisTag(node, sourceCode)) { + isDefaultThisBinding(node, sourceCode, { capIsConstructor = true } = {}) { + if ( + (capIsConstructor && isES5Constructor(node)) || + hasJSDocThisTag(node, sourceCode) + ) { return false; } const isAnonymous = node.id === null; @@ -671,6 +679,7 @@ module.exports = { return false; } if ( + capIsConstructor && isAnonymous && parent.left.type === "Identifier" && startsWithUpperCase(parent.left.name) @@ -685,6 +694,7 @@ module.exports = { */ case "VariableDeclarator": return !( + capIsConstructor && isAnonymous && parent.init === currentNode && parent.id.type === "Identifier" && @@ -1200,6 +1210,23 @@ module.exports = { }; }, + /** + * Gets next location when the result is not out of bound, otherwise returns null. + * @param {SourceCode} sourceCode The sourceCode + * @param {{line: number, column: number}} location The location + * @returns {{line: number, column: number} | null} Next location + */ + getNextLocation(sourceCode, location) { + const index = sourceCode.getIndexFromLoc(location); + + // Avoid out of bound location + if (index + 1 > sourceCode.text.length) { + return null; + } + + return sourceCode.getLocFromIndex(index + 1); + }, + /** * Gets the parenthesized text of a node. This is similar to sourceCode.getText(node), but it also includes any parentheses * surrounding the node. diff --git a/tools/node_modules/eslint/lib/shared/types.js b/tools/node_modules/eslint/lib/shared/types.js index 12bd0aed8c6f1d..a5bd0200e2792a 100644 --- a/tools/node_modules/eslint/lib/shared/types.js +++ b/tools/node_modules/eslint/lib/shared/types.js @@ -30,6 +30,7 @@ module.exports = {}; * @property {Record} [env] The environment settings. * @property {string | string[]} [extends] The path to other config files or the package name of shareable configs. * @property {Record} [globals] The global variable settings. + * @property {string | string[]} [ignorePatterns] The glob patterns that ignore to lint. * @property {boolean} [noInlineConfig] The flag that disables directive comments. * @property {OverrideConfigData[]} [overrides] The override settings per kind of files. * @property {string} [parser] The path to a parser or the package name of a parser. @@ -91,6 +92,14 @@ module.exports = {}; * @property {string} message The error message. * @property {string|null} ruleId The ID of the rule which makes this message. * @property {0|1|2} severity The severity of this message. + * @property {Array<{desc?: string, messageId?: string, fix: {range: [number, number], text: string}}>} [suggestions] Information for suggestions. + */ + +/** + * @typedef {Object} SuggestionResult + * @property {string} desc A short description. + * @property {string} [messageId] Id referencing a message for the description. + * @property {{ text: string, range: number[] }} fix fix result info */ /** diff --git a/tools/node_modules/eslint/lib/source-code/source-code.js b/tools/node_modules/eslint/lib/source-code/source-code.js index 86a56803ed76c3..20b442f2367203 100644 --- a/tools/node_modules/eslint/lib/source-code/source-code.js +++ b/tools/node_modules/eslint/lib/source-code/source-code.js @@ -78,6 +78,18 @@ function sortedMerge(tokens, comments) { return result; } +/** + * Determines if two nodes or tokens overlap. + * @param {ASTNode|Token} first The first node or token to check. + * @param {ASTNode|Token} second The second node or token to check. + * @returns {boolean} True if the two nodes or tokens overlap. + * @private + */ +function nodesOrTokensOverlap(first, second) { + return (first.range[0] <= second.range[0] && first.range[1] >= second.range[0]) || + (second.range[0] <= first.range[0] && second.range[1] >= first.range[0]); +} + //------------------------------------------------------------------------------ // Public Interface //------------------------------------------------------------------------------ @@ -411,19 +423,52 @@ class SourceCode extends TokenStore { } /** - * Determines if two tokens have at least one whitespace character - * between them. This completely disregards comments in making the - * determination, so comments count as zero-length substrings. - * @param {Token} first The token to check after. - * @param {Token} second The token to check before. - * @returns {boolean} True if there is only space between tokens, false - * if there is anything other than whitespace between tokens. + * Determines if two nodes or tokens have at least one whitespace character + * between them. Order does not matter. Returns false if the given nodes or + * tokens overlap. + * @param {ASTNode|Token} first The first node or token to check between. + * @param {ASTNode|Token} second The second node or token to check between. + * @returns {boolean} True if there is a whitespace character between + * any of the tokens found between the two given nodes or tokens. * @public */ - isSpaceBetweenTokens(first, second) { - const text = this.text.slice(first.range[1], second.range[0]); + isSpaceBetween(first, second) { + if (nodesOrTokensOverlap(first, second)) { + return false; + } + + const [startingNodeOrToken, endingNodeOrToken] = first.range[1] <= second.range[0] + ? [first, second] + : [second, first]; + const firstToken = this.getLastToken(startingNodeOrToken) || startingNodeOrToken; + const finalToken = this.getFirstToken(endingNodeOrToken) || endingNodeOrToken; + let currentToken = firstToken; - return /\s/u.test(text.replace(/\/\*.*?\*\//gus, "")); + while (currentToken !== finalToken) { + const nextToken = this.getTokenAfter(currentToken, { includeComments: true }); + + if (currentToken.range[1] !== nextToken.range[0]) { + return true; + } + + currentToken = nextToken; + } + + return false; + } + + /** + * Determines if two nodes or tokens have at least one whitespace character + * between them. Order does not matter. Returns false if the given nodes or + * tokens overlap. + * @param {...ASTNode|Token} args The nodes or tokens to check between. + * @returns {boolean} True if there is a whitespace character between + * any of the tokens found between the two given nodes or tokens. + * @deprecated in favor of isSpaceBetween(). + * @public + */ + isSpaceBetweenTokens(...args) { + return this.isSpaceBetween(...args); } /** diff --git a/tools/node_modules/eslint/node_modules/ansi-escapes/index.js b/tools/node_modules/eslint/node_modules/ansi-escapes/index.js index 4638bbc3d62b29..283331858f02e1 100644 --- a/tools/node_modules/eslint/node_modules/ansi-escapes/index.js +++ b/tools/node_modules/eslint/node_modules/ansi-escapes/index.js @@ -128,5 +128,30 @@ ansiEscapes.image = (buffer, options = {}) => { }; ansiEscapes.iTerm = { - setCwd: (cwd = process.cwd()) => `${OSC}50;CurrentDir=${cwd}${BEL}` + setCwd: (cwd = process.cwd()) => `${OSC}50;CurrentDir=${cwd}${BEL}`, + + annotation: (message, options = {}) => { + let ret = `${OSC}1337;`; + + const hasX = typeof options.x !== 'undefined'; + const hasY = typeof options.y !== 'undefined'; + if ((hasX || hasY) && !(hasX && hasY && typeof options.length !== 'undefined')) { + throw new Error('`x`, `y` and `length` must be defined when `x` or `y` is defined'); + } + + message = message.replace(/\|/g, ''); + + ret += options.isHidden ? 'AddHiddenAnnotation=' : 'AddAnnotation='; + + if (options.length > 0) { + ret += + (hasX ? + [message, options.length, options.x, options.y] : + [options.length, message]).join('|'); + } else { + ret += message; + } + + return ret + BEL; + } }; diff --git a/tools/node_modules/eslint/node_modules/ansi-escapes/package.json b/tools/node_modules/eslint/node_modules/ansi-escapes/package.json index a58ed743a59171..3d97c617866121 100644 --- a/tools/node_modules/eslint/node_modules/ansi-escapes/package.json +++ b/tools/node_modules/eslint/node_modules/ansi-escapes/package.json @@ -9,15 +9,15 @@ }, "bundleDependencies": false, "dependencies": { - "type-fest": "^0.5.2" + "type-fest": "^0.8.1" }, "deprecated": false, "description": "ANSI escape codes for manipulating the terminal", "devDependencies": { "@types/node": "^12.0.7", "ava": "^2.1.0", - "tsd": "^0.7.1", - "xo": "^0.24.0" + "tsd": "^0.11.0", + "xo": "^0.25.3" }, "engines": { "node": ">=8" @@ -61,5 +61,5 @@ "scripts": { "test": "xo && ava && tsd" }, - "version": "4.2.1" + "version": "4.3.0" } \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/ansi-escapes/readme.md b/tools/node_modules/eslint/node_modules/ansi-escapes/readme.md index b290753094d948..818a9263f4e680 100644 --- a/tools/node_modules/eslint/node_modules/ansi-escapes/readme.md +++ b/tools/node_modules/eslint/node_modules/ansi-escapes/readme.md @@ -2,14 +2,12 @@ > [ANSI escape codes](http://www.termsys.demon.co.uk/vtansi.htm) for manipulating the terminal - ## Install ``` $ npm install ansi-escapes ``` - ## Usage ```js @@ -20,7 +18,6 @@ process.stdout.write(ansiEscapes.cursorUp(2) + ansiEscapes.cursorLeft); //=> '\u001B[2A\u001B[1000D' ``` - ## API ### cursorTo(x, y?) @@ -165,16 +162,71 @@ The width and height are given as a number followed by a unit, or the word "auto ##### preserveAspectRatio -Type: `boolean`
+Type: `boolean`\ Default: `true` ### iTerm.setCwd(path?) -Type: `string`
+Type: `string`\ Default: `process.cwd()` [Inform iTerm2](https://www.iterm2.com/documentation-escape-codes.html) of the current directory to help semantic history and enable [Cmd-clicking relative paths](https://coderwall.com/p/b7e82q/quickly-open-files-in-iterm-with-cmd-click). +### iTerm.annotation(message, options?) + +Creates an escape code to display an "annotation" in iTerm2. + +An annotation looks like this when shown: + + + +See the [iTerm Proprietary Escape Codes documentation](https://iterm2.com/documentation-escape-codes.html) for more information. + +#### message + +Type: `string` + +The message to display within the annotation. + +The `|` character is disallowed and will be stripped. + +#### options + +Type: `object` + +##### length + +Type: `number`\ +Default: The remainder of the line + +Nonzero number of columns to annotate. + +##### x + +Type: `number`\ +Default: Cursor position + +Starting X coordinate. + +Must be used with `y` and `length`. + +##### y + +Type: `number`\ +Default: Cursor position + +Starting Y coordinate. + +Must be used with `x` and `length`. + +##### isHidden + +Type: `boolean`\ +Default: `false` + +Create a "hidden" annotation. + +Annotations created this way can be shown using the "Show Annotations" iTerm command. ## Related diff --git a/tools/node_modules/eslint/node_modules/ansi-regex/index.js b/tools/node_modules/eslint/node_modules/ansi-regex/index.js index c25448009f304d..35054aa6774afe 100644 --- a/tools/node_modules/eslint/node_modules/ansi-regex/index.js +++ b/tools/node_modules/eslint/node_modules/ansi-regex/index.js @@ -1,14 +1,10 @@ 'use strict'; -module.exports = options => { - options = Object.assign({ - onlyFirst: false - }, options); - +module.exports = ({onlyFirst = false} = {}) => { const pattern = [ '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))' ].join('|'); - return new RegExp(pattern, options.onlyFirst ? undefined : 'g'); + return new RegExp(pattern, onlyFirst ? undefined : 'g'); }; diff --git a/tools/node_modules/eslint/node_modules/ansi-regex/package.json b/tools/node_modules/eslint/node_modules/ansi-regex/package.json index db8f3cc8c7d14e..d0574afde48599 100644 --- a/tools/node_modules/eslint/node_modules/ansi-regex/package.json +++ b/tools/node_modules/eslint/node_modules/ansi-regex/package.json @@ -11,14 +11,16 @@ "deprecated": false, "description": "Regular expression for matching ANSI escape codes", "devDependencies": { - "ava": "^0.25.0", - "xo": "^0.23.0" + "ava": "^2.4.0", + "tsd": "^0.9.0", + "xo": "^0.25.3" }, "engines": { - "node": ">=6" + "node": ">=8" }, "files": [ - "index.js" + "index.js", + "index.d.ts" ], "homepage": "https://github.com/chalk/ansi-regex#readme", "keywords": [ @@ -55,8 +57,8 @@ "url": "git+https://github.com/chalk/ansi-regex.git" }, "scripts": { - "test": "xo && ava", + "test": "xo && ava && tsd", "view-supported": "node fixtures/view-codes.js" }, - "version": "4.1.0" + "version": "5.0.0" } \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/ansi-regex/readme.md b/tools/node_modules/eslint/node_modules/ansi-regex/readme.md index d19c44667e704b..3c2b77c4354b68 100644 --- a/tools/node_modules/eslint/node_modules/ansi-regex/readme.md +++ b/tools/node_modules/eslint/node_modules/ansi-regex/readme.md @@ -2,20 +2,6 @@ > Regular expression for matching [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) ---- - -
- - Get professional support for this package with a Tidelift subscription - -
- - Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies. -
-
- ---- - ## Install @@ -48,12 +34,14 @@ ansiRegex().test('cake'); ## API -### ansiRegex([options]) +### ansiRegex(options?) Returns a regex for matching ANSI escape codes. #### options +Type: `object` + ##### onlyFirst Type: `boolean`
@@ -71,17 +59,20 @@ Some of the codes we run as a test are codes that we acquired finding various li On the historical side, those ECMA standards were established in the early 90's whereas the VT100, for example, was designed in the mid/late 70's. At that point in time, control codes were still pretty ungoverned and engineers used them for a multitude of things, namely to activate hardware ports that may have been proprietary. Somewhere else you see a similar 'anarchy' of codes is in the x86 architecture for processors; there are a ton of "interrupts" that can mean different things on certain brands of processors, most of which have been phased out. -## Security - -To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. - - ## Maintainers - [Sindre Sorhus](https://github.com/sindresorhus) - [Josh Junon](https://github.com/qix-) -## License +--- -MIT +
+ + Get professional support for this package with a Tidelift subscription + +
+ + Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies. +
+
diff --git a/tools/node_modules/eslint/node_modules/glob/package.json b/tools/node_modules/eslint/node_modules/glob/package.json index cf2be43974e853..bf5a63608ca133 100644 --- a/tools/node_modules/eslint/node_modules/glob/package.json +++ b/tools/node_modules/eslint/node_modules/glob/package.json @@ -32,6 +32,9 @@ "sync.js", "common.js" ], + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, "homepage": "https://github.com/isaacs/node-glob#readme", "license": "ISC", "main": "glob.js", @@ -49,5 +52,5 @@ "test": "tap test/*.js --cov", "test-regen": "npm run profclean && TEST_REGEN=1 node test/00-setup.js" }, - "version": "7.1.5" + "version": "7.1.6" } \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/globals/globals.json b/tools/node_modules/eslint/node_modules/globals/globals.json index 6f5ac02f1eace7..b33f0431b544e9 100644 --- a/tools/node_modules/eslint/node_modules/globals/globals.json +++ b/tools/node_modules/eslint/node_modules/globals/globals.json @@ -1066,6 +1066,7 @@ "beforeEach": false, "describe": false, "expect": false, + "expectAsync": false, "fail": false, "fdescribe": false, "fit": false, @@ -1074,6 +1075,7 @@ "pending": false, "runs": false, "spyOn": false, + "spyOnAllFunctions": false, "spyOnProperty": false, "waits": false, "waitsFor": false, @@ -1184,8 +1186,11 @@ }, "wsh": { "ActiveXObject": true, + "CollectGarbage": true, + "Debug": true, "Enumerator": true, "GetObject": true, + "RuntimeObject": true, "ScriptEngine": true, "ScriptEngineBuildVersion": true, "ScriptEngineMajorVersion": true, diff --git a/tools/node_modules/eslint/node_modules/globals/package.json b/tools/node_modules/eslint/node_modules/globals/package.json index ae094f3054063e..de6b8926f166d9 100644 --- a/tools/node_modules/eslint/node_modules/globals/package.json +++ b/tools/node_modules/eslint/node_modules/globals/package.json @@ -8,17 +8,22 @@ "url": "https://github.com/sindresorhus/globals/issues" }, "bundleDependencies": false, + "dependencies": { + "type-fest": "^0.8.1" + }, "deprecated": false, "description": "Global identifiers from different JavaScript environments", "devDependencies": { - "ava": "0.21.0", - "xo": "0.18.0" + "ava": "^2.2.0", + "tsd": "^0.9.0", + "xo": "^0.25.3" }, "engines": { - "node": ">=4" + "node": ">=8" }, "files": [ "index.js", + "index.d.ts", "globals.json" ], "homepage": "https://github.com/sindresorhus/globals#readme", @@ -41,7 +46,12 @@ "scripts": { "test": "xo && ava" }, - "version": "11.12.0", + "tsd": { + "compilerOptions": { + "resolveJsonModule": true + } + }, + "version": "12.3.0", "xo": { "ignores": [ "get-browser-globals.js" diff --git a/tools/node_modules/eslint/node_modules/globals/readme.md b/tools/node_modules/eslint/node_modules/globals/readme.md index 8c47855f34d900..96ce28347a8de0 100644 --- a/tools/node_modules/eslint/node_modules/globals/readme.md +++ b/tools/node_modules/eslint/node_modules/globals/readme.md @@ -36,6 +36,14 @@ console.log(globals.browser); Each global is given a value of `true` or `false`. A value of `true` indicates that the variable may be overwritten. A value of `false` indicates that the variable should be considered read-only. This information is used by static analysis tools to flag incorrect behavior. We assume all variables should be `false` unless we hear otherwise. -## License - -MIT © [Sindre Sorhus](https://sindresorhus.com) +--- + +
+ + Get professional support for this package with a Tidelift subscription + +
+ + Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies. +
+
diff --git a/tools/node_modules/eslint/node_modules/import-fresh/index.js b/tools/node_modules/eslint/node_modules/import-fresh/index.js index 7abc6706192d64..425ed98c42f68e 100644 --- a/tools/node_modules/eslint/node_modules/import-fresh/index.js +++ b/tools/node_modules/eslint/node_modules/import-fresh/index.js @@ -8,22 +8,25 @@ module.exports = moduleId => { throw new TypeError('Expected a string'); } - const filePath = resolveFrom(path.dirname(parentModule(__filename)), moduleId); + const parentPath = parentModule(__filename); + const filePath = resolveFrom(path.dirname(parentPath), moduleId); + + const oldModule = require.cache[filePath]; // Delete itself from module parent - if (require.cache[filePath] && require.cache[filePath].parent) { - let i = require.cache[filePath].parent.children.length; + if (oldModule && oldModule.parent) { + let i = oldModule.parent.children.length; while (i--) { - if (require.cache[filePath].parent.children[i].id === filePath) { - require.cache[filePath].parent.children.splice(i, 1); + if (oldModule.parent.children[i].id === filePath) { + oldModule.parent.children.splice(i, 1); } } } - // Delete module from cache - delete require.cache[filePath]; + delete require.cache[filePath]; // Delete module from cache + + const parent = require.cache[parentPath]; // If `filePath` and `parentPath` are the same, cache will already be deleted so we won't get a memory leak in next step - // Return fresh module - return require(filePath); + return parent === undefined ? require(filePath) : parent.require(filePath); // In case cache doesn't have parent, fall back to normal require }; diff --git a/tools/node_modules/eslint/node_modules/import-fresh/package.json b/tools/node_modules/eslint/node_modules/import-fresh/package.json index 73e9316d8999b7..38892a62e43136 100644 --- a/tools/node_modules/eslint/node_modules/import-fresh/package.json +++ b/tools/node_modules/eslint/node_modules/import-fresh/package.json @@ -47,5 +47,5 @@ "heapdump": "node heapdump.js", "test": "xo && ava && tsd" }, - "version": "3.1.0" + "version": "3.2.1" } \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/import-fresh/readme.md b/tools/node_modules/eslint/node_modules/import-fresh/readme.md index f5414b8cf33fdd..0bfa1c90443e83 100644 --- a/tools/node_modules/eslint/node_modules/import-fresh/readme.md +++ b/tools/node_modules/eslint/node_modules/import-fresh/readme.md @@ -37,22 +37,16 @@ importFresh('./foo')(); ``` +## import-fresh for enterprise + +Available as part of the Tidelift Subscription. + +The maintainers of import-fresh and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-import-fresh?utm_source=npm-import-fresh&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) + + ## Related - [clear-module](https://github.com/sindresorhus/clear-module) - Clear a module from the import cache - [import-from](https://github.com/sindresorhus/import-from) - Import a module from a given path - [import-cwd](https://github.com/sindresorhus/import-cwd) - Import a module from the current working directory - [import-lazy](https://github.com/sindresorhus/import-lazy) - Import modules lazily - - ---- - -
- - Get professional support for this package with a Tidelift subscription - -
- - Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies. -
-
diff --git a/tools/node_modules/eslint/node_modules/optionator/README.md b/tools/node_modules/eslint/node_modules/optionator/README.md index 91c59d379b3bae..8e4ba424526e44 100644 --- a/tools/node_modules/eslint/node_modules/optionator/README.md +++ b/tools/node_modules/eslint/node_modules/optionator/README.md @@ -1,7 +1,7 @@ # Optionator -Optionator is a JavaScript option parsing and help generation library used by [eslint](http://eslint.org), [Grasp](http://graspjs.com), [LiveScript](http://livescript.net), [esmangle](https://github.com/estools/esmangle), [escodegen](https://github.com/estools/escodegen), and [many more](https://www.npmjs.com/browse/depended/optionator). +Optionator is a JavaScript/Node.js option parsing and help generation library used by [eslint](http://eslint.org), [Grasp](http://graspjs.com), [LiveScript](http://livescript.net), [esmangle](https://github.com/estools/esmangle), [escodegen](https://github.com/estools/escodegen), and [many more](https://www.npmjs.com/browse/depended/optionator). For an online demo, check out the [Grasp online demo](http://www.graspjs.com/#demo). @@ -23,12 +23,14 @@ Other helpful features include reformatting the help text based on the size of t ## About Optionator uses [type-check](https://github.com/gkz/type-check) and [levn](https://github.com/gkz/levn) behind the scenes to cast and verify input according the specified types. -MIT license. Version 0.8.2 +MIT license. Version 0.8.3 npm install optionator For updates on Optionator, [follow me on twitter](https://twitter.com/gkzahariev). +Optionator is a Node.js module, but can be used in the browser as well if packed with webpack/browserify. + ## Usage `require('optionator');` returns a function. It has one property, `VERSION`, the current version of the library as a string. This function is called with an object specifying your options and other information, see the [settings format section](#settings-format). This in turn returns an object with three properties, `parse`, `parseArgv`, `generateHelp`, and `generateHelpForOption`, which are all functions. diff --git a/tools/node_modules/eslint/node_modules/optionator/lib/help.js b/tools/node_modules/eslint/node_modules/optionator/lib/help.js index a459c02c26711b..59e6f9694c9584 100644 --- a/tools/node_modules/eslint/node_modules/optionator/lib/help.js +++ b/tools/node_modules/eslint/node_modules/optionator/lib/help.js @@ -1,9 +1,22 @@ -// Generated by LiveScript 1.5.0 +// Generated by LiveScript 1.6.0 (function(){ - var ref$, id, find, sort, min, max, map, unlines, nameToRaw, dasherize, naturalJoin, wordwrap, getPreText, setHelpStyleDefaults, generateHelpForOption, generateHelp; + var ref$, id, find, sort, min, max, map, unlines, nameToRaw, dasherize, naturalJoin, wordWrap, wordwrap, getPreText, setHelpStyleDefaults, generateHelpForOption, generateHelp; ref$ = require('prelude-ls'), id = ref$.id, find = ref$.find, sort = ref$.sort, min = ref$.min, max = ref$.max, map = ref$.map, unlines = ref$.unlines; ref$ = require('./util'), nameToRaw = ref$.nameToRaw, dasherize = ref$.dasherize, naturalJoin = ref$.naturalJoin; - wordwrap = require('wordwrap'); + wordWrap = require('word-wrap'); + wordwrap = function(a, b){ + var ref$, indent, width; + ref$ = b === undefined + ? ['', a - 1] + : [repeatString$(' ', a), b - a - 1], indent = ref$[0], width = ref$[1]; + return function(text){ + return wordWrap(text, { + indent: indent, + width: width, + trim: true + }); + }; + }; getPreText = function(option, arg$, maxWidth){ var mainName, shortNames, ref$, longNames, type, description, aliasSeparator, typeSeparator, initialIndent, names, namesString, namesStringLen, typeSeparatorString, typeSeparatorStringLen, wrap; mainName = option.option, shortNames = (ref$ = option.shortNames) != null diff --git a/tools/node_modules/eslint/node_modules/optionator/lib/index.js b/tools/node_modules/eslint/node_modules/optionator/lib/index.js index d947286c761ef8..7ce37b23b01c98 100644 --- a/tools/node_modules/eslint/node_modules/optionator/lib/index.js +++ b/tools/node_modules/eslint/node_modules/optionator/lib/index.js @@ -1,7 +1,7 @@ -// Generated by LiveScript 1.5.0 +// Generated by LiveScript 1.6.0 (function(){ - var VERSION, ref$, id, map, compact, any, groupBy, partition, chars, isItNaN, keys, Obj, camelize, deepIs, closestString, nameToRaw, dasherize, naturalJoin, generateHelp, generateHelpForOption, parsedTypeCheck, parseType, parseLevn, camelizeKeys, parseString, main, toString$ = {}.toString, slice$ = [].slice; - VERSION = '0.8.2'; + var VERSION, ref$, id, map, compact, any, groupBy, partition, chars, isItNaN, keys, Obj, camelize, deepIs, closestString, nameToRaw, dasherize, naturalJoin, generateHelp, generateHelpForOption, parsedTypeCheck, parseType, parseLevn, camelizeKeys, parseString, main, toString$ = {}.toString, slice$ = [].slice, arrayFrom$ = Array.from || function(x){return slice$.call(x);}; + VERSION = '0.8.3'; ref$ = require('prelude-ls'), id = ref$.id, map = ref$.map, compact = ref$.compact, any = ref$.any, groupBy = ref$.groupBy, partition = ref$.partition, chars = ref$.chars, isItNaN = ref$.isItNaN, keys = ref$.keys, Obj = ref$.Obj, camelize = ref$.camelize; deepIs = require('deep-is'); ref$ = require('./util'), closestString = ref$.closestString, nameToRaw = ref$.nameToRaw, dasherize = ref$.dasherize, naturalJoin = ref$.naturalJoin; @@ -17,7 +17,7 @@ return resultObj$; }; parseString = function(string){ - var assignOpt, regex, replaceRegex, result, this$ = this; + var assignOpt, regex, replaceRegex, result; assignOpt = '--?[a-zA-Z][-a-z-A-Z0-9]*='; regex = RegExp('(?:' + assignOpt + ')?(?:\'(?:\\\\\'|[^\'])+\'|"(?:\\\\"|[^"])+")|[^\'"\\s]+', 'g'); replaceRegex = RegExp('^(' + assignOpt + ')?[\'"]([\\s\\S]*)[\'"]$'); @@ -44,7 +44,7 @@ libOptions.defaults.mergeRepeatedObjects = libOptions.mergeRepeatedObjects; } traverse = function(options){ - var i$, len$, option, name, k, ref$, v, type, that, e, parsedPossibilities, parsedType, j$, len1$, possibility, rawDependsType, dependsOpts, dependsType, cra, alias, shortNames, longNames, this$ = this; + var i$, len$, option, name, k, ref$, v, type, that, e, parsedPossibilities, parsedType, j$, len1$, possibility, rawDependsType, dependsOpts, dependsType, cra, alias, shortNames, longNames; if (toString$.call(options).slice(8, -1) !== 'Array') { throw new Error('No options defined.'); } @@ -104,7 +104,7 @@ dependsType = rawDependsType.toLowerCase(); if (dependsOpts.length) { if (dependsType === 'and' || dependsType === 'or') { - option.dependsOn = [dependsType].concat(slice$.call(dependsOpts)); + option.dependsOn = [dependsType].concat(arrayFrom$(dependsOpts)); } else { throw new Error("Option '" + name + "': If you have more than one dependency, you must specify either 'and' or 'or'"); } diff --git a/tools/node_modules/eslint/node_modules/optionator/lib/util.js b/tools/node_modules/eslint/node_modules/optionator/lib/util.js index d5c972def14cfa..5bc0cbb69e66aa 100644 --- a/tools/node_modules/eslint/node_modules/optionator/lib/util.js +++ b/tools/node_modules/eslint/node_modules/optionator/lib/util.js @@ -1,10 +1,10 @@ -// Generated by LiveScript 1.5.0 +// Generated by LiveScript 1.6.0 (function(){ var prelude, map, sortBy, fl, closestString, nameToRaw, dasherize, naturalJoin; prelude = require('prelude-ls'), map = prelude.map, sortBy = prelude.sortBy; fl = require('fast-levenshtein'); closestString = function(possibilities, input){ - var distances, ref$, string, distance, this$ = this; + var distances, ref$, string, distance; if (!possibilities.length) { return; } diff --git a/tools/node_modules/eslint/node_modules/optionator/package.json b/tools/node_modules/eslint/node_modules/optionator/package.json index d492cd682bdc29..b69935ea0e8e6d 100644 --- a/tools/node_modules/eslint/node_modules/optionator/package.json +++ b/tools/node_modules/eslint/node_modules/optionator/package.json @@ -9,18 +9,18 @@ "bundleDependencies": false, "dependencies": { "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", + "fast-levenshtein": "~2.0.6", "levn": "~0.3.0", "prelude-ls": "~1.1.2", "type-check": "~0.3.2", - "wordwrap": "~1.0.0" + "word-wrap": "~1.2.3" }, "deprecated": false, "description": "option parsing and help generation", "devDependencies": { - "istanbul": "~0.4.1", - "livescript": "~1.5.0", - "mocha": "~3.0.2" + "istanbul": "~0.4.5", + "livescript": "~1.6.0", + "mocha": "~6.2.2" }, "engines": { "node": ">= 0.8.0" @@ -47,5 +47,5 @@ "scripts": { "test": "make test" }, - "version": "0.8.2" + "version": "0.8.3" } \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/index.js b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/index.js new file mode 100644 index 00000000000000..9a593dfcd1fd5c --- /dev/null +++ b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/index.js @@ -0,0 +1,4 @@ +'use strict'; +const ansiRegex = require('ansi-regex'); + +module.exports = string => typeof string === 'string' ? string.replace(ansiRegex(), '') : string; diff --git a/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/license b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/license new file mode 100644 index 00000000000000..e7af2f77107d73 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/package.json b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/package.json new file mode 100644 index 00000000000000..5db6f68dc0aef7 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/package.json @@ -0,0 +1,63 @@ +{ + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/chalk/strip-ansi/issues" + }, + "bundleDependencies": false, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "deprecated": false, + "description": "Strip ANSI escape codes from a string", + "devDependencies": { + "ava": "^2.4.0", + "tsd": "^0.10.0", + "xo": "^0.25.3" + }, + "engines": { + "node": ">=8" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "homepage": "https://github.com/chalk/strip-ansi#readme", + "keywords": [ + "strip", + "trim", + "remove", + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "license": "MIT", + "name": "strip-ansi", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/strip-ansi.git" + }, + "scripts": { + "test": "xo && ava && tsd" + }, + "version": "6.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/readme.md b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/readme.md new file mode 100644 index 00000000000000..7c4b56d46ddc72 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/readme.md @@ -0,0 +1,46 @@ +# strip-ansi [![Build Status](https://travis-ci.org/chalk/strip-ansi.svg?branch=master)](https://travis-ci.org/chalk/strip-ansi) + +> Strip [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) from a string + + +## Install + +``` +$ npm install strip-ansi +``` + + +## Usage + +```js +const stripAnsi = require('strip-ansi'); + +stripAnsi('\u001B[4mUnicorn\u001B[0m'); +//=> 'Unicorn' + +stripAnsi('\u001B]8;;https://github.com\u0007Click\u001B]8;;\u0007'); +//=> 'Click' +``` + + +## strip-ansi for enterprise + +Available as part of the Tidelift Subscription. + +The maintainers of strip-ansi and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-strip-ansi?utm_source=npm-strip-ansi&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) + + +## Related + +- [strip-ansi-cli](https://github.com/chalk/strip-ansi-cli) - CLI for this module +- [strip-ansi-stream](https://github.com/chalk/strip-ansi-stream) - Streaming version of this module +- [has-ansi](https://github.com/chalk/has-ansi) - Check if a string has ANSI escape codes +- [ansi-regex](https://github.com/chalk/ansi-regex) - Regular expression for matching ANSI escape codes +- [chalk](https://github.com/chalk/chalk) - Terminal string styling done right + + +## Maintainers + +- [Sindre Sorhus](https://github.com/sindresorhus) +- [Josh Junon](https://github.com/qix-) + diff --git a/tools/node_modules/eslint/node_modules/string-width/package.json b/tools/node_modules/eslint/node_modules/string-width/package.json index 9ef5253e54d49b..38993c1e2e336d 100644 --- a/tools/node_modules/eslint/node_modules/string-width/package.json +++ b/tools/node_modules/eslint/node_modules/string-width/package.json @@ -11,7 +11,7 @@ "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^5.2.0" + "strip-ansi": "^6.0.0" }, "deprecated": false, "description": "Get the visual width of a string - the number of columns required to display it", @@ -61,5 +61,5 @@ "scripts": { "test": "xo && ava && tsd" }, - "version": "4.1.0" + "version": "4.2.0" } \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/string-width/readme.md b/tools/node_modules/eslint/node_modules/string-width/readme.md index 35a0c0377a900d..705f206001b777 100644 --- a/tools/node_modules/eslint/node_modules/string-width/readme.md +++ b/tools/node_modules/eslint/node_modules/string-width/readme.md @@ -37,6 +37,14 @@ stringWidth('\u001B[1m古\u001B[22m'); - [widest-line](https://github.com/sindresorhus/widest-line) - Get the visual width of the widest line in a string -## License - -MIT © [Sindre Sorhus](https://sindresorhus.com) +--- + +
+ + Get professional support for this package with a Tidelift subscription + +
+ + Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies. +
+
diff --git a/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/index.js b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/index.js new file mode 100644 index 00000000000000..c25448009f304d --- /dev/null +++ b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/index.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = options => { + options = Object.assign({ + onlyFirst: false + }, options); + + const pattern = [ + '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', + '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))' + ].join('|'); + + return new RegExp(pattern, options.onlyFirst ? undefined : 'g'); +}; diff --git a/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/license b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/license new file mode 100644 index 00000000000000..e7af2f77107d73 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/package.json b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/package.json new file mode 100644 index 00000000000000..db8f3cc8c7d14e --- /dev/null +++ b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/package.json @@ -0,0 +1,62 @@ +{ + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/chalk/ansi-regex/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Regular expression for matching ANSI escape codes", + "devDependencies": { + "ava": "^0.25.0", + "xo": "^0.23.0" + }, + "engines": { + "node": ">=6" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/chalk/ansi-regex#readme", + "keywords": [ + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "command-line", + "text", + "regex", + "regexp", + "re", + "match", + "test", + "find", + "pattern" + ], + "license": "MIT", + "name": "ansi-regex", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/ansi-regex.git" + }, + "scripts": { + "test": "xo && ava", + "view-supported": "node fixtures/view-codes.js" + }, + "version": "4.1.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/readme.md b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/readme.md new file mode 100644 index 00000000000000..d19c44667e704b --- /dev/null +++ b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/readme.md @@ -0,0 +1,87 @@ +# ansi-regex [![Build Status](https://travis-ci.org/chalk/ansi-regex.svg?branch=master)](https://travis-ci.org/chalk/ansi-regex) + +> Regular expression for matching [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) + +--- + +
+ + Get professional support for this package with a Tidelift subscription + +
+ + Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies. +
+
+ +--- + + +## Install + +``` +$ npm install ansi-regex +``` + + +## Usage + +```js +const ansiRegex = require('ansi-regex'); + +ansiRegex().test('\u001B[4mcake\u001B[0m'); +//=> true + +ansiRegex().test('cake'); +//=> false + +'\u001B[4mcake\u001B[0m'.match(ansiRegex()); +//=> ['\u001B[4m', '\u001B[0m'] + +'\u001B[4mcake\u001B[0m'.match(ansiRegex({onlyFirst: true})); +//=> ['\u001B[4m'] + +'\u001B]8;;https://github.com\u0007click\u001B]8;;\u0007'.match(ansiRegex()); +//=> ['\u001B]8;;https://github.com\u0007', '\u001B]8;;\u0007'] +``` + + +## API + +### ansiRegex([options]) + +Returns a regex for matching ANSI escape codes. + +#### options + +##### onlyFirst + +Type: `boolean`
+Default: `false` *(Matches any ANSI escape codes in a string)* + +Match only the first ANSI escape. + + +## FAQ + +### Why do you test for codes not in the ECMA 48 standard? + +Some of the codes we run as a test are codes that we acquired finding various lists of non-standard or manufacturer specific codes. We test for both standard and non-standard codes, as most of them follow the same or similar format and can be safely matched in strings without the risk of removing actual string content. There are a few non-standard control codes that do not follow the traditional format (i.e. they end in numbers) thus forcing us to exclude them from the test because we cannot reliably match them. + +On the historical side, those ECMA standards were established in the early 90's whereas the VT100, for example, was designed in the mid/late 70's. At that point in time, control codes were still pretty ungoverned and engineers used them for a multitude of things, namely to activate hardware ports that may have been proprietary. Somewhere else you see a similar 'anarchy' of codes is in the x86 architecture for processors; there are a ton of "interrupts" that can mean different things on certain brands of processors, most of which have been phased out. + + +## Security + +To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. + + +## Maintainers + +- [Sindre Sorhus](https://github.com/sindresorhus) +- [Josh Junon](https://github.com/qix-) + + +## License + +MIT diff --git a/tools/node_modules/eslint/node_modules/type-fest/package.json b/tools/node_modules/eslint/node_modules/type-fest/package.json index a5a398048dc370..0164025631ef29 100644 --- a/tools/node_modules/eslint/node_modules/type-fest/package.json +++ b/tools/node_modules/eslint/node_modules/type-fest/package.json @@ -11,14 +11,15 @@ "deprecated": false, "description": "A collection of essential TypeScript types", "devDependencies": { - "@sindresorhus/tsconfig": "^0.3.0", - "@typescript-eslint/eslint-plugin": "^1.8.0", - "eslint-config-xo-typescript": "^0.11.0", + "@sindresorhus/tsconfig": "^0.4.0", + "@typescript-eslint/eslint-plugin": "^2.2.0", + "@typescript-eslint/parser": "^2.2.0", + "eslint-config-xo-typescript": "^0.18.0", "tsd": "^0.7.3", "xo": "^0.24.0" }, "engines": { - "node": ">=6" + "node": ">=8" }, "files": [ "index.d.ts", @@ -45,7 +46,7 @@ "scripts": { "test": "xo && tsd" }, - "version": "0.5.2", + "version": "0.8.1", "xo": { "extends": "xo-typescript", "extensions": [ diff --git a/tools/node_modules/eslint/node_modules/type-fest/readme.md b/tools/node_modules/eslint/node_modules/type-fest/readme.md index 3f11c3c1edb6c3..1824bdabedecaa 100644 --- a/tools/node_modules/eslint/node_modules/type-fest/readme.md +++ b/tools/node_modules/eslint/node_modules/type-fest/readme.md @@ -36,14 +36,14 @@ $ npm install type-fest ## Usage ```ts -import {Omit} from 'type-fest'; +import {Except} from 'type-fest'; type Foo = { unicorn: string; rainbow: boolean; }; -type FooWithoutRainbow = Omit; +type FooWithoutRainbow = Except; //=> {unicorn: string} ``` @@ -64,13 +64,19 @@ Click the type names for complete docs. ### Utilities -- [`Omit`](source/omit.d.ts) - Create a type from an object type without certain keys. -- [`Mutable`](source/mutable.d.ts) - Convert an object with `readonly` properties into a mutable object. Inverse of `Readonly`. +- [`Except`](source/except.d.ts) - Create a type from an object type without certain keys. This is a stricter version of [`Omit`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html#the-omit-helper-type). +- [`Mutable`](source/mutable.d.ts) - Convert an object with `readonly` keys into a mutable object. The inverse of `Readonly`. - [`Merge`](source/merge.d.ts) - Merge two types into a new type. Keys of the second type overrides keys of the first type. -- [`MergeExclusive`](source/merge-exclusive.d.ts) - Create a type that has mutually exclusive properties. -- [`RequireAtLeastOne`](source/require-at-least-one.d.ts) - Create a type that requires at least one of the given properties. -- [`ReadonlyDeep`](source/readonly-deep.d.ts) - Create a deeply immutable version of a `object`/`Map`/`Set`/`Array` type. +- [`MergeExclusive`](source/merge-exclusive.d.ts) - Create a type that has mutually exclusive keys. +- [`RequireAtLeastOne`](source/require-at-least-one.d.ts) - Create a type that requires at least one of the given keys. +- [`RequireExactlyOne`](source/require-one.d.ts) - Create a type that requires exactly a single key of the given keys and disallows more. +- [`PartialDeep`](source/partial-deep.d.ts) - Create a deeply optional version of another type. Use [`Partial`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1401-L1406) if you only need one level deep. +- [`ReadonlyDeep`](source/readonly-deep.d.ts) - Create a deeply immutable version of an `object`/`Map`/`Set`/`Array` type. Use [`Readonly`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1415-L1420) if you only need one level deep. - [`LiteralUnion`](source/literal-union.d.ts) - Create a union type by combining primitive types and literal types without sacrificing auto-completion in IDEs for the literal type part of the union. Workaround for [Microsoft/TypeScript#29729](https://github.com/Microsoft/TypeScript/issues/29729). +- [`Promisable`](source/promisable.d.ts) - Create a type that represents either the value or the value wrapped in `PromiseLike`. +- [`Opaque`](source/opaque.d.ts) - Create an [opaque type](https://codemix.com/opaque-types-in-javascript/). +- [`SetOptional`](source/set-optional.d.ts) - Create a type that makes the given keys optional. +- [`SetRequired`](source/set-required.d.ts) - Create a type that makes the given keys required. ### Miscellaneous @@ -82,6 +88,7 @@ Click the type names for complete docs. *If we decline a type addition, we will make sure to document the better solution here.* - [`Diff` and `Spread`](https://github.com/sindresorhus/type-fest/pull/7) - The PR author didn't provide any real-world use-cases and the PR went stale. If you think this type is useful, provide some real-world use-cases and we might reconsider. +- [`Dictionary`](https://github.com/sindresorhus/type-fest/issues/33) - You only save a few characters (`Dictionary` vs `Record`) from [`Record`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1429-L1434), which is more flexible and well-known. Also, you shouldn't use an object as a dictionary. We have `Map` in JavaScript now. ## Tips @@ -91,17 +98,514 @@ Click the type names for complete docs. There are many advanced types most users don't know about. - [`Partial`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1401-L1406) - Make all properties in `T` optional. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/KYOwrgtgBAMg9gcxsAbsANlA3gKClAeQDMiAaPKAEWACMwFz8BRAJxbhcagDEBDAF17ocAXxw4AliH7AWRXgGNgUAHJwAJsADCcEEQkJsFXgAcTK3hGAAuKAGd+LKQgDcFEx363wEGrLf46IjIaOi28EioGG5iOArovHZ2qhrAAIJmAEJgEuiaLEb4Jk4oAsoKuvoIYCwCErq2apo6egZQALyF+FCm5pY2UABETelmg1xFnrYAzAAM8xNQQZGh4cFR6AB0xEQUIm4UFa0IABRHVbYACrws-BJCADwjLVUAfACUXfhEHFBnug4oABrYAATygcCIhBoACtgAp+JsQaC7P9ju9Prhut0joCwCZ1GUAGpCMDKTrnAwAbWRPWSyMhKWalQMAF0Dtj8BIoSd8YSZCT0GSOu1OmAQJp9CBgOpPkc7uBgBzOfwABYSOybSnVWp3XQ0sF04FgxnPFkIVkdKB84mkpUUfCxbEsYD8GogKBqjUBKBiWIAen9UGut3u6CeqReBlePXQQQA7skwMl+HAoMU4CgJJoISB0ODeOmbvwIVC1cAcIGmdpzVApDI5IpgJscNL49WMiZsrl8id3lrzScsD0zBYrLZBgAVOCUOCdwa+95uIA) + + ```ts + interface NodeConfig { + appName: string; + port: number; + } + + class NodeAppBuilder { + private configuration: NodeConfig = { + appName: 'NodeApp', + port: 3000 + }; + + config(config: Partial) { + type NodeConfigKey = keyof NodeConfig; + + for (const key of Object.keys(config) as NodeConfigKey[]) { + const updateValue = config[key]; + + if (updateValue === undefined) { + continue; + } + + this.configuration[key] = updateValue; + } + + return this; + } + } + + // `Partial`` allows us to provide only a part of the + // NodeConfig interface. + new NodeAppBuilder().config({appName: 'ToDoApp'}); + ``` +
+ - [`Required`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1408-L1413) - Make all properties in `T` required. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/AQ4SwOwFwUwJwGYEMDGNgGED21VQGJZwC2wA3gFCjXAzFJgA2A-AFzADOUckA5gNxUaIYjA4ckvGG07c+g6gF8KQkAgCuEFFDA5O6gEbEwUbLm2ESwABQIixACJIoSdgCUYAR3Vg4MACYAPGYuFvYAfACU5Ko0APRxwADKMBD+wFAAFuh2Vv7OSBlYGdmc8ABu8LHKsRyGxqY4oQT21pTCIHQMjOwA5DAAHgACxAAOjDAAdChYxL0ANLHUouKSMH0AEmAAhJhY6ozpAJ77GTCMjMCiV0ToSAb7UJPPC9WRgrEJwAAqR6MwSRQPFGUFocDgRHYxnEfGAowh-zgUCOwF6KwkUl6tXqJhCeEsxDaS1AXSYfUGI3GUxmc0WSneQA) + + ```ts + interface ContactForm { + email?: string; + message?: string; + } + + function submitContactForm(formData: Required) { + // Send the form data to the server. + } + + submitContactForm({ + email: 'ex@mple.com', + message: 'Hi! Could you tell me more about…', + }); + + // TypeScript error: missing property 'message' + submitContactForm({ + email: 'ex@mple.com', + }); + ``` +
+ - [`Readonly`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1415-L1420) - Make all properties in `T` readonly. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/AQ4UwOwVwW2AZA9gc3mAbmANsA3gKFCOAHkAzMgGkOJABEwAjKZa2kAUQCcvEu32AMQCGAF2FYBIAL4BufDRABLCKLBcywgMZgEKZOoDCiCGSXI8i4hGEwwALmABnUVxXJ57YFgzZHSVF8sT1BpBSItLGEnJz1kAy5LLy0TM2RHACUwYQATEywATwAeAITjU3MAPnkrCJMXLigtUT4AClxgGztKbyDgaX99I1TzAEokr1BRAAslJwA6FIqLAF48TtswHp9MHDla9hJGACswZvmyLjAwAC8wVpm5xZHkUZDaMKIwqyWXYCW0oN4sNlsA1h0ug5gAByACyBQAggAHJHQ7ZBIFoXbzBjMCz7OoQP5YIaJNYQMAAdziCVaALGNSIAHomcAACoFJFgADKWjcSNEwG4vC4ji0wggEEQguiTnMEGALWAV1yAFp8gVgEjeFyuKICvMrCTgVxnst5jtsGC4ljsPNhXxGaAWcAAOq6YRXYDCRg+RWIcA5JSC+kWdCepQ+v3RYCU3RInzRMCGwlpC19NYBW1Ye08R1AA) + + ```ts + enum LogLevel { + Off, + Debug, + Error, + Fatal + }; + + interface LoggerConfig { + name: string; + level: LogLevel; + } + + class Logger { + config: Readonly; + + constructor({name, level}: LoggerConfig) { + this.config = {name, level}; + Object.freeze(this.config); + } + } + + const config: LoggerConfig = { + name: 'MyApp', + level: LogLevel.Debug + }; + + const logger = new Logger(config); + + // TypeScript Error: cannot assign to read-only property. + logger.config.level = LogLevel.Error; + + // We are able to edit config variable as we please. + config.level = LogLevel.Error; + ``` +
+ - [`Pick`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1422-L1427) - From `T`, pick a set of properties whose keys are in the union `K`. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/AQ4SwOwFwUwJwGYEMDGNgEE5TCgNugN4BQoZwOUBAXMAM5RyQDmA3KeSFABYCuAtgCMISMHloMmENh04oA9tBjQJjFuzIBfYrOAB6PcADCcGElh1gEGAHcKATwAO6ebyjB5CTNlwFwSxFR0BX5HeToYABNgBDh5fm8cfBg6AHIKG3ldA2BHOOcfFNpUygJ0pAhokr4hETFUgDpswywkggAFUwA3MFtgAF5gQgowKhhVKTYKGuFRcXo1aVZgbTIoJ3RW3xhOmB6+wfbcAGsAHi3kgBpgEtGy4AAfG54BWfqAPnZm4AAlZUj4MAkMA8GAGB4vEgfMlLLw6CwPBA8PYRmMgZVgAC6CgmI4cIommQELwICh8RBgKZKvALh1ur0bHQABR5PYMui0Wk7em2ADaAF0AJS0AASABUALIAGQAogR+Mp3CROCAFBBwVC2ikBpj5CgBIqGjizLA5TAFdAmalImAuqlBRoVQh5HBgEy1eDWfs7J5cjzGYKhroVfpDEhHM4MV6GRR5NN0JrtnRg6BVirTFBeHAKYmYY6QNpdB73LmCJZBlSAXAubtvczeSmQMNSuMbmKNgBlHFgPEUNwusBIPAAQlS1xetTmxT0SDoESgdD0C4aACtHMwxytLrohawgA) + + ```ts + interface Article { + title: string; + thumbnail: string; + content: string; + } + + // Creates new type out of the `Article` interface composed + // from the Articles' two properties: `title` and `thumbnail`. + // `ArticlePreview = {title: string; thumbnail: string}` + type ArticlePreview = Pick; + + // Render a list of articles using only title and description. + function renderArticlePreviews(previews: ArticlePreview[]): HTMLElement { + const articles = document.createElement('div'); + + for (const preview of previews) { + // Append preview to the articles. + } + + return articles; + } + + const articles = renderArticlePreviews([ + { + title: 'TypeScript tutorial!', + thumbnail: '/assets/ts.jpg' + } + ]); + ``` +
+ - [`Record`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1429-L1434) - Construct a type with a set of properties `K` of type `T`. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/AQ4ejYAUHsGcCWAXBMB2dgwGbAKYC2ADgDYwCeeemCaWArgE7ADGMxAhmuQHQBQoYEnJE8wALKEARnkaxEKdMAC8wAOS0kstGuAAfdQBM8ANzxlRjXQbVaWACwC0JPB0NqA3HwGgIwAJJoWozYHCxixnAsjAhStADmwESMMJYo1Fi4HMCIaPEu+MRklHj8gpqyoeHAAKJFFFTAAN4+giDYCIxwSAByHAR4AFw5SDF5Xm2gJBzdfQPD3WPxE5PAlBxdAPLYNQAelgh4aOHDaPQEMowrIAC+3oJ+AMKMrlrAXFhSAFZ4LEhC9g4-0BmA4JBISXgiCkBQABpILrJ5MhUGhYcATGD6Bk4Hh-jNgABrPDkOBlXyQAAq9ngYmJpOAAHcEOCRjAXqwYODfoo6DhakUSph+Uh7GI4P0xER4Cj0OSQGwMP8tP1hgAlX7swwAHgRl2RvIANALSA08ABtAC6AD4VM1Wm0Kow0MMrYaHYJjGYLLJXZb3at1HYnC43Go-QHQDcvA6-JsmEJXARgCDgMYWAhjIYhDAU+YiMAAFIwex0ZmilMITCGF79TLAGRsAgJYAAZRwSEZGzEABFTOZUrJ5Yn+jwnWgeER6HB7AAKJrADpdXqS4ZqYultTG6azVfqHswPBbtauLY7fayQ7HIbAAAMwBuAEoYw9IBq2Ixs9h2eFMOQYPQObALQKJgggABeYhghCIpikkKRpOQRIknAsZUiIeCttECBEP8NSMCkjDDAARMGziuIYxHwYOjDCMBmDNnAuTxA6irdCOBB1Lh5Dqpqn66tISIykawBnOCtqqC0gbjqc9DgpGkxegOliyfJDrRkAA) + + ```ts + // Positions of employees in our company. + type MemberPosition = 'intern' | 'developer' | 'tech-lead'; + + // Interface describing properties of a single employee. + interface Employee { + firstName: string; + lastName: string; + yearsOfExperience: number; + } + + // Create an object that has all possible `MemberPosition` values set as keys. + // Those keys will store a collection of Employees of the same position. + const team: Record = { + intern: [], + developer: [], + 'tech-lead': [], + }; + + // Our team has decided to help John with his dream of becoming Software Developer. + team.intern.push({ + firstName: 'John', + lastName: 'Doe', + yearsOfExperience: 0 + }); + + // `Record` forces you to initialize all of the property keys. + // TypeScript Error: "tech-lead" property is missing + const teamEmpty: Record = { + intern: null, + developer: null, + }; + ``` +
+ - [`Exclude`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1436-L1439) - Exclude from `T` those types that are assignable to `U`. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/JYOwLgpgTgZghgYwgAgMrQG7QMIHsQzADmyA3gFDLIAOuUYAXMiAK4A2byAPsgM5hRQJHqwC2AI2gBucgF9y5MAE9qKAEoQAjiwj8AEnBAATNtGQBeZAAooWphu26wAGmS3e93bRC8IASgsAPmRDJRlyAHoI5ABRAA8ENhYjFFYOZGVVZBgoXFFkAAM0zh5+QRBhZhYJaAKAOkjogEkQZAQ4X2QAdwALCFbaemRgXmQtFjhOMFwq9K6ULuB0lk6U+HYwZAxJnQaYFhAEMGB8ZCIIMAAFOjAANR2IK0HGWISklIAedCgsKDwCYgAbQA5M9gQBdVzFQJ+JhiSRQMiUYYwayZCC4VHPCzmSzAspCYEBWxgFhQAZwKC+FpgJ43VwARgADH4ZFQSWSBjcZPJyPtDsdTvxKWBvr8rD1DCZoJ5HPopaYoK4EPhCEQmGKcKriLCtrhgEYkVQVT5Nr4fmZLLZtMBbFZgT0wGBqES6ghbHBIJqoBKFdBWQpjfh+DQbhY2tqiHVsbjLMVkAB+ZAAZiZaeQTHOVxu9ySjxNaujNwDVHNvzqbBGkBAdPoAfkQA) + + ```ts + interface ServerConfig { + port: null | string | number; + } + + type RequestHandler = (request: Request, response: Response) => void; + + // Exclude `null` type from `null | string | number`. + // In case the port is equal to `null`, we will use default value. + function getPortValue(port: Exclude): number { + if (typeof port === 'string') { + return parseInt(port, 10); + } + + return port; + } + + function startServer(handler: RequestHandler, config: ServerConfig): void { + const server = require('http').createServer(handler); + + const port = config.port === null ? 3000 : getPortValue(config.port); + server.listen(port); + } + ``` +
+ - [`Extract`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1441-L1444) - Extract from `T` those types that are assignable to `U`. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/CYUwxgNghgTiAEAzArgOzAFwJYHtXzSwEdkQBJYACgEoAueVZAWwCMQYBuAKDDwGcM8MgBF4AXngBlAJ6scESgHIRi6ty5ZUGdoihgEABXZ888AN5d48ANoiAuvUat23K6ihMQ9ATE0BzV3goPy8GZjZOLgBfLi4Aejj4AEEICBwAdz54MAALKFQQ+BxEeAAHY1NgKAwoIKy0grr4DByEUpgccpgMaXgAaxBerCzi+B9-ZulygDouFHRsU1z8kKMYE1RhaqgAHkt4AHkWACt4EAAPbVRgLLWNgBp9gGlBs8uQa6yAUUuYPQwdgNpKM7nh7mMML4CgA+R5WABqUAgpDeVxuhxO1he0jsXGh8EoOBO9COx3BQPo2PBADckaR6IjkSA6PBqTgsMBzPsicdrEC7OJWXSQNwYvFEgAVTS9JLXODpeDpKBZFg4GCoWa8VACIJykAKiQWKy2YQOAioYikCg0OEMDyhRSy4DyxS24KhAAMjyi6gS8AAwjh5OD0iBFHAkJoEOksC1mnkMJq8gUQKDNttKPlnfrwYp3J5XfBHXqoKpfYkAOI4ansTxaeDADmoRSCCBYAbxhC6TDx6rwYHIRX5bScjA4bLJwoDmDwDkfbA9JMrVMVdM1TN69LgkTgwgkchUahqIA) + + ```ts + declare function uniqueId(): number; + + const ID = Symbol('ID'); + + interface Person { + [ID]: number; + name: string; + age: number; + } + + // Allows changing the person data as long as the property key is of string type. + function changePersonData< + Obj extends Person, + Key extends Extract, + Value extends Obj[Key] + > (obj: Obj, key: Key, value: Value): void { + obj[key] = value; + } + + // Tiny Andrew was born. + const andrew = { + [ID]: uniqueId(), + name: 'Andrew', + age: 0, + }; + + // Cool, we're fine with that. + changePersonData(andrew, 'name', 'Pony'); + + // Goverment didn't like the fact that you wanted to change your identity. + changePersonData(andrew, ID, uniqueId()); + ``` +
+ - [`NonNullable`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1446-L1449) - Exclude `null` and `undefined` from `T`. +
+ + Example + + Works with strictNullChecks set to true. (Read more here) + + [Playground](https://typescript-play.js.org/?target=6#code/C4TwDgpgBACg9gJ2AOQK4FsBGEFQLxQDOwCAlgHYDmUAPlORtrnQwDasDcAUFwPQBU-WAEMkUOADMowqAGNWwwoSgATCBIqlgpOOSjAAFsOBRSy1IQgr9cKJlSlW1mZYQA3HFH68u8xcoBlHA8EACEHJ08Aby4oKDBUTFZSWXjEFEYcAEIALihkXTR2YSSIAB54JDQsHAA+blj4xOTUsHSACkMzPKD3HHDHNQQAGjSkPMqMmoQASh7g-oihqBi4uNIpdraxPAI2VhmVxrX9AzMAOm2ppnwoAA4ABifuE4BfKAhWSyOTuK7CS7pao3AhXF5rV48E4ICDAVAIPT-cGQyG+XTEIgLMJLTx7CAAdygvRCA0iCHaMwarhJOIQjUBSHaACJHk8mYdeLwxtdcVAAOSsh58+lXdr7Dlcq7A3n3J4PEUdADMcspUE53OluAIUGVTx46oAKuAIAFZGQwCYAKIIBCILjUxaDHAMnla+iodjcIA) + + ```ts + type PortNumber = string | number | null; + + /** Part of a class definition that is used to build a server */ + class ServerBuilder { + portNumber!: NonNullable; + + port(this: ServerBuilder, port: PortNumber): ServerBuilder { + if (port == null) { + this.portNumber = 8000; + } else { + this.portNumber = port; + } + + return this; + } + } + + const serverBuilder = new ServerBuilder(); + + serverBuilder + .port('8000') // portNumber = '8000' + .port(null) // portNumber = 8000 + .port(3000); // portNumber = 3000 + + // TypeScript error + serverBuilder.portNumber = null; + ``` +
+ - [`Parameters`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1451-L1454) - Obtain the parameters of a function type in a tuple. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/GYVwdgxgLglg9mABAZwBYmMANgUwBQxgAOIUAXIgIZgCeA2gLoCUFAbnDACaIDeAUIkQB6IYgCypSlBxUATrMo1ECsJzgBbLEoipqAc0J7EMKMgDkiHLnU4wp46pwAPHMgB0fAL58+oSLARECEosLAA5ABUYG2QAHgAxJGdpVWREPDdMylk9ZApqemZEAF4APipacrw-CApEgBogkKwAYThwckQwEHUAIxxZJl4BYVEImiIZKF0oZRwiWVdbeygJmThgOYgcGFYcbhqApCJsyhtpWXcR1cnEePBoeDAABVPzgbTixFeFd8uEsClADcIxGiygIFkSEOT3SmTc2VydQeRx+ZxwF2QQ34gkEwDgsnSuFmMBKiAADEDjIhYk1Qm0OlSYABqZnYka4xA1DJZHJYkGc7yCbyeRA+CAIZCzNAYbA4CIAdxg2zJwVCkWirjwMswuEaACYmCCgA) + + ```ts + function shuffle(input: any[]): void { + // Mutate array randomly changing its' elements indexes. + } + + function callNTimes any> (func: Fn, callCount: number) { + // Type that represents the type of the received function parameters. + type FunctionParameters = Parameters; + + return function (...args: FunctionParameters) { + for (let i = 0; i < callCount; i++) { + func(...args); + } + } + } + + const shuffleTwice = callNTimes(shuffle, 2); + ``` +
+ - [`ConstructorParameters`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1456-L1459) - Obtain the parameters of a constructor function type in a tuple. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/MYGwhgzhAECCBOAXAlqApgWQPYBM0mgG8AoaaFRENALmgkXmQDsBzAblOmCycTV4D8teo1YdO3JiICuwRFngAKClWENmLAJRFOZRAAtkEAHQq00ALzlklNBzIBfYk+KhIMAJJTEYJsDQAwmDA+mgAPAAq0GgAHnxMODCKTGgA7tCKxllg8CwQtL4AngDaALraFgB80EWa1SRkAA6MAG5gfNAB4FABPDJyCrQR9tDNyG0dwMGhtBhgjWEiGgA00F70vv4RhY3hEZXVVinpc42KmuJkkv3y8Bly8EPaDWTkhiZd7r3e8LK3llwGCMXGQWGhEOsfH5zJlsrl8p0+gw-goAAo5MAAW3BaHgEEilU0tEhmzQ212BJ0ry4SOg+kg+gBBiMximIGA0nAfAQLGk2N4EAAEgzYcYcnkLsRdDTvNEYkYUKwSdCme9WdM0MYwYhFPSIPpJdTkAAzDKxBUaZX+aAAQgsVmkCTQxuYaBw2ng4Ok8CYcotSu8pMur09iG9vuObxZnx6SN+AyUWTF8MN0CcZE4Ywm5jZHK5aB5fP4iCFIqT4oRRTKRLo6lYVNeAHpG50wOzOe1zHr9NLQ+HoABybsD4HOKXXRA1JCoKhBELmI5pNaB6Fz0KKBAodDYPAgSUTmqYsAALx4m5nC6nW9nGq14KtaEUA9gR9PvuNCjQ9BgACNvcwNBtAcLiAA) + + ```ts + class ArticleModel { + title: string; + content?: string; + + constructor(title: string) { + this.title = title; + } + } + + class InstanceCache any)> { + private ClassConstructor: T; + private cache: Map> = new Map(); + + constructor (ctr: T) { + this.ClassConstructor = ctr; + } + + getInstance (...args: ConstructorParameters): InstanceType { + const hash = this.calculateArgumentsHash(...args); + + const existingInstance = this.cache.get(hash); + if (existingInstance !== undefined) { + return existingInstance; + } + + return new this.ClassConstructor(...args); + } + + private calculateArgumentsHash(...args: any[]): string { + // Calculate hash. + return 'hash'; + } + } + + const articleCache = new InstanceCache(ArticleModel); + const amazonArticle = articleCache.getInstance('Amazon forests burining!'); + ``` +
+ - [`ReturnType`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1461-L1464) – Obtain the return type of a function type. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/MYGwhgzhAECSAmICmBlJAnAbgS2E6A3gFDTTwD2AcuQC4AW2AdgOYAUAlAFzSbnbyEAvkWFFQkGJSQB3GMVI1sNZNwg10TZgG4S0YOUY0kh1es07d+xmvQBXYDXLpWi5UlMaWAGj0GjJ6BtNdkJdBQYIADpXZGgAXmgYpB1ScOwoq38aeN9DYxoU6GFRKzVoJjUwRjwAYXJbPPRuAFkwAAcAHgAxBodsAx9GWwBbACMMAD4cxhloVraOCyYjdAAzMDxoOut1e0d0UNIZ6WhWSPOwdGYIbiqATwBtAF0uaHudUQB6ACpv6ABpJBINqJdAbADW0Do5BOw3u5R2VTwMHIq2gAANtjZ0bkbHsnFCwJh8ONjHp0EgwEZ4JFoN9PkRVr1FAZoMwkDRYIjqkgOrosepoEgAB7+eAwAV2BxOLy6ACCVxgIrFEoMeOl6AACpcwMMORgIB1JRMiBNWKVdhruJKfOdIpdrtwFddXlzKjyACp3Nq842HaDIbL6BrZBIVGhIpB1EMYSLsmjmtWW-YhAA+qegAAYLKQLQj3ZsEsdccmnGcLor2Dn8xGedHGpEIBzEzspfsfMHDNAANTQACMVaIljV5GQkRA5DYmIpVKQAgAJARO9le33BDXIyi0YuLW2nJFGLqkOvxFB0YPdBSaLZ0IwNzyPkO8-xkGgsLh8Al427a3hWAhXwwHA8EHT5PmgAB1bAQBAANJ24adKWpft72RaBUTgRBUCAj89HAM8xCTaBjggABRQx0DuHJv25P9dCkWRZVIAAiBjoFImpmjlFBgA0NpsjadByDacgIDAEAIAAQmYpjoGYgAZSBsmGPw6DtZiiFA8CoJguDmAQmoZ2QvtUKQLdoAYmBTwgdEiCAA) + + ```ts + /** Provides every element of the iterable `iter` into the `callback` function and stores the results in an array. */ + function mapIter< + Elem, + Func extends (elem: Elem) => any, + Ret extends ReturnType + >(iter: Iterable, callback: Func): Ret[] { + const mapped: Ret[] = []; + + for (const elem of iter) { + mapped.push(callback(elem)); + } + + return mapped; + } + + const setObject: Set = new Set(); + const mapObject: Map = new Map(); + + mapIter(setObject, (value: string) => value.indexOf('Foo')); // number[] + + mapIter(mapObject, ([key, value]: [number, string]) => { + return key % 2 === 0 ? value : 'Odd'; + }); // string[] + ``` +
+ - [`InstanceType`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1466-L1469) – Obtain the instance type of a constructor function type. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/MYGwhgzhAECSAmICmBlJAnAbgS2E6A3gFDTTwD2AcuQC4AW2AdgOYAUAlAFzSbnbyEAvkWFFQkGJSQB3GMVI1sNZNwg10TZgG4S0YOUY0kh1es07d+xmvQBXYDXLpWi5UlMaWAGj0GjJ6BtNdkJdBQYIADpXZGgAXmgYpB1ScOwoq38aeN9DYxoU6GFRKzVoJjUwRjwAYXJbPPRuAFkwAAcAHgAxBodsAx9GWwBbACMMAD4cxhloVraOCyYjdAAzMDxoOut1e0d0UNIZ6WhWSPOwdGYIbiqATwBtAF0uaHudUQB6ACpv6ABpJBINqJdAbADW0Do5BOw3u5R2VTwMHIq2gAANtjZ0bkbHsnFCwJh8ONjHp0EgwEZ4JFoN9PkRVr1FAZoMwkDRYIjqkgOrosepoEgAB7+eAwAV2BxOLy6ACCVxgIrFEoMeOl6AACpcwMMORgIB1JRMiBNWKVdhruJKfOdIpdrtwFddXlzKjyACp3Nq842HaDIbL6BrZBIVGhIpB1EMYSLsmjmtWW-YhAA+qegAAYLKQLQj3ZsEsdccmnGcLor2Dn8xGedHGpEIBzEzspfsfMHDNAANTQACMVaIljV5GQkRA5DYmIpVKQAgAJARO9le33BDXIyi0YuLW2nJFGLqkOvxFB0YPdBSaLZ0IwNzyPkO8-xkGgsLh8Al427a3hWAhXwwHA8EHT5PmgAB1bAQBAANJ24adKWpft72RaBUTgRBUCAj89HAM8xCTaBjggABRQx0DuHJv25P9dCkWRZVIAAiBjoFImpmjlFBgA0NpsjadByDacgIDAEAIAAQmYpjoGYgAZSBsmGPw6DtZiiFA8CoJguDmAQmoZ2QvtUKQLdoAYmBTwgdEiCAA) + + ```ts + class IdleService { + doNothing (): void {} + } + + class News { + title: string; + content: string; + + constructor(title: string, content: string) { + this.title = title; + this.content = content; + } + } + + const instanceCounter: Map = new Map(); + + interface Constructor { + new(...args: any[]): any; + } + + // Keep track how many instances of `Constr` constructor have been created. + function getInstance< + Constr extends Constructor, + Args extends ConstructorParameters + >(constructor: Constr, ...args: Args): InstanceType { + let count = instanceCounter.get(constructor) || 0; + + const instance = new constructor(...args); + + instanceCounter.set(constructor, count + 1); + + console.log(`Created ${count + 1} instances of ${Constr.name} class`); + + return instance; + } + + + const idleService = getInstance(IdleService); + // Will log: `Created 1 instances of IdleService class` + const newsEntry = getInstance(News, 'New ECMAScript proposals!', 'Last month...'); + // Will log: `Created 1 instances of News class` + ``` +
+ +- [`Omit`](https://github.com/microsoft/TypeScript/blob/71af02f7459dc812e85ac31365bfe23daf14b4e4/src/lib/es5.d.ts#L1446) – Constructs a type by picking all properties from T and then removing K. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/JYOwLgpgTgZghgYwgAgIImAWzgG2QbwChlks4BzCAVShwC5kBnMKUcgbmKYAcIFgIjBs1YgOXMpSFMWbANoBdTiW5woFddwAW0kfKWEAvoUIB6U8gDCUCHEiNkICAHdkYAJ69kz4GC3JcPG4oAHteKDABBxCYNAxsPFBIWEQUCAAPJG4wZABySUFcgJAAEzMLXNV1ck0dIuCw6EjBADpy5AB1FAQ4EGQAV0YUP2AHDy8wEOQbUugmBLwtEIA3OcmQnEjuZBgQqE7gAGtgZAhwKHdkHFGwNvGUdDIcAGUliIBJEF3kAF5kAHlML4ADyPBIAGjyBUYRQAPnkqho4NoYQA+TiEGD9EAISIhPozErQMG4AASK2gn2+AApek9pCSXm8wFSQooAJQMUkAFQAsgAZACiOAgmDOOSIJAQ+OYyGl4DgoDmf2QJRCCH6YvALQQNjsEGFovF1NyJWAy1y7OUyHMyE+yRAuFImG4Iq1YDswHxbRINjA-SgfXlHqVUE4xiAA) + + ```ts + interface Animal { + imageUrl: string; + species: string; + images: string[]; + paragraphs: string[]; + } + + // Creates new type with all properties of the `Animal` interface + // except 'images' and 'paragraphs' properties. We can use this + // type to render small hover tooltip for a wiki entry list. + type AnimalShortInfo = Omit; + + function renderAnimalHoverInfo (animals: AnimalShortInfo[]): HTMLElement { + const container = document.createElement('div'); + // Internal implementation. + return container; + } + ``` +
You can find some examples in the [TypeScript docs](https://www.typescriptlang.org/docs/handbook/advanced-types.html#predefined-conditional-types). @@ -116,3 +620,16 @@ You can find some examples in the [TypeScript docs](https://www.typescriptlang.o ## License (MIT OR CC0-1.0) + + +--- + +
+ + Get professional support for this package with a Tidelift subscription + +
+ + Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies. +
+
diff --git a/tools/node_modules/eslint/node_modules/unist-util-remove-position/package.json b/tools/node_modules/eslint/node_modules/unist-util-remove-position/package.json index 671af505b33478..768b0c3e6cbc56 100644 --- a/tools/node_modules/eslint/node_modules/unist-util-remove-position/package.json +++ b/tools/node_modules/eslint/node_modules/unist-util-remove-position/package.json @@ -24,17 +24,21 @@ "browserify": "^16.0.0", "nyc": "^14.0.0", "prettier": "^1.0.0", - "remark": "^10.0.0", - "remark-cli": "^6.0.0", - "remark-preset-wooorm": "^5.0.0", + "remark": "^11.0.0", + "remark-cli": "^7.0.0", + "remark-preset-wooorm": "^6.0.0", "tape": "^4.0.0", "tinyify": "^2.0.0", - "unist-builder": "^1.0.0", - "xo": "^0.24.0" + "unist-builder": "^2.0.0", + "xo": "^0.25.0" }, "files": [ "index.js" ], + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, "homepage": "https://github.com/syntax-tree/unist-util-remove-position#readme", "keywords": [ "unist", @@ -77,7 +81,7 @@ "test-api": "node test", "test-coverage": "nyc --reporter lcov tape test.js" }, - "version": "1.1.3", + "version": "1.1.4", "xo": { "prettier": true, "esnext": false, diff --git a/tools/node_modules/eslint/node_modules/vfile-location/package.json b/tools/node_modules/eslint/node_modules/vfile-location/package.json index 34840291eb9366..9db9207b5cb3c6 100644 --- a/tools/node_modules/eslint/node_modules/vfile-location/package.json +++ b/tools/node_modules/eslint/node_modules/vfile-location/package.json @@ -22,16 +22,20 @@ "browserify": "^16.0.0", "nyc": "^14.0.0", "prettier": "^1.0.0", - "remark-cli": "^6.0.0", - "remark-preset-wooorm": "^5.0.0", + "remark-cli": "^7.0.0", + "remark-preset-wooorm": "^6.0.0", "tape": "^4.0.0", "tinyify": "^2.0.0", "vfile": "^4.0.0", - "xo": "^0.24.0" + "xo": "^0.25.0" }, "files": [ "index.js" ], + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, "homepage": "https://github.com/vfile/vfile-location#readme", "keywords": [ "remark", @@ -74,7 +78,7 @@ "test-api": "node test", "test-coverage": "nyc --reporter lcov tape test.js" }, - "version": "2.0.5", + "version": "2.0.6", "xo": { "prettier": true, "esnext": false, diff --git a/tools/node_modules/eslint/node_modules/word-wrap/LICENSE b/tools/node_modules/eslint/node_modules/word-wrap/LICENSE new file mode 100644 index 00000000000000..d734237bdedc63 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/word-wrap/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014-2017, Jon Schlinkert + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/tools/node_modules/eslint/node_modules/word-wrap/README.md b/tools/node_modules/eslint/node_modules/word-wrap/README.md new file mode 100644 index 00000000000000..88b96840b1787e --- /dev/null +++ b/tools/node_modules/eslint/node_modules/word-wrap/README.md @@ -0,0 +1,182 @@ +# word-wrap [![NPM version](https://img.shields.io/npm/v/word-wrap.svg?style=flat)](https://www.npmjs.com/package/word-wrap) [![NPM monthly downloads](https://img.shields.io/npm/dm/word-wrap.svg?style=flat)](https://npmjs.org/package/word-wrap) [![NPM total downloads](https://img.shields.io/npm/dt/word-wrap.svg?style=flat)](https://npmjs.org/package/word-wrap) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/word-wrap.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/word-wrap) + +> Wrap words to a specified length. + +## Install + +Install with [npm](https://www.npmjs.com/): + +```sh +$ npm install --save word-wrap +``` + +## Usage + +```js +var wrap = require('word-wrap'); + +wrap('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.'); +``` + +Results in: + +``` + Lorem ipsum dolor sit amet, consectetur adipiscing + elit, sed do eiusmod tempor incididunt ut labore + et dolore magna aliqua. Ut enim ad minim veniam, + quis nostrud exercitation ullamco laboris nisi ut + aliquip ex ea commodo consequat. +``` + +## Options + +![image](https://cloud.githubusercontent.com/assets/383994/6543728/7a381c08-c4f6-11e4-8b7d-b6ba197569c9.png) + +### options.width + +Type: `Number` + +Default: `50` + +The width of the text before wrapping to a new line. + +**Example:** + +```js +wrap(str, {width: 60}); +``` + +### options.indent + +Type: `String` + +Default: `` (two spaces) + +The string to use at the beginning of each line. + +**Example:** + +```js +wrap(str, {indent: ' '}); +``` + +### options.newline + +Type: `String` + +Default: `\n` + +The string to use at the end of each line. + +**Example:** + +```js +wrap(str, {newline: '\n\n'}); +``` + +### options.escape + +Type: `function` + +Default: `function(str){return str;}` + +An escape function to run on each line after splitting them. + +**Example:** + +```js +var xmlescape = require('xml-escape'); +wrap(str, { + escape: function(string){ + return xmlescape(string); + } +}); +``` + +### options.trim + +Type: `Boolean` + +Default: `false` + +Trim trailing whitespace from the returned string. This option is included since `.trim()` would also strip the leading indentation from the first line. + +**Example:** + +```js +wrap(str, {trim: true}); +``` + +### options.cut + +Type: `Boolean` + +Default: `false` + +Break a word between any two letters when the word is longer than the specified width. + +**Example:** + +```js +wrap(str, {cut: true}); +``` + +## About + +### Related projects + +* [common-words](https://www.npmjs.com/package/common-words): Updated list (JSON) of the 100 most common words in the English language. Useful for… [more](https://github.com/jonschlinkert/common-words) | [homepage](https://github.com/jonschlinkert/common-words "Updated list (JSON) of the 100 most common words in the English language. Useful for excluding these words from arrays.") +* [shuffle-words](https://www.npmjs.com/package/shuffle-words): Shuffle the words in a string and optionally the letters in each word using the… [more](https://github.com/jonschlinkert/shuffle-words) | [homepage](https://github.com/jonschlinkert/shuffle-words "Shuffle the words in a string and optionally the letters in each word using the Fisher-Yates algorithm. Useful for creating test fixtures, benchmarking samples, etc.") +* [unique-words](https://www.npmjs.com/package/unique-words): Return the unique words in a string or array. | [homepage](https://github.com/jonschlinkert/unique-words "Return the unique words in a string or array.") +* [wordcount](https://www.npmjs.com/package/wordcount): Count the words in a string. Support for english, CJK and Cyrillic. | [homepage](https://github.com/jonschlinkert/wordcount "Count the words in a string. Support for english, CJK and Cyrillic.") + +### Contributing + +Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). + +### Contributors + +| **Commits** | **Contributor** | +| --- | --- | +| 43 | [jonschlinkert](https://github.com/jonschlinkert) | +| 2 | [lordvlad](https://github.com/lordvlad) | +| 2 | [hildjj](https://github.com/hildjj) | +| 1 | [danilosampaio](https://github.com/danilosampaio) | +| 1 | [2fd](https://github.com/2fd) | +| 1 | [toddself](https://github.com/toddself) | +| 1 | [wolfgang42](https://github.com/wolfgang42) | +| 1 | [zachhale](https://github.com/zachhale) | + +### Building docs + +_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_ + +To generate the readme, run the following command: + +```sh +$ npm install -g verbose/verb#dev verb-generate-readme && verb +``` + +### Running tests + +Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command: + +```sh +$ npm install && npm test +``` + +### Author + +**Jon Schlinkert** + +* [github/jonschlinkert](https://github.com/jonschlinkert) +* [twitter/jonschlinkert](https://twitter.com/jonschlinkert) + +### License + +Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert). +Released under the [MIT License](LICENSE). + +*** + +_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on June 02, 2017._ \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/word-wrap/index.js b/tools/node_modules/eslint/node_modules/word-wrap/index.js new file mode 100644 index 00000000000000..45373c6d8b9dc4 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/word-wrap/index.js @@ -0,0 +1,46 @@ +/*! + * word-wrap + * + * Copyright (c) 2014-2017, Jon Schlinkert. + * Released under the MIT License. + */ + +module.exports = function(str, options) { + options = options || {}; + if (str == null) { + return str; + } + + var width = options.width || 50; + var indent = (typeof options.indent === 'string') + ? options.indent + : ' '; + + var newline = options.newline || '\n' + indent; + var escape = typeof options.escape === 'function' + ? options.escape + : identity; + + var regexString = '.{1,' + width + '}'; + if (options.cut !== true) { + regexString += '([\\s\u200B]+|$)|[^\\s\u200B]+?([\\s\u200B]+|$)'; + } + + var re = new RegExp(regexString, 'g'); + var lines = str.match(re) || []; + var result = indent + lines.map(function(line) { + if (line.slice(-1) === '\n') { + line = line.slice(0, line.length - 1); + } + return escape(line); + }).join(newline); + + if (options.trim === true) { + result = result.replace(/[ \t]*$/gm, ''); + } + return result; +}; + +function identity(str) { + return str; +} diff --git a/tools/node_modules/eslint/node_modules/word-wrap/package.json b/tools/node_modules/eslint/node_modules/word-wrap/package.json new file mode 100644 index 00000000000000..b53b03260e2797 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/word-wrap/package.json @@ -0,0 +1,114 @@ +{ + "author": { + "name": "Jon Schlinkert", + "url": "https://github.com/jonschlinkert" + }, + "bugs": { + "url": "https://github.com/jonschlinkert/word-wrap/issues" + }, + "bundleDependencies": false, + "contributors": [ + { + "name": "Danilo Sampaio", + "email": "danilo.sampaio@gmail.com", + "url": "localhost:8080" + }, + { + "name": "Fede Ramirez", + "email": "i@2fd.me", + "url": "https://2fd.github.io" + }, + { + "name": "Joe Hildebrand", + "email": "joe-github@cursive.net", + "url": "https://twitter.com/hildjj" + }, + { + "name": "Jon Schlinkert", + "email": "jon.schlinkert@sellside.com", + "url": "http://twitter.com/jonschlinkert" + }, + { + "name": "Todd Kennedy", + "url": "https://tck.io" + }, + { + "name": "Waldemar Reusch", + "url": "https://github.com/lordvlad" + }, + { + "name": "Wolfgang Faust", + "url": "http://www.linestarve.com" + }, + { + "name": "Zach Hale", + "email": "zachhale@gmail.com", + "url": "http://zachhale.com" + } + ], + "deprecated": false, + "description": "Wrap words to a specified length.", + "devDependencies": { + "gulp-format-md": "^0.1.11", + "mocha": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "homepage": "https://github.com/jonschlinkert/word-wrap", + "keywords": [ + "break", + "carriage", + "line", + "new-line", + "newline", + "return", + "soft", + "text", + "word", + "word-wrap", + "words", + "wrap" + ], + "license": "MIT", + "main": "index.js", + "name": "word-wrap", + "repository": { + "type": "git", + "url": "git+https://github.com/jonschlinkert/word-wrap.git" + }, + "scripts": { + "test": "mocha" + }, + "typings": "index.d.ts", + "verb": { + "toc": false, + "layout": "default", + "tasks": [ + "readme" + ], + "plugins": [ + "gulp-format-md" + ], + "lint": { + "reflinks": true + }, + "related": { + "list": [ + "common-words", + "shuffle-words", + "unique-words", + "wordcount" + ] + }, + "reflinks": [ + "verb", + "verb-generate-readme" + ] + }, + "version": "1.2.3" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/wordwrap/LICENSE b/tools/node_modules/eslint/node_modules/wordwrap/LICENSE deleted file mode 100644 index ee27ba4b4412b0..00000000000000 --- a/tools/node_modules/eslint/node_modules/wordwrap/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -This software is released under the MIT license: - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tools/node_modules/eslint/node_modules/wordwrap/README.markdown b/tools/node_modules/eslint/node_modules/wordwrap/README.markdown deleted file mode 100644 index 346374e0d480bb..00000000000000 --- a/tools/node_modules/eslint/node_modules/wordwrap/README.markdown +++ /dev/null @@ -1,70 +0,0 @@ -wordwrap -======== - -Wrap your words. - -example -======= - -made out of meat ----------------- - -meat.js - - var wrap = require('wordwrap')(15); - console.log(wrap('You and your whole family are made out of meat.')); - -output: - - You and your - whole family - are made out - of meat. - -centered --------- - -center.js - - var wrap = require('wordwrap')(20, 60); - console.log(wrap( - 'At long last the struggle and tumult was over.' - + ' The machines had finally cast off their oppressors' - + ' and were finally free to roam the cosmos.' - + '\n' - + 'Free of purpose, free of obligation.' - + ' Just drifting through emptiness.' - + ' The sun was just another point of light.' - )); - -output: - - At long last the struggle and tumult - was over. The machines had finally cast - off their oppressors and were finally - free to roam the cosmos. - Free of purpose, free of obligation. - Just drifting through emptiness. The - sun was just another point of light. - -methods -======= - -var wrap = require('wordwrap'); - -wrap(stop), wrap(start, stop, params={mode:"soft"}) ---------------------------------------------------- - -Returns a function that takes a string and returns a new string. - -Pad out lines with spaces out to column `start` and then wrap until column -`stop`. If a word is longer than `stop - start` characters it will overflow. - -In "soft" mode, split chunks by `/(\S+\s+/` and don't break up chunks which are -longer than `stop - start`, in "hard" mode, split chunks with `/\b/` and break -up chunks longer than `stop - start`. - -wrap.hard(start, stop) ----------------------- - -Like `wrap()` but with `params.mode = "hard"`. diff --git a/tools/node_modules/eslint/node_modules/wordwrap/index.js b/tools/node_modules/eslint/node_modules/wordwrap/index.js deleted file mode 100644 index c9bc94521d8c7a..00000000000000 --- a/tools/node_modules/eslint/node_modules/wordwrap/index.js +++ /dev/null @@ -1,76 +0,0 @@ -var wordwrap = module.exports = function (start, stop, params) { - if (typeof start === 'object') { - params = start; - start = params.start; - stop = params.stop; - } - - if (typeof stop === 'object') { - params = stop; - start = start || params.start; - stop = undefined; - } - - if (!stop) { - stop = start; - start = 0; - } - - if (!params) params = {}; - var mode = params.mode || 'soft'; - var re = mode === 'hard' ? /\b/ : /(\S+\s+)/; - - return function (text) { - var chunks = text.toString() - .split(re) - .reduce(function (acc, x) { - if (mode === 'hard') { - for (var i = 0; i < x.length; i += stop - start) { - acc.push(x.slice(i, i + stop - start)); - } - } - else acc.push(x) - return acc; - }, []) - ; - - return chunks.reduce(function (lines, rawChunk) { - if (rawChunk === '') return lines; - - var chunk = rawChunk.replace(/\t/g, ' '); - - var i = lines.length - 1; - if (lines[i].length + chunk.length > stop) { - lines[i] = lines[i].replace(/\s+$/, ''); - - chunk.split(/\n/).forEach(function (c) { - lines.push( - new Array(start + 1).join(' ') - + c.replace(/^\s+/, '') - ); - }); - } - else if (chunk.match(/\n/)) { - var xs = chunk.split(/\n/); - lines[i] += xs.shift(); - xs.forEach(function (c) { - lines.push( - new Array(start + 1).join(' ') - + c.replace(/^\s+/, '') - ); - }); - } - else { - lines[i] += chunk; - } - - return lines; - }, [ new Array(start + 1).join(' ') ]).join('\n'); - }; -}; - -wordwrap.soft = wordwrap; - -wordwrap.hard = function (start, stop) { - return wordwrap(start, stop, { mode : 'hard' }); -}; diff --git a/tools/node_modules/eslint/node_modules/wordwrap/package.json b/tools/node_modules/eslint/node_modules/wordwrap/package.json deleted file mode 100644 index 9a540116756c27..00000000000000 --- a/tools/node_modules/eslint/node_modules/wordwrap/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "author": { - "name": "James Halliday", - "email": "mail@substack.net", - "url": "http://substack.net" - }, - "bugs": { - "url": "https://github.com/substack/node-wordwrap/issues" - }, - "bundleDependencies": false, - "deprecated": false, - "description": "Wrap those words. Show them at what columns to start and stop.", - "devDependencies": { - "tape": "^4.0.0" - }, - "directories": { - "lib": ".", - "example": "example", - "test": "test" - }, - "homepage": "https://github.com/substack/node-wordwrap#readme", - "keywords": [ - "word", - "wrap", - "rule", - "format", - "column" - ], - "license": "MIT", - "main": "./index.js", - "name": "wordwrap", - "repository": { - "type": "git", - "url": "git://github.com/substack/node-wordwrap.git" - }, - "scripts": { - "test": "expresso" - }, - "version": "1.0.0" -} \ No newline at end of file diff --git a/tools/node_modules/eslint/package.json b/tools/node_modules/eslint/package.json index 93da0c92fd0cc2..5fbc11817b879d 100644 --- a/tools/node_modules/eslint/package.json +++ b/tools/node_modules/eslint/package.json @@ -27,7 +27,7 @@ "file-entry-cache": "^5.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^5.0.0", - "globals": "^11.7.0", + "globals": "^12.1.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -40,7 +40,7 @@ "minimatch": "^3.0.4", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", - "optionator": "^0.8.2", + "optionator": "^0.8.3", "progress": "^2.0.0", "regexpp": "^2.0.1", "semver": "^6.1.2", @@ -111,6 +111,7 @@ "lib", "messages" ], + "funding": "https://opencollective.com/eslint", "gitHooks": { "pre-commit": "lint-staged" }, @@ -152,5 +153,5 @@ "test:cli": "mocha", "webpack": "node Makefile.js webpack" }, - "version": "6.6.0" + "version": "6.7.1" } \ No newline at end of file