From add6c6de5e4940acf2923329f8e2dc7a52070ecc Mon Sep 17 00:00:00 2001 From: Ivan Goncharov Date: Mon, 20 Jan 2020 23:55:02 +0800 Subject: [PATCH] ESLint: Forbid importing directories (#2377) Motivation #2277 --- .eslintrc.yml | 2 + package.json | 2 +- resources/eslint-rules/no-dir-import.js | 38 +++++++++++++++++++ src/index.d.ts | 20 +++++----- src/index.js | 28 +++++++------- src/language/__tests__/parser-benchmark.js | 2 +- src/language/__tests__/parser-test.js | 2 +- src/language/__tests__/printer-test.js | 2 +- src/language/__tests__/schema-parser-test.js | 2 +- src/language/__tests__/schema-printer-test.js | 2 +- src/language/__tests__/visitor-test.js | 2 +- .../__tests__/buildASTSchema-benchmark.js | 2 +- .../__tests__/buildClientSchema-benchmark.js | 2 +- .../introspectionFromSchema-benchmark.js | 2 +- .../__tests__/stripIgnoredCharacters-test.js | 2 +- .../__tests__/validateGQL-benchmark.js | 2 +- .../__tests__/validateInvalidGQL-benchmark.js | 2 +- .../__tests__/validateSDL-benchmark.js | 2 +- 18 files changed, 78 insertions(+), 38 deletions(-) create mode 100644 resources/eslint-rules/no-dir-import.js diff --git a/.eslintrc.yml b/.eslintrc.yml index 6897957d90..7f2d69cb4f 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -10,6 +10,8 @@ plugins: - import rules: + no-dir-import: error + ############################################################################## # `eslint-plugin-flowtype` rule list based on `v4.6.x` # https://github.com/gajus/eslint-plugin-flowtype#eslint-plugin-flowtype diff --git a/package.json b/package.json index 035f35bc2b..b8838a82dc 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "test:ci": "yarn check --integrity && npm run prettier:check && npm run lint -- --no-cache && npm run check && npm run testonly:cover && npm run check:ts && npm run check:spelling && npm run build", "testonly": "mocha --full-trace src/**/__tests__/**/*-test.js", "testonly:cover": "nyc npm run testonly", - "lint": "eslint --cache --ext .js,.ts src resources", + "lint": "eslint --rulesdir './resources/eslint-rules' --cache --ext .js,.ts src resources", "benchmark": "node --noconcurrent_sweeping --expose-gc --predictable ./resources/benchmark.js", "prettier": "prettier --ignore-path .gitignore --write --list-different \"**/*.{js,ts,md,json,yml}\"", "prettier:check": "prettier --ignore-path .gitignore --check \"**/*.{js,ts,md,json,yml}\"", diff --git a/resources/eslint-rules/no-dir-import.js b/resources/eslint-rules/no-dir-import.js new file mode 100644 index 0000000000..41220c35fa --- /dev/null +++ b/resources/eslint-rules/no-dir-import.js @@ -0,0 +1,38 @@ +// @noflow + +'use strict'; + +const fs = require('fs'); +const path = require('path'); + +module.exports = function(context) { + return { + ImportDeclaration: checkImporPath, + ExportNamedDeclaration: checkImporPath, + }; + + function checkImporPath(node) { + const { source } = node; + + // bail if the declaration doesn't have a source, e.g. "export { foo };" + if (!source) { + return; + } + + const importPath = source.value; + if (importPath.startsWith('./') || importPath.startsWith('../')) { + const baseDir = path.dirname(context.getFilename()); + const resolvedPath = path.resolve(baseDir, importPath); + + if ( + fs.existsSync(resolvedPath) && + fs.statSync(resolvedPath).isDirectory() + ) { + context.report({ + node: source, + message: 'It is not allowed to import from directory', + }); + } + } + } +}; diff --git a/src/index.d.ts b/src/index.d.ts index 148f95da37..b3594d0fa5 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -124,7 +124,7 @@ export { // Validate GraphQL schema. validateSchema, assertValidSchema, -} from './type'; +} from './type/index'; export { GraphQLType, @@ -167,7 +167,7 @@ export { GraphQLScalarSerializer, GraphQLScalarValueParser, GraphQLScalarLiteralParser, -} from './type'; +} from './type/index'; // Parse and operate on GraphQL language source files. export { @@ -202,7 +202,7 @@ export { isTypeDefinitionNode, isTypeSystemExtensionNode, isTypeExtensionNode, -} from './language'; +} from './language/index'; export { ParseOptions, @@ -274,7 +274,7 @@ export { UnionTypeExtensionNode, EnumTypeExtensionNode, InputObjectTypeExtensionNode, -} from './language'; +} from './language/index'; // Execute GraphQL queries. export { @@ -285,13 +285,13 @@ export { getDirectiveValues, ExecutionArgs, ExecutionResult, -} from './execution'; +} from './execution/index'; export { subscribe, createSourceEventStream, SubscriptionArgs, -} from './subscription'; +} from './subscription/index'; // Validate GraphQL documents. export { @@ -326,7 +326,7 @@ export { VariablesAreInputTypesRule, VariablesInAllowedPositionRule, ValidationRule, -} from './validation'; +} from './validation/index'; // Create, format, and print GraphQL errors. export { @@ -336,7 +336,7 @@ export { printError, formatError, GraphQLFormattedError, -} from './error'; +} from './error/index'; // Utilities for operating on GraphQL type schema and parsed sources. export { @@ -406,7 +406,7 @@ export { findDangerousChanges, // Report all deprecated usage within a GraphQL document. findDeprecatedUsages, -} from './utilities'; +} from './utilities/index'; export { IntrospectionOptions, @@ -434,4 +434,4 @@ export { BuildSchemaOptions, BreakingChange, DangerousChange, -} from './utilities'; +} from './utilities/index'; diff --git a/src/index.js b/src/index.js index 9ea2be9858..7fac793ca7 100644 --- a/src/index.js +++ b/src/index.js @@ -125,7 +125,7 @@ export { // Validate GraphQL schema. validateSchema, assertValidSchema, -} from './type'; +} from './type/index'; export type { GraphQLType, @@ -168,7 +168,7 @@ export type { GraphQLScalarSerializer, GraphQLScalarValueParser, GraphQLScalarLiteralParser, -} from './type'; +} from './type/index'; // Parse and operate on GraphQL language source files. export { @@ -203,7 +203,7 @@ export { isTypeDefinitionNode, isTypeSystemExtensionNode, isTypeExtensionNode, -} from './language'; +} from './language/index'; export type { ParseOptions, @@ -275,7 +275,7 @@ export type { UnionTypeExtensionNode, EnumTypeExtensionNode, InputObjectTypeExtensionNode, -} from './language'; +} from './language/index'; // Execute GraphQL queries. export { @@ -284,12 +284,12 @@ export { defaultTypeResolver, responsePathAsArray, getDirectiveValues, -} from './execution'; +} from './execution/index'; -export type { ExecutionArgs, ExecutionResult } from './execution'; +export type { ExecutionArgs, ExecutionResult } from './execution/index'; -export { subscribe, createSourceEventStream } from './subscription'; -export type { SubscriptionArgs } from './subscription'; +export { subscribe, createSourceEventStream } from './subscription/index'; +export type { SubscriptionArgs } from './subscription/index'; // Validate GraphQL documents. export { @@ -323,9 +323,9 @@ export { ValuesOfCorrectTypeRule, VariablesAreInputTypesRule, VariablesInAllowedPositionRule, -} from './validation'; +} from './validation/index'; -export type { ValidationRule } from './validation'; +export type { ValidationRule } from './validation/index'; // Create, format, and print GraphQL errors. export { @@ -334,9 +334,9 @@ export { locatedError, printError, formatError, -} from './error'; +} from './error/index'; -export type { GraphQLFormattedError } from './error'; +export type { GraphQLFormattedError } from './error/index'; // Utilities for operating on GraphQL type schema and parsed sources. export { @@ -406,7 +406,7 @@ export { findDangerousChanges, // Report all deprecated usage within a GraphQL document. findDeprecatedUsages, -} from './utilities'; +} from './utilities/index'; export type { IntrospectionOptions, @@ -434,4 +434,4 @@ export type { BuildSchemaOptions, BreakingChange, DangerousChange, -} from './utilities'; +} from './utilities/index'; diff --git a/src/language/__tests__/parser-benchmark.js b/src/language/__tests__/parser-benchmark.js index 3a9dc8f579..f443f7237b 100644 --- a/src/language/__tests__/parser-benchmark.js +++ b/src/language/__tests__/parser-benchmark.js @@ -2,7 +2,7 @@ import { parse } from '../parser'; -import { kitchenSinkQuery } from '../../__fixtures__'; +import { kitchenSinkQuery } from '../../__fixtures__/index'; export const name = 'Parse kitchen sink'; export const count = 1000; diff --git a/src/language/__tests__/parser-test.js b/src/language/__tests__/parser-test.js index 291a721798..c3ff3a57de 100644 --- a/src/language/__tests__/parser-test.js +++ b/src/language/__tests__/parser-test.js @@ -13,7 +13,7 @@ import { Source } from '../source'; import { TokenKind } from '../tokenKind'; import { parse, parseValue, parseType } from '../parser'; -import { kitchenSinkQuery } from '../../__fixtures__'; +import { kitchenSinkQuery } from '../../__fixtures__/index'; import toJSONDeep from './toJSONDeep'; diff --git a/src/language/__tests__/printer-test.js b/src/language/__tests__/printer-test.js index aab7daa182..d6cf65adc8 100644 --- a/src/language/__tests__/printer-test.js +++ b/src/language/__tests__/printer-test.js @@ -8,7 +8,7 @@ import dedent from '../../jsutils/dedent'; import { parse } from '../parser'; import { print } from '../printer'; -import { kitchenSinkQuery } from '../../__fixtures__'; +import { kitchenSinkQuery } from '../../__fixtures__/index'; describe('Printer: Query document', () => { it('does not alter ast', () => { diff --git a/src/language/__tests__/schema-parser-test.js b/src/language/__tests__/schema-parser-test.js index 7c002e9ac0..7f823701b2 100644 --- a/src/language/__tests__/schema-parser-test.js +++ b/src/language/__tests__/schema-parser-test.js @@ -5,7 +5,7 @@ import { describe, it } from 'mocha'; import dedent from '../../jsutils/dedent'; -import { kitchenSinkSDL } from '../../__fixtures__'; +import { kitchenSinkSDL } from '../../__fixtures__/index'; import { parse } from '../parser'; diff --git a/src/language/__tests__/schema-printer-test.js b/src/language/__tests__/schema-printer-test.js index a96f8c3d98..aa9dc74184 100644 --- a/src/language/__tests__/schema-printer-test.js +++ b/src/language/__tests__/schema-printer-test.js @@ -8,7 +8,7 @@ import dedent from '../../jsutils/dedent'; import { parse } from '../parser'; import { print } from '../printer'; -import { kitchenSinkSDL } from '../../__fixtures__'; +import { kitchenSinkSDL } from '../../__fixtures__/index'; describe('Printer: SDL document', () => { it('prints minimal ast', () => { diff --git a/src/language/__tests__/visitor-test.js b/src/language/__tests__/visitor-test.js index 7781a94cab..0d713d5565 100644 --- a/src/language/__tests__/visitor-test.js +++ b/src/language/__tests__/visitor-test.js @@ -9,7 +9,7 @@ import { Kind } from '../kinds'; import { parse } from '../parser'; import { visit, visitInParallel, BREAK, QueryDocumentKeys } from '../visitor'; -import { kitchenSinkQuery } from '../../__fixtures__'; +import { kitchenSinkQuery } from '../../__fixtures__/index'; function checkVisitorFnArgs(ast, args, isEdited) { const [node, key, parent, path, ancestors] = args; diff --git a/src/utilities/__tests__/buildASTSchema-benchmark.js b/src/utilities/__tests__/buildASTSchema-benchmark.js index 58f65dd5cf..9a4a276959 100644 --- a/src/utilities/__tests__/buildASTSchema-benchmark.js +++ b/src/utilities/__tests__/buildASTSchema-benchmark.js @@ -4,7 +4,7 @@ import { parse } from '../../language/parser'; import { buildASTSchema } from '../buildASTSchema'; -import { bigSchemaSDL } from '../../__fixtures__'; +import { bigSchemaSDL } from '../../__fixtures__/index'; const schemaAST = parse(bigSchemaSDL); diff --git a/src/utilities/__tests__/buildClientSchema-benchmark.js b/src/utilities/__tests__/buildClientSchema-benchmark.js index d7d9500ed3..c9678782b4 100644 --- a/src/utilities/__tests__/buildClientSchema-benchmark.js +++ b/src/utilities/__tests__/buildClientSchema-benchmark.js @@ -2,7 +2,7 @@ import { buildClientSchema } from '../buildClientSchema'; -import { bigSchemaIntrospectionResult } from '../../__fixtures__'; +import { bigSchemaIntrospectionResult } from '../../__fixtures__/index'; export const name = 'Build Schema from Introspection'; export const count = 10; diff --git a/src/utilities/__tests__/introspectionFromSchema-benchmark.js b/src/utilities/__tests__/introspectionFromSchema-benchmark.js index b9c79d2d5d..87a48f52f8 100644 --- a/src/utilities/__tests__/introspectionFromSchema-benchmark.js +++ b/src/utilities/__tests__/introspectionFromSchema-benchmark.js @@ -6,7 +6,7 @@ import { execute } from '../../execution/execute'; import { buildSchema } from '../buildASTSchema'; import { getIntrospectionQuery } from '../getIntrospectionQuery'; -import { bigSchemaSDL } from '../../__fixtures__'; +import { bigSchemaSDL } from '../../__fixtures__/index'; const schema = buildSchema(bigSchemaSDL, { assumeValid: true }); const document = parse(getIntrospectionQuery()); diff --git a/src/utilities/__tests__/stripIgnoredCharacters-test.js b/src/utilities/__tests__/stripIgnoredCharacters-test.js index 12c895a86c..42ad82eeaf 100644 --- a/src/utilities/__tests__/stripIgnoredCharacters-test.js +++ b/src/utilities/__tests__/stripIgnoredCharacters-test.js @@ -12,7 +12,7 @@ import { Lexer } from '../../language/lexer'; import { stripIgnoredCharacters } from '../stripIgnoredCharacters'; -import { kitchenSinkQuery, kitchenSinkSDL } from '../../__fixtures__'; +import { kitchenSinkQuery, kitchenSinkSDL } from '../../__fixtures__/index'; const ignoredTokens = [ // UnicodeBOM :: diff --git a/src/validation/__tests__/validateGQL-benchmark.js b/src/validation/__tests__/validateGQL-benchmark.js index 509fd7cdc9..203ed29358 100644 --- a/src/validation/__tests__/validateGQL-benchmark.js +++ b/src/validation/__tests__/validateGQL-benchmark.js @@ -6,7 +6,7 @@ import { getIntrospectionQuery } from '../../utilities/getIntrospectionQuery'; import { validate } from '../validate'; -import { bigSchemaSDL } from '../../__fixtures__'; +import { bigSchemaSDL } from '../../__fixtures__/index'; const schema = buildSchema(bigSchemaSDL, { assumeValid: true }); const queryAST = parse(getIntrospectionQuery()); diff --git a/src/validation/__tests__/validateInvalidGQL-benchmark.js b/src/validation/__tests__/validateInvalidGQL-benchmark.js index 13e9402d64..8e9bb12a22 100644 --- a/src/validation/__tests__/validateInvalidGQL-benchmark.js +++ b/src/validation/__tests__/validateInvalidGQL-benchmark.js @@ -5,7 +5,7 @@ import { buildSchema } from '../../utilities/buildASTSchema'; import { validate } from '../validate'; -import { bigSchemaSDL } from '../../__fixtures__'; +import { bigSchemaSDL } from '../../__fixtures__/index'; const schema = buildSchema(bigSchemaSDL, { assumeValid: true }); const queryAST = parse(` diff --git a/src/validation/__tests__/validateSDL-benchmark.js b/src/validation/__tests__/validateSDL-benchmark.js index 921ce506e7..e56eb06236 100644 --- a/src/validation/__tests__/validateSDL-benchmark.js +++ b/src/validation/__tests__/validateSDL-benchmark.js @@ -4,7 +4,7 @@ import { parse } from '../../language/parser'; import { validateSDL } from '../validate'; -import { bigSchemaSDL } from '../../__fixtures__'; +import { bigSchemaSDL } from '../../__fixtures__/index'; const sdlAST = parse(bigSchemaSDL);