diff --git a/CHANGELOG.md b/CHANGELOG.md
index 93b89c408ec7..f2cf9b51d1d0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.14.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.13.0...v4.14.0) (2021-01-18)
+
+
+### Features
+
+* add support for decorator metadata in scope analysis and in consistent-type-imports ([#2751](https://github.com/typescript-eslint/typescript-eslint/issues/2751)) ([445e416](https://github.com/typescript-eslint/typescript-eslint/commit/445e416878b27a54bf07c2d3b84dabd7b06e51bc)), closes [#2559](https://github.com/typescript-eslint/typescript-eslint/issues/2559)
+* **eslint-plugin:** add `object-curly-spacing` rule ([#2892](https://github.com/typescript-eslint/typescript-eslint/issues/2892)) ([32bd18d](https://github.com/typescript-eslint/typescript-eslint/commit/32bd18de80f4f8388717d0f0c16d493234362aa5))
+
+
+
+
+
# [4.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.12.0...v4.13.0) (2021-01-11)
diff --git a/lerna.json b/lerna.json
index 480c0ab5f98c..d55cb70f0537 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
- "version": "4.13.0",
+ "version": "4.14.0",
"npmClient": "yarn",
"useWorkspaces": true,
"stream": true
diff --git a/packages/eslint-plugin-internal/CHANGELOG.md b/packages/eslint-plugin-internal/CHANGELOG.md
index 6ac7f11060e0..d57138c4ad58 100644
--- a/packages/eslint-plugin-internal/CHANGELOG.md
+++ b/packages/eslint-plugin-internal/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.14.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.13.0...v4.14.0) (2021-01-18)
+
+**Note:** Version bump only for package @typescript-eslint/eslint-plugin-internal
+
+
+
+
+
# [4.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.12.0...v4.13.0) (2021-01-11)
**Note:** Version bump only for package @typescript-eslint/eslint-plugin-internal
diff --git a/packages/eslint-plugin-internal/package.json b/packages/eslint-plugin-internal/package.json
index c050e1a20992..546896073089 100644
--- a/packages/eslint-plugin-internal/package.json
+++ b/packages/eslint-plugin-internal/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/eslint-plugin-internal",
- "version": "4.13.0",
+ "version": "4.14.0",
"private": true,
"main": "dist/index.js",
"scripts": {
@@ -14,7 +14,7 @@
},
"dependencies": {
"@types/prettier": "*",
- "@typescript-eslint/experimental-utils": "4.13.0",
+ "@typescript-eslint/experimental-utils": "4.14.0",
"prettier": "*"
}
}
diff --git a/packages/eslint-plugin-tslint/CHANGELOG.md b/packages/eslint-plugin-tslint/CHANGELOG.md
index a643bbfe6364..ad6a3fafdde1 100644
--- a/packages/eslint-plugin-tslint/CHANGELOG.md
+++ b/packages/eslint-plugin-tslint/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.14.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.13.0...v4.14.0) (2021-01-18)
+
+**Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint
+
+
+
+
+
# [4.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.12.0...v4.13.0) (2021-01-11)
**Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint
diff --git a/packages/eslint-plugin-tslint/package.json b/packages/eslint-plugin-tslint/package.json
index 2008ec82198c..7d02cd87ff07 100644
--- a/packages/eslint-plugin-tslint/package.json
+++ b/packages/eslint-plugin-tslint/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/eslint-plugin-tslint",
- "version": "4.13.0",
+ "version": "4.14.0",
"main": "dist/index.js",
"typings": "src/index.ts",
"description": "TSLint wrapper plugin for ESLint",
@@ -38,7 +38,7 @@
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
- "@typescript-eslint/experimental-utils": "4.13.0",
+ "@typescript-eslint/experimental-utils": "4.14.0",
"lodash": "^4.17.15"
},
"peerDependencies": {
@@ -48,6 +48,6 @@
},
"devDependencies": {
"@types/lodash": "*",
- "@typescript-eslint/parser": "4.13.0"
+ "@typescript-eslint/parser": "4.14.0"
}
}
diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md
index bcb5f1b3b8d6..af2174cba993 100644
--- a/packages/eslint-plugin/CHANGELOG.md
+++ b/packages/eslint-plugin/CHANGELOG.md
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.14.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.13.0...v4.14.0) (2021-01-18)
+
+
+### Features
+
+* add support for decorator metadata in scope analysis and in consistent-type-imports ([#2751](https://github.com/typescript-eslint/typescript-eslint/issues/2751)) ([445e416](https://github.com/typescript-eslint/typescript-eslint/commit/445e416878b27a54bf07c2d3b84dabd7b06e51bc)), closes [#2559](https://github.com/typescript-eslint/typescript-eslint/issues/2559)
+* **eslint-plugin:** add `object-curly-spacing` rule ([#2892](https://github.com/typescript-eslint/typescript-eslint/issues/2892)) ([32bd18d](https://github.com/typescript-eslint/typescript-eslint/commit/32bd18de80f4f8388717d0f0c16d493234362aa5))
+
+
+
+
+
# [4.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.12.0...v4.13.0) (2021-01-11)
diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md
index 7c55b54aca6f..3a504fe7d83a 100644
--- a/packages/eslint-plugin/README.md
+++ b/packages/eslint-plugin/README.md
@@ -218,6 +218,7 @@ In these cases, we create what we call an extension rule; a rule within our plug
| [`@typescript-eslint/no-unused-vars`](./docs/rules/no-unused-vars.md) | Disallow unused variables | :heavy_check_mark: | | |
| [`@typescript-eslint/no-use-before-define`](./docs/rules/no-use-before-define.md) | Disallow the use of variables before they are defined | | | |
| [`@typescript-eslint/no-useless-constructor`](./docs/rules/no-useless-constructor.md) | Disallow unnecessary constructors | | | |
+| [`@typescript-eslint/object-curly-spacing`](./docs/rules/object-curly-spacing.md) | Enforce consistent spacing inside braces | | :wrench: | |
| [`@typescript-eslint/quotes`](./docs/rules/quotes.md) | Enforce the consistent use of either backticks, double, or single quotes | | :wrench: | |
| [`@typescript-eslint/require-await`](./docs/rules/require-await.md) | Disallow async functions which have no `await` expression | :heavy_check_mark: | | :thought_balloon: |
| [`@typescript-eslint/return-await`](./docs/rules/return-await.md) | Enforces consistent returning of awaited values | | :wrench: | :thought_balloon: |
diff --git a/packages/eslint-plugin/docs/rules/consistent-type-imports.md b/packages/eslint-plugin/docs/rules/consistent-type-imports.md
index a6d06aae8514..c30e59b40b99 100644
--- a/packages/eslint-plugin/docs/rules/consistent-type-imports.md
+++ b/packages/eslint-plugin/docs/rules/consistent-type-imports.md
@@ -25,7 +25,7 @@ const defaultOptions: Options = {
This option defines the expected import kind for type-only imports. Valid values for `prefer` are:
-- `type-imports` will enforce that you always use `import type Foo from '...'`. It is default.
+- `type-imports` will enforce that you always use `import type Foo from '...'` except referenced by metadata of decorators. It is default.
- `no-type-imports` will enforce that you always use `import Foo from '...'`.
Examples of **correct** code with `{prefer: 'type-imports'}`, and **incorrect** code with `{prefer: 'no-type-imports'}`.
diff --git a/packages/eslint-plugin/docs/rules/object-curly-spacing.md b/packages/eslint-plugin/docs/rules/object-curly-spacing.md
new file mode 100644
index 000000000000..44bd35afb592
--- /dev/null
+++ b/packages/eslint-plugin/docs/rules/object-curly-spacing.md
@@ -0,0 +1,22 @@
+# Enforce consistent spacing inside braces (`object-curly-spacing`)
+
+## Rule Details
+
+This rule extends the base [`eslint/object-curly-spacing`](https://eslint.org/docs/rules/object-curly-spacing) rule.
+It adds support for TypeScript's object types.
+
+## How to use
+
+```cjson
+{
+ // note you must disable the base rule as it can report incorrect errors
+ "object-curly-spacing": "off",
+ "@typescript-eslint/object-curly-spacing": ["error"]
+}
+```
+
+## Options
+
+See [`eslint/object-curly-spacing` options](https://eslint.org/docs/rules/object-curly-spacing#options).
+
+Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/object-curly-spacing.md)
diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json
index 87b1178bf425..d8edd5bce698 100644
--- a/packages/eslint-plugin/package.json
+++ b/packages/eslint-plugin/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/eslint-plugin",
- "version": "4.13.0",
+ "version": "4.14.0",
"description": "TypeScript plugin for ESLint",
"keywords": [
"eslint",
@@ -42,8 +42,8 @@
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
- "@typescript-eslint/experimental-utils": "4.13.0",
- "@typescript-eslint/scope-manager": "4.13.0",
+ "@typescript-eslint/experimental-utils": "4.14.0",
+ "@typescript-eslint/scope-manager": "4.14.0",
"debug": "^4.1.1",
"functional-red-black-tree": "^1.0.1",
"lodash": "^4.17.15",
diff --git a/packages/eslint-plugin/src/configs/all.ts b/packages/eslint-plugin/src/configs/all.ts
index 36cd690afa05..98afc4ae3a45 100644
--- a/packages/eslint-plugin/src/configs/all.ts
+++ b/packages/eslint-plugin/src/configs/all.ts
@@ -113,6 +113,8 @@ export = {
'@typescript-eslint/no-useless-constructor': 'error',
'@typescript-eslint/no-var-requires': 'error',
'@typescript-eslint/non-nullable-type-assertion-style': 'error',
+ 'object-curly-spacing': 'off',
+ '@typescript-eslint/object-curly-spacing': 'error',
'@typescript-eslint/prefer-as-const': 'error',
'@typescript-eslint/prefer-enum-initializers': 'error',
'@typescript-eslint/prefer-for-of': 'error',
diff --git a/packages/eslint-plugin/src/rules/consistent-type-imports.ts b/packages/eslint-plugin/src/rules/consistent-type-imports.ts
index c4cb6586a00f..5d14576a2e5b 100644
--- a/packages/eslint-plugin/src/rules/consistent-type-imports.ts
+++ b/packages/eslint-plugin/src/rules/consistent-type-imports.ts
@@ -20,6 +20,8 @@ interface SourceImports {
reportValueImports: ReportValueImport[];
// ImportDeclaration for type-only import only with named imports.
typeOnlyNamedImport: TSESTree.ImportDeclaration | null;
+ // ImportDeclaration for value-only import only with named imports.
+ valueOnlyNamedImport: TSESTree.ImportDeclaration | null;
}
interface ReportValueImport {
node: TSESTree.ImportDeclaration;
@@ -39,12 +41,15 @@ function isTypeToken(
): token is TSESTree.IdentifierToken & { value: 'type' } {
return token.type === AST_TOKEN_TYPES.Identifier && token.value === 'type';
}
+
type MessageIds =
| 'typeOverValue'
| 'someImportsAreOnlyTypes'
| 'aImportIsOnlyTypes'
| 'valueOverType'
- | 'noImportTypeAnnotations';
+ | 'noImportTypeAnnotations'
+ | 'someImportsInDecoMeta'
+ | 'aImportInDecoMeta';
export default util.createRule({
name: 'consistent-type-imports',
meta: {
@@ -59,6 +64,10 @@ export default util.createRule({
'All imports in the declaration are only used as types. Use `import type`',
someImportsAreOnlyTypes: 'Imports {{typeImports}} are only used as types',
aImportIsOnlyTypes: 'Import {{typeImports}} is only used as types',
+ someImportsInDecoMeta:
+ 'Type imports {{typeImports}} are used by decorator metadata',
+ aImportInDecoMeta:
+ 'Type import {{typeImports}} is used by decorator metadata',
valueOverType: 'Use an `import` instead of an `import type`.',
noImportTypeAnnotations: '`import()` type annotations are forbidden.',
},
@@ -105,6 +114,7 @@ export default util.createRule({
source,
reportValueImports: [],
typeOnlyNamedImport: null,
+ valueOnlyNamedImport: null,
});
if (node.importKind === 'type') {
if (
@@ -116,9 +126,18 @@ export default util.createRule({
) {
sourceImports.typeOnlyNamedImport = node;
}
- return;
+ } else {
+ if (
+ !sourceImports.valueOnlyNamedImport &&
+ node.specifiers.every(
+ specifier =>
+ specifier.type === AST_NODE_TYPES.ImportSpecifier,
+ )
+ ) {
+ sourceImports.valueOnlyNamedImport = node;
+ }
}
- // if importKind === 'value'
+
const typeSpecifiers: TSESTree.ImportClause[] = [];
const valueSpecifiers: TSESTree.ImportClause[] = [];
const unusedSpecifiers: TSESTree.ImportClause[] = [];
@@ -129,6 +148,21 @@ export default util.createRule({
} else {
const onlyHasTypeReferences = variable.references.every(
ref => {
+ /**
+ * keep origin import kind when export
+ * export { Type }
+ * export default Type;
+ */
+ if (
+ ref.identifier.parent?.type ===
+ AST_NODE_TYPES.ExportSpecifier ||
+ ref.identifier.parent?.type ===
+ AST_NODE_TYPES.ExportDefaultDeclaration
+ ) {
+ if (ref.isValueReference && ref.isTypeReference) {
+ return node.importKind === 'type';
+ }
+ }
if (ref.isValueReference) {
// `type T = typeof foo` will create a value reference because "foo" must be a value type
// however this value reference is safe to use with type-only imports
@@ -157,7 +191,10 @@ export default util.createRule({
}
}
- if (typeSpecifiers.length) {
+ if (
+ (node.importKind === 'value' && typeSpecifiers.length) ||
+ (node.importKind === 'type' && valueSpecifiers.length)
+ ) {
sourceImports.reportValueImports.push({
node,
typeSpecifiers,
@@ -185,27 +222,42 @@ export default util.createRule({
},
});
} else {
+ const isTypeImport = report.node.importKind === 'type';
+
// we have a mixed type/value import, so we need to split them out into multiple exports
- const typeImportNames: string[] = report.typeSpecifiers.map(
- specifier => `"${specifier.local.name}"`,
- );
+ const importNames = (isTypeImport
+ ? report.valueSpecifiers
+ : report.typeSpecifiers
+ ).map(specifier => `"${specifier.local.name}"`);
context.report({
node: report.node,
messageId:
- typeImportNames.length === 1
- ? 'aImportIsOnlyTypes'
+ importNames.length === 1
+ ? isTypeImport
+ ? 'aImportInDecoMeta'
+ : 'aImportIsOnlyTypes'
+ : isTypeImport
+ ? 'someImportsInDecoMeta'
: 'someImportsAreOnlyTypes',
data: {
typeImports:
- typeImportNames.length === 1
- ? typeImportNames[0]
+ importNames.length === 1
+ ? importNames[0]
: [
- typeImportNames.slice(0, -1).join(', '),
- typeImportNames.slice(-1)[0],
+ importNames.slice(0, -1).join(', '),
+ importNames.slice(-1)[0],
].join(' and '),
},
*fix(fixer) {
- yield* fixToTypeImport(fixer, report, sourceImports);
+ if (isTypeImport) {
+ yield* fixToValueImportInDecoMeta(
+ fixer,
+ report,
+ sourceImports,
+ );
+ } else {
+ yield* fixToTypeImport(fixer, report, sourceImports);
+ }
},
});
}
@@ -240,13 +292,13 @@ export default util.createRule({
: {}),
};
- function* fixToTypeImport(
- fixer: TSESLint.RuleFixer,
- report: ReportValueImport,
- sourceImports: SourceImports,
- ): IterableIterator {
- const { node } = report;
-
+ function classifySpecifier(
+ node: TSESTree.ImportDeclaration,
+ ): {
+ defaultSpecifier: TSESTree.ImportDefaultSpecifier | null;
+ namespaceSpecifier: TSESTree.ImportNamespaceSpecifier | null;
+ namedSpecifiers: TSESTree.ImportSpecifier[];
+ } {
const defaultSpecifier: TSESTree.ImportDefaultSpecifier | null =
node.specifiers[0].type === AST_NODE_TYPES.ImportDefaultSpecifier
? node.specifiers[0]
@@ -260,6 +312,175 @@ export default util.createRule({
(specifier): specifier is TSESTree.ImportSpecifier =>
specifier.type === AST_NODE_TYPES.ImportSpecifier,
);
+ return {
+ defaultSpecifier,
+ namespaceSpecifier,
+ namedSpecifiers,
+ };
+ }
+
+ /**
+ * Returns information for fixing named specifiers.
+ */
+ function getFixesNamedSpecifiers(
+ fixer: TSESLint.RuleFixer,
+ node: TSESTree.ImportDeclaration,
+ typeNamedSpecifiers: TSESTree.ImportSpecifier[],
+ allNamedSpecifiers: TSESTree.ImportSpecifier[],
+ ): {
+ typeNamedSpecifiersText: string;
+ removeTypeNamedSpecifiers: TSESLint.RuleFix[];
+ } {
+ if (allNamedSpecifiers.length === 0) {
+ return {
+ typeNamedSpecifiersText: '',
+ removeTypeNamedSpecifiers: [],
+ };
+ }
+ const typeNamedSpecifiersTexts: string[] = [];
+ const removeTypeNamedSpecifiers: TSESLint.RuleFix[] = [];
+ if (typeNamedSpecifiers.length === allNamedSpecifiers.length) {
+ // e.g.
+ // import Foo, {Type1, Type2} from 'foo'
+ // import DefType, {Type1, Type2} from 'foo'
+ const openingBraceToken = util.nullThrows(
+ sourceCode.getTokenBefore(
+ typeNamedSpecifiers[0],
+ util.isOpeningBraceToken,
+ ),
+ util.NullThrowsReasons.MissingToken('{', node.type),
+ );
+ const commaToken = util.nullThrows(
+ sourceCode.getTokenBefore(openingBraceToken, util.isCommaToken),
+ util.NullThrowsReasons.MissingToken(',', node.type),
+ );
+ const closingBraceToken = util.nullThrows(
+ sourceCode.getFirstTokenBetween(
+ openingBraceToken,
+ node.source,
+ util.isClosingBraceToken,
+ ),
+ util.NullThrowsReasons.MissingToken('}', node.type),
+ );
+
+ // import DefType, {...} from 'foo'
+ // ^^^^^^^ remove
+ removeTypeNamedSpecifiers.push(
+ fixer.removeRange([commaToken.range[0], closingBraceToken.range[1]]),
+ );
+
+ typeNamedSpecifiersTexts.push(
+ sourceCode.text.slice(
+ openingBraceToken.range[1],
+ closingBraceToken.range[0],
+ ),
+ );
+ } else {
+ const typeNamedSpecifierGroups: TSESTree.ImportSpecifier[][] = [];
+ let group: TSESTree.ImportSpecifier[] = [];
+ for (const namedSpecifier of allNamedSpecifiers) {
+ if (typeNamedSpecifiers.includes(namedSpecifier)) {
+ group.push(namedSpecifier);
+ } else if (group.length) {
+ typeNamedSpecifierGroups.push(group);
+ group = [];
+ }
+ }
+ if (group.length) {
+ typeNamedSpecifierGroups.push(group);
+ }
+ for (const namedSpecifiers of typeNamedSpecifierGroups) {
+ const { removeRange, textRange } = getNamedSpecifierRanges(
+ namedSpecifiers,
+ allNamedSpecifiers,
+ );
+ removeTypeNamedSpecifiers.push(fixer.removeRange(removeRange));
+
+ typeNamedSpecifiersTexts.push(sourceCode.text.slice(...textRange));
+ }
+ }
+ return {
+ typeNamedSpecifiersText: typeNamedSpecifiersTexts.join(','),
+ removeTypeNamedSpecifiers,
+ };
+ }
+
+ /**
+ * Returns ranges for fixing named specifier.
+ */
+ function getNamedSpecifierRanges(
+ namedSpecifierGroup: TSESTree.ImportSpecifier[],
+ allNamedSpecifiers: TSESTree.ImportSpecifier[],
+ ): {
+ textRange: TSESTree.Range;
+ removeRange: TSESTree.Range;
+ } {
+ const first = namedSpecifierGroup[0];
+ const last = namedSpecifierGroup[namedSpecifierGroup.length - 1];
+ const removeRange: TSESTree.Range = [first.range[0], last.range[1]];
+ const textRange: TSESTree.Range = [...removeRange];
+ const before = sourceCode.getTokenBefore(first)!;
+ textRange[0] = before.range[1];
+ if (util.isCommaToken(before)) {
+ removeRange[0] = before.range[0];
+ } else {
+ removeRange[0] = before.range[1];
+ }
+
+ const isFirst = allNamedSpecifiers[0] === first;
+ const isLast = allNamedSpecifiers[allNamedSpecifiers.length - 1] === last;
+ const after = sourceCode.getTokenAfter(last)!;
+ textRange[1] = after.range[0];
+ if (isFirst || isLast) {
+ if (util.isCommaToken(after)) {
+ removeRange[1] = after.range[1];
+ }
+ }
+
+ return {
+ textRange,
+ removeRange,
+ };
+ }
+
+ /**
+ * insert specifiers to named import node.
+ * e.g.
+ * import type { Already, Type1, Type2 } from 'foo'
+ * ^^^^^^^^^^^^^ insert
+ */
+ function insertToNamedImport(
+ fixer: TSESLint.RuleFixer,
+ target: TSESTree.ImportDeclaration,
+ insertText: string,
+ ): TSESLint.RuleFix {
+ const closingBraceToken = util.nullThrows(
+ sourceCode.getFirstTokenBetween(
+ sourceCode.getFirstToken(target)!,
+ target.source,
+ util.isClosingBraceToken,
+ ),
+ util.NullThrowsReasons.MissingToken('}', target.type),
+ );
+ const before = sourceCode.getTokenBefore(closingBraceToken)!;
+ if (!util.isCommaToken(before) && !util.isOpeningBraceToken(before)) {
+ insertText = ',' + insertText;
+ }
+ return fixer.insertTextBefore(closingBraceToken, insertText);
+ }
+
+ function* fixToTypeImport(
+ fixer: TSESLint.RuleFixer,
+ report: ReportValueImport,
+ sourceImports: SourceImports,
+ ): IterableIterator {
+ const { node } = report;
+
+ const {
+ defaultSpecifier,
+ namespaceSpecifier,
+ namedSpecifiers,
+ } = classifySpecifier(node);
if (namespaceSpecifier && !defaultSpecifier) {
// e.g.
@@ -296,33 +517,18 @@ export default util.createRule({
);
const fixesNamedSpecifiers = getFixesNamedSpecifiers(
+ fixer,
+ node,
typeNamedSpecifiers,
namedSpecifiers,
);
const afterFixes: TSESLint.RuleFix[] = [];
if (typeNamedSpecifiers.length) {
if (sourceImports.typeOnlyNamedImport) {
- const closingBraceToken = util.nullThrows(
- sourceCode.getFirstTokenBetween(
- sourceCode.getFirstToken(sourceImports.typeOnlyNamedImport)!,
- sourceImports.typeOnlyNamedImport.source,
- util.isClosingBraceToken,
- ),
- util.NullThrowsReasons.MissingToken(
- '}',
- sourceImports.typeOnlyNamedImport.type,
- ),
- );
- let insertText = fixesNamedSpecifiers.typeNamedSpecifiersText;
- const before = sourceCode.getTokenBefore(closingBraceToken)!;
- if (!util.isCommaToken(before) && !util.isOpeningBraceToken(before)) {
- insertText = ',' + insertText;
- }
- // import type { Already, Type1, Type2 } from 'foo'
- // ^^^^^^^^^^^^^ insert
- const insertTypeNamedSpecifiers = fixer.insertTextBefore(
- closingBraceToken,
- insertText,
+ const insertTypeNamedSpecifiers = insertToNamedImport(
+ fixer,
+ sourceImports.typeOnlyNamedImport,
+ fixesNamedSpecifiers.typeNamedSpecifiersText,
);
if (sourceImports.typeOnlyNamedImport.range[1] <= node.range[0]) {
yield insertTypeNamedSpecifiers;
@@ -413,132 +619,6 @@ export default util.createRule({
yield* fixesRemoveTypeNamespaceSpecifier;
yield* afterFixes;
-
- /**
- * Returns information for fixing named specifiers.
- */
- function getFixesNamedSpecifiers(
- typeNamedSpecifiers: TSESTree.ImportSpecifier[],
- allNamedSpecifiers: TSESTree.ImportSpecifier[],
- ): {
- typeNamedSpecifiersText: string;
- removeTypeNamedSpecifiers: TSESLint.RuleFix[];
- } {
- if (allNamedSpecifiers.length === 0) {
- return {
- typeNamedSpecifiersText: '',
- removeTypeNamedSpecifiers: [],
- };
- }
- const typeNamedSpecifiersTexts: string[] = [];
- const removeTypeNamedSpecifiers: TSESLint.RuleFix[] = [];
- if (typeNamedSpecifiers.length === allNamedSpecifiers.length) {
- // e.g.
- // import Foo, {Type1, Type2} from 'foo'
- // import DefType, {Type1, Type2} from 'foo'
- const openingBraceToken = util.nullThrows(
- sourceCode.getTokenBefore(
- typeNamedSpecifiers[0],
- util.isOpeningBraceToken,
- ),
- util.NullThrowsReasons.MissingToken('{', node.type),
- );
- const commaToken = util.nullThrows(
- sourceCode.getTokenBefore(openingBraceToken, util.isCommaToken),
- util.NullThrowsReasons.MissingToken(',', node.type),
- );
- const closingBraceToken = util.nullThrows(
- sourceCode.getFirstTokenBetween(
- openingBraceToken,
- node.source,
- util.isClosingBraceToken,
- ),
- util.NullThrowsReasons.MissingToken('}', node.type),
- );
-
- // import DefType, {...} from 'foo'
- // ^^^^^^^ remove
- removeTypeNamedSpecifiers.push(
- fixer.removeRange([
- commaToken.range[0],
- closingBraceToken.range[1],
- ]),
- );
-
- typeNamedSpecifiersTexts.push(
- sourceCode.text.slice(
- openingBraceToken.range[1],
- closingBraceToken.range[0],
- ),
- );
- } else {
- const typeNamedSpecifierGroups: TSESTree.ImportSpecifier[][] = [];
- let group: TSESTree.ImportSpecifier[] = [];
- for (const namedSpecifier of allNamedSpecifiers) {
- if (typeNamedSpecifiers.includes(namedSpecifier)) {
- group.push(namedSpecifier);
- } else if (group.length) {
- typeNamedSpecifierGroups.push(group);
- group = [];
- }
- }
- if (group.length) {
- typeNamedSpecifierGroups.push(group);
- }
- for (const namedSpecifiers of typeNamedSpecifierGroups) {
- const { removeRange, textRange } = getNamedSpecifierRanges(
- namedSpecifiers,
- allNamedSpecifiers,
- );
- removeTypeNamedSpecifiers.push(fixer.removeRange(removeRange));
-
- typeNamedSpecifiersTexts.push(sourceCode.text.slice(...textRange));
- }
- }
- return {
- typeNamedSpecifiersText: typeNamedSpecifiersTexts.join(','),
- removeTypeNamedSpecifiers,
- };
- }
-
- /**
- * Returns ranges for fixing named specifier.
- */
- function getNamedSpecifierRanges(
- namedSpecifierGroup: TSESTree.ImportSpecifier[],
- allNamedSpecifiers: TSESTree.ImportSpecifier[],
- ): {
- textRange: TSESTree.Range;
- removeRange: TSESTree.Range;
- } {
- const first = namedSpecifierGroup[0];
- const last = namedSpecifierGroup[namedSpecifierGroup.length - 1];
- const removeRange: TSESTree.Range = [first.range[0], last.range[1]];
- const textRange: TSESTree.Range = [...removeRange];
- const before = sourceCode.getTokenBefore(first)!;
- textRange[0] = before.range[1];
- if (util.isCommaToken(before)) {
- removeRange[0] = before.range[0];
- } else {
- removeRange[0] = before.range[1];
- }
-
- const isFirst = allNamedSpecifiers[0] === first;
- const isLast =
- allNamedSpecifiers[allNamedSpecifiers.length - 1] === last;
- const after = sourceCode.getTokenAfter(last)!;
- textRange[1] = after.range[0];
- if (isFirst || isLast) {
- if (util.isCommaToken(after)) {
- removeRange[1] = after.range[1];
- }
- }
-
- return {
- textRange,
- removeRange,
- };
- }
}
function* fixToTypeImportByInsertType(
@@ -600,10 +680,89 @@ export default util.createRule({
}
}
- function fixToValueImport(
+ function* fixToValueImportInDecoMeta(
+ fixer: TSESLint.RuleFixer,
+ report: ReportValueImport,
+ sourceImports: SourceImports,
+ ): IterableIterator {
+ const { node } = report;
+
+ const {
+ defaultSpecifier,
+ namespaceSpecifier,
+ namedSpecifiers,
+ } = classifySpecifier(node);
+
+ if (namespaceSpecifier) {
+ // e.g.
+ // import type * as types from 'foo'
+ yield* fixToValueImport(fixer, node);
+ return;
+ } else if (defaultSpecifier) {
+ if (
+ report.valueSpecifiers.includes(defaultSpecifier) &&
+ namedSpecifiers.length === 0
+ ) {
+ // e.g.
+ // import type Type from 'foo'
+ yield* fixToValueImport(fixer, node);
+ return;
+ }
+ } else {
+ if (
+ namedSpecifiers.every(specifier =>
+ report.valueSpecifiers.includes(specifier),
+ )
+ ) {
+ // e.g.
+ // import type {Type1, Type2} from 'foo'
+ yield* fixToValueImport(fixer, node);
+ return;
+ }
+ }
+
+ const valueNamedSpecifiers = namedSpecifiers.filter(specifier =>
+ report.valueSpecifiers.includes(specifier),
+ );
+
+ const fixesNamedSpecifiers = getFixesNamedSpecifiers(
+ fixer,
+ node,
+ valueNamedSpecifiers,
+ namedSpecifiers,
+ );
+ const afterFixes: TSESLint.RuleFix[] = [];
+ if (valueNamedSpecifiers.length) {
+ if (sourceImports.valueOnlyNamedImport) {
+ const insertTypeNamedSpecifiers = insertToNamedImport(
+ fixer,
+ sourceImports.valueOnlyNamedImport,
+ fixesNamedSpecifiers.typeNamedSpecifiersText,
+ );
+ if (sourceImports.valueOnlyNamedImport.range[1] <= node.range[0]) {
+ yield insertTypeNamedSpecifiers;
+ } else {
+ afterFixes.push(insertTypeNamedSpecifiers);
+ }
+ } else {
+ yield fixer.insertTextBefore(
+ node,
+ `import {${
+ fixesNamedSpecifiers.typeNamedSpecifiersText
+ }} from ${sourceCode.getText(node.source)};\n`,
+ );
+ }
+ }
+
+ yield* fixesNamedSpecifiers.removeTypeNamedSpecifiers;
+
+ yield* afterFixes;
+ }
+
+ function* fixToValueImport(
fixer: TSESLint.RuleFixer,
node: TSESTree.ImportDeclaration,
- ): TSESLint.RuleFix {
+ ): IterableIterator {
// import type Foo from 'foo'
// ^^^^ remove
const importToken = util.nullThrows(
@@ -622,7 +781,7 @@ export default util.createRule({
sourceCode.getTokenAfter(typeToken, { includeComments: true }),
util.NullThrowsReasons.MissingToken('any token', node.type),
);
- return fixer.removeRange([typeToken.range[0], afterToken.range[0]]);
+ yield fixer.removeRange([typeToken.range[0], afterToken.range[0]]);
}
},
});
diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts
index 1be014e46c55..ffa70e57a290 100644
--- a/packages/eslint-plugin/src/rules/index.ts
+++ b/packages/eslint-plugin/src/rules/index.ts
@@ -79,6 +79,7 @@ import noUseBeforeDefine from './no-use-before-define';
import noUselessConstructor from './no-useless-constructor';
import noVarRequires from './no-var-requires';
import nonNullableTypeAssertionStyle from './non-nullable-type-assertion-style';
+import objectCurlySpacing from './object-curly-spacing';
import preferAsConst from './prefer-as-const';
import preferEnumInitializers from './prefer-enum-initializers';
import preferForOf from './prefer-for-of';
@@ -195,6 +196,7 @@ export default {
'no-useless-constructor': noUselessConstructor,
'no-var-requires': noVarRequires,
'non-nullable-type-assertion-style': nonNullableTypeAssertionStyle,
+ 'object-curly-spacing': objectCurlySpacing,
'prefer-as-const': preferAsConst,
'prefer-enum-initializers': preferEnumInitializers,
'prefer-for-of': preferForOf,
diff --git a/packages/eslint-plugin/src/rules/object-curly-spacing.ts b/packages/eslint-plugin/src/rules/object-curly-spacing.ts
new file mode 100644
index 000000000000..581e7ac15557
--- /dev/null
+++ b/packages/eslint-plugin/src/rules/object-curly-spacing.ts
@@ -0,0 +1,267 @@
+import {
+ AST_NODE_TYPES,
+ AST_TOKEN_TYPES,
+ TSESTree,
+} from '@typescript-eslint/experimental-utils';
+import baseRule from 'eslint/lib/rules/object-curly-spacing';
+import {
+ createRule,
+ InferMessageIdsTypeFromRule,
+ InferOptionsTypeFromRule,
+ isClosingBraceToken,
+ isClosingBracketToken,
+ isTokenOnSameLine,
+} from '../util';
+
+export type Options = InferOptionsTypeFromRule;
+export type MessageIds = InferMessageIdsTypeFromRule;
+
+export default createRule({
+ name: 'object-curly-spacing',
+ meta: {
+ ...baseRule.meta,
+ docs: {
+ description: 'Enforce consistent spacing inside braces',
+ category: 'Stylistic Issues',
+ recommended: false,
+ extendsBaseRule: true,
+ },
+ },
+ defaultOptions: ['never'],
+ create(context) {
+ const spaced = context.options[0] === 'always';
+ const sourceCode = context.getSourceCode();
+
+ /**
+ * Determines whether an option is set, relative to the spacing option.
+ * If spaced is "always", then check whether option is set to false.
+ * If spaced is "never", then check whether option is set to true.
+ * @param option The option to exclude.
+ * @returns Whether or not the property is excluded.
+ */
+ function isOptionSet(
+ option: 'arraysInObjects' | 'objectsInObjects',
+ ): boolean {
+ return context.options[1]
+ ? context.options[1][option] === !spaced
+ : false;
+ }
+
+ const options = {
+ spaced,
+ arraysInObjectsException: isOptionSet('arraysInObjects'),
+ objectsInObjectsException: isOptionSet('objectsInObjects'),
+ };
+
+ //--------------------------------------------------------------------------
+ // Helpers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Reports that there shouldn't be a space after the first token
+ * @param node The node to report in the event of an error.
+ * @param token The token to use for the report.
+ */
+ function reportNoBeginningSpace(
+ node: TSESTree.TSTypeLiteral,
+ token: TSESTree.Token,
+ ): void {
+ const nextToken = context
+ .getSourceCode()
+ .getTokenAfter(token, { includeComments: true })!;
+
+ context.report({
+ node,
+ loc: { start: token.loc.end, end: nextToken.loc.start },
+ messageId: 'unexpectedSpaceAfter',
+ data: {
+ token: token.value,
+ },
+ fix(fixer) {
+ return fixer.removeRange([token.range[1], nextToken.range[0]]);
+ },
+ });
+ }
+
+ /**
+ * Reports that there shouldn't be a space before the last token
+ * @param node The node to report in the event of an error.
+ * @param token The token to use for the report.
+ */
+ function reportNoEndingSpace(
+ node: TSESTree.TSTypeLiteral,
+ token: TSESTree.Token,
+ ): void {
+ const previousToken = context
+ .getSourceCode()
+ .getTokenBefore(token, { includeComments: true })!;
+
+ context.report({
+ node,
+ loc: { start: previousToken.loc.end, end: token.loc.start },
+ messageId: 'unexpectedSpaceBefore',
+ data: {
+ token: token.value,
+ },
+ fix(fixer) {
+ return fixer.removeRange([previousToken.range[1], token.range[0]]);
+ },
+ });
+ }
+
+ /**
+ * Reports that there should be a space after the first token
+ * @param node The node to report in the event of an error.
+ * @param token The token to use for the report.
+ */
+ function reportRequiredBeginningSpace(
+ node: TSESTree.TSTypeLiteral,
+ token: TSESTree.Token,
+ ): void {
+ context.report({
+ node,
+ loc: token.loc,
+ messageId: 'requireSpaceAfter',
+ data: {
+ token: token.value,
+ },
+ fix(fixer) {
+ return fixer.insertTextAfter(token, ' ');
+ },
+ });
+ }
+
+ /**
+ * Reports that there should be a space before the last token
+ * @param node The node to report in the event of an error.
+ * @param token The token to use for the report.
+ */
+ function reportRequiredEndingSpace(
+ node: TSESTree.TSTypeLiteral,
+ token: TSESTree.Token,
+ ): void {
+ context.report({
+ node,
+ loc: token.loc,
+ messageId: 'requireSpaceBefore',
+ data: {
+ token: token.value,
+ },
+ fix(fixer) {
+ return fixer.insertTextBefore(token, ' ');
+ },
+ });
+ }
+
+ /**
+ * Determines if spacing in curly braces is valid.
+ * @param node The AST node to check.
+ * @param first The first token to check (should be the opening brace)
+ * @param second The second token to check (should be first after the opening brace)
+ * @param penultimate The penultimate token to check (should be last before closing brace)
+ * @param last The last token to check (should be closing brace)
+ */
+ function validateBraceSpacing(
+ node: TSESTree.TSTypeLiteral,
+ first: TSESTree.Token,
+ second: TSESTree.Token | TSESTree.Comment,
+ penultimate: TSESTree.Token | TSESTree.Comment,
+ last: TSESTree.Token,
+ ): void {
+ if (isTokenOnSameLine(first, second)) {
+ const firstSpaced = sourceCode.isSpaceBetween!(first, second);
+ const secondType = sourceCode.getNodeByRangeIndex(second.range[0])!
+ .type;
+
+ const openingCurlyBraceMustBeSpaced =
+ options.arraysInObjectsException &&
+ secondType === AST_NODE_TYPES.TSIndexSignature
+ ? !options.spaced
+ : options.spaced;
+
+ if (openingCurlyBraceMustBeSpaced && !firstSpaced) {
+ reportRequiredBeginningSpace(node, first);
+ }
+ if (
+ !openingCurlyBraceMustBeSpaced &&
+ firstSpaced &&
+ second.type !== AST_TOKEN_TYPES.Line
+ ) {
+ reportNoBeginningSpace(node, first);
+ }
+ }
+
+ if (isTokenOnSameLine(penultimate, last)) {
+ const shouldCheckPenultimate =
+ (options.arraysInObjectsException &&
+ isClosingBracketToken(penultimate)) ||
+ (options.objectsInObjectsException &&
+ isClosingBraceToken(penultimate));
+ const penultimateType =
+ shouldCheckPenultimate &&
+ sourceCode.getNodeByRangeIndex(penultimate.range[0])!.type;
+
+ const closingCurlyBraceMustBeSpaced =
+ (options.arraysInObjectsException &&
+ penultimateType === AST_NODE_TYPES.TSTupleType) ||
+ (options.objectsInObjectsException &&
+ penultimateType === AST_NODE_TYPES.TSTypeLiteral)
+ ? !options.spaced
+ : options.spaced;
+
+ const lastSpaced = sourceCode.isSpaceBetween!(penultimate, last);
+
+ if (closingCurlyBraceMustBeSpaced && !lastSpaced) {
+ reportRequiredEndingSpace(node, last);
+ }
+ if (!closingCurlyBraceMustBeSpaced && lastSpaced) {
+ reportNoEndingSpace(node, last);
+ }
+ }
+ }
+
+ /**
+ * Gets '}' token of an object node.
+ *
+ * Because the last token of object patterns might be a type annotation,
+ * this traverses tokens preceded by the last property, then returns the
+ * first '}' token.
+ * @param node The node to get. This node is an
+ * ObjectExpression or an ObjectPattern. And this node has one or
+ * more properties.
+ * @returns '}' token.
+ */
+ function getClosingBraceOfObject(
+ node: TSESTree.TSTypeLiteral,
+ ): TSESTree.Token | null {
+ const lastProperty = node.members[node.members.length - 1];
+
+ return sourceCode.getTokenAfter(lastProperty, isClosingBraceToken);
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ const rules = baseRule.create(context);
+ return {
+ ...rules,
+ TSTypeLiteral(node: TSESTree.TSTypeLiteral): void {
+ if (node.members.length === 0) {
+ return;
+ }
+
+ const first = sourceCode.getFirstToken(node)!;
+ const last = getClosingBraceOfObject(node)!;
+ const second = sourceCode.getTokenAfter(first, {
+ includeComments: true,
+ })!;
+ const penultimate = sourceCode.getTokenBefore(last, {
+ includeComments: true,
+ })!;
+
+ validateBraceSpacing(node, first, second, penultimate, last);
+ },
+ };
+ },
+});
diff --git a/packages/eslint-plugin/tests/fixtures/tsconfig-withmeta.json b/packages/eslint-plugin/tests/fixtures/tsconfig-withmeta.json
new file mode 100644
index 000000000000..4987fc7e1745
--- /dev/null
+++ b/packages/eslint-plugin/tests/fixtures/tsconfig-withmeta.json
@@ -0,0 +1,6 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "emitDecoratorMetadata": true,
+ }
+}
\ No newline at end of file
diff --git a/packages/eslint-plugin/tests/rules/consistent-type-imports.test.ts b/packages/eslint-plugin/tests/rules/consistent-type-imports.test.ts
index 8818225a024a..83d803cf3809 100644
--- a/packages/eslint-plugin/tests/rules/consistent-type-imports.test.ts
+++ b/packages/eslint-plugin/tests/rules/consistent-type-imports.test.ts
@@ -1,5 +1,5 @@
import rule from '../../src/rules/consistent-type-imports';
-import { RuleTester, noFormat } from '../RuleTester';
+import { RuleTester, noFormat, getFixturesRootDir } from '../RuleTester';
const ruleTester = new RuleTester({
parser: '@typescript-eslint/parser',
@@ -9,6 +9,11 @@ const ruleTester = new RuleTester({
},
});
+const withMetaParserOptions = {
+ tsconfigRootDir: getFixturesRootDir(),
+ project: './tsconfig-withmeta.json',
+};
+
ruleTester.run('consistent-type-imports', rule, {
valid: [
`
@@ -230,6 +235,112 @@ ruleTester.run('consistent-type-imports', rule, {
const a: typeof Default = Default;
const b: typeof Rest = Rest;
`,
+ {
+ code: `
+ import Foo from 'foo';
+ @deco
+ class A {
+ constructor(foo: Foo) {}
+ }
+ `,
+ parserOptions: withMetaParserOptions,
+ },
+ {
+ code: `
+ import Foo from 'foo';
+ class A {
+ @deco
+ foo: Foo;
+ }
+ `,
+ parserOptions: withMetaParserOptions,
+ },
+ {
+ code: `
+ import Foo from 'foo';
+ class A {
+ @deco
+ foo(foo: Foo) {}
+ }
+ `,
+ parserOptions: withMetaParserOptions,
+ },
+ {
+ code: `
+ import Foo from 'foo';
+ class A {
+ @deco
+ foo(): Foo {}
+ }
+ `,
+ parserOptions: withMetaParserOptions,
+ },
+ {
+ code: `
+ import Foo from 'foo';
+ class A {
+ foo(@deco foo: Foo) {}
+ }
+ `,
+ parserOptions: withMetaParserOptions,
+ },
+ {
+ code: `
+ import Foo from 'foo';
+ class A {
+ @deco
+ set foo(value: Foo) {}
+ }
+ `,
+ parserOptions: withMetaParserOptions,
+ },
+ {
+ code: `
+ import Foo from 'foo';
+ class A {
+ @deco
+ get foo() {}
+
+ set foo(value: Foo) {}
+ }
+ `,
+ parserOptions: withMetaParserOptions,
+ },
+ {
+ code: `
+ import Foo from 'foo';
+ class A {
+ @deco
+ get foo() {}
+
+ set ['foo'](value: Foo) {}
+ }
+ `,
+ parserOptions: withMetaParserOptions,
+ },
+ {
+ code: `
+ import type { Foo } from 'foo';
+ const key = 'k';
+ class A {
+ @deco
+ get [key]() {}
+
+ set [key](value: Foo) {}
+ }
+ `,
+ parserOptions: withMetaParserOptions,
+ },
+ {
+ code: `
+ import * as foo from 'foo';
+ @deco
+ class A {
+ constructor(foo: foo.Foo) {}
+ }
+ `,
+ parserOptions: withMetaParserOptions,
+ },
],
invalid: [
{
@@ -1215,5 +1326,188 @@ const a: Default = '';
},
],
},
+ {
+ code: `
+ import Foo from 'foo';
+ @deco
+ class A {
+ constructor(foo: Foo) {}
+ }
+ `,
+ output: `
+ import type Foo from 'foo';
+ @deco
+ class A {
+ constructor(foo: Foo) {}
+ }
+ `,
+ errors: [
+ {
+ messageId: 'typeOverValue',
+ line: 2,
+ column: 9,
+ },
+ ],
+ },
+ {
+ code: `
+ import type Foo from 'foo';
+ @deco
+ class A {
+ constructor(foo: Foo) {}
+ }
+ `,
+ output: noFormat`
+ import Foo from 'foo';
+ @deco
+ class A {
+ constructor(foo: Foo) {}
+ }
+ `,
+ errors: [
+ {
+ messageId: 'aImportInDecoMeta',
+ data: { typeImports: '"Foo"' },
+ line: 2,
+ column: 9,
+ },
+ ],
+ parserOptions: withMetaParserOptions,
+ },
+ {
+ code: `
+ import type { Foo } from 'foo';
+ @deco
+ class A {
+ constructor(foo: Foo) {}
+ }
+ `,
+ output: noFormat`
+ import { Foo } from 'foo';
+ @deco
+ class A {
+ constructor(foo: Foo) {}
+ }
+ `,
+ errors: [
+ {
+ messageId: 'aImportInDecoMeta',
+ data: { typeImports: '"Foo"' },
+ line: 2,
+ column: 9,
+ },
+ ],
+ parserOptions: withMetaParserOptions,
+ },
+ {
+ code: noFormat`
+ import type { Type } from 'foo';
+ import { Foo, Bar } from 'foo';
+ @deco
+ class A {
+ constructor(foo: Foo) {}
+ }
+ type T = Bar;
+ `,
+ output: noFormat`
+ import type { Type , Bar } from 'foo';
+ import { Foo } from 'foo';
+ @deco
+ class A {
+ constructor(foo: Foo) {}
+ }
+ type T = Bar;
+ `,
+ errors: [
+ {
+ messageId: 'aImportIsOnlyTypes',
+ data: { typeImports: '"Bar"' },
+ line: 3,
+ column: 9,
+ },
+ ],
+ parserOptions: withMetaParserOptions,
+ },
+ {
+ code: `
+ import { V } from 'foo';
+ import type { Foo, Bar, T } from 'foo';
+ @deco
+ class A {
+ constructor(foo: Foo) {}
+ foo(@deco bar: Bar) {}
+ }
+ `,
+ output: noFormat`
+ import { V , Foo, Bar} from 'foo';
+ import type { T } from 'foo';
+ @deco
+ class A {
+ constructor(foo: Foo) {}
+ foo(@deco bar: Bar) {}
+ }
+ `,
+ errors: [
+ {
+ messageId: 'someImportsInDecoMeta',
+ data: { typeImports: '"Foo" and "Bar"' },
+ line: 3,
+ column: 9,
+ },
+ ],
+ parserOptions: withMetaParserOptions,
+ },
+ {
+ code: `
+ import type { Foo, T } from 'foo';
+ import { V } from 'foo';
+ @deco
+ class A {
+ constructor(foo: Foo) {}
+ }
+ `,
+ output: noFormat`
+ import type { T } from 'foo';
+ import { V , Foo} from 'foo';
+ @deco
+ class A {
+ constructor(foo: Foo) {}
+ }
+ `,
+ errors: [
+ {
+ messageId: 'aImportInDecoMeta',
+ data: { typeImports: '"Foo"' },
+ line: 2,
+ column: 9,
+ },
+ ],
+ parserOptions: withMetaParserOptions,
+ },
+ {
+ code: `
+ import type * as Type from 'foo';
+ @deco
+ class A {
+ constructor(foo: Type.Foo) {}
+ }
+ `,
+ output: noFormat`
+ import * as Type from 'foo';
+ @deco
+ class A {
+ constructor(foo: Type.Foo) {}
+ }
+ `,
+ errors: [
+ {
+ messageId: 'aImportInDecoMeta',
+ data: { typeImports: '"Type"' },
+ line: 2,
+ column: 9,
+ },
+ ],
+ parserOptions: withMetaParserOptions,
+ },
],
});
diff --git a/packages/eslint-plugin/tests/rules/object-curly-spacing.test.ts b/packages/eslint-plugin/tests/rules/object-curly-spacing.test.ts
new file mode 100644
index 000000000000..5c28cbef7ed7
--- /dev/null
+++ b/packages/eslint-plugin/tests/rules/object-curly-spacing.test.ts
@@ -0,0 +1,1955 @@
+/* eslint-disable eslint-comments/no-use */
+// this rule tests the position of braces, which prettier will want to fix and break the tests
+/* eslint "@typescript-eslint/internal/plugin-test-formatting": ["error", { formatWithPrettier: false }] */
+/* eslint-enable eslint-comments/no-use */
+
+import { AST_NODE_TYPES } from '@typescript-eslint/experimental-utils';
+import rule from '../../src/rules/object-curly-spacing';
+import { RuleTester } from '../RuleTester';
+
+const ruleTester = new RuleTester({
+ parser: '@typescript-eslint/parser',
+});
+
+ruleTester.run('object-curly-spacing', rule, {
+ valid: [
+ // always - object literals
+ { code: 'var obj = { foo: bar, baz: qux };', options: ['always'] },
+ {
+ code: 'var obj = { foo: { bar: quxx }, baz: qux };',
+ options: ['always'],
+ },
+ { code: 'var obj = {\nfoo: bar,\nbaz: qux\n};', options: ['always'] },
+ { code: 'var obj = { /**/foo:bar/**/ };', options: ['always'] },
+ { code: 'var obj = { //\nfoo:bar };', options: ['always'] },
+
+ // always - destructuring
+ {
+ code: 'var { x } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var { x, y } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var { x,y } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {\nx,y } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {\nx,y\n} = z',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var { /**/x/**/ } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var { //\nx } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var { x = 10, y } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var { x: { z }, y } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {\ny,\n} = x',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var { y, } = x',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var { y: x } = x',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+
+ // always - import / export
+ {
+ code: "import door from 'room'",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import * as door from 'room'",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import { door } from 'room'",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {\ndoor } from 'room'",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import { /**/door/**/ } from 'room'",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import { //\ndoor } from 'room'",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export { door } from 'room'",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import { house, mouse } from 'caravan'",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import house, { mouse } from 'caravan'",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import door, { house, mouse } from 'caravan'",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: 'var door = 0;export { door }',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import 'room'",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import { bar as x } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import { x, } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {\nx,\n} from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export { x, } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export {\nx,\n} from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export { /**/x/**/ } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export { //\nx } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: 'var x = 1;\nexport { /**/x/**/ };',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: 'var x = 1;\nexport { //\nx };',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+
+ // always - empty object
+ { code: 'var foo = {};', options: ['always'] },
+
+ // always - objectsInObjects
+ {
+ code: "var obj = { 'foo': { 'bar': 1, 'baz': 2 }};",
+ options: ['always', { objectsInObjects: false }],
+ },
+ {
+ code: 'var a = { noop: function () {} };',
+ options: ['always', { objectsInObjects: false }],
+ },
+ {
+ code: 'var { y: { z }} = x',
+ options: ['always', { objectsInObjects: false }],
+ parserOptions: { ecmaVersion: 6 },
+ },
+
+ // always - arraysInObjects
+ {
+ code: "var obj = { 'foo': [ 1, 2 ]};",
+ options: ['always', { arraysInObjects: false }],
+ },
+ {
+ code: 'var a = { thingInList: list[0] };',
+ options: ['always', { arraysInObjects: false }],
+ },
+
+ // always - arraysInObjects, objectsInObjects
+ {
+ code: "var obj = { 'qux': [ 1, 2 ], 'foo': { 'bar': 1, 'baz': 2 }};",
+ options: ['always', { arraysInObjects: false, objectsInObjects: false }],
+ },
+
+ // always - arraysInObjects, objectsInObjects (reverse)
+ {
+ code: "var obj = { 'foo': { 'bar': 1, 'baz': 2 }, 'qux': [ 1, 2 ]};",
+ options: ['always', { arraysInObjects: false, objectsInObjects: false }],
+ },
+
+ // never
+ { code: 'var obj = {foo: bar,\nbaz: qux\n};', options: ['never'] },
+ { code: 'var obj = {\nfoo: bar,\nbaz: qux};', options: ['never'] },
+
+ // never - object literals
+ { code: 'var obj = {foo: bar, baz: qux};', options: ['never'] },
+ { code: 'var obj = {foo: {bar: quxx}, baz: qux};', options: ['never'] },
+ { code: 'var obj = {foo: {\nbar: quxx}, baz: qux\n};', options: ['never'] },
+ { code: 'var obj = {foo: {\nbar: quxx\n}, baz: qux};', options: ['never'] },
+ { code: 'var obj = {\nfoo: bar,\nbaz: qux\n};', options: ['never'] },
+ { code: 'var obj = {foo: bar, baz: qux /* */};', options: ['never'] },
+ { code: 'var obj = {/* */ foo: bar, baz: qux};', options: ['never'] },
+ { code: 'var obj = {//\n foo: bar};', options: ['never'] },
+ {
+ code: 'var obj = { // line comment exception\n foo: bar};',
+ options: ['never'],
+ },
+
+ // never - destructuring
+ {
+ code: 'var {x} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {x, y} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {x,y} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {\nx,y\n} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {x = 10} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {x = 10, y} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {x: {z}, y} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {\nx: {z\n}, y} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {\ny,\n} = x',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {y,} = x',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {y:x} = x',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {/* */ y} = x',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {y /* */} = x',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {//\n y} = x',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var { // line comment exception\n y} = x',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+
+ // never - import / export
+ {
+ code: "import door from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import * as door from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {door} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export {door} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {/* */ door} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export {/* */ door} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {door /* */} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export {door /* */} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {//\n door} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export {//\n door} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: 'var door = foo;\nexport {//\n door}',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import { // line comment exception\n door} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export { // line comment exception\n door} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: 'var door = foo; export { // line comment exception\n door}',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {\ndoor} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export {\ndoor\n} from 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {house,mouse} from 'caravan'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {house, mouse} from 'caravan'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: 'var door = 0;export {door}',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import 'room'",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import x, {bar} from 'foo';",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import x, {bar, baz} from 'foo';",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {bar as y} from 'foo';",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {x,} from 'foo';",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "import {\nx,\n} from 'foo';",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export {x,} from 'foo';",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export {\nx,\n} from 'foo';",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+
+ // never - empty object
+ { code: 'var foo = {};', options: ['never'] },
+
+ // never - objectsInObjects
+ {
+ code: "var obj = {'foo': {'bar': 1, 'baz': 2} };",
+ options: ['never', { objectsInObjects: true }],
+ },
+
+ /*
+ * https://github.com/eslint/eslint/issues/3658
+ * Empty cases.
+ */
+ { code: 'var {} = foo;', parserOptions: { ecmaVersion: 6 } },
+ { code: 'var [] = foo;', parserOptions: { ecmaVersion: 6 } },
+ { code: 'var {a: {}} = foo;', parserOptions: { ecmaVersion: 6 } },
+ { code: 'var {a: []} = foo;', parserOptions: { ecmaVersion: 6 } },
+ {
+ code: "import {} from 'foo';",
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export {} from 'foo';",
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: 'export {};',
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: 'var {} = foo;',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var [] = foo;',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {a: {}} = foo;',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: 'var {a: []} = foo;',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ },
+ {
+ code: "import {} from 'foo';",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: "export {} from 'foo';",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+ {
+ code: 'export {};',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ },
+
+ // https://github.com/eslint/eslint/issues/6940
+ {
+ code: 'function foo ({a, b}: Props) {\n}',
+ options: ['never'],
+ },
+
+ // default - object literal types
+ {
+ code: 'const x:{}',
+ },
+ {
+ code: 'const x:{ }',
+ },
+ {
+ code: 'const x:{f: number}',
+ },
+ {
+ code: 'const x:{ // line-comment\nf: number\n}',
+ },
+ {
+ code: 'const x:{// line-comment\nf: number\n}',
+ },
+ {
+ code: 'const x:{/* inline-comment */f: number/* inline-comment */}',
+ },
+ {
+ code: 'const x:{\nf: number\n}',
+ },
+ {
+ code: 'const x:{f: {g: number}}',
+ },
+ {
+ code: 'const x:{f: [number]}',
+ },
+ {
+ code: 'const x:{[key: string]: value}',
+ },
+ {
+ code: 'const x:{[key: string]: [number]}',
+ },
+
+ // never - object literal types
+ {
+ code: 'const x:{f: {g: number} }',
+ options: ['never', { objectsInObjects: true }],
+ },
+ {
+ code: 'const x:{f: {g: number}}',
+ options: ['never', { objectsInObjects: false }],
+ },
+ {
+ code: 'const x:{f: () => {g: number} }',
+ options: ['never', { objectsInObjects: true }],
+ },
+ {
+ code: 'const x:{f: () => {g: number}}',
+ options: ['never', { objectsInObjects: false }],
+ },
+ {
+ code: 'const x:{f: [number] }',
+ options: ['never', { arraysInObjects: true }],
+ },
+ {
+ code: 'const x:{f: [ number ]}',
+ options: ['never', { arraysInObjects: false }],
+ },
+ {
+ code: 'const x:{ [key: string]: value}',
+ options: ['never', { arraysInObjects: true }],
+ },
+ {
+ code: 'const x:{[key: string]: value}',
+ options: ['never', { arraysInObjects: false }],
+ },
+ {
+ code: 'const x:{ [key: string]: [number] }',
+ options: ['never', { arraysInObjects: true }],
+ },
+ {
+ code: 'const x:{[key: string]: [number]}',
+ options: ['never', { arraysInObjects: false }],
+ },
+
+ // always - object literal types
+ {
+ code: 'const x:{}',
+ options: ['always'],
+ },
+ {
+ code: 'const x:{ }',
+ options: ['always'],
+ },
+ {
+ code: 'const x:{ f: number }',
+ options: ['always'],
+ },
+ {
+ code: 'const x:{ // line-comment\nf: number\n}',
+ options: ['always'],
+ },
+ {
+ code: 'const x:{ /* inline-comment */ f: number /* inline-comment */ }',
+ options: ['always'],
+ },
+ {
+ code: 'const x:{\nf: number\n}',
+ options: ['always'],
+ },
+ {
+ code: 'const x:{ f: [number] }',
+ options: ['always'],
+ },
+
+ // always - objectsInObjects
+ {
+ code: 'const x:{ f: { g: number } }',
+ options: ['always', { objectsInObjects: true }],
+ },
+ {
+ code: 'const x:{ f: { g: number }}',
+ options: ['always', { objectsInObjects: false }],
+ },
+ {
+ code: 'const x:{ f: () => { g: number } }',
+ options: ['always', { objectsInObjects: true }],
+ },
+ {
+ code: 'const x:{ f: () => { g: number }}',
+ options: ['always', { objectsInObjects: false }],
+ },
+
+ // always - arraysInObjects
+ {
+ code: 'const x:{ f: [number] }',
+ options: ['always', { arraysInObjects: true }],
+ },
+ {
+ code: 'const x:{ f: [ number ]}',
+ options: ['always', { arraysInObjects: false }],
+ },
+ {
+ code: 'const x:{ [key: string]: value }',
+ options: ['always', { arraysInObjects: true }],
+ },
+ {
+ code: 'const x:{[key: string]: value }',
+ options: ['always', { arraysInObjects: false }],
+ },
+ {
+ code: 'const x:{ [key: string]: [number] }',
+ options: ['always', { arraysInObjects: true }],
+ },
+ {
+ code: 'const x:{[key: string]: [number]}',
+ options: ['always', { arraysInObjects: false }],
+ },
+ ],
+
+ invalid: [
+ {
+ code: "import {bar} from 'foo.js';",
+ output: "import { bar } from 'foo.js';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 8,
+ endLine: 1,
+ endColumn: 9,
+ },
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 12,
+ endLine: 1,
+ endColumn: 13,
+ },
+ ],
+ },
+ {
+ code: "import { bar as y} from 'foo.js';",
+ output: "import { bar as y } from 'foo.js';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 18,
+ endLine: 1,
+ endColumn: 19,
+ },
+ ],
+ },
+ {
+ code: "import {bar as y} from 'foo.js';",
+ output: "import { bar as y } from 'foo.js';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 8,
+ endLine: 1,
+ endColumn: 9,
+ },
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 17,
+ endLine: 1,
+ endColumn: 18,
+ },
+ ],
+ },
+ {
+ code: "import { bar} from 'foo.js';",
+ output: "import { bar } from 'foo.js';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 13,
+ endLine: 1,
+ endColumn: 14,
+ },
+ ],
+ },
+ {
+ code: "import x, { bar} from 'foo';",
+ output: "import x, { bar } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 16,
+ endLine: 1,
+ endColumn: 17,
+ },
+ ],
+ },
+ {
+ code: "import x, { bar/* */} from 'foo';",
+ output: "import x, { bar/* */ } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 21,
+ endLine: 1,
+ endColumn: 22,
+ },
+ ],
+ },
+ {
+ code: "import x, {/* */bar } from 'foo';",
+ output: "import x, { /* */bar } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 12,
+ },
+ ],
+ },
+ {
+ code: "import x, {//\n bar } from 'foo';",
+ output: "import x, { //\n bar } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 12,
+ },
+ ],
+ },
+ {
+ code: "import x, { bar, baz} from 'foo';",
+ output: "import x, { bar, baz } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 21,
+ endLine: 1,
+ endColumn: 22,
+ },
+ ],
+ },
+ {
+ code: "import x, {bar} from 'foo';",
+ output: "import x, { bar } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 12,
+ },
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 15,
+ endLine: 1,
+ endColumn: 16,
+ },
+ ],
+ },
+ {
+ code: "import x, {bar, baz} from 'foo';",
+ output: "import x, { bar, baz } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 12,
+ },
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 20,
+ endLine: 1,
+ endColumn: 21,
+ },
+ ],
+ },
+ {
+ code: "import {bar,} from 'foo';",
+ output: "import { bar, } from 'foo';",
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 8,
+ endLine: 1,
+ endColumn: 9,
+ },
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 13,
+ endLine: 1,
+ endColumn: 14,
+ },
+ ],
+ },
+ {
+ code: "import { bar, } from 'foo';",
+ output: "import {bar,} from 'foo';",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 9,
+ endLine: 1,
+ endColumn: 10,
+ },
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 14,
+ endLine: 1,
+ endColumn: 15,
+ },
+ ],
+ },
+ {
+ code: "import { /* */ bar, /* */ } from 'foo';",
+ output: "import {/* */ bar, /* */} from 'foo';",
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 9,
+ endLine: 1,
+ endColumn: 10,
+ },
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ImportDeclaration,
+ line: 1,
+ column: 26,
+ endLine: 1,
+ endColumn: 27,
+ },
+ ],
+ },
+ {
+ code: 'var bar = 0;\nexport {bar};',
+ output: 'var bar = 0;\nexport { bar };',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ExportNamedDeclaration,
+ line: 2,
+ column: 8,
+ endLine: 2,
+ endColumn: 9,
+ },
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ExportNamedDeclaration,
+ line: 2,
+ column: 12,
+ },
+ ],
+ },
+ {
+ code: 'var bar = 0;\nexport {/* */ bar /* */};',
+ output: 'var bar = 0;\nexport { /* */ bar /* */ };',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ExportNamedDeclaration,
+ line: 2,
+ column: 8,
+ endLine: 2,
+ endColumn: 9,
+ },
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ExportNamedDeclaration,
+ line: 2,
+ column: 24,
+ endLine: 2,
+ endColumn: 25,
+ },
+ ],
+ },
+ {
+ code: 'var bar = 0;\nexport {//\n bar };',
+ output: 'var bar = 0;\nexport { //\n bar };',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ExportNamedDeclaration,
+ line: 2,
+ column: 8,
+ endLine: 2,
+ endColumn: 9,
+ },
+ ],
+ },
+ {
+ code: 'var bar = 0;\nexport { /* */ bar /* */ };',
+ output: 'var bar = 0;\nexport {/* */ bar /* */};',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ExportNamedDeclaration,
+ line: 2,
+ column: 9,
+ endLine: 2,
+ endColumn: 10,
+ },
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ExportNamedDeclaration,
+ line: 2,
+ column: 25,
+ endLine: 2,
+ endColumn: 26,
+ },
+ ],
+ },
+
+ // always - arraysInObjects
+ {
+ code: "var obj = { 'foo': [ 1, 2 ] };",
+ output: "var obj = { 'foo': [ 1, 2 ]};",
+ options: ['always', { arraysInObjects: false }],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 28,
+ endLine: 1,
+ endColumn: 29,
+ },
+ ],
+ },
+ {
+ code: "var obj = { 'foo': [ 1, 2 ] , 'bar': [ 'baz', 'qux' ] };",
+ output: "var obj = { 'foo': [ 1, 2 ] , 'bar': [ 'baz', 'qux' ]};",
+ options: ['always', { arraysInObjects: false }],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 54,
+ endLine: 1,
+ endColumn: 55,
+ },
+ ],
+ },
+
+ // always-objectsInObjects
+ {
+ code: "var obj = { 'foo': { 'bar': 1, 'baz': 2 } };",
+ output: "var obj = { 'foo': { 'bar': 1, 'baz': 2 }};",
+ options: ['always', { objectsInObjects: false }],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 42,
+ endLine: 1,
+ endColumn: 43,
+ },
+ ],
+ },
+ {
+ code: "var obj = { 'foo': [ 1, 2 ] , 'bar': { 'baz': 1, 'qux': 2 } };",
+ output: "var obj = { 'foo': [ 1, 2 ] , 'bar': { 'baz': 1, 'qux': 2 }};",
+ options: ['always', { objectsInObjects: false }],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 60,
+ endLine: 1,
+ endColumn: 61,
+ },
+ ],
+ },
+
+ // always-destructuring trailing comma
+ {
+ code: 'var { a,} = x;',
+ output: 'var { a, } = x;',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 9,
+ endLine: 1,
+ endColumn: 10,
+ },
+ ],
+ },
+ {
+ code: 'var {a, } = x;',
+ output: 'var {a,} = x;',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 8,
+ endLine: 1,
+ endColumn: 9,
+ },
+ ],
+ },
+ {
+ code: 'var {a:b } = x;',
+ output: 'var {a:b} = x;',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 9,
+ endLine: 1,
+ endColumn: 10,
+ },
+ ],
+ },
+ {
+ code: 'var { a:b } = x;',
+ output: 'var {a:b} = x;',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 6,
+ endLine: 1,
+ endColumn: 7,
+ },
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 10,
+ endLine: 1,
+ endColumn: 11,
+ },
+ ],
+ },
+ {
+ code: 'var { a:b } = x;',
+ output: 'var {a:b} = x;',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 6,
+ endLine: 1,
+ endColumn: 8,
+ },
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 13,
+ },
+ ],
+ },
+ {
+ code: 'var { a:b } = x;',
+ output: 'var {a:b} = x;',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 6,
+ endLine: 1,
+ endColumn: 9,
+ },
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 12,
+ endLine: 1,
+ endColumn: 16,
+ },
+ ],
+ },
+
+ // never-objectsInObjects
+ {
+ code: "var obj = {'foo': {'bar': 1, 'baz': 2}};",
+ output: "var obj = {'foo': {'bar': 1, 'baz': 2} };",
+ options: ['never', { objectsInObjects: true }],
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 39,
+ endLine: 1,
+ endColumn: 40,
+ },
+ ],
+ },
+ {
+ code: "var obj = {'foo': [1, 2] , 'bar': {'baz': 1, 'qux': 2}};",
+ output: "var obj = {'foo': [1, 2] , 'bar': {'baz': 1, 'qux': 2} };",
+ options: ['never', { objectsInObjects: true }],
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 55,
+ endLine: 1,
+ endColumn: 56,
+ },
+ ],
+ },
+
+ // always & never
+ {
+ code: 'var obj = {foo: bar, baz: qux};',
+ output: 'var obj = { foo: bar, baz: qux };',
+ options: ['always'],
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 12,
+ },
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 30,
+ endLine: 1,
+ endColumn: 31,
+ },
+ ],
+ },
+ {
+ code: 'var obj = {foo: bar, baz: qux };',
+ output: 'var obj = { foo: bar, baz: qux };',
+ options: ['always'],
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 12,
+ },
+ ],
+ },
+ {
+ code: 'var obj = {/* */foo: bar, baz: qux };',
+ output: 'var obj = { /* */foo: bar, baz: qux };',
+ options: ['always'],
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 12,
+ },
+ ],
+ },
+ {
+ code: 'var obj = {//\n foo: bar };',
+ output: 'var obj = { //\n foo: bar };',
+ options: ['always'],
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 12,
+ },
+ ],
+ },
+ {
+ code: 'var obj = { foo: bar, baz: qux};',
+ output: 'var obj = { foo: bar, baz: qux };',
+ options: ['always'],
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 31,
+ endLine: 1,
+ endColumn: 32,
+ },
+ ],
+ },
+ {
+ code: 'var obj = { foo: bar, baz: qux/* */};',
+ output: 'var obj = { foo: bar, baz: qux/* */ };',
+ options: ['always'],
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 36,
+ endLine: 1,
+ endColumn: 37,
+ },
+ ],
+ },
+ {
+ code: 'var obj = { foo: bar, baz: qux };',
+ output: 'var obj = {foo: bar, baz: qux};',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 12,
+ endLine: 1,
+ endColumn: 13,
+ },
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 31,
+ endLine: 1,
+ endColumn: 32,
+ },
+ ],
+ },
+ {
+ code: 'var obj = { foo: bar, baz: qux };',
+ output: 'var obj = {foo: bar, baz: qux};',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 12,
+ endLine: 1,
+ endColumn: 14,
+ },
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 32,
+ endLine: 1,
+ endColumn: 33,
+ },
+ ],
+ },
+ {
+ code: 'var obj = {foo: bar, baz: qux };',
+ output: 'var obj = {foo: bar, baz: qux};',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 30,
+ endLine: 1,
+ endColumn: 31,
+ },
+ ],
+ },
+ {
+ code: 'var obj = {foo: bar, baz: qux };',
+ output: 'var obj = {foo: bar, baz: qux};',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 30,
+ endLine: 1,
+ endColumn: 32,
+ },
+ ],
+ },
+ {
+ code: 'var obj = {foo: bar, baz: qux /* */ };',
+ output: 'var obj = {foo: bar, baz: qux /* */};',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 36,
+ endLine: 1,
+ endColumn: 37,
+ },
+ ],
+ },
+ {
+ code: 'var obj = { foo: bar, baz: qux};',
+ output: 'var obj = {foo: bar, baz: qux};',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 12,
+ endLine: 1,
+ endColumn: 13,
+ },
+ ],
+ },
+ {
+ code: 'var obj = { foo: bar, baz: qux};',
+ output: 'var obj = {foo: bar, baz: qux};',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 12,
+ endLine: 1,
+ endColumn: 14,
+ },
+ ],
+ },
+ {
+ code: 'var obj = { /* */ foo: bar, baz: qux};',
+ output: 'var obj = {/* */ foo: bar, baz: qux};',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 12,
+ endLine: 1,
+ endColumn: 13,
+ },
+ ],
+ },
+ {
+ code: 'var obj = { // line comment exception\n foo: bar };',
+ output: 'var obj = { // line comment exception\n foo: bar};',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 2,
+ column: 10,
+ endLine: 2,
+ endColumn: 11,
+ },
+ ],
+ },
+ {
+ code: 'var obj = { foo: { bar: quxx}, baz: qux};',
+ output: 'var obj = {foo: {bar: quxx}, baz: qux};',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 12,
+ endLine: 1,
+ endColumn: 13,
+ },
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 19,
+ endLine: 1,
+ endColumn: 20,
+ },
+ ],
+ },
+ {
+ code: 'var obj = {foo: {bar: quxx }, baz: qux };',
+ output: 'var obj = {foo: {bar: quxx}, baz: qux};',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 27,
+ endLine: 1,
+ endColumn: 28,
+ },
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 39,
+ endLine: 1,
+ endColumn: 40,
+ },
+ ],
+ },
+ {
+ code: 'export const thing = {value: 1 };',
+ output: 'export const thing = { value: 1 };',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6, sourceType: 'module' },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 22,
+ endLine: 1,
+ endColumn: 23,
+ },
+ ],
+ },
+
+ // destructuring
+ {
+ code: 'var {x, y} = y',
+ output: 'var { x, y } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 5,
+ endLine: 1,
+ endColumn: 6,
+ },
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 10,
+ endLine: 1,
+ endColumn: 11,
+ },
+ ],
+ },
+ {
+ code: 'var { x, y} = y',
+ output: 'var { x, y } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 12,
+ },
+ ],
+ },
+ {
+ code: 'var { x, y/* */} = y',
+ output: 'var { x, y/* */ } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 16,
+ endLine: 1,
+ endColumn: 17,
+ },
+ ],
+ },
+ {
+ code: 'var {/* */x, y } = y',
+ output: 'var { /* */x, y } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 5,
+ endLine: 1,
+ endColumn: 6,
+ },
+ ],
+ },
+ {
+ code: 'var {//\n x } = y',
+ output: 'var { //\n x } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 5,
+ endLine: 1,
+ endColumn: 6,
+ },
+ ],
+ },
+ {
+ code: 'var { x, y } = y',
+ output: 'var {x, y} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 6,
+ endLine: 1,
+ endColumn: 7,
+ },
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 12,
+ },
+ ],
+ },
+ {
+ code: 'var {x, y } = y',
+ output: 'var {x, y} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 10,
+ endLine: 1,
+ endColumn: 11,
+ },
+ ],
+ },
+ {
+ code: 'var {x, y/* */ } = y',
+ output: 'var {x, y/* */} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 15,
+ endLine: 1,
+ endColumn: 16,
+ },
+ ],
+ },
+ {
+ code: 'var { /* */x, y} = y',
+ output: 'var {/* */x, y} = y',
+ options: ['never'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'unexpectedSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 6,
+ endLine: 1,
+ endColumn: 7,
+ },
+ ],
+ },
+ {
+ code: 'var { x=10} = y',
+ output: 'var { x=10 } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 11,
+ endLine: 1,
+ endColumn: 12,
+ },
+ ],
+ },
+ {
+ code: 'var {x=10 } = y',
+ output: 'var { x=10 } = y',
+ options: ['always'],
+ parserOptions: { ecmaVersion: 6 },
+ errors: [
+ {
+ messageId: 'requireSpaceAfter',
+ data: { token: '{' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 5,
+ endLine: 1,
+ endColumn: 6,
+ },
+ ],
+ },
+
+ // never - arraysInObjects
+ {
+ code: "var obj = {'foo': [1, 2]};",
+ output: "var obj = {'foo': [1, 2] };",
+ options: ['never', { arraysInObjects: true }],
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 25,
+ endLine: 1,
+ endColumn: 26,
+ },
+ ],
+ },
+ {
+ code: "var obj = {'foo': [1, 2] , 'bar': ['baz', 'qux']};",
+ output: "var obj = {'foo': [1, 2] , 'bar': ['baz', 'qux'] };",
+ options: ['never', { arraysInObjects: true }],
+ errors: [
+ {
+ messageId: 'requireSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectExpression,
+ line: 1,
+ column: 49,
+ endLine: 1,
+ endColumn: 50,
+ },
+ ],
+ },
+
+ // https://github.com/eslint/eslint/issues/6940
+ {
+ code: 'function foo ({a, b }: Props) {\n}',
+ output: 'function foo ({a, b}: Props) {\n}',
+ options: ['never'],
+ errors: [
+ {
+ messageId: 'unexpectedSpaceBefore',
+ data: { token: '}' },
+ type: AST_NODE_TYPES.ObjectPattern,
+ line: 1,
+ column: 20,
+ endLine: 1,
+ endColumn: 21,
+ },
+ ],
+ },
+
+ // object literal types
+ {
+ code: 'type x = { f: number }',
+ output: 'type x = {f: number}',
+ errors: [
+ { messageId: 'unexpectedSpaceAfter' },
+ { messageId: 'unexpectedSpaceBefore' },
+ ],
+ },
+ {
+ code: 'type x = { f: number}',
+ output: 'type x = {f: number}',
+ errors: [{ messageId: 'unexpectedSpaceAfter' }],
+ },
+ {
+ code: 'type x = {f: number }',
+ output: 'type x = {f: number}',
+ errors: [{ messageId: 'unexpectedSpaceBefore' }],
+ },
+ {
+ code: 'type x = {f: number}',
+ output: 'type x = { f: number }',
+ options: ['always'],
+ errors: [
+ { messageId: 'requireSpaceAfter' },
+ { messageId: 'requireSpaceBefore' },
+ ],
+ },
+ {
+ code: 'type x = {f: number }',
+ output: 'type x = { f: number }',
+ options: ['always'],
+ errors: [{ messageId: 'requireSpaceAfter' }],
+ },
+ {
+ code: 'type x = { f: number}',
+ output: 'type x = { f: number }',
+ options: ['always'],
+ errors: [{ messageId: 'requireSpaceBefore' }],
+ },
+ ],
+});
diff --git a/packages/eslint-plugin/typings/eslint-rules.d.ts b/packages/eslint-plugin/typings/eslint-rules.d.ts
index ca726d6fb837..49e48541f873 100644
--- a/packages/eslint-plugin/typings/eslint-rules.d.ts
+++ b/packages/eslint-plugin/typings/eslint-rules.d.ts
@@ -830,3 +830,28 @@ declare module 'eslint/lib/rules/prefer-const' {
>;
export = rule;
}
+
+declare module 'eslint/lib/rules/object-curly-spacing' {
+ import { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils';
+
+ const rule: TSESLint.RuleModule<
+ | 'requireSpaceBefore'
+ | 'requireSpaceAfter'
+ | 'unexpectedSpaceBefore'
+ | 'unexpectedSpaceAfter',
+ [
+ 'always' | 'never',
+ {
+ arraysInObjects?: boolean;
+ objectsInObjects?: boolean;
+ }?,
+ ],
+ {
+ ObjectPattern(node: TSESTree.ObjectPattern): void;
+ ObjectExpression(node: TSESTree.ObjectExpression): void;
+ ImportDeclaration(node: TSESTree.ImportDeclaration): void;
+ ExportNamedDeclaration(node: TSESTree.ExportNamedDeclaration): void;
+ }
+ >;
+ export = rule;
+}
diff --git a/packages/experimental-utils/CHANGELOG.md b/packages/experimental-utils/CHANGELOG.md
index e7056ae1cc49..0f77d4718bd4 100644
--- a/packages/experimental-utils/CHANGELOG.md
+++ b/packages/experimental-utils/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.14.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.13.0...v4.14.0) (2021-01-18)
+
+**Note:** Version bump only for package @typescript-eslint/experimental-utils
+
+
+
+
+
# [4.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.12.0...v4.13.0) (2021-01-11)
**Note:** Version bump only for package @typescript-eslint/experimental-utils
diff --git a/packages/experimental-utils/package.json b/packages/experimental-utils/package.json
index 446f71254a7f..c084e2430103 100644
--- a/packages/experimental-utils/package.json
+++ b/packages/experimental-utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/experimental-utils",
- "version": "4.13.0",
+ "version": "4.14.0",
"description": "(Experimental) Utilities for working with TypeScript + ESLint together",
"keywords": [
"eslint",
@@ -40,9 +40,9 @@
},
"dependencies": {
"@types/json-schema": "^7.0.3",
- "@typescript-eslint/scope-manager": "4.13.0",
- "@typescript-eslint/types": "4.13.0",
- "@typescript-eslint/typescript-estree": "4.13.0",
+ "@typescript-eslint/scope-manager": "4.14.0",
+ "@typescript-eslint/types": "4.14.0",
+ "@typescript-eslint/typescript-estree": "4.14.0",
"eslint-scope": "^5.0.0",
"eslint-utils": "^2.0.0"
},
diff --git a/packages/parser/CHANGELOG.md b/packages/parser/CHANGELOG.md
index 58e16f75421a..e0443ad166f3 100644
--- a/packages/parser/CHANGELOG.md
+++ b/packages/parser/CHANGELOG.md
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.14.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.13.0...v4.14.0) (2021-01-18)
+
+
+### Features
+
+* add support for decorator metadata in scope analysis and in consistent-type-imports ([#2751](https://github.com/typescript-eslint/typescript-eslint/issues/2751)) ([445e416](https://github.com/typescript-eslint/typescript-eslint/commit/445e416878b27a54bf07c2d3b84dabd7b06e51bc)), closes [#2559](https://github.com/typescript-eslint/typescript-eslint/issues/2559)
+
+
+
+
+
# [4.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.12.0...v4.13.0) (2021-01-11)
**Note:** Version bump only for package @typescript-eslint/parser
diff --git a/packages/parser/package.json b/packages/parser/package.json
index 2fa108132913..0e9f82c64cdc 100644
--- a/packages/parser/package.json
+++ b/packages/parser/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/parser",
- "version": "4.13.0",
+ "version": "4.14.0",
"description": "An ESLint custom parser which leverages TypeScript ESTree",
"main": "dist/index.js",
"types": "dist/index.d.ts",
@@ -44,15 +44,15 @@
"eslint": "^5.0.0 || ^6.0.0 || ^7.0.0"
},
"dependencies": {
- "@typescript-eslint/scope-manager": "4.13.0",
- "@typescript-eslint/types": "4.13.0",
- "@typescript-eslint/typescript-estree": "4.13.0",
+ "@typescript-eslint/scope-manager": "4.14.0",
+ "@typescript-eslint/types": "4.14.0",
+ "@typescript-eslint/typescript-estree": "4.14.0",
"debug": "^4.1.1"
},
"devDependencies": {
"@types/glob": "*",
- "@typescript-eslint/experimental-utils": "4.13.0",
- "@typescript-eslint/shared-fixtures": "4.13.0",
+ "@typescript-eslint/experimental-utils": "4.14.0",
+ "@typescript-eslint/shared-fixtures": "4.14.0",
"glob": "*",
"typescript": "*"
},
diff --git a/packages/parser/src/parser.ts b/packages/parser/src/parser.ts
index c1422dc261b1..f304a837d8b6 100644
--- a/packages/parser/src/parser.ts
+++ b/packages/parser/src/parser.ts
@@ -164,6 +164,10 @@ function parseForESLint(
);
}
}
+ if (compilerOptions.emitDecoratorMetadata === true) {
+ analyzeOptions.emitDecoratorMetadata =
+ compilerOptions.emitDecoratorMetadata;
+ }
}
const scopeManager = analyze(ast, analyzeOptions);
diff --git a/packages/scope-manager/CHANGELOG.md b/packages/scope-manager/CHANGELOG.md
index 9323d04481a2..2cf11abdb397 100644
--- a/packages/scope-manager/CHANGELOG.md
+++ b/packages/scope-manager/CHANGELOG.md
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.14.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.13.0...v4.14.0) (2021-01-18)
+
+
+### Features
+
+* add support for decorator metadata in scope analysis and in consistent-type-imports ([#2751](https://github.com/typescript-eslint/typescript-eslint/issues/2751)) ([445e416](https://github.com/typescript-eslint/typescript-eslint/commit/445e416878b27a54bf07c2d3b84dabd7b06e51bc)), closes [#2559](https://github.com/typescript-eslint/typescript-eslint/issues/2559)
+
+
+
+
+
# [4.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.12.0...v4.13.0) (2021-01-11)
**Note:** Version bump only for package @typescript-eslint/scope-manager
diff --git a/packages/scope-manager/README.md b/packages/scope-manager/README.md
index 24ccd839dff0..7671214e8d7c 100644
--- a/packages/scope-manager/README.md
+++ b/packages/scope-manager/README.md
@@ -83,6 +83,12 @@ interface AnalyzeOptions {
* The source type of the script.
*/
sourceType?: 'script' | 'module';
+
+ /**
+ * Emit design-type metadata for decorated declarations in source.
+ * Defaults to `false`.
+ */
+ emitDecoratorMetadata?: boolean;
}
```
diff --git a/packages/scope-manager/package.json b/packages/scope-manager/package.json
index cea3d7616577..3e602a08dbff 100644
--- a/packages/scope-manager/package.json
+++ b/packages/scope-manager/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/scope-manager",
- "version": "4.13.0",
+ "version": "4.14.0",
"description": "TypeScript scope analyser for ESLint",
"keywords": [
"eslint",
@@ -39,12 +39,12 @@
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
- "@typescript-eslint/types": "4.13.0",
- "@typescript-eslint/visitor-keys": "4.13.0"
+ "@typescript-eslint/types": "4.14.0",
+ "@typescript-eslint/visitor-keys": "4.14.0"
},
"devDependencies": {
"@types/glob": "*",
- "@typescript-eslint/typescript-estree": "4.13.0",
+ "@typescript-eslint/typescript-estree": "4.14.0",
"glob": "*",
"jest-specific-snapshot": "*",
"make-dir": "*",
diff --git a/packages/scope-manager/src/analyze.ts b/packages/scope-manager/src/analyze.ts
index 9e734925da19..1aaf874049fc 100644
--- a/packages/scope-manager/src/analyze.ts
+++ b/packages/scope-manager/src/analyze.ts
@@ -61,6 +61,12 @@ interface AnalyzeOptions {
* The source type of the script.
*/
sourceType?: 'script' | 'module';
+
+ /**
+ * Emit design-type metadata for decorated declarations in source.
+ * Defaults to `false`.
+ */
+ emitDecoratorMetadata?: boolean;
}
const DEFAULT_OPTIONS: Required = {
@@ -72,6 +78,7 @@ const DEFAULT_OPTIONS: Required = {
jsxFragmentName: null,
lib: ['es2018'],
sourceType: 'script',
+ emitDecoratorMetadata: false,
};
function mapEcmaVersion(version: EcmaVersion | undefined): Lib {
@@ -106,6 +113,9 @@ function analyze(
providedOptions?.jsxFragmentName ?? DEFAULT_OPTIONS.jsxFragmentName,
sourceType: providedOptions?.sourceType ?? DEFAULT_OPTIONS.sourceType,
lib: providedOptions?.lib ?? [mapEcmaVersion(ecmaVersion)],
+ emitDecoratorMetadata:
+ providedOptions?.emitDecoratorMetadata ??
+ DEFAULT_OPTIONS.emitDecoratorMetadata,
};
// ensure the option is lower cased
diff --git a/packages/scope-manager/src/referencer/ClassVisitor.ts b/packages/scope-manager/src/referencer/ClassVisitor.ts
new file mode 100644
index 000000000000..ff758a3ae8f8
--- /dev/null
+++ b/packages/scope-manager/src/referencer/ClassVisitor.ts
@@ -0,0 +1,362 @@
+import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/types';
+import { ClassNameDefinition, ParameterDefinition } from '../definition';
+import { Referencer } from './Referencer';
+import { TypeVisitor } from './TypeVisitor';
+import { Visitor } from './Visitor';
+
+class ClassVisitor extends Visitor {
+ readonly #classNode: TSESTree.ClassDeclaration | TSESTree.ClassExpression;
+ readonly #referencer: Referencer;
+ readonly #emitDecoratorMetadata: boolean;
+
+ constructor(
+ referencer: Referencer,
+ node: TSESTree.ClassDeclaration | TSESTree.ClassExpression,
+ emitDecoratorMetadata: boolean,
+ ) {
+ super(referencer);
+ this.#referencer = referencer;
+ this.#classNode = node;
+ this.#emitDecoratorMetadata = emitDecoratorMetadata;
+ }
+
+ static visit(
+ referencer: Referencer,
+ node: TSESTree.ClassDeclaration | TSESTree.ClassExpression,
+ emitDecoratorMetadata: boolean,
+ ): void {
+ const classVisitor = new ClassVisitor(
+ referencer,
+ node,
+ emitDecoratorMetadata,
+ );
+ classVisitor.visitClass(node);
+ }
+
+ ///////////////////
+ // Visit helpers //
+ ///////////////////
+
+ protected visitClass(
+ node: TSESTree.ClassDeclaration | TSESTree.ClassExpression,
+ ): void {
+ if (node.type === AST_NODE_TYPES.ClassDeclaration && node.id) {
+ this.#referencer
+ .currentScope()
+ .defineIdentifier(node.id, new ClassNameDefinition(node.id, node));
+ }
+
+ node.decorators?.forEach(d => this.visit(d));
+
+ this.#referencer.scopeManager.nestClassScope(node);
+
+ if (node.id) {
+ // define the class name again inside the new scope
+ // references to the class should not resolve directly to the parent class
+ this.#referencer
+ .currentScope()
+ .defineIdentifier(node.id, new ClassNameDefinition(node.id, node));
+ }
+
+ this.#referencer.visit(node.superClass);
+
+ // visit the type param declarations
+ this.visitType(node.typeParameters);
+ // then the usages
+ this.visitType(node.superTypeParameters);
+ node.implements?.forEach(imp => this.visitType(imp));
+
+ this.visit(node.body);
+
+ this.#referencer.close(node);
+ }
+
+ protected visitClassProperty(
+ node: TSESTree.TSAbstractClassProperty | TSESTree.ClassProperty,
+ ): void {
+ this.visitProperty(node);
+ /**
+ * class A {
+ * @meta // <--- check this
+ * foo: Type;
+ * }
+ */
+ this.visitMetadataType(node.typeAnnotation, !!node.decorators);
+ }
+
+ protected visitFunctionParameterTypeAnnotation(
+ node: TSESTree.Parameter,
+ withDecorators: boolean,
+ ): void {
+ if ('typeAnnotation' in node) {
+ this.visitMetadataType(node.typeAnnotation, withDecorators);
+ } else if (node.type === AST_NODE_TYPES.AssignmentPattern) {
+ this.visitMetadataType(node.left.typeAnnotation, withDecorators);
+ } else if (node.type === AST_NODE_TYPES.TSParameterProperty) {
+ this.visitFunctionParameterTypeAnnotation(node.parameter, withDecorators);
+ }
+ }
+
+ protected visitMethodFunction(
+ node: TSESTree.FunctionExpression,
+ methodNode: TSESTree.MethodDefinition,
+ ): void {
+ if (node.id) {
+ // FunctionExpression with name creates its special scope;
+ // FunctionExpressionNameScope.
+ this.#referencer.scopeManager.nestFunctionExpressionNameScope(node);
+ }
+
+ // Consider this function is in the MethodDefinition.
+ this.#referencer.scopeManager.nestFunctionScope(node, true);
+
+ /**
+ * class A {
+ * @meta // <--- check this
+ * foo(a: Type) {}
+ *
+ * @meta // <--- check this
+ * foo(): Type {}
+ * }
+ */
+ let withMethodDecorators = !!methodNode.decorators;
+ /**
+ * class A {
+ * foo(
+ * @meta // <--- check this
+ * a: Type
+ * ) {}
+ *
+ * set foo(
+ * @meta // <--- EXCEPT this. TS do nothing for this
+ * a: Type
+ * ) {}
+ * }
+ */
+ withMethodDecorators =
+ withMethodDecorators ||
+ (methodNode.kind !== 'set' &&
+ node.params.some(param => param.decorators));
+ if (!withMethodDecorators && methodNode.kind === 'set') {
+ const keyName = getLiteralMethodKeyName(methodNode);
+
+ /**
+ * class A {
+ * @meta // <--- check this
+ * get a() {}
+ * set ['a'](v: Type) {}
+ * }
+ */
+ if (
+ keyName !== null &&
+ this.#classNode.body.body.find(
+ (node): node is TSESTree.MethodDefinition =>
+ node !== methodNode &&
+ node.type === AST_NODE_TYPES.MethodDefinition &&
+ // Node must both be static or not
+ node.static === methodNode.static &&
+ getLiteralMethodKeyName(node) === keyName,
+ )?.decorators
+ ) {
+ withMethodDecorators = true;
+ }
+ }
+
+ /**
+ * @meta // <--- check this
+ * class A {
+ * constructor(a: Type) {}
+ * }
+ */
+ if (
+ !withMethodDecorators &&
+ methodNode.kind === 'constructor' &&
+ this.#classNode.decorators
+ ) {
+ withMethodDecorators = true;
+ }
+
+ // Process parameter declarations.
+ for (const param of node.params) {
+ this.visitPattern(
+ param,
+ (pattern, info) => {
+ this.#referencer
+ .currentScope()
+ .defineIdentifier(
+ pattern,
+ new ParameterDefinition(pattern, node, info.rest),
+ );
+
+ this.#referencer.referencingDefaultValue(
+ pattern,
+ info.assignments,
+ null,
+ true,
+ );
+ },
+ { processRightHandNodes: true },
+ );
+ this.visitFunctionParameterTypeAnnotation(param, withMethodDecorators);
+ param.decorators?.forEach(d => this.visit(d));
+ }
+
+ this.visitMetadataType(node.returnType, withMethodDecorators);
+ this.visitType(node.typeParameters);
+
+ // In TypeScript there are a number of function-like constructs which have no body,
+ // so check it exists before traversing
+ if (node.body) {
+ // Skip BlockStatement to prevent creating BlockStatement scope.
+ if (node.body.type === AST_NODE_TYPES.BlockStatement) {
+ this.#referencer.visitChildren(node.body);
+ } else {
+ this.#referencer.visit(node.body);
+ }
+ }
+
+ this.#referencer.close(node);
+ }
+
+ protected visitProperty(
+ node:
+ | TSESTree.ClassProperty
+ | TSESTree.TSAbstractClassProperty
+ | TSESTree.TSAbstractMethodDefinition,
+ ): void {
+ if (node.computed) {
+ this.#referencer.visit(node.key);
+ }
+
+ this.#referencer.visit(node.value);
+
+ if ('decorators' in node) {
+ node.decorators?.forEach(d => this.#referencer.visit(d));
+ }
+ }
+
+ protected visitMethod(node: TSESTree.MethodDefinition): void {
+ if (node.computed) {
+ this.#referencer.visit(node.key);
+ }
+
+ if (node.value.type === AST_NODE_TYPES.FunctionExpression) {
+ this.visitMethodFunction(node.value, node);
+ } else {
+ this.#referencer.visit(node.value);
+ }
+
+ if ('decorators' in node) {
+ node.decorators?.forEach(d => this.#referencer.visit(d));
+ }
+ }
+
+ protected visitType(node: TSESTree.Node | null | undefined): void {
+ if (!node) {
+ return;
+ }
+ TypeVisitor.visit(this.#referencer, node);
+ }
+
+ protected visitMetadataType(
+ node: TSESTree.TSTypeAnnotation | null | undefined,
+ withDecorators: boolean,
+ ): void {
+ if (!node) {
+ return;
+ }
+ // emit decorators metadata only work for TSTypeReference in ClassDeclaration
+ if (
+ this.#classNode.type === AST_NODE_TYPES.ClassDeclaration &&
+ !this.#classNode.declare &&
+ node.typeAnnotation.type === AST_NODE_TYPES.TSTypeReference &&
+ this.#emitDecoratorMetadata
+ ) {
+ let identifier: TSESTree.Identifier;
+ if (
+ node.typeAnnotation.typeName.type === AST_NODE_TYPES.TSQualifiedName
+ ) {
+ let iter = node.typeAnnotation.typeName;
+ while (iter.left.type === AST_NODE_TYPES.TSQualifiedName) {
+ iter = iter.left;
+ }
+ identifier = iter.left;
+ } else {
+ identifier = node.typeAnnotation.typeName;
+ }
+
+ if (withDecorators) {
+ return this.#referencer
+ .currentScope()
+ .referenceDualValueType(identifier);
+ }
+ }
+ this.visitType(node);
+ }
+
+ /////////////////////
+ // Visit selectors //
+ /////////////////////
+
+ protected ClassProperty(node: TSESTree.ClassProperty): void {
+ this.visitClassProperty(node);
+ }
+
+ protected MethodDefinition(node: TSESTree.MethodDefinition): void {
+ this.visitMethod(node);
+ }
+
+ protected TSAbstractClassProperty(
+ node: TSESTree.TSAbstractClassProperty,
+ ): void {
+ this.visitClassProperty(node);
+ }
+
+ protected TSAbstractMethodDefinition(
+ node: TSESTree.TSAbstractMethodDefinition,
+ ): void {
+ this.visitProperty(node);
+ }
+
+ protected Identifier(node: TSESTree.Identifier): void {
+ this.#referencer.visit(node);
+ }
+}
+
+/**
+ * Only if key is one of [identifier, string, number], ts will combine metadata of accessors .
+ * class A {
+ * get a() {}
+ * set ['a'](v: Type) {}
+ *
+ * get [1]() {}
+ * set [1](v: Type) {}
+ *
+ * // Following won't be combined
+ * get [key]() {}
+ * set [key](v: Type) {}
+ *
+ * get [true]() {}
+ * set [true](v: Type) {}
+ *
+ * get ['a'+'b']() {}
+ * set ['a'+'b']() {}
+ * }
+ */
+function getLiteralMethodKeyName(
+ node: TSESTree.MethodDefinition,
+): string | number | null {
+ if (node.computed && node.key.type === AST_NODE_TYPES.Literal) {
+ if (
+ typeof node.key.value === 'string' ||
+ typeof node.key.value === 'number'
+ ) {
+ return node.key.value;
+ }
+ } else if (!node.computed && node.key.type === AST_NODE_TYPES.Identifier) {
+ return node.key.name;
+ }
+ return null;
+}
+
+export { ClassVisitor };
diff --git a/packages/scope-manager/src/referencer/Referencer.ts b/packages/scope-manager/src/referencer/Referencer.ts
index a36dcfc9c1a0..b0e7557ee7eb 100644
--- a/packages/scope-manager/src/referencer/Referencer.ts
+++ b/packages/scope-manager/src/referencer/Referencer.ts
@@ -1,4 +1,5 @@
import { AST_NODE_TYPES, Lib, TSESTree } from '@typescript-eslint/types';
+import { ClassVisitor } from './ClassVisitor';
import { ExportVisitor } from './ExportVisitor';
import { ImportVisitor } from './ImportVisitor';
import { PatternVisitor } from './PatternVisitor';
@@ -9,7 +10,6 @@ import { Visitor, VisitorOptions } from './Visitor';
import { assert } from '../assert';
import {
CatchClauseDefinition,
- ClassNameDefinition,
FunctionNameDefinition,
ImportBindingDefinition,
ParameterDefinition,
@@ -25,16 +25,17 @@ interface ReferencerOptions extends VisitorOptions {
jsxPragma: string;
jsxFragmentName: string | null;
lib: Lib[];
+ emitDecoratorMetadata: boolean;
}
// Referencing variables and creating bindings.
class Referencer extends Visitor {
- #isInnerMethodDefinition: boolean;
#jsxPragma: string;
#jsxFragmentName: string | null;
#hasReferencedJsxFactory = false;
#hasReferencedJsxFragmentFactory = false;
#lib: Lib[];
+ readonly #emitDecoratorMetadata: boolean;
public readonly scopeManager: ScopeManager;
constructor(options: ReferencerOptions, scopeManager: ScopeManager) {
@@ -43,7 +44,7 @@ class Referencer extends Visitor {
this.#jsxPragma = options.jsxPragma;
this.#jsxFragmentName = options.jsxFragmentName;
this.#lib = options.lib;
- this.#isInnerMethodDefinition = false;
+ this.#emitDecoratorMetadata = options.emitDecoratorMetadata;
}
public currentScope(): Scope;
@@ -63,22 +64,7 @@ class Referencer extends Visitor {
}
}
- protected pushInnerMethodDefinition(
- isInnerMethodDefinition: boolean,
- ): boolean {
- const previous = this.#isInnerMethodDefinition;
-
- this.#isInnerMethodDefinition = isInnerMethodDefinition;
- return previous;
- }
-
- protected popInnerMethodDefinition(
- isInnerMethodDefinition: boolean | undefined,
- ): void {
- this.#isInnerMethodDefinition = !!isInnerMethodDefinition;
- }
-
- protected referencingDefaultValue(
+ public referencingDefaultValue(
pattern: TSESTree.Identifier,
assignments: (TSESTree.AssignmentExpression | TSESTree.AssignmentPattern)[],
maybeImplicitGlobal: ReferenceImplicitGlobal | null,
@@ -162,44 +148,7 @@ class Referencer extends Visitor {
protected visitClass(
node: TSESTree.ClassDeclaration | TSESTree.ClassExpression,
): void {
- if (node.type === AST_NODE_TYPES.ClassDeclaration && node.id) {
- this.currentScope().defineIdentifier(
- node.id,
- new ClassNameDefinition(node.id, node),
- );
- }
-
- node.decorators?.forEach(d => this.visit(d));
-
- this.scopeManager.nestClassScope(node);
-
- if (node.id) {
- // define the class name again inside the new scope
- // references to the class should not resolve directly to the parent class
- this.currentScope().defineIdentifier(
- node.id,
- new ClassNameDefinition(node.id, node),
- );
- }
-
- this.visit(node.superClass);
-
- // visit the type param declarations
- this.visitType(node.typeParameters);
- // then the usages
- this.visitType(node.superTypeParameters);
- node.implements?.forEach(imp => this.visitType(imp));
-
- this.visit(node.body);
-
- this.close(node);
- }
-
- protected visitClassProperty(
- node: TSESTree.TSAbstractClassProperty | TSESTree.ClassProperty,
- ): void {
- this.visitProperty(node);
- this.visitType(node.typeAnnotation);
+ ClassVisitor.visit(this, node, this.#emitDecoratorMetadata);
}
protected visitForIn(
@@ -296,7 +245,7 @@ class Referencer extends Visitor {
}
// Consider this function is in the MethodDefinition.
- this.scopeManager.nestFunctionScope(node, this.#isInnerMethodDefinition);
+ this.scopeManager.nestFunctionScope(node, false);
// Process parameter declarations.
for (const param of node.params) {
@@ -333,32 +282,12 @@ class Referencer extends Visitor {
this.close(node);
}
- protected visitProperty(
- node:
- | TSESTree.ClassProperty
- | TSESTree.MethodDefinition
- | TSESTree.Property
- | TSESTree.TSAbstractClassProperty
- | TSESTree.TSAbstractMethodDefinition,
- ): void {
- let previous;
-
+ protected visitProperty(node: TSESTree.Property): void {
if (node.computed) {
this.visit(node.key);
}
- const isMethodDefinition = node.type === AST_NODE_TYPES.MethodDefinition;
- if (isMethodDefinition) {
- previous = this.pushInnerMethodDefinition(true);
- }
this.visit(node.value);
- if (isMethodDefinition) {
- this.popInnerMethodDefinition(previous);
- }
-
- if ('decorators' in node) {
- node.decorators?.forEach(d => this.visit(d));
- }
}
protected visitType(node: TSESTree.Node | null | undefined): void {
@@ -487,10 +416,6 @@ class Referencer extends Visitor {
this.visitClass(node);
}
- protected ClassProperty(node: TSESTree.ClassProperty): void {
- this.visitClassProperty(node);
- }
-
protected ContinueStatement(): void {
// don't reference the continue statement's label
}
@@ -622,10 +547,6 @@ class Referencer extends Visitor {
// meta properties all builtin globals
}
- protected MethodDefinition(node: TSESTree.MethodDefinition): void {
- this.visitProperty(node);
- }
-
protected NewExpression(node: TSESTree.NewExpression): void {
this.visitChildren(node, ['typeParameters']);
this.visitType(node.typeParameters);
@@ -682,18 +603,6 @@ class Referencer extends Visitor {
this.visitType(node.typeParameters);
}
- protected TSAbstractClassProperty(
- node: TSESTree.TSAbstractClassProperty,
- ): void {
- this.visitClassProperty(node);
- }
-
- protected TSAbstractMethodDefinition(
- node: TSESTree.TSAbstractMethodDefinition,
- ): void {
- this.visitProperty(node);
- }
-
protected TSAsExpression(node: TSESTree.TSAsExpression): void {
this.visitTypeAssertion(node);
}
diff --git a/packages/scope-manager/src/referencer/TypeVisitor.ts b/packages/scope-manager/src/referencer/TypeVisitor.ts
index 75b5f614e32f..155aea5c5d93 100644
--- a/packages/scope-manager/src/referencer/TypeVisitor.ts
+++ b/packages/scope-manager/src/referencer/TypeVisitor.ts
@@ -260,6 +260,11 @@ class TypeVisitor extends Visitor {
this.#referencer.currentScope().referenceValue(expr);
}
}
+
+ protected TSTypeAnnotation(node: TSESTree.TSTypeAnnotation): void {
+ // check
+ this.visitChildren(node);
+ }
}
export { TypeVisitor };
diff --git a/packages/scope-manager/tests/eslint-scope/references.test.ts b/packages/scope-manager/tests/eslint-scope/references.test.ts
index 573d0ce84b30..5f07dd671e93 100644
--- a/packages/scope-manager/tests/eslint-scope/references.test.ts
+++ b/packages/scope-manager/tests/eslint-scope/references.test.ts
@@ -540,4 +540,119 @@ describe('References:', () => {
}),
);
});
+
+ describe('When emitDecoratorMetadata is true', () => {
+ it('check type referenced by decorator metadata', () => {
+ const { scopeManager } = parseAndAnalyze(
+ `
+ @deco
+ class A {
+ property: Type1;
+ @deco
+ propertyWithDeco: a.Foo;
+
+ set foo(@deco a: SetterType) {}
+
+ constructor(foo: b.Foo) {}
+
+ foo1(@deco a: Type2, b: Type0) {}
+
+ @deco
+ foo2(a: Type3) {}
+
+ @deco
+ foo3(): Type4 {}
+
+ set ['a'](a: Type5) {}
+ set [0](a: Type6) {}
+ @deco
+ get a() {}
+ @deco
+ get [0]() {}
+ }
+
+ const keyName = 'foo';
+ class B {
+ constructor(@deco foo: c.Foo) {}
+
+ set [keyName](a: Type) {}
+ @deco
+ get [keyName]() {}
+ }
+
+ declare class C {
+ @deco
+ foo(): TypeC;
+ }
+ `,
+ {
+ emitDecoratorMetadata: true,
+ },
+ );
+
+ const classAScope = scopeManager.globalScope!.childScopes[0];
+ const propertyTypeRef = classAScope.references[2];
+ expect(propertyTypeRef.identifier.name).toBe('a');
+ expect(propertyTypeRef.isTypeReference).toBe(true);
+ expect(propertyTypeRef.isValueReference).toBe(true);
+
+ const setterParamTypeRef = classAScope.childScopes[0].references[0];
+ expect(setterParamTypeRef.identifier.name).toBe('SetterType');
+ expect(setterParamTypeRef.isTypeReference).toBe(true);
+ expect(setterParamTypeRef.isValueReference).toBe(false);
+
+ const constructorParamTypeRef = classAScope.childScopes[1].references[0];
+ expect(constructorParamTypeRef.identifier.name).toBe('b');
+ expect(constructorParamTypeRef.isTypeReference).toBe(true);
+ expect(constructorParamTypeRef.isValueReference).toBe(true);
+
+ const methodParamTypeRef = classAScope.childScopes[2].references[0];
+ expect(methodParamTypeRef.identifier.name).toBe('Type2');
+ expect(methodParamTypeRef.isTypeReference).toBe(true);
+ expect(methodParamTypeRef.isValueReference).toBe(true);
+ const methodParamTypeRef0 = classAScope.childScopes[2].references[2];
+ expect(methodParamTypeRef0.identifier.name).toBe('Type0');
+ expect(methodParamTypeRef0.isTypeReference).toBe(true);
+ expect(methodParamTypeRef0.isValueReference).toBe(true);
+
+ const methodParamTypeRef1 = classAScope.childScopes[3].references[0];
+ expect(methodParamTypeRef1.identifier.name).toBe('Type3');
+ expect(methodParamTypeRef1.isTypeReference).toBe(true);
+ expect(methodParamTypeRef1.isValueReference).toBe(true);
+
+ const methodReturnTypeRef = classAScope.childScopes[4].references[0];
+ expect(methodReturnTypeRef.identifier.name).toBe('Type4');
+ expect(methodReturnTypeRef.isTypeReference).toBe(true);
+ expect(methodReturnTypeRef.isValueReference).toBe(true);
+
+ const setterParamTypeRef1 = classAScope.childScopes[5].references[0];
+ expect(setterParamTypeRef1.identifier.name).toBe('Type5');
+ expect(setterParamTypeRef1.isTypeReference).toBe(true);
+ expect(setterParamTypeRef1.isValueReference).toBe(true);
+
+ const setterParamTypeRef2 = classAScope.childScopes[6].references[0];
+ expect(setterParamTypeRef2.identifier.name).toBe('Type6');
+ expect(setterParamTypeRef2.isTypeReference).toBe(true);
+ expect(setterParamTypeRef2.isValueReference).toBe(true);
+
+ const classBScope = scopeManager.globalScope!.childScopes[1];
+
+ const constructorParamTypeRef1 = classBScope.childScopes[0].references[0];
+ expect(constructorParamTypeRef1.identifier.name).toBe('c');
+ expect(constructorParamTypeRef1.isTypeReference).toBe(true);
+ expect(constructorParamTypeRef1.isValueReference).toBe(true);
+
+ const setterParamTypeRef3 = classBScope.childScopes[1].references[0];
+ expect(setterParamTypeRef3.identifier.name).toBe('Type');
+ expect(setterParamTypeRef3.isTypeReference).toBe(true);
+ expect(setterParamTypeRef3.isValueReference).toBe(false);
+
+ const classCScope = scopeManager.globalScope!.childScopes[2];
+
+ const methodReturnTypeRef1 = classCScope.childScopes[0].references[0];
+ expect(methodReturnTypeRef1.identifier.name).toBe('TypeC');
+ expect(methodReturnTypeRef1.isTypeReference).toBe(true);
+ expect(methodReturnTypeRef1.isValueReference).toBe(false);
+ });
+ });
});
diff --git a/packages/scope-manager/tests/fixtures.test.ts b/packages/scope-manager/tests/fixtures.test.ts
index 08eea4f4995e..69833caa7faa 100644
--- a/packages/scope-manager/tests/fixtures.test.ts
+++ b/packages/scope-manager/tests/fixtures.test.ts
@@ -44,6 +44,7 @@ const ALLOWED_OPTIONS: Map = new Map<
['jsxPragma', ['string']],
['jsxFragmentName', ['string']],
['sourceType', ['string', new Set(['module', 'script'])]],
+ ['emitDecoratorMetadata', ['boolean']],
]);
function nestDescribe(
diff --git a/packages/scope-manager/tests/fixtures/class/emit-metadata/accessor-deco.ts b/packages/scope-manager/tests/fixtures/class/emit-metadata/accessor-deco.ts
new file mode 100644
index 000000000000..41c40b287703
--- /dev/null
+++ b/packages/scope-manager/tests/fixtures/class/emit-metadata/accessor-deco.ts
@@ -0,0 +1,23 @@
+//// @emitDecoratorMetadata = true
+
+function deco(...param: any) {}
+
+class T {}
+const keyName = 'foo';
+
+class A {
+ @deco
+ set b(b: T) {}
+
+ set ['a'](a: T) {}
+ @deco
+ get a() {}
+
+ set [0](a: T) {}
+ @deco
+ get [0]() {}
+
+ set [keyName](a: T) {}
+ @deco
+ get [keyName]() {}
+}
diff --git a/packages/scope-manager/tests/fixtures/class/emit-metadata/accessor-deco.ts.shot b/packages/scope-manager/tests/fixtures/class/emit-metadata/accessor-deco.ts.shot
new file mode 100644
index 000000000000..f535f8e61be2
--- /dev/null
+++ b/packages/scope-manager/tests/fixtures/class/emit-metadata/accessor-deco.ts.shot
@@ -0,0 +1,470 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`class emit-metadata accessor-deco 1`] = `
+ScopeManager {
+ variables: Array [
+ ImplicitGlobalConstTypeVariable,
+ Variable$2 {
+ defs: Array [
+ FunctionNameDefinition$1 {
+ name: Identifier<"deco">,
+ node: FunctionDeclaration$1,
+ },
+ ],
+ name: "deco",
+ references: Array [
+ Reference$3 {
+ identifier: Identifier<"deco">,
+ isRead: true,
+ isTypeReference: false,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$2,
+ },
+ Reference$5 {
+ identifier: Identifier<"deco">,
+ isRead: true,
+ isTypeReference: false,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$2,
+ },
+ Reference$7 {
+ identifier: Identifier<"deco">,
+ isRead: true,
+ isTypeReference: false,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$2,
+ },
+ Reference$11 {
+ identifier: Identifier<"deco">,
+ isRead: true,
+ isTypeReference: false,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$2,
+ },
+ ],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$3 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$4 {
+ defs: Array [
+ ParameterDefinition$2 {
+ name: Identifier<"param">,
+ node: FunctionDeclaration$1,
+ },
+ ],
+ name: "param",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$5 {
+ defs: Array [
+ ClassNameDefinition$3 {
+ name: Identifier<"T">,
+ node: ClassDeclaration$2,
+ },
+ ],
+ name: "T",
+ references: Array [
+ Reference$2 {
+ identifier: Identifier<"T">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$5,
+ },
+ Reference$4 {
+ identifier: Identifier<"T">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$5,
+ },
+ Reference$6 {
+ identifier: Identifier<"T">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$5,
+ },
+ Reference$9 {
+ identifier: Identifier<"T">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: false,
+ isWrite: false,
+ resolved: Variable$5,
+ },
+ ],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$6 {
+ defs: Array [
+ ClassNameDefinition$4 {
+ name: Identifier<"T">,
+ node: ClassDeclaration$2,
+ },
+ ],
+ name: "T",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$7 {
+ defs: Array [
+ VariableDefinition$5 {
+ name: Identifier<"keyName">,
+ node: VariableDeclarator$3,
+ },
+ ],
+ name: "keyName",
+ references: Array [
+ Reference$1 {
+ identifier: Identifier<"keyName">,
+ init: true,
+ isRead: false,
+ isTypeReference: false,
+ isValueReference: true,
+ isWrite: true,
+ resolved: Variable$7,
+ writeExpr: Literal$4,
+ },
+ Reference$8 {
+ identifier: Identifier<"keyName">,
+ isRead: true,
+ isTypeReference: false,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$7,
+ },
+ Reference$10 {
+ identifier: Identifier<"keyName">,
+ isRead: true,
+ isTypeReference: false,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$7,
+ },
+ ],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$8 {
+ defs: Array [
+ ClassNameDefinition$6 {
+ name: Identifier<"A">,
+ node: ClassDeclaration$5,
+ },
+ ],
+ name: "A",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$9 {
+ defs: Array [
+ ClassNameDefinition$7 {
+ name: Identifier<"A">,
+ node: ClassDeclaration$5,
+ },
+ ],
+ name: "A",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$10 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$11 {
+ defs: Array [
+ ParameterDefinition$8 {
+ name: Identifier<"b">,
+ node: FunctionExpression$6,
+ },
+ ],
+ name: "b",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$12 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$13 {
+ defs: Array [
+ ParameterDefinition$9 {
+ name: Identifier<"a">,
+ node: FunctionExpression$7,
+ },
+ ],
+ name: "a",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$14 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$15 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$16 {
+ defs: Array [
+ ParameterDefinition$10 {
+ name: Identifier<"a">,
+ node: FunctionExpression$8,
+ },
+ ],
+ name: "a",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$17 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$18 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$19 {
+ defs: Array [
+ ParameterDefinition$11 {
+ name: Identifier<"a">,
+ node: FunctionExpression$9,
+ },
+ ],
+ name: "a",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$20 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ ],
+ scopes: Array [
+ GlobalScope$1 {
+ block: Program$10,
+ isStrict: false,
+ references: Array [
+ Reference$1,
+ ],
+ set: Map {
+ "const" => ImplicitGlobalConstTypeVariable,
+ "deco" => Variable$2,
+ "T" => Variable$5,
+ "keyName" => Variable$7,
+ "A" => Variable$8,
+ },
+ type: "global",
+ upper: null,
+ variables: Array [
+ ImplicitGlobalConstTypeVariable,
+ Variable$2,
+ Variable$5,
+ Variable$7,
+ Variable$8,
+ ],
+ },
+ FunctionScope$2 {
+ block: FunctionDeclaration$1,
+ isStrict: false,
+ references: Array [],
+ set: Map {
+ "arguments" => Variable$3,
+ "param" => Variable$4,
+ },
+ type: "function",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$3,
+ Variable$4,
+ ],
+ },
+ ClassScope$3 {
+ block: ClassDeclaration$2,
+ isStrict: true,
+ references: Array [],
+ set: Map {
+ "T" => Variable$6,
+ },
+ type: "class",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$6,
+ ],
+ },
+ ClassScope$4 {
+ block: ClassDeclaration$5,
+ isStrict: true,
+ references: Array [
+ Reference$3,
+ Reference$5,
+ Reference$7,
+ Reference$8,
+ Reference$10,
+ Reference$11,
+ ],
+ set: Map {
+ "A" => Variable$9,
+ },
+ type: "class",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$9,
+ ],
+ },
+ FunctionScope$5 {
+ block: FunctionExpression$6,
+ isStrict: true,
+ references: Array [
+ Reference$2,
+ ],
+ set: Map {
+ "arguments" => Variable$10,
+ "b" => Variable$11,
+ },
+ type: "function",
+ upper: ClassScope$4,
+ variables: Array [
+ Variable$10,
+ Variable$11,
+ ],
+ },
+ FunctionScope$6 {
+ block: FunctionExpression$7,
+ isStrict: true,
+ references: Array [
+ Reference$4,
+ ],
+ set: Map {
+ "arguments" => Variable$12,
+ "a" => Variable$13,
+ },
+ type: "function",
+ upper: ClassScope$4,
+ variables: Array [
+ Variable$12,
+ Variable$13,
+ ],
+ },
+ FunctionScope$7 {
+ block: FunctionExpression$11,
+ isStrict: true,
+ references: Array [],
+ set: Map {
+ "arguments" => Variable$14,
+ },
+ type: "function",
+ upper: ClassScope$4,
+ variables: Array [
+ Variable$14,
+ ],
+ },
+ FunctionScope$8 {
+ block: FunctionExpression$8,
+ isStrict: true,
+ references: Array [
+ Reference$6,
+ ],
+ set: Map {
+ "arguments" => Variable$15,
+ "a" => Variable$16,
+ },
+ type: "function",
+ upper: ClassScope$4,
+ variables: Array [
+ Variable$15,
+ Variable$16,
+ ],
+ },
+ FunctionScope$9 {
+ block: FunctionExpression$12,
+ isStrict: true,
+ references: Array [],
+ set: Map {
+ "arguments" => Variable$17,
+ },
+ type: "function",
+ upper: ClassScope$4,
+ variables: Array [
+ Variable$17,
+ ],
+ },
+ FunctionScope$10 {
+ block: FunctionExpression$9,
+ isStrict: true,
+ references: Array [
+ Reference$9,
+ ],
+ set: Map {
+ "arguments" => Variable$18,
+ "a" => Variable$19,
+ },
+ type: "function",
+ upper: ClassScope$4,
+ variables: Array [
+ Variable$18,
+ Variable$19,
+ ],
+ },
+ FunctionScope$11 {
+ block: FunctionExpression$13,
+ isStrict: true,
+ references: Array [],
+ set: Map {
+ "arguments" => Variable$20,
+ },
+ type: "function",
+ upper: ClassScope$4,
+ variables: Array [
+ Variable$20,
+ ],
+ },
+ ],
+}
+`;
diff --git a/packages/scope-manager/tests/fixtures/class/emit-metadata/method-deco.ts b/packages/scope-manager/tests/fixtures/class/emit-metadata/method-deco.ts
new file mode 100644
index 000000000000..ab030a5b0b9a
--- /dev/null
+++ b/packages/scope-manager/tests/fixtures/class/emit-metadata/method-deco.ts
@@ -0,0 +1,11 @@
+//// @emitDecoratorMetadata = true
+
+function deco(...param: any) {}
+
+class T {}
+
+class A {
+ foo(a: T): T {}
+ @deco
+ foo1(a: T, b: T): T {}
+}
diff --git a/packages/scope-manager/tests/fixtures/class/emit-metadata/method-deco.ts.shot b/packages/scope-manager/tests/fixtures/class/emit-metadata/method-deco.ts.shot
new file mode 100644
index 000000000000..74707c85f354
--- /dev/null
+++ b/packages/scope-manager/tests/fixtures/class/emit-metadata/method-deco.ts.shot
@@ -0,0 +1,291 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`class emit-metadata method-deco 1`] = `
+ScopeManager {
+ variables: Array [
+ ImplicitGlobalConstTypeVariable,
+ Variable$2 {
+ defs: Array [
+ FunctionNameDefinition$1 {
+ name: Identifier<"deco">,
+ node: FunctionDeclaration$1,
+ },
+ ],
+ name: "deco",
+ references: Array [
+ Reference$6 {
+ identifier: Identifier<"deco">,
+ isRead: true,
+ isTypeReference: false,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$2,
+ },
+ ],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$3 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$4 {
+ defs: Array [
+ ParameterDefinition$2 {
+ name: Identifier<"param">,
+ node: FunctionDeclaration$1,
+ },
+ ],
+ name: "param",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$5 {
+ defs: Array [
+ ClassNameDefinition$3 {
+ name: Identifier<"T">,
+ node: ClassDeclaration$2,
+ },
+ ],
+ name: "T",
+ references: Array [
+ Reference$1 {
+ identifier: Identifier<"T">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: false,
+ isWrite: false,
+ resolved: Variable$5,
+ },
+ Reference$2 {
+ identifier: Identifier<"T">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: false,
+ isWrite: false,
+ resolved: Variable$5,
+ },
+ Reference$3 {
+ identifier: Identifier<"T">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$5,
+ },
+ Reference$4 {
+ identifier: Identifier<"T">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$5,
+ },
+ Reference$5 {
+ identifier: Identifier<"T">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$5,
+ },
+ ],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$6 {
+ defs: Array [
+ ClassNameDefinition$4 {
+ name: Identifier<"T">,
+ node: ClassDeclaration$2,
+ },
+ ],
+ name: "T",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$7 {
+ defs: Array [
+ ClassNameDefinition$5 {
+ name: Identifier<"A">,
+ node: ClassDeclaration$3,
+ },
+ ],
+ name: "A",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$8 {
+ defs: Array [
+ ClassNameDefinition$6 {
+ name: Identifier<"A">,
+ node: ClassDeclaration$3,
+ },
+ ],
+ name: "A",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$9 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$10 {
+ defs: Array [
+ ParameterDefinition$7 {
+ name: Identifier<"a">,
+ node: FunctionExpression$4,
+ },
+ ],
+ name: "a",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$11 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$12 {
+ defs: Array [
+ ParameterDefinition$8 {
+ name: Identifier<"a">,
+ node: FunctionExpression$5,
+ },
+ ],
+ name: "a",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$13 {
+ defs: Array [
+ ParameterDefinition$9 {
+ name: Identifier<"b">,
+ node: FunctionExpression$5,
+ },
+ ],
+ name: "b",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ ],
+ scopes: Array [
+ GlobalScope$1 {
+ block: Program$6,
+ isStrict: false,
+ references: Array [],
+ set: Map {
+ "const" => ImplicitGlobalConstTypeVariable,
+ "deco" => Variable$2,
+ "T" => Variable$5,
+ "A" => Variable$7,
+ },
+ type: "global",
+ upper: null,
+ variables: Array [
+ ImplicitGlobalConstTypeVariable,
+ Variable$2,
+ Variable$5,
+ Variable$7,
+ ],
+ },
+ FunctionScope$2 {
+ block: FunctionDeclaration$1,
+ isStrict: false,
+ references: Array [],
+ set: Map {
+ "arguments" => Variable$3,
+ "param" => Variable$4,
+ },
+ type: "function",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$3,
+ Variable$4,
+ ],
+ },
+ ClassScope$3 {
+ block: ClassDeclaration$2,
+ isStrict: true,
+ references: Array [],
+ set: Map {
+ "T" => Variable$6,
+ },
+ type: "class",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$6,
+ ],
+ },
+ ClassScope$4 {
+ block: ClassDeclaration$3,
+ isStrict: true,
+ references: Array [
+ Reference$6,
+ ],
+ set: Map {
+ "A" => Variable$8,
+ },
+ type: "class",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$8,
+ ],
+ },
+ FunctionScope$5 {
+ block: FunctionExpression$4,
+ isStrict: true,
+ references: Array [
+ Reference$1,
+ Reference$2,
+ ],
+ set: Map {
+ "arguments" => Variable$9,
+ "a" => Variable$10,
+ },
+ type: "function",
+ upper: ClassScope$4,
+ variables: Array [
+ Variable$9,
+ Variable$10,
+ ],
+ },
+ FunctionScope$6 {
+ block: FunctionExpression$5,
+ isStrict: true,
+ references: Array [
+ Reference$3,
+ Reference$4,
+ Reference$5,
+ ],
+ set: Map {
+ "arguments" => Variable$11,
+ "a" => Variable$12,
+ "b" => Variable$13,
+ },
+ type: "function",
+ upper: ClassScope$4,
+ variables: Array [
+ Variable$11,
+ Variable$12,
+ Variable$13,
+ ],
+ },
+ ],
+}
+`;
diff --git a/packages/scope-manager/tests/fixtures/class/emit-metadata/nested-class-both.ts b/packages/scope-manager/tests/fixtures/class/emit-metadata/nested-class-both.ts
new file mode 100644
index 000000000000..39fa3c402325
--- /dev/null
+++ b/packages/scope-manager/tests/fixtures/class/emit-metadata/nested-class-both.ts
@@ -0,0 +1,15 @@
+//// @emitDecoratorMetadata = true
+
+function deco(...param: any) {}
+
+class T {}
+
+@deco
+class A {
+ constructor(foo: T) {
+ @deco
+ class B {
+ constructor(bar: T) {}
+ }
+ }
+}
diff --git a/packages/scope-manager/tests/fixtures/class/emit-metadata/nested-class-both.ts.shot b/packages/scope-manager/tests/fixtures/class/emit-metadata/nested-class-both.ts.shot
new file mode 100644
index 000000000000..01686cb28848
--- /dev/null
+++ b/packages/scope-manager/tests/fixtures/class/emit-metadata/nested-class-both.ts.shot
@@ -0,0 +1,298 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`class emit-metadata nested-class-both 1`] = `
+ScopeManager {
+ variables: Array [
+ ImplicitGlobalConstTypeVariable,
+ Variable$2 {
+ defs: Array [
+ FunctionNameDefinition$1 {
+ name: Identifier<"deco">,
+ node: FunctionDeclaration$1,
+ },
+ ],
+ name: "deco",
+ references: Array [
+ Reference$1 {
+ identifier: Identifier<"deco">,
+ isRead: true,
+ isTypeReference: false,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$2,
+ },
+ Reference$3 {
+ identifier: Identifier<"deco">,
+ isRead: true,
+ isTypeReference: false,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$2,
+ },
+ ],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$3 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$4 {
+ defs: Array [
+ ParameterDefinition$2 {
+ name: Identifier<"param">,
+ node: FunctionDeclaration$1,
+ },
+ ],
+ name: "param",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$5 {
+ defs: Array [
+ ClassNameDefinition$3 {
+ name: Identifier<"T">,
+ node: ClassDeclaration$2,
+ },
+ ],
+ name: "T",
+ references: Array [
+ Reference$2 {
+ identifier: Identifier<"T">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$5,
+ },
+ Reference$4 {
+ identifier: Identifier<"T">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$5,
+ },
+ ],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$6 {
+ defs: Array [
+ ClassNameDefinition$4 {
+ name: Identifier<"T">,
+ node: ClassDeclaration$2,
+ },
+ ],
+ name: "T",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$7 {
+ defs: Array [
+ ClassNameDefinition$5 {
+ name: Identifier<"A">,
+ node: ClassDeclaration$3,
+ },
+ ],
+ name: "A",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$8 {
+ defs: Array [
+ ClassNameDefinition$6 {
+ name: Identifier<"A">,
+ node: ClassDeclaration$3,
+ },
+ ],
+ name: "A",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$9 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$10 {
+ defs: Array [
+ ParameterDefinition$7 {
+ name: Identifier<"foo">,
+ node: FunctionExpression$4,
+ },
+ ],
+ name: "foo",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$11 {
+ defs: Array [
+ ClassNameDefinition$8 {
+ name: Identifier<"B">,
+ node: ClassDeclaration$5,
+ },
+ ],
+ name: "B",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$12 {
+ defs: Array [
+ ClassNameDefinition$9 {
+ name: Identifier<"B">,
+ node: ClassDeclaration$5,
+ },
+ ],
+ name: "B",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$13 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$14 {
+ defs: Array [
+ ParameterDefinition$10 {
+ name: Identifier<"bar">,
+ node: FunctionExpression$6,
+ },
+ ],
+ name: "bar",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ ],
+ scopes: Array [
+ GlobalScope$1 {
+ block: Program$7,
+ isStrict: false,
+ references: Array [
+ Reference$1,
+ ],
+ set: Map {
+ "const" => ImplicitGlobalConstTypeVariable,
+ "deco" => Variable$2,
+ "T" => Variable$5,
+ "A" => Variable$7,
+ },
+ type: "global",
+ upper: null,
+ variables: Array [
+ ImplicitGlobalConstTypeVariable,
+ Variable$2,
+ Variable$5,
+ Variable$7,
+ ],
+ },
+ FunctionScope$2 {
+ block: FunctionDeclaration$1,
+ isStrict: false,
+ references: Array [],
+ set: Map {
+ "arguments" => Variable$3,
+ "param" => Variable$4,
+ },
+ type: "function",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$3,
+ Variable$4,
+ ],
+ },
+ ClassScope$3 {
+ block: ClassDeclaration$2,
+ isStrict: true,
+ references: Array [],
+ set: Map {
+ "T" => Variable$6,
+ },
+ type: "class",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$6,
+ ],
+ },
+ ClassScope$4 {
+ block: ClassDeclaration$3,
+ isStrict: true,
+ references: Array [],
+ set: Map {
+ "A" => Variable$8,
+ },
+ type: "class",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$8,
+ ],
+ },
+ FunctionScope$5 {
+ block: FunctionExpression$4,
+ isStrict: true,
+ references: Array [
+ Reference$2,
+ Reference$3,
+ ],
+ set: Map {
+ "arguments" => Variable$9,
+ "foo" => Variable$10,
+ "B" => Variable$11,
+ },
+ type: "function",
+ upper: ClassScope$4,
+ variables: Array [
+ Variable$9,
+ Variable$10,
+ Variable$11,
+ ],
+ },
+ ClassScope$6 {
+ block: ClassDeclaration$5,
+ isStrict: true,
+ references: Array [],
+ set: Map {
+ "B" => Variable$12,
+ },
+ type: "class",
+ upper: FunctionScope$5,
+ variables: Array [
+ Variable$12,
+ ],
+ },
+ FunctionScope$7 {
+ block: FunctionExpression$6,
+ isStrict: true,
+ references: Array [
+ Reference$4,
+ ],
+ set: Map {
+ "arguments" => Variable$13,
+ "bar" => Variable$14,
+ },
+ type: "function",
+ upper: ClassScope$6,
+ variables: Array [
+ Variable$13,
+ Variable$14,
+ ],
+ },
+ ],
+}
+`;
diff --git a/packages/scope-manager/tests/fixtures/class/emit-metadata/nested-class-inner.ts b/packages/scope-manager/tests/fixtures/class/emit-metadata/nested-class-inner.ts
new file mode 100644
index 000000000000..d2fd5be90214
--- /dev/null
+++ b/packages/scope-manager/tests/fixtures/class/emit-metadata/nested-class-inner.ts
@@ -0,0 +1,14 @@
+//// @emitDecoratorMetadata = true
+
+function deco(...param: any) {}
+
+class T {}
+
+class A {
+ constructor(foo: T) {
+ @deco
+ class B {
+ constructor(bar: T) {}
+ }
+ }
+}
diff --git a/packages/scope-manager/tests/fixtures/class/emit-metadata/nested-class-inner.ts.shot b/packages/scope-manager/tests/fixtures/class/emit-metadata/nested-class-inner.ts.shot
new file mode 100644
index 000000000000..963402a90367
--- /dev/null
+++ b/packages/scope-manager/tests/fixtures/class/emit-metadata/nested-class-inner.ts.shot
@@ -0,0 +1,288 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`class emit-metadata nested-class-inner 1`] = `
+ScopeManager {
+ variables: Array [
+ ImplicitGlobalConstTypeVariable,
+ Variable$2 {
+ defs: Array [
+ FunctionNameDefinition$1 {
+ name: Identifier<"deco">,
+ node: FunctionDeclaration$1,
+ },
+ ],
+ name: "deco",
+ references: Array [
+ Reference$2 {
+ identifier: Identifier<"deco">,
+ isRead: true,
+ isTypeReference: false,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$2,
+ },
+ ],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$3 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$4 {
+ defs: Array [
+ ParameterDefinition$2 {
+ name: Identifier<"param">,
+ node: FunctionDeclaration$1,
+ },
+ ],
+ name: "param",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$5 {
+ defs: Array [
+ ClassNameDefinition$3 {
+ name: Identifier<"T">,
+ node: ClassDeclaration$2,
+ },
+ ],
+ name: "T",
+ references: Array [
+ Reference$1 {
+ identifier: Identifier<"T">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: false,
+ isWrite: false,
+ resolved: Variable$5,
+ },
+ Reference$3 {
+ identifier: Identifier<"T">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$5,
+ },
+ ],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$6 {
+ defs: Array [
+ ClassNameDefinition$4 {
+ name: Identifier<"T">,
+ node: ClassDeclaration$2,
+ },
+ ],
+ name: "T",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$7 {
+ defs: Array [
+ ClassNameDefinition$5 {
+ name: Identifier<"A">,
+ node: ClassDeclaration$3,
+ },
+ ],
+ name: "A",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$8 {
+ defs: Array [
+ ClassNameDefinition$6 {
+ name: Identifier<"A">,
+ node: ClassDeclaration$3,
+ },
+ ],
+ name: "A",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$9 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$10 {
+ defs: Array [
+ ParameterDefinition$7 {
+ name: Identifier<"foo">,
+ node: FunctionExpression$4,
+ },
+ ],
+ name: "foo",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$11 {
+ defs: Array [
+ ClassNameDefinition$8 {
+ name: Identifier<"B">,
+ node: ClassDeclaration$5,
+ },
+ ],
+ name: "B",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$12 {
+ defs: Array [
+ ClassNameDefinition$9 {
+ name: Identifier<"B">,
+ node: ClassDeclaration$5,
+ },
+ ],
+ name: "B",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$13 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$14 {
+ defs: Array [
+ ParameterDefinition$10 {
+ name: Identifier<"bar">,
+ node: FunctionExpression$6,
+ },
+ ],
+ name: "bar",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ ],
+ scopes: Array [
+ GlobalScope$1 {
+ block: Program$7,
+ isStrict: false,
+ references: Array [],
+ set: Map {
+ "const" => ImplicitGlobalConstTypeVariable,
+ "deco" => Variable$2,
+ "T" => Variable$5,
+ "A" => Variable$7,
+ },
+ type: "global",
+ upper: null,
+ variables: Array [
+ ImplicitGlobalConstTypeVariable,
+ Variable$2,
+ Variable$5,
+ Variable$7,
+ ],
+ },
+ FunctionScope$2 {
+ block: FunctionDeclaration$1,
+ isStrict: false,
+ references: Array [],
+ set: Map {
+ "arguments" => Variable$3,
+ "param" => Variable$4,
+ },
+ type: "function",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$3,
+ Variable$4,
+ ],
+ },
+ ClassScope$3 {
+ block: ClassDeclaration$2,
+ isStrict: true,
+ references: Array [],
+ set: Map {
+ "T" => Variable$6,
+ },
+ type: "class",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$6,
+ ],
+ },
+ ClassScope$4 {
+ block: ClassDeclaration$3,
+ isStrict: true,
+ references: Array [],
+ set: Map {
+ "A" => Variable$8,
+ },
+ type: "class",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$8,
+ ],
+ },
+ FunctionScope$5 {
+ block: FunctionExpression$4,
+ isStrict: true,
+ references: Array [
+ Reference$1,
+ Reference$2,
+ ],
+ set: Map {
+ "arguments" => Variable$9,
+ "foo" => Variable$10,
+ "B" => Variable$11,
+ },
+ type: "function",
+ upper: ClassScope$4,
+ variables: Array [
+ Variable$9,
+ Variable$10,
+ Variable$11,
+ ],
+ },
+ ClassScope$6 {
+ block: ClassDeclaration$5,
+ isStrict: true,
+ references: Array [],
+ set: Map {
+ "B" => Variable$12,
+ },
+ type: "class",
+ upper: FunctionScope$5,
+ variables: Array [
+ Variable$12,
+ ],
+ },
+ FunctionScope$7 {
+ block: FunctionExpression$6,
+ isStrict: true,
+ references: Array [
+ Reference$3,
+ ],
+ set: Map {
+ "arguments" => Variable$13,
+ "bar" => Variable$14,
+ },
+ type: "function",
+ upper: ClassScope$6,
+ variables: Array [
+ Variable$13,
+ Variable$14,
+ ],
+ },
+ ],
+}
+`;
diff --git a/packages/scope-manager/tests/fixtures/class/emit-metadata/nested-class-outer.ts b/packages/scope-manager/tests/fixtures/class/emit-metadata/nested-class-outer.ts
new file mode 100644
index 000000000000..2f5247067bf1
--- /dev/null
+++ b/packages/scope-manager/tests/fixtures/class/emit-metadata/nested-class-outer.ts
@@ -0,0 +1,14 @@
+//// @emitDecoratorMetadata = true
+
+function deco(...param: any) {}
+
+class T {}
+
+@deco
+class A {
+ constructor(foo: T) {
+ class B {
+ constructor(bar: T) {}
+ }
+ }
+}
diff --git a/packages/scope-manager/tests/fixtures/class/emit-metadata/nested-class-outer.ts.shot b/packages/scope-manager/tests/fixtures/class/emit-metadata/nested-class-outer.ts.shot
new file mode 100644
index 000000000000..5db83db9116b
--- /dev/null
+++ b/packages/scope-manager/tests/fixtures/class/emit-metadata/nested-class-outer.ts.shot
@@ -0,0 +1,289 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`class emit-metadata nested-class-outer 1`] = `
+ScopeManager {
+ variables: Array [
+ ImplicitGlobalConstTypeVariable,
+ Variable$2 {
+ defs: Array [
+ FunctionNameDefinition$1 {
+ name: Identifier<"deco">,
+ node: FunctionDeclaration$1,
+ },
+ ],
+ name: "deco",
+ references: Array [
+ Reference$1 {
+ identifier: Identifier<"deco">,
+ isRead: true,
+ isTypeReference: false,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$2,
+ },
+ ],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$3 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$4 {
+ defs: Array [
+ ParameterDefinition$2 {
+ name: Identifier<"param">,
+ node: FunctionDeclaration$1,
+ },
+ ],
+ name: "param",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$5 {
+ defs: Array [
+ ClassNameDefinition$3 {
+ name: Identifier<"T">,
+ node: ClassDeclaration$2,
+ },
+ ],
+ name: "T",
+ references: Array [
+ Reference$2 {
+ identifier: Identifier<"T">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$5,
+ },
+ Reference$3 {
+ identifier: Identifier<"T">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: false,
+ isWrite: false,
+ resolved: Variable$5,
+ },
+ ],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$6 {
+ defs: Array [
+ ClassNameDefinition$4 {
+ name: Identifier<"T">,
+ node: ClassDeclaration$2,
+ },
+ ],
+ name: "T",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$7 {
+ defs: Array [
+ ClassNameDefinition$5 {
+ name: Identifier<"A">,
+ node: ClassDeclaration$3,
+ },
+ ],
+ name: "A",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$8 {
+ defs: Array [
+ ClassNameDefinition$6 {
+ name: Identifier<"A">,
+ node: ClassDeclaration$3,
+ },
+ ],
+ name: "A",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$9 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$10 {
+ defs: Array [
+ ParameterDefinition$7 {
+ name: Identifier<"foo">,
+ node: FunctionExpression$4,
+ },
+ ],
+ name: "foo",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$11 {
+ defs: Array [
+ ClassNameDefinition$8 {
+ name: Identifier<"B">,
+ node: ClassDeclaration$5,
+ },
+ ],
+ name: "B",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$12 {
+ defs: Array [
+ ClassNameDefinition$9 {
+ name: Identifier<"B">,
+ node: ClassDeclaration$5,
+ },
+ ],
+ name: "B",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$13 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$14 {
+ defs: Array [
+ ParameterDefinition$10 {
+ name: Identifier<"bar">,
+ node: FunctionExpression$6,
+ },
+ ],
+ name: "bar",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ ],
+ scopes: Array [
+ GlobalScope$1 {
+ block: Program$7,
+ isStrict: false,
+ references: Array [
+ Reference$1,
+ ],
+ set: Map {
+ "const" => ImplicitGlobalConstTypeVariable,
+ "deco" => Variable$2,
+ "T" => Variable$5,
+ "A" => Variable$7,
+ },
+ type: "global",
+ upper: null,
+ variables: Array [
+ ImplicitGlobalConstTypeVariable,
+ Variable$2,
+ Variable$5,
+ Variable$7,
+ ],
+ },
+ FunctionScope$2 {
+ block: FunctionDeclaration$1,
+ isStrict: false,
+ references: Array [],
+ set: Map {
+ "arguments" => Variable$3,
+ "param" => Variable$4,
+ },
+ type: "function",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$3,
+ Variable$4,
+ ],
+ },
+ ClassScope$3 {
+ block: ClassDeclaration$2,
+ isStrict: true,
+ references: Array [],
+ set: Map {
+ "T" => Variable$6,
+ },
+ type: "class",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$6,
+ ],
+ },
+ ClassScope$4 {
+ block: ClassDeclaration$3,
+ isStrict: true,
+ references: Array [],
+ set: Map {
+ "A" => Variable$8,
+ },
+ type: "class",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$8,
+ ],
+ },
+ FunctionScope$5 {
+ block: FunctionExpression$4,
+ isStrict: true,
+ references: Array [
+ Reference$2,
+ ],
+ set: Map {
+ "arguments" => Variable$9,
+ "foo" => Variable$10,
+ "B" => Variable$11,
+ },
+ type: "function",
+ upper: ClassScope$4,
+ variables: Array [
+ Variable$9,
+ Variable$10,
+ Variable$11,
+ ],
+ },
+ ClassScope$6 {
+ block: ClassDeclaration$5,
+ isStrict: true,
+ references: Array [],
+ set: Map {
+ "B" => Variable$12,
+ },
+ type: "class",
+ upper: FunctionScope$5,
+ variables: Array [
+ Variable$12,
+ ],
+ },
+ FunctionScope$7 {
+ block: FunctionExpression$6,
+ isStrict: true,
+ references: Array [
+ Reference$3,
+ ],
+ set: Map {
+ "arguments" => Variable$13,
+ "bar" => Variable$14,
+ },
+ type: "function",
+ upper: ClassScope$6,
+ variables: Array [
+ Variable$13,
+ Variable$14,
+ ],
+ },
+ ],
+}
+`;
diff --git a/packages/scope-manager/tests/fixtures/class/emit-metadata/parameters-deco.ts b/packages/scope-manager/tests/fixtures/class/emit-metadata/parameters-deco.ts
new file mode 100644
index 000000000000..fc0eb3c0525f
--- /dev/null
+++ b/packages/scope-manager/tests/fixtures/class/emit-metadata/parameters-deco.ts
@@ -0,0 +1,14 @@
+//// @emitDecoratorMetadata = true
+
+function deco(...param: any) {}
+
+class T {}
+
+@deco
+class A {
+ constructor(@deco foo: T) {}
+
+ set foo(@deco a: T) {}
+
+ foo1(@deco a: T, b: T) {}
+}
diff --git a/packages/scope-manager/tests/fixtures/class/emit-metadata/parameters-deco.ts.shot b/packages/scope-manager/tests/fixtures/class/emit-metadata/parameters-deco.ts.shot
new file mode 100644
index 000000000000..ac1f8b41de5e
--- /dev/null
+++ b/packages/scope-manager/tests/fixtures/class/emit-metadata/parameters-deco.ts.shot
@@ -0,0 +1,344 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`class emit-metadata parameters-deco 1`] = `
+ScopeManager {
+ variables: Array [
+ ImplicitGlobalConstTypeVariable,
+ Variable$2 {
+ defs: Array [
+ FunctionNameDefinition$1 {
+ name: Identifier<"deco">,
+ node: FunctionDeclaration$1,
+ },
+ ],
+ name: "deco",
+ references: Array [
+ Reference$1 {
+ identifier: Identifier<"deco">,
+ isRead: true,
+ isTypeReference: false,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$2,
+ },
+ Reference$3 {
+ identifier: Identifier<"deco">,
+ isRead: true,
+ isTypeReference: false,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$2,
+ },
+ Reference$5 {
+ identifier: Identifier<"deco">,
+ isRead: true,
+ isTypeReference: false,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$2,
+ },
+ Reference$7 {
+ identifier: Identifier<"deco">,
+ isRead: true,
+ isTypeReference: false,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$2,
+ },
+ ],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$3 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$4 {
+ defs: Array [
+ ParameterDefinition$2 {
+ name: Identifier<"param">,
+ node: FunctionDeclaration$1,
+ },
+ ],
+ name: "param",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$5 {
+ defs: Array [
+ ClassNameDefinition$3 {
+ name: Identifier<"T">,
+ node: ClassDeclaration$2,
+ },
+ ],
+ name: "T",
+ references: Array [
+ Reference$2 {
+ identifier: Identifier<"T">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$5,
+ },
+ Reference$4 {
+ identifier: Identifier<"T">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: false,
+ isWrite: false,
+ resolved: Variable$5,
+ },
+ Reference$6 {
+ identifier: Identifier<"T">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$5,
+ },
+ Reference$8 {
+ identifier: Identifier<"T">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$5,
+ },
+ ],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$6 {
+ defs: Array [
+ ClassNameDefinition$4 {
+ name: Identifier<"T">,
+ node: ClassDeclaration$2,
+ },
+ ],
+ name: "T",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$7 {
+ defs: Array [
+ ClassNameDefinition$5 {
+ name: Identifier<"A">,
+ node: ClassDeclaration$3,
+ },
+ ],
+ name: "A",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$8 {
+ defs: Array [
+ ClassNameDefinition$6 {
+ name: Identifier<"A">,
+ node: ClassDeclaration$3,
+ },
+ ],
+ name: "A",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$9 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$10 {
+ defs: Array [
+ ParameterDefinition$7 {
+ name: Identifier<"foo">,
+ node: FunctionExpression$4,
+ },
+ ],
+ name: "foo",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$11 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$12 {
+ defs: Array [
+ ParameterDefinition$8 {
+ name: Identifier<"a">,
+ node: FunctionExpression$5,
+ },
+ ],
+ name: "a",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$13 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$14 {
+ defs: Array [
+ ParameterDefinition$9 {
+ name: Identifier<"a">,
+ node: FunctionExpression$6,
+ },
+ ],
+ name: "a",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$15 {
+ defs: Array [
+ ParameterDefinition$10 {
+ name: Identifier<"b">,
+ node: FunctionExpression$6,
+ },
+ ],
+ name: "b",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ ],
+ scopes: Array [
+ GlobalScope$1 {
+ block: Program$7,
+ isStrict: false,
+ references: Array [
+ Reference$1,
+ ],
+ set: Map {
+ "const" => ImplicitGlobalConstTypeVariable,
+ "deco" => Variable$2,
+ "T" => Variable$5,
+ "A" => Variable$7,
+ },
+ type: "global",
+ upper: null,
+ variables: Array [
+ ImplicitGlobalConstTypeVariable,
+ Variable$2,
+ Variable$5,
+ Variable$7,
+ ],
+ },
+ FunctionScope$2 {
+ block: FunctionDeclaration$1,
+ isStrict: false,
+ references: Array [],
+ set: Map {
+ "arguments" => Variable$3,
+ "param" => Variable$4,
+ },
+ type: "function",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$3,
+ Variable$4,
+ ],
+ },
+ ClassScope$3 {
+ block: ClassDeclaration$2,
+ isStrict: true,
+ references: Array [],
+ set: Map {
+ "T" => Variable$6,
+ },
+ type: "class",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$6,
+ ],
+ },
+ ClassScope$4 {
+ block: ClassDeclaration$3,
+ isStrict: true,
+ references: Array [],
+ set: Map {
+ "A" => Variable$8,
+ },
+ type: "class",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$8,
+ ],
+ },
+ FunctionScope$5 {
+ block: FunctionExpression$4,
+ isStrict: true,
+ references: Array [
+ Reference$2,
+ Reference$3,
+ ],
+ set: Map {
+ "arguments" => Variable$9,
+ "foo" => Variable$10,
+ },
+ type: "function",
+ upper: ClassScope$4,
+ variables: Array [
+ Variable$9,
+ Variable$10,
+ ],
+ },
+ FunctionScope$6 {
+ block: FunctionExpression$5,
+ isStrict: true,
+ references: Array [
+ Reference$4,
+ Reference$5,
+ ],
+ set: Map {
+ "arguments" => Variable$11,
+ "a" => Variable$12,
+ },
+ type: "function",
+ upper: ClassScope$4,
+ variables: Array [
+ Variable$11,
+ Variable$12,
+ ],
+ },
+ FunctionScope$7 {
+ block: FunctionExpression$6,
+ isStrict: true,
+ references: Array [
+ Reference$6,
+ Reference$7,
+ Reference$8,
+ ],
+ set: Map {
+ "arguments" => Variable$13,
+ "a" => Variable$14,
+ "b" => Variable$15,
+ },
+ type: "function",
+ upper: ClassScope$4,
+ variables: Array [
+ Variable$13,
+ Variable$14,
+ Variable$15,
+ ],
+ },
+ ],
+}
+`;
diff --git a/packages/scope-manager/tests/fixtures/class/emit-metadata/property-deco.ts b/packages/scope-manager/tests/fixtures/class/emit-metadata/property-deco.ts
new file mode 100644
index 000000000000..a4c17dcaa5d1
--- /dev/null
+++ b/packages/scope-manager/tests/fixtures/class/emit-metadata/property-deco.ts
@@ -0,0 +1,13 @@
+//// @emitDecoratorMetadata = true
+
+function deco(...param: any) {}
+
+namespace a {
+ export class B {}
+}
+
+class A {
+ property: a.B;
+ @deco
+ propertyWithDeco: a.B;
+}
diff --git a/packages/scope-manager/tests/fixtures/class/emit-metadata/property-deco.ts.shot b/packages/scope-manager/tests/fixtures/class/emit-metadata/property-deco.ts.shot
new file mode 100644
index 000000000000..348c6009163d
--- /dev/null
+++ b/packages/scope-manager/tests/fixtures/class/emit-metadata/property-deco.ts.shot
@@ -0,0 +1,205 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`class emit-metadata property-deco 1`] = `
+ScopeManager {
+ variables: Array [
+ ImplicitGlobalConstTypeVariable,
+ Variable$2 {
+ defs: Array [
+ FunctionNameDefinition$1 {
+ name: Identifier<"deco">,
+ node: FunctionDeclaration$1,
+ },
+ ],
+ name: "deco",
+ references: Array [
+ Reference$2 {
+ identifier: Identifier<"deco">,
+ isRead: true,
+ isTypeReference: false,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$2,
+ },
+ ],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$3 {
+ defs: Array [],
+ name: "arguments",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$4 {
+ defs: Array [
+ ParameterDefinition$2 {
+ name: Identifier<"param">,
+ node: FunctionDeclaration$1,
+ },
+ ],
+ name: "param",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: false,
+ },
+ Variable$5 {
+ defs: Array [
+ TSModuleNameDefinition$3 {
+ name: Identifier<"a">,
+ node: TSModuleDeclaration$2,
+ },
+ ],
+ name: "a",
+ references: Array [
+ Reference$1 {
+ identifier: Identifier<"a">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: false,
+ isWrite: false,
+ resolved: Variable$5,
+ },
+ Reference$3 {
+ identifier: Identifier<"a">,
+ isRead: true,
+ isTypeReference: true,
+ isValueReference: true,
+ isWrite: false,
+ resolved: Variable$5,
+ },
+ ],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$6 {
+ defs: Array [
+ ClassNameDefinition$4 {
+ name: Identifier<"B">,
+ node: ClassDeclaration$3,
+ },
+ ],
+ name: "B",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$7 {
+ defs: Array [
+ ClassNameDefinition$5 {
+ name: Identifier<"B">,
+ node: ClassDeclaration$3,
+ },
+ ],
+ name: "B",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$8 {
+ defs: Array [
+ ClassNameDefinition$6 {
+ name: Identifier<"A">,
+ node: ClassDeclaration$4,
+ },
+ ],
+ name: "A",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ Variable$9 {
+ defs: Array [
+ ClassNameDefinition$7 {
+ name: Identifier<"A">,
+ node: ClassDeclaration$4,
+ },
+ ],
+ name: "A",
+ references: Array [],
+ isValueVariable: true,
+ isTypeVariable: true,
+ },
+ ],
+ scopes: Array [
+ GlobalScope$1 {
+ block: Program$5,
+ isStrict: false,
+ references: Array [],
+ set: Map {
+ "const" => ImplicitGlobalConstTypeVariable,
+ "deco" => Variable$2,
+ "a" => Variable$5,
+ "A" => Variable$8,
+ },
+ type: "global",
+ upper: null,
+ variables: Array [
+ ImplicitGlobalConstTypeVariable,
+ Variable$2,
+ Variable$5,
+ Variable$8,
+ ],
+ },
+ FunctionScope$2 {
+ block: FunctionDeclaration$1,
+ isStrict: false,
+ references: Array [],
+ set: Map {
+ "arguments" => Variable$3,
+ "param" => Variable$4,
+ },
+ type: "function",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$3,
+ Variable$4,
+ ],
+ },
+ TSModuleScope$3 {
+ block: TSModuleDeclaration$2,
+ isStrict: true,
+ references: Array [],
+ set: Map {
+ "B" => Variable$6,
+ },
+ type: "tsModule",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$6,
+ ],
+ },
+ ClassScope$4 {
+ block: ClassDeclaration$3,
+ isStrict: true,
+ references: Array [],
+ set: Map {
+ "B" => Variable$7,
+ },
+ type: "class",
+ upper: TSModuleScope$3,
+ variables: Array [
+ Variable$7,
+ ],
+ },
+ ClassScope$5 {
+ block: ClassDeclaration$4,
+ isStrict: true,
+ references: Array [
+ Reference$1,
+ Reference$2,
+ Reference$3,
+ ],
+ set: Map {
+ "A" => Variable$9,
+ },
+ type: "class",
+ upper: GlobalScope$1,
+ variables: Array [
+ Variable$9,
+ ],
+ },
+ ],
+}
+`;
diff --git a/packages/shared-fixtures/CHANGELOG.md b/packages/shared-fixtures/CHANGELOG.md
index 1d199cd813b1..a5372be7f103 100644
--- a/packages/shared-fixtures/CHANGELOG.md
+++ b/packages/shared-fixtures/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.14.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.13.0...v4.14.0) (2021-01-18)
+
+**Note:** Version bump only for package @typescript-eslint/shared-fixtures
+
+
+
+
+
# [4.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.12.0...v4.13.0) (2021-01-11)
**Note:** Version bump only for package @typescript-eslint/shared-fixtures
diff --git a/packages/shared-fixtures/package.json b/packages/shared-fixtures/package.json
index ca4814529850..726da455b4c3 100644
--- a/packages/shared-fixtures/package.json
+++ b/packages/shared-fixtures/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/shared-fixtures",
- "version": "4.13.0",
+ "version": "4.14.0",
"private": true,
"scripts": {
"build": "tsc -b tsconfig.build.json",
diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md
index b79e2c221f29..e40692d8614f 100644
--- a/packages/types/CHANGELOG.md
+++ b/packages/types/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.14.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.13.0...v4.14.0) (2021-01-18)
+
+**Note:** Version bump only for package @typescript-eslint/types
+
+
+
+
+
# [4.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.12.0...v4.13.0) (2021-01-11)
**Note:** Version bump only for package @typescript-eslint/types
diff --git a/packages/types/package.json b/packages/types/package.json
index 326f50624515..dc575b3be566 100644
--- a/packages/types/package.json
+++ b/packages/types/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/types",
- "version": "4.13.0",
+ "version": "4.14.0",
"description": "Types for the TypeScript-ESTree AST spec",
"keywords": [
"eslint",
diff --git a/packages/typescript-estree/CHANGELOG.md b/packages/typescript-estree/CHANGELOG.md
index d57dd5006ea7..87064eac93b7 100644
--- a/packages/typescript-estree/CHANGELOG.md
+++ b/packages/typescript-estree/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.14.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.13.0...v4.14.0) (2021-01-18)
+
+**Note:** Version bump only for package @typescript-eslint/typescript-estree
+
+
+
+
+
# [4.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.12.0...v4.13.0) (2021-01-11)
**Note:** Version bump only for package @typescript-eslint/typescript-estree
diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json
index e773e01c2400..cbfc388b5f42 100644
--- a/packages/typescript-estree/package.json
+++ b/packages/typescript-estree/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/typescript-estree",
- "version": "4.13.0",
+ "version": "4.14.0",
"description": "A parser that converts TypeScript source code into an ESTree compatible form",
"main": "dist/index.js",
"types": "dist/index.d.ts",
@@ -41,8 +41,8 @@
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
- "@typescript-eslint/types": "4.13.0",
- "@typescript-eslint/visitor-keys": "4.13.0",
+ "@typescript-eslint/types": "4.14.0",
+ "@typescript-eslint/visitor-keys": "4.14.0",
"debug": "^4.1.1",
"globby": "^11.0.1",
"is-glob": "^4.0.1",
@@ -61,7 +61,7 @@
"@types/lodash": "*",
"@types/semver": "^7.1.0",
"@types/tmp": "^0.2.0",
- "@typescript-eslint/shared-fixtures": "4.13.0",
+ "@typescript-eslint/shared-fixtures": "4.14.0",
"glob": "*",
"jest-specific-snapshot": "*",
"make-dir": "*",
diff --git a/packages/visitor-keys/CHANGELOG.md b/packages/visitor-keys/CHANGELOG.md
index b5fc6e7eaa44..c0cbad34c318 100644
--- a/packages/visitor-keys/CHANGELOG.md
+++ b/packages/visitor-keys/CHANGELOG.md
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+# [4.14.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.13.0...v4.14.0) (2021-01-18)
+
+**Note:** Version bump only for package @typescript-eslint/visitor-keys
+
+
+
+
+
# [4.13.0](https://github.com/typescript-eslint/typescript-eslint/compare/v4.12.0...v4.13.0) (2021-01-11)
**Note:** Version bump only for package @typescript-eslint/visitor-keys
diff --git a/packages/visitor-keys/package.json b/packages/visitor-keys/package.json
index a317676556fb..4774308be6a3 100644
--- a/packages/visitor-keys/package.json
+++ b/packages/visitor-keys/package.json
@@ -1,6 +1,6 @@
{
"name": "@typescript-eslint/visitor-keys",
- "version": "4.13.0",
+ "version": "4.14.0",
"description": "Visitor keys used to help traverse the TypeScript-ESTree AST",
"keywords": [
"eslint",
@@ -38,7 +38,7 @@
"typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
- "@typescript-eslint/types": "4.13.0",
+ "@typescript-eslint/types": "4.14.0",
"eslint-visitor-keys": "^2.0.0"
},
"devDependencies": {