diff --git a/docs/rules/keyword-spacing.md b/docs/rules/keyword-spacing.md index 8ca6739371c..031873b5ed8 100644 --- a/docs/rules/keyword-spacing.md +++ b/docs/rules/keyword-spacing.md @@ -243,14 +243,15 @@ if(foo) { ### overrides -Examples of **correct** code for this rule with the `{ "overrides": { "if": { "after": false }, "for": { "after": false }, "while": { "after": false }, "static": { "after": false } } }` option: +Examples of **correct** code for this rule with the `{ "overrides": { "if": { "after": false }, "for": { "after": false }, "while": { "after": false }, "static": { "after": false }, "as": { "after": false } } }` option: ```js /*eslint keyword-spacing: ["error", { "overrides": { "if": { "after": false }, "for": { "after": false }, "while": { "after": false }, - "static": { "after": false } + "static": { "after": false }, + "as": { "after": false } } }]*/ if(foo) { @@ -272,6 +273,8 @@ class C { //... } } + +export { C as"my class" }; ``` ## When Not To Use It diff --git a/lib/rules/keyword-spacing.js b/lib/rules/keyword-spacing.js index 956fc42656d..16a65d78696 100644 --- a/lib/rules/keyword-spacing.js +++ b/lib/rules/keyword-spacing.js @@ -469,6 +469,7 @@ module.exports = { const asToken = sourceCode.getTokenBefore(node.exported); checkSpacingBefore(asToken, PREV_TOKEN_M); + checkSpacingAfter(asToken, NEXT_TOKEN_M); } if (node.source) { @@ -479,6 +480,35 @@ module.exports = { } } + /** + * Reports `as` keyword of a given node if usage of spacing around this + * keyword is invalid. + * @param {ASTNode} node An `ImportSpecifier` node to check. + * @returns {void} + */ + function checkSpacingForImportSpecifier(node) { + if (node.imported.range[0] !== node.local.range[0]) { + const asToken = sourceCode.getTokenBefore(node.local); + + checkSpacingBefore(asToken, PREV_TOKEN_M); + } + } + + /** + * Reports `as` keyword of a given node if usage of spacing around this + * keyword is invalid. + * @param {ASTNode} node An `ExportSpecifier` node to check. + * @returns {void} + */ + function checkSpacingForExportSpecifier(node) { + if (node.local.range[0] !== node.exported.range[0]) { + const asToken = sourceCode.getTokenBefore(node.exported); + + checkSpacingBefore(asToken, PREV_TOKEN_M); + checkSpacingAfter(asToken, NEXT_TOKEN_M); + } + } + /** * Reports `as` keyword of a given node if usage of spacing around this * keyword is invalid. @@ -588,6 +618,8 @@ module.exports = { YieldExpression: checkSpacingBeforeFirstToken, // Others + ImportSpecifier: checkSpacingForImportSpecifier, + ExportSpecifier: checkSpacingForExportSpecifier, ImportNamespaceSpecifier: checkSpacingForImportNamespaceSpecifier, MethodDefinition: checkSpacingForProperty, PropertyDefinition: checkSpacingForProperty, diff --git a/tests/lib/rules/keyword-spacing.js b/tests/lib/rules/keyword-spacing.js index 35c8a06e43e..97d198f56fc 100644 --- a/tests/lib/rules/keyword-spacing.js +++ b/tests/lib/rules/keyword-spacing.js @@ -126,14 +126,53 @@ ruleTester.run("keyword-spacing", rule, { // as //---------------------------------------------------------------------- + // import { a as b } + { code: "import { a } from \"foo\"", parserOptions: { ecmaVersion: 6, sourceType: "module" } }, + { code: "import { a as b } from \"foo\"", parserOptions: { ecmaVersion: 6, sourceType: "module" } }, + { code: "import { \"a\" as b } from \"foo\"", parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, + { code: "import{ a }from\"foo\"", options: [NEITHER], parserOptions: { ecmaVersion: 6, sourceType: "module" } }, + { code: "import{ a as b }from\"foo\"", options: [NEITHER], parserOptions: { ecmaVersion: 6, sourceType: "module" } }, + { code: "import{ \"a\"as b }from\"foo\"", options: [NEITHER], parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, + { code: "import{ \"a\" as b }from\"foo\"", options: [override("as", BOTH)], parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, + { code: "import { a as b } from \"foo\"", options: [override("as", NEITHER)], parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, + { code: "import { \"a\"as b } from \"foo\"", options: [override("as", NEITHER)], parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, + + // export { a as b } + { code: "let a; export { a };", parserOptions: { ecmaVersion: 6, sourceType: "module" } }, + { code: "export { \"a\" } from \"foo\";", parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, + { code: "let a; export { a as b };", parserOptions: { ecmaVersion: 6, sourceType: "module" } }, + { code: "let a; export { a as \"b\" };", parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, + { code: "export { \"a\" as b } from \"foo\";", parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, + { code: "export { \"a\" as \"b\" } from \"foo\";", parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, + { code: "let a; export{ a };", options: [NEITHER], parserOptions: { ecmaVersion: 6, sourceType: "module" } }, + { code: "export{ \"a\" }from\"foo\";", options: [NEITHER], parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, + { code: "let a; export{ a as b };", options: [NEITHER], parserOptions: { ecmaVersion: 6, sourceType: "module" } }, + { code: "let a; export{ a as\"b\" };", options: [NEITHER], parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, + { code: "export{ \"a\"as b }from\"foo\";", options: [NEITHER], parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, + { code: "export{ \"a\"as\"b\" }from\"foo\";", options: [NEITHER], parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, + { code: "let a; export{ a as \"b\" };", options: [override("as", BOTH)], parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, + { code: "export{ \"a\" as b }from\"foo\";", options: [override("as", BOTH)], parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, + { code: "export{ \"a\" as \"b\" }from\"foo\";", options: [override("as", BOTH)], parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, + { code: "let a; export { a as b };", options: [override("as", NEITHER)], parserOptions: { ecmaVersion: 6, sourceType: "module" } }, + { code: "let a; export { a as\"b\" };", options: [override("as", NEITHER)], parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, + { code: "export { \"a\"as b } from \"foo\";", options: [override("as", NEITHER)], parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, + { code: "export { \"a\"as\"b\" } from \"foo\";", options: [override("as", NEITHER)], parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, + + // import * as a { code: "import * as a from \"foo\"", parserOptions: { ecmaVersion: 6, sourceType: "module" } }, { code: "import*as a from\"foo\"", options: [NEITHER], parserOptions: { ecmaVersion: 6, sourceType: "module" } }, { code: "import* as a from\"foo\"", options: [override("as", BOTH)], parserOptions: { ecmaVersion: 6, sourceType: "module" } }, { code: "import *as a from \"foo\"", options: [override("as", NEITHER)], parserOptions: { ecmaVersion: 6, sourceType: "module" } }, + + // export * as a { code: "export * as a from \"foo\"", parserOptions: { ecmaVersion: 2020, sourceType: "module" } }, + { code: "export * as \"a\" from \"foo\"", parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, { code: "export*as a from\"foo\"", options: [NEITHER], parserOptions: { ecmaVersion: 2020, sourceType: "module" } }, + { code: "export*as\"a\"from\"foo\"", options: [NEITHER], parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, { code: "export* as a from\"foo\"", options: [override("as", BOTH)], parserOptions: { ecmaVersion: 2020, sourceType: "module" } }, + { code: "export* as \"a\"from\"foo\"", options: [override("as", BOTH)], parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, { code: "export *as a from \"foo\"", options: [override("as", NEITHER)], parserOptions: { ecmaVersion: 2020, sourceType: "module" } }, + { code: "export *as\"a\" from \"foo\"", options: [override("as", NEITHER)], parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, //---------------------------------------------------------------------- // async @@ -654,15 +693,21 @@ ruleTester.run("keyword-spacing", rule, { { code: "import {foo} from \"foo\"", parserOptions: { ecmaVersion: 6, sourceType: "module" } }, { code: "export {foo} from \"foo\"", parserOptions: { ecmaVersion: 6, sourceType: "module" } }, { code: "export * from \"foo\"", parserOptions: { ecmaVersion: 6, sourceType: "module" } }, + { code: "export * as \"x\" from \"foo\"", parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, { code: "import{foo}from\"foo\"", options: [NEITHER], parserOptions: { ecmaVersion: 6, sourceType: "module" } }, { code: "export{foo}from\"foo\"", options: [NEITHER], parserOptions: { ecmaVersion: 6, sourceType: "module" } }, { code: "export*from\"foo\"", options: [NEITHER], parserOptions: { ecmaVersion: 6, sourceType: "module" } }, + { code: "export*as x from\"foo\"", options: [NEITHER], parserOptions: { ecmaVersion: 2020, sourceType: "module" } }, + { code: "export*as\"x\"from\"foo\"", options: [NEITHER], parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, { code: "import{foo} from \"foo\"", options: [override("from", BOTH)], parserOptions: { ecmaVersion: 6, sourceType: "module" } }, { code: "export{foo} from \"foo\"", options: [override("from", BOTH)], parserOptions: { ecmaVersion: 6, sourceType: "module" } }, { code: "export* from \"foo\"", options: [override("from", BOTH)], parserOptions: { ecmaVersion: 6, sourceType: "module" } }, + { code: "export*as\"x\" from \"foo\"", options: [override("from", BOTH)], parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, { code: "import {foo}from\"foo\"", options: [override("from", NEITHER)], parserOptions: { ecmaVersion: 6, sourceType: "module" } }, { code: "export {foo}from\"foo\"", options: [override("from", NEITHER)], parserOptions: { ecmaVersion: 6, sourceType: "module" } }, { code: "export *from\"foo\"", options: [override("from", NEITHER)], parserOptions: { ecmaVersion: 6, sourceType: "module" } }, + { code: "export * as x from\"foo\"", options: [override("from", NEITHER)], parserOptions: { ecmaVersion: 2020, sourceType: "module" } }, + { code: "export * as \"x\"from\"foo\"", options: [override("from", NEITHER)], parserOptions: { ecmaVersion: 2022, sourceType: "module" } }, //---------------------------------------------------------------------- // function @@ -1469,6 +1514,119 @@ ruleTester.run("keyword-spacing", rule, { // as //---------------------------------------------------------------------- + // import { a as b } + { + code: "import { \"a\"as b } from \"foo\"", + output: "import { \"a\" as b } from \"foo\"", + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: expectedBefore("as") + }, + { + code: "import{ \"a\" as b }from\"foo\"", + output: "import{ \"a\"as b }from\"foo\"", + options: [NEITHER], + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: unexpectedBefore("as") + }, + { + code: "import{ \"a\"as b }from\"foo\"", + output: "import{ \"a\" as b }from\"foo\"", + options: [override("as", BOTH)], + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: expectedBefore("as") + }, + { + code: "import { \"a\" as b } from \"foo\"", + output: "import { \"a\"as b } from \"foo\"", + options: [override("as", NEITHER)], + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: unexpectedBefore("as") + }, + + // export { a as b } + { + code: "let a; export { a as\"b\" };", + output: "let a; export { a as \"b\" };", + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: expectedAfter("as") + }, + { + code: "export { \"a\"as b } from \"foo\";", + output: "export { \"a\" as b } from \"foo\";", + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: expectedBefore("as") + }, + { + code: "export { \"a\"as\"b\" } from \"foo\";", + output: "export { \"a\" as \"b\" } from \"foo\";", + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: expectedBeforeAndAfter("as") + }, + { + code: "let a; export{ a as \"b\" };", + output: "let a; export{ a as\"b\" };", + options: [NEITHER], + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: unexpectedAfter("as") + }, + { + code: "export{ \"a\" as b }from\"foo\";", + output: "export{ \"a\"as b }from\"foo\";", + options: [NEITHER], + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: unexpectedBefore("as") + }, + { + code: "export{ \"a\" as \"b\" }from\"foo\";", + output: "export{ \"a\"as\"b\" }from\"foo\";", + options: [NEITHER], + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: unexpectedBeforeAndAfter("as") + }, + { + code: "let a; export{ a as\"b\" };", + output: "let a; export{ a as \"b\" };", + options: [override("as", BOTH)], + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: expectedAfter("as") + }, + { + code: "export{ \"a\"as b }from\"foo\";", + output: "export{ \"a\" as b }from\"foo\";", + options: [override("as", BOTH)], + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: expectedBefore("as") + }, + { + code: "export{ \"a\"as\"b\" }from\"foo\";", + output: "export{ \"a\" as \"b\" }from\"foo\";", + options: [override("as", BOTH)], + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: expectedBeforeAndAfter("as") + }, + { + code: "let a; export { a as \"b\" };", + output: "let a; export { a as\"b\" };", + options: [override("as", NEITHER)], + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: unexpectedAfter("as") + }, + { + code: "export { \"a\" as b } from \"foo\";", + output: "export { \"a\"as b } from \"foo\";", + options: [override("as", NEITHER)], + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: unexpectedBefore("as") + }, + { + code: "export { \"a\" as \"b\" } from \"foo\";", + output: "export { \"a\"as\"b\" } from \"foo\";", + options: [override("as", NEITHER)], + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: unexpectedBeforeAndAfter("as") + }, + + // import * as a { code: "import *as a from \"foo\"", output: "import * as a from \"foo\"", @@ -1524,12 +1682,20 @@ ruleTester.run("keyword-spacing", rule, { parserOptions: { ecmaVersion: 6, sourceType: "module" }, errors: unexpectedBefore("as") }, + + // export * as a { code: "export *as a from \"foo\"", output: "export * as a from \"foo\"", parserOptions: { ecmaVersion: 2020, sourceType: "module" }, errors: expectedBefore("as") }, + { + code: "export *as\"a\" from \"foo\"", + output: "export * as \"a\" from \"foo\"", + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: expectedBeforeAndAfter("as") + }, { code: "export* as a from\"foo\"", output: "export*as a from\"foo\"", @@ -1537,6 +1703,13 @@ ruleTester.run("keyword-spacing", rule, { parserOptions: { ecmaVersion: 2020, sourceType: "module" }, errors: unexpectedBefore("as") }, + { + code: "export* as \"a\"from\"foo\"", + output: "export*as\"a\"from\"foo\"", + options: [NEITHER], + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: unexpectedBeforeAndAfter("as") + }, { code: "export*as a from\"foo\"", output: "export* as a from\"foo\"", @@ -1544,6 +1717,13 @@ ruleTester.run("keyword-spacing", rule, { parserOptions: { ecmaVersion: 2020, sourceType: "module" }, errors: expectedBefore("as") }, + { + code: "export*as\"a\"from\"foo\"", + output: "export* as \"a\"from\"foo\"", + options: [override("as", BOTH)], + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: expectedBeforeAndAfter("as") + }, { code: "export * as a from \"foo\"", output: "export *as a from \"foo\"", @@ -1551,6 +1731,13 @@ ruleTester.run("keyword-spacing", rule, { parserOptions: { ecmaVersion: 2020, sourceType: "module" }, errors: unexpectedBefore("as") }, + { + code: "export * as \"a\" from \"foo\"", + output: "export *as\"a\" from \"foo\"", + options: [override("as", NEITHER)], + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: unexpectedBeforeAndAfter("as") + }, //---------------------------------------------------------------------- // async @@ -2399,6 +2586,12 @@ ruleTester.run("keyword-spacing", rule, { parserOptions: { ecmaVersion: 6, sourceType: "module" }, errors: expectedBeforeAndAfter("from") }, + { + code: "export * as \"a\"from\"foo\"", + output: "export * as \"a\" from \"foo\"", + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: expectedBeforeAndAfter("from") + }, { code: "import{foo} from \"foo\"", output: "import{foo}from\"foo\"", @@ -2420,6 +2613,20 @@ ruleTester.run("keyword-spacing", rule, { parserOptions: { ecmaVersion: 6, sourceType: "module" }, errors: unexpectedBeforeAndAfter("from") }, + { + code: "export*as x from \"foo\"", + output: "export*as x from\"foo\"", + options: [NEITHER], + parserOptions: { ecmaVersion: 2020, sourceType: "module" }, + errors: unexpectedAfter("from") + }, + { + code: "export*as\"x\" from \"foo\"", + output: "export*as\"x\"from\"foo\"", + options: [NEITHER], + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: unexpectedBeforeAndAfter("from") + }, { code: "import{foo}from\"foo\"", output: "import{foo} from \"foo\"", @@ -2441,6 +2648,13 @@ ruleTester.run("keyword-spacing", rule, { parserOptions: { ecmaVersion: 6, sourceType: "module" }, errors: expectedBeforeAndAfter("from") }, + { + code: "export*as\"x\"from\"foo\"", + output: "export*as\"x\" from \"foo\"", + options: [override("from", BOTH)], + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: expectedBeforeAndAfter("from") + }, { code: "import {foo} from \"foo\"", output: "import {foo}from\"foo\"", @@ -2462,6 +2676,20 @@ ruleTester.run("keyword-spacing", rule, { parserOptions: { ecmaVersion: 6, sourceType: "module" }, errors: unexpectedBeforeAndAfter("from") }, + { + code: "export * as x from \"foo\"", + output: "export * as x from\"foo\"", + options: [override("from", NEITHER)], + parserOptions: { ecmaVersion: 2020, sourceType: "module" }, + errors: unexpectedAfter("from") + }, + { + code: "export * as \"x\" from \"foo\"", + output: "export * as \"x\"from\"foo\"", + options: [override("from", NEITHER)], + parserOptions: { ecmaVersion: 2022, sourceType: "module" }, + errors: unexpectedBeforeAndAfter("from") + }, //---------------------------------------------------------------------- // function