diff --git a/.eslintignore b/.eslintignore index 49737ab8fd0..ae833e7482e 100644 --- a/.eslintignore +++ b/.eslintignore @@ -7,6 +7,7 @@ /tests/fixtures/** /tests/performance/** /tmp/** +/tools/internal-rules/node_modules/** /lib/rules/utils/unicode/is-combining-character.js test.js !.eslintrc.js diff --git a/.gitignore b/.gitignore index b550439d36b..0bff39a122b 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ versions.json .eslintcache .cache /packages/**/node_modules +/tools/internal-rules/node_modules /.vscode .sublimelinterrc .eslint-release-info.json diff --git a/conf/environments.js b/conf/environments.js index f77340cea27..10e6fdaa70f 100644 --- a/conf/environments.js +++ b/conf/environments.js @@ -10,15 +10,76 @@ const globals = require("globals"); +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +/** + * Get the object that has differentce. + * @param {Record} current The newer object. + * @param {Record} prev The older object. + * @returns {Record} The difference object. + */ +function getDiff(current, prev) { + const retv = {}; + + for (const [key, value] of Object.entries(current)) { + if (!Object.hasOwnProperty.call(prev, key)) { + retv[key] = value; + } + } + + return retv; +} + +const newGlobals2015 = getDiff(globals.es2015, globals.es5); // 19 variables such as Promise, Map, ... +const newGlobals2017 = { + Atomics: false, + SharedArrayBuffer: false +}; +const newGlobals2020 = { + BigInt: false, + BigInt64Array: false, + BigUint64Array: false +}; + //------------------------------------------------------------------------------ // Public Interface //------------------------------------------------------------------------------ /** @type {Map} */ module.exports = new Map(Object.entries({ + + // Language builtin: { globals: globals.es5 }, + es6: { + globals: newGlobals2015, + parserOptions: { + ecmaVersion: 6 + } + }, + es2015: { + globals: newGlobals2015, + parserOptions: { + ecmaVersion: 6 + } + }, + es2017: { + globals: { ...newGlobals2015, ...newGlobals2017 }, + parserOptions: { + ecmaVersion: 8 + } + }, + es2020: { + globals: { ...newGlobals2015, ...newGlobals2017, ...newGlobals2020 }, + parserOptions: { + ecmaVersion: 11 + } + }, + + // Platforms browser: { globals: globals.browser }, @@ -30,6 +91,17 @@ module.exports = new Map(Object.entries({ } } }, + "shared-node-browser": { + globals: globals["shared-node-browser"] + }, + worker: { + globals: globals.worker + }, + serviceworker: { + globals: globals.serviceworker + }, + + // Frameworks commonjs: { globals: globals.commonjs, parserOptions: { @@ -38,12 +110,6 @@ module.exports = new Map(Object.entries({ } } }, - "shared-node-browser": { - globals: globals["shared-node-browser"] - }, - worker: { - globals: globals.worker - }, amd: { globals: globals.amd }, @@ -86,9 +152,6 @@ module.exports = new Map(Object.entries({ nashorn: { globals: globals.nashorn }, - serviceworker: { - globals: globals.serviceworker - }, atomtest: { globals: globals.atomtest }, @@ -98,12 +161,6 @@ module.exports = new Map(Object.entries({ webextensions: { globals: globals.webextensions }, - es6: { - globals: globals.es2015, - parserOptions: { - ecmaVersion: 6 - } - }, greasemonkey: { globals: globals.greasemonkey } diff --git a/lib/linter/code-path-analysis/code-path-analyzer.js b/lib/linter/code-path-analysis/code-path-analyzer.js index c1f4a600451..821477aef99 100644 --- a/lib/linter/code-path-analysis/code-path-analyzer.js +++ b/lib/linter/code-path-analysis/code-path-analyzer.js @@ -526,6 +526,7 @@ function processCodePathToExit(analyzer, node) { break; case "CallExpression": + case "ImportExpression": case "MemberExpression": case "NewExpression": state.makeFirstThrowablePathInTryBlock(); diff --git a/lib/rules/func-call-spacing.js b/lib/rules/func-call-spacing.js index f9c8e780577..e2edd4282da 100644 --- a/lib/rules/func-call-spacing.js +++ b/lib/rules/func-call-spacing.js @@ -78,21 +78,13 @@ module.exports = { /** * Check if open space is present in a function name * @param {ASTNode} node node to evaluate + * @param {Token} leftToken The last token of the callee. This may be the closing parenthesis that encloses the callee. + * @param {Token} rightToken Tha first token of the arguments. this is the opening parenthesis that encloses the arguments. * @returns {void} * @private */ - function checkSpacing(node) { - const lastToken = sourceCode.getLastToken(node); - const lastCalleeToken = sourceCode.getLastToken(node.callee); - const parenToken = sourceCode.getFirstTokenBetween(lastCalleeToken, lastToken, astUtils.isOpeningParenToken); - const prevToken = parenToken && sourceCode.getTokenBefore(parenToken); - - // Parens in NewExpression are optional - if (!(parenToken && parenToken.range[1] < node.range[1])) { - return; - } - - const textBetweenTokens = text.slice(prevToken.range[1], parenToken.range[0]).replace(/\/\*.*?\*\//gu, ""); + function checkSpacing(node, leftToken, rightToken) { + const textBetweenTokens = text.slice(leftToken.range[1], rightToken.range[0]).replace(/\/\*.*?\*\//gu, ""); const hasWhitespace = /\s/u.test(textBetweenTokens); const hasNewline = hasWhitespace && astUtils.LINEBREAK_MATCHER.test(textBetweenTokens); @@ -123,7 +115,7 @@ module.exports = { if (never && hasWhitespace) { context.report({ node, - loc: lastCalleeToken.loc.start, + loc: leftToken.loc.start, messageId: "unexpected", fix(fixer) { @@ -132,7 +124,7 @@ module.exports = { * https://github.com/eslint/eslint/issues/7787 */ if (!hasNewline) { - return fixer.removeRange([prevToken.range[1], parenToken.range[0]]); + return fixer.removeRange([leftToken.range[1], rightToken.range[0]]); } return null; @@ -141,27 +133,45 @@ module.exports = { } else if (!never && !hasWhitespace) { context.report({ node, - loc: lastCalleeToken.loc.start, + loc: leftToken.loc.start, messageId: "missing", fix(fixer) { - return fixer.insertTextBefore(parenToken, " "); + return fixer.insertTextBefore(rightToken, " "); } }); } else if (!never && !allowNewlines && hasNewline) { context.report({ node, - loc: lastCalleeToken.loc.start, + loc: leftToken.loc.start, messageId: "unexpected", fix(fixer) { - return fixer.replaceTextRange([prevToken.range[1], parenToken.range[0]], " "); + return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], " "); } }); } } return { - CallExpression: checkSpacing, - NewExpression: checkSpacing + "CallExpression, NewExpression"(node) { + const lastToken = sourceCode.getLastToken(node); + const lastCalleeToken = sourceCode.getLastToken(node.callee); + const parenToken = sourceCode.getFirstTokenBetween(lastCalleeToken, lastToken, astUtils.isOpeningParenToken); + const prevToken = parenToken && sourceCode.getTokenBefore(parenToken); + + // Parens in NewExpression are optional + if (!(parenToken && parenToken.range[1] < node.range[1])) { + return; + } + + checkSpacing(node, prevToken, parenToken); + }, + + ImportExpression(node) { + const leftToken = sourceCode.getFirstToken(node); + const rightToken = sourceCode.getTokenAfter(leftToken); + + checkSpacing(node, leftToken, rightToken); + } }; } diff --git a/lib/rules/function-paren-newline.js b/lib/rules/function-paren-newline.js index 0a0b57a372a..c9f09fdefa7 100644 --- a/lib/rules/function-paren-newline.js +++ b/lib/rules/function-paren-newline.js @@ -232,25 +232,15 @@ module.exports = { }; } - default: - throw new TypeError(`unexpected node with type ${node.type}`); - } - } - - /** - * Validates the parentheses for a node - * @param {ASTNode} node The node with parens - * @returns {void} - */ - function validateNode(node) { - const parens = getParenTokens(node); - - if (parens) { - validateParens(parens, astUtils.isFunction(node) ? node.params : node.arguments); + case "ImportExpression": { + const leftParen = sourceCode.getFirstToken(node, 1); + const rightParen = sourceCode.getLastToken(node); - if (multilineArgumentsOption) { - validateArguments(parens, astUtils.isFunction(node) ? node.params : node.arguments); + return { leftParen, rightParen }; } + + default: + throw new TypeError(`unexpected node with type ${node.type}`); } } @@ -259,11 +249,33 @@ module.exports = { //---------------------------------------------------------------------- return { - ArrowFunctionExpression: validateNode, - CallExpression: validateNode, - FunctionDeclaration: validateNode, - FunctionExpression: validateNode, - NewExpression: validateNode + [[ + "ArrowFunctionExpression", + "CallExpression", + "FunctionDeclaration", + "FunctionExpression", + "ImportExpression", + "NewExpression" + ]](node) { + const parens = getParenTokens(node); + let params; + + if (node.type === "ImportExpression") { + params = [node.source]; + } else if (astUtils.isFunction(node)) { + params = node.params; + } else { + params = node.arguments; + } + + if (parens) { + validateParens(parens, params); + + if (multilineArgumentsOption) { + validateArguments(parens, params); + } + } + } }; } }; diff --git a/lib/rules/indent.js b/lib/rules/indent.js index 345c69e81c1..79b1063137e 100644 --- a/lib/rules/indent.js +++ b/lib/rules/indent.js @@ -99,7 +99,8 @@ const KNOWN_NODES = new Set([ "ImportDeclaration", "ImportSpecifier", "ImportDefaultSpecifier", - "ImportNamespaceSpecifier" + "ImportNamespaceSpecifier", + "ImportExpression" ]); /* @@ -1109,7 +1110,6 @@ module.exports = { CallExpression: addFunctionCallIndent, - "ClassDeclaration[superClass], ClassExpression[superClass]"(node) { const classToken = sourceCode.getFirstToken(node); const extendsToken = sourceCode.getTokenBefore(node.superClass, astUtils.isNotOpeningParenToken); @@ -1236,6 +1236,17 @@ module.exports = { } }, + ImportExpression(node) { + const openingParen = sourceCode.getFirstToken(node, 1); + const closingParen = sourceCode.getLastToken(node); + + parameterParens.add(openingParen); + parameterParens.add(closingParen); + offsets.setDesiredOffset(openingParen, sourceCode.getTokenBefore(openingParen), 0); + + addElementListIndent([node.source], openingParen, closingParen, options.CallExpression.arguments); + }, + "MemberExpression, JSXMemberExpression, MetaProperty"(node) { const object = node.type === "MetaProperty" ? node.meta : node.object; const firstNonObjectToken = sourceCode.getFirstTokenBetween(object, node.property, astUtils.isNotClosingParenToken); diff --git a/lib/rules/new-cap.js b/lib/rules/new-cap.js index dcae238d9b9..cee979310ea 100644 --- a/lib/rules/new-cap.js +++ b/lib/rules/new-cap.js @@ -23,7 +23,8 @@ const CAPS_ALLOWED = [ "Object", "RegExp", "String", - "Symbol" + "Symbol", + "BigInt" ]; /** diff --git a/lib/rules/no-extra-parens.js b/lib/rules/no-extra-parens.js index 5e41d746833..aa455c6a254 100644 --- a/lib/rules/no-extra-parens.js +++ b/lib/rules/no-extra-parens.js @@ -8,6 +8,7 @@ // Rule Definition //------------------------------------------------------------------------------ +const { isParenthesized: isParenthesizedRaw } = require("eslint-utils"); const astUtils = require("./utils/ast-utils.js"); module.exports = { @@ -68,7 +69,6 @@ module.exports = { const sourceCode = context.getSourceCode(); const tokensToIgnore = new WeakSet(); - const isParenthesised = astUtils.isParenthesised.bind(astUtils, sourceCode); const precedence = astUtils.getPrecedence; const ALL_NODES = context.options[0] !== "functions"; const EXCEPT_COND_ASSIGN = ALL_NODES && context.options[1] && context.options[1].conditionalAssign === false; @@ -118,6 +118,16 @@ module.exports = { return ALL_NODES || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression"; } + /** + * Determines if a node is surrounded by parentheses. + * @param {ASTNode} node - The node to be checked. + * @returns {boolean} True if the node is parenthesised. + * @private + */ + function isParenthesised(node) { + return isParenthesizedRaw(1, node, sourceCode); + } + /** * Determines if a node is surrounded by parentheses twice. * @param {ASTNode} node - The node to be checked. @@ -125,12 +135,7 @@ module.exports = { * @private */ function isParenthesisedTwice(node) { - const previousToken = sourceCode.getTokenBefore(node, 1), - nextToken = sourceCode.getTokenAfter(node, 1); - - return isParenthesised(node) && previousToken && nextToken && - astUtils.isOpeningParenToken(previousToken) && previousToken.range[1] <= node.range[0] && - astUtils.isClosingParenToken(nextToken) && nextToken.range[0] >= node.range[1]; + return isParenthesizedRaw(2, node, sourceCode); } /** @@ -406,15 +411,9 @@ module.exports = { report(node.callee); } } - if (node.arguments.length === 1) { - if (hasDoubleExcessParens(node.arguments[0]) && precedence(node.arguments[0]) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) { - report(node.arguments[0]); - } - } else { - node.arguments - .filter(arg => hasExcessParens(arg) && precedence(arg) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) - .forEach(report); - } + node.arguments + .filter(arg => hasExcessParens(arg) && precedence(arg) >= PRECEDENCE_OF_ASSIGNMENT_EXPR) + .forEach(report); } /** @@ -712,7 +711,7 @@ module.exports = { }, DoWhileStatement(node) { - if (hasDoubleExcessParens(node.test) && !isCondAssignException(node)) { + if (hasExcessParens(node.test) && !isCondAssignException(node)) { report(node.test); } }, @@ -837,11 +836,23 @@ module.exports = { }, IfStatement(node) { - if (hasDoubleExcessParens(node.test) && !isCondAssignException(node)) { + if (hasExcessParens(node.test) && !isCondAssignException(node)) { report(node.test); } }, + ImportExpression(node) { + const { source } = node; + + if (source.type === "SequenceExpression") { + if (hasDoubleExcessParens(source)) { + report(source); + } + } else if (hasExcessParens(source)) { + report(source); + } + }, + LogicalExpression: checkBinaryLogical, MemberExpression(node) { @@ -924,7 +935,7 @@ module.exports = { }, SwitchStatement(node) { - if (hasDoubleExcessParens(node.discriminant)) { + if (hasExcessParens(node.discriminant)) { report(node.discriminant); } }, @@ -952,13 +963,13 @@ module.exports = { }, WhileStatement(node) { - if (hasDoubleExcessParens(node.test) && !isCondAssignException(node)) { + if (hasExcessParens(node.test) && !isCondAssignException(node)) { report(node.test); } }, WithStatement(node) { - if (hasDoubleExcessParens(node.object)) { + if (hasExcessParens(node.object)) { report(node.object); } }, diff --git a/lib/rules/utils/ast-utils.js b/lib/rules/utils/ast-utils.js index 7c17fc2ec0c..f0b926e3298 100644 --- a/lib/rules/utils/ast-utils.js +++ b/lib/rules/utils/ast-utils.js @@ -848,6 +848,7 @@ module.exports = { return 17; case "CallExpression": + case "ImportExpression": return 18; case "NewExpression": @@ -1302,7 +1303,7 @@ module.exports = { * set `node.value` to a unicode regex. To make sure a literal is actually `null`, check * `node.regex` instead. Also see: https://github.com/eslint/eslint/issues/8020 */ - return node.type === "Literal" && node.value === null && !node.regex; + return node.type === "Literal" && node.value === null && !node.regex && !node.bigint; }, /** diff --git a/package.json b/package.json index 097466ca9c5..3bd9f7b1215 100644 --- a/package.json +++ b/package.json @@ -50,9 +50,9 @@ "debug": "^4.0.1", "doctrine": "^3.0.0", "eslint-scope": "^5.0.0", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^6.0.0", + "eslint-utils": "^1.4.0", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.0", "esquery": "^1.0.1", "esutils": "^2.0.2", "file-entry-cache": "^5.0.1", @@ -84,7 +84,7 @@ "devDependencies": { "@babel/core": "^7.4.3", "@babel/preset-env": "^7.4.3", - "acorn": "^6.1.1", + "acorn": "^7.0.0", "babel-loader": "^8.0.5", "chai": "^4.0.1", "cheerio": "^0.22.0", diff --git a/tests/lib/rules/eqeqeq.js b/tests/lib/rules/eqeqeq.js index e82fae04851..b74e9bc2ca5 100644 --- a/tests/lib/rules/eqeqeq.js +++ b/tests/lib/rules/eqeqeq.js @@ -50,7 +50,10 @@ ruleTester.run("eqeqeq", rule, { { code: "null != null", options: ["always", { null: "never" }] }, // https://github.com/eslint/eslint/issues/8020 - { code: "foo === /abc/u", options: ["always", { null: "never" }], parserOptions: { ecmaVersion: 6 } } + { code: "foo === /abc/u", options: ["always", { null: "never" }], parserOptions: { ecmaVersion: 2015 } }, + + // bigint + { code: "foo === 1n", options: ["always", { null: "never" }], parserOptions: { ecmaVersion: 2020 } } ], invalid: [ { code: "a == b", errors: [{ messageId: "unexpected", data: wantedEqEqEq, type: "BinaryExpression" }] }, diff --git a/tests/lib/rules/func-call-spacing.js b/tests/lib/rules/func-call-spacing.js index 460a6022d18..d276ae9fc2f 100644 --- a/tests/lib/rules/func-call-spacing.js +++ b/tests/lib/rules/func-call-spacing.js @@ -38,6 +38,10 @@ ruleTester.run("func-call-spacing", rule, { "f(0, (1))", "describe/**/('foo', function () {});", "new (foo())", + { + code: "import(source)", + parserOptions: { ecmaVersion: 2020 } + }, // "never" { @@ -104,6 +108,11 @@ ruleTester.run("func-call-spacing", rule, { code: "new (foo())", options: ["never"] }, + { + code: "import(source)", + options: ["never"], + parserOptions: { ecmaVersion: 2020 } + }, // "always" { @@ -154,6 +163,11 @@ ruleTester.run("func-call-spacing", rule, { code: "f ();\n t ();", options: ["always"] }, + { + code: "import (source)", + options: ["always"], + parserOptions: { ecmaVersion: 2020 } + }, // "always", "allowNewlines": true { @@ -199,6 +213,11 @@ ruleTester.run("func-call-spacing", rule, { { code: "f\r\n();", options: ["always", { allowNewlines: true }] + }, + { + code: "import\n(source)", + options: ["always", { allowNewlines: true }], + parserOptions: { ecmaVersion: 2020 } } ], invalid: [ @@ -262,6 +281,12 @@ ruleTester.run("func-call-spacing", rule, { { messageId: "unexpected", type: "CallExpression" } ] }, + { + code: "import (source);", + output: "import(source);", + parserOptions: { ecmaVersion: 2020 }, + errors: [{ messageId: "unexpected", type: "ImportExpression" }] + }, // https://github.com/eslint/eslint/issues/7787 { @@ -289,6 +314,12 @@ ruleTester.run("func-call-spacing", rule, { output: null, // no change errors: [{ messageId: "unexpected", type: "CallExpression" }] }, + { + code: "import\n(source);", + output: null, + parserOptions: { ecmaVersion: 2020 }, + errors: [{ messageId: "unexpected", type: "ImportExpression" }] + }, // "never" { @@ -360,6 +391,13 @@ ruleTester.run("func-call-spacing", rule, { { messageId: "unexpected", type: "CallExpression" } ] }, + { + code: "import (source);", + output: "import(source);", + options: ["never"], + parserOptions: { ecmaVersion: 2020 }, + errors: [{ messageId: "unexpected", type: "ImportExpression" }] + }, // https://github.com/eslint/eslint/issues/7787 { @@ -567,6 +605,13 @@ ruleTester.run("func-call-spacing", rule, { options: ["always"], errors: [{ messageId: "missing", type: "CallExpression" }] }, + { + code: "import(source);", + output: "import (source);", + options: ["always"], + parserOptions: { ecmaVersion: 2020 }, + errors: [{ messageId: "missing", type: "ImportExpression" }] + }, { code: "f();\n t();", output: "f ();\n t ();", diff --git a/tests/lib/rules/function-paren-newline.js b/tests/lib/rules/function-paren-newline.js index 854df7a8c68..f918a38ba83 100644 --- a/tests/lib/rules/function-paren-newline.js +++ b/tests/lib/rules/function-paren-newline.js @@ -87,6 +87,14 @@ ruleTester.run("function-paren-newline", rule, { code: "function baz(foo, bar) {}", options: ["multiline"] }, + { + code: "import(source)", + parserOptions: { ecmaVersion: 2020 } + }, + { + code: "import(source\n + ext)", + parserOptions: { ecmaVersion: 2020 } + }, // multiline-arguments { @@ -241,6 +249,16 @@ ruleTester.run("function-paren-newline", rule, { code: "new (Foo)", options: ["multiline-arguments"] }, + { + code: "import(source)", + options: ["multiline-arguments"], + parserOptions: { ecmaVersion: 2020 } + }, + { + code: "import(source\n + ext)", + options: ["multiline-arguments"], + parserOptions: { ecmaVersion: 2020 } + }, { code: ` @@ -311,6 +329,11 @@ ruleTester.run("function-paren-newline", rule, { `, options: ["always"] }, + { + code: "import(\n source\n)", + options: ["always"], + parserOptions: { ecmaVersion: 2020 } + }, // never option { @@ -337,6 +360,11 @@ ruleTester.run("function-paren-newline", rule, { code: "function baz() {}", options: ["never"] }, + { + code: "import(source)", + options: ["never"], + parserOptions: { ecmaVersion: 2020 } + }, // minItems option { @@ -363,6 +391,18 @@ ruleTester.run("function-paren-newline", rule, { code: "baz(foo, bar);", options: [{ minItems: 3 }] }, + { + code: "import(source)", + options: [{ minItems: 3 }], + parserOptions: { ecmaVersion: 2020 } + }, + { + code: "import(\n source\n)", + options: [{ minItems: 1 }], + parserOptions: { ecmaVersion: 2020 } + }, + + // consistent option { code: "foo(bar, baz)", options: ["consistent"] @@ -390,6 +430,16 @@ ruleTester.run("function-paren-newline", rule, { ) `, options: ["consistent"] + }, + { + code: "import(source)", + options: ["consistent"], + parserOptions: { ecmaVersion: 2020 } + }, + { + code: "import(\n source\n)", + options: ["consistent"], + parserOptions: { ecmaVersion: 2020 } } ], @@ -514,6 +564,12 @@ ruleTester.run("function-paren-newline", rule, { output: null, errors: [RIGHT_UNEXPECTED_ERROR] }, + { + code: "import(\n source\n)", + output: "import(source)", + parserOptions: { ecmaVersion: 2020 }, + errors: [LEFT_UNEXPECTED_ERROR, RIGHT_UNEXPECTED_ERROR] + }, // multiline-arguments { @@ -701,6 +757,20 @@ ruleTester.run("function-paren-newline", rule, { options: ["multiline-arguments"], errors: [RIGHT_UNEXPECTED_ERROR] }, + { + code: "import(source\n)", + output: "import(source)", + options: ["multiline-arguments"], + parserOptions: { ecmaVersion: 2020 }, + errors: [RIGHT_UNEXPECTED_ERROR] + }, + { + code: "import(\n source)", + output: "import(\n source\n)", + options: ["multiline-arguments"], + parserOptions: { ecmaVersion: 2020 }, + errors: [RIGHT_MISSING_ERROR] + }, // always option { @@ -779,6 +849,13 @@ ruleTester.run("function-paren-newline", rule, { options: ["always"], errors: [LEFT_MISSING_ERROR, RIGHT_MISSING_ERROR] }, + { + code: "import(source)", + output: "import(\nsource\n)", + options: ["always"], + parserOptions: { ecmaVersion: 2020 }, + errors: [LEFT_MISSING_ERROR, RIGHT_MISSING_ERROR] + }, // never option { @@ -888,6 +965,13 @@ ruleTester.run("function-paren-newline", rule, { options: ["never"], errors: [LEFT_UNEXPECTED_ERROR, RIGHT_UNEXPECTED_ERROR] }, + { + code: "import(\n source\n)", + output: "import(source)", + options: ["never"], + parserOptions: { ecmaVersion: 2020 }, + errors: [LEFT_UNEXPECTED_ERROR, RIGHT_UNEXPECTED_ERROR] + }, // minItems option { @@ -928,6 +1012,22 @@ ruleTester.run("function-paren-newline", rule, { options: [{ minItems: 3 }], errors: [LEFT_UNEXPECTED_ERROR, RIGHT_UNEXPECTED_ERROR] }, + { + code: "import(\n source\n)", + output: "import(source)", + options: [{ minItems: 3 }], + parserOptions: { ecmaVersion: 2020 }, + errors: [LEFT_UNEXPECTED_ERROR, RIGHT_UNEXPECTED_ERROR] + }, + { + code: "import(source)", + output: "import(\nsource\n)", + options: [{ minItems: 1 }], + parserOptions: { ecmaVersion: 2020 }, + errors: [LEFT_MISSING_ERROR, RIGHT_MISSING_ERROR] + }, + + // consistent option { code: ` foo( @@ -954,6 +1054,20 @@ ruleTester.run("function-paren-newline", rule, { `, options: ["consistent"], errors: [RIGHT_UNEXPECTED_ERROR] + }, + { + code: "import(source\n)", + output: "import(source)", + options: ["consistent"], + parserOptions: { ecmaVersion: 2020 }, + errors: [RIGHT_UNEXPECTED_ERROR] + }, + { + code: "import(\n source)", + output: "import(\n source\n)", + options: ["consistent"], + parserOptions: { ecmaVersion: 2020 }, + errors: [RIGHT_MISSING_ERROR] } ] }); diff --git a/tests/lib/rules/indent.js b/tests/lib/rules/indent.js index 48d6e4eb191..b38c97e960b 100644 --- a/tests/lib/rules/indent.js +++ b/tests/lib/rules/indent.js @@ -4949,7 +4949,19 @@ ruleTester.run("indent", rule, { { // comment bar }]; - ` + `, + + // import expressions + { + code: unIndent` + import( + // before + source + // after + ) + `, + parserOptions: { ecmaVersion: 2020 } + } ], invalid: [ @@ -9593,6 +9605,25 @@ ruleTester.run("indent", rule, { }]; `, errors: expectedErrors([5, 0, 4, "Line"]) + }, + + // import expressions + { + code: unIndent` + import( + source + ) + `, + output: unIndent` + import( + source + ) + `, + parserOptions: { ecmaVersion: 2020 }, + errors: expectedErrors([ + [2, 4, 0, "Identifier"], + [3, 0, 4, "Punctuator"] + ]) } ] }); diff --git a/tests/lib/rules/new-cap.js b/tests/lib/rules/new-cap.js index ba321ec31e7..706bca1983c 100644 --- a/tests/lib/rules/new-cap.js +++ b/tests/lib/rules/new-cap.js @@ -43,6 +43,7 @@ ruleTester.run("new-cap", rule, { "var x = RegExp(42)", "var x = String(42)", "var x = Symbol('symbol')", + "var x = BigInt('1n')", "var x = _();", "var x = $();", { code: "var x = Foo(42)", options: [{ capIsNew: false }] }, diff --git a/tests/lib/rules/no-extra-parens.js b/tests/lib/rules/no-extra-parens.js index 86b5ae4d621..2b20f20f6b0 100644 --- a/tests/lib/rules/no-extra-parens.js +++ b/tests/lib/rules/no-extra-parens.js @@ -44,7 +44,7 @@ function invalid(code, output, type, line, config) { const ruleTester = new RuleTester({ parserOptions: { - ecmaVersion: 2018, + ecmaVersion: 2020, ecmaFeatures: { jsx: true } @@ -97,6 +97,8 @@ ruleTester.run("no-extra-parens", rule, { "(new (Foo || Bar))()", "(2 + 3) ** 4", "2 ** (2 + 3)", + "new (import(source))", + "import((s,t))", // same precedence "a, b, c", @@ -1820,6 +1822,29 @@ ruleTester.run("no-extra-parens", rule, { messageId: "unexpected" } ) - } + }, + + // import expressions + invalid( + "import((source))", + "import(source)", + "Identifier", + 1, + { parserOptions: { ecmaVersion: 2020 } } + ), + invalid( + "import((source = 'foo.js'))", + "import(source = 'foo.js')", + "AssignmentExpression", + 1, + { parserOptions: { ecmaVersion: 2020 } } + ), + invalid( + "import(((s,t)))", + "import((s,t))", + "SequenceExpression", + 1, + { parserOptions: { ecmaVersion: 2020 } } + ) ] });