diff --git a/.eslintrc.js b/.eslintrc.js index 99382e2eca..9ae98af6d7 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,6 +1,8 @@ module.exports = { root: true, extends: ['@deephaven/eslint-config'], + // The `deephaven` eslint plugin is defined in `packages/eslint-config/plugin` + plugins: ['deephaven'], ignorePatterns: ['packages/golden-layout/*', 'jest.config.*'], overrides: [ { @@ -9,6 +11,9 @@ module.exports = { project: ['./tsconfig.eslint.json', './packages/*/tsconfig.json'], tsconfigRootDir: __dirname, }, + rules: { + 'deephaven/no-self-package-import': 'error', + }, }, ], }; diff --git a/package-lock.json b/package-lock.json index fc52995906..40bbbeb769 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10856,6 +10856,10 @@ "node": ">=4" } }, + "node_modules/eslint-plugin-deephaven": { + "resolved": "packages/eslint-config/plugin", + "link": true + }, "node_modules/eslint-plugin-es": { "version": "4.1.0", "license": "MIT", @@ -25510,6 +25514,9 @@ "eslint-config-prettier": "8.3.0", "eslint-config-react-app": "7.0.0" }, + "devDependencies": { + "eslint-plugin-deephaven": "file:./plugin" + }, "peerDependencies": { "@typescript-eslint/eslint-plugin": "^5.46.0", "@typescript-eslint/parser": "^5.46.0", @@ -25520,6 +25527,9 @@ "eslint-plugin-react-refresh": "0.3.4" } }, + "packages/eslint-config/plugin": { + "dev": true + }, "packages/file-explorer": { "name": "@deephaven/file-explorer", "version": "0.46.1", @@ -27380,7 +27390,8 @@ "requires": { "eslint-config-airbnb": "^19.0.4", "eslint-config-prettier": "8.3.0", - "eslint-config-react-app": "7.0.0" + "eslint-config-react-app": "7.0.0", + "eslint-plugin-deephaven": "file:plugin" } }, "@deephaven/file-explorer": { @@ -34040,6 +34051,9 @@ } } }, + "eslint-plugin-deephaven": { + "version": "file:packages/eslint-config/plugin" + }, "eslint-plugin-es": { "version": "4.1.0", "peer": true, diff --git a/packages/eslint-config/README.md b/packages/eslint-config/README.md index 022ff47901..43538216dc 100644 --- a/packages/eslint-config/README.md +++ b/packages/eslint-config/README.md @@ -16,6 +16,25 @@ In your package.json, add a `eslintConfig` configuration: "eslintConfig": { "extends": "@deephaven/eslint-config" } ``` +## Deephaven Eslint Plugin (eslint-plugin-deephaven) +The `deephaven` eslint plugin is defined inside of the `plugin` folder. There is +a `devDependency` mapping in `package.json` which allows it to be consumed in an +eslint config file using the name `deephaven`. + +```json +"devDependencies": { + "eslint-plugin-deephaven": "file:./plugin" +}, +``` + +e.g. +```javascript +module.exports = { + root: true, + plugins: ['deephaven'], +} +``` + # Legal Notices Deephaven Data Labs and any contributors grant you a license to the content of this repository under the Apache 2.0 License, see the [LICENSE](../../LICENSE) file. diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index 3eaba1e810..9c6f6686cf 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -14,6 +14,9 @@ "eslint-config-prettier": "8.3.0", "eslint-config-react-app": "7.0.0" }, + "devDependencies": { + "eslint-plugin-deephaven": "file:./plugin" + }, "peerDependencies": { "@typescript-eslint/eslint-plugin": "^5.46.0", "@typescript-eslint/parser": "^5.46.0", diff --git a/packages/eslint-config/plugin/index.js b/packages/eslint-config/plugin/index.js new file mode 100644 index 0000000000..9367aed951 --- /dev/null +++ b/packages/eslint-config/plugin/index.js @@ -0,0 +1,7 @@ +const noSelfPackageImportRule = require('./no-self-package-import'); + +const plugin = { + rules: { 'no-self-package-import': noSelfPackageImportRule }, +}; + +module.exports = plugin; diff --git a/packages/eslint-config/plugin/no-self-package-import.js b/packages/eslint-config/plugin/no-self-package-import.js new file mode 100644 index 0000000000..c0d31650a8 --- /dev/null +++ b/packages/eslint-config/plugin/no-self-package-import.js @@ -0,0 +1,60 @@ +/** + * Derive the `@deephaven` package name from the given path. + * Note that this assumes that the package folder name uses the same naming + * convention as the package.json `name` field. + */ +function deriveDeephavenPackageNameFromPath(path) { + // In the off chance that there is a packages folder higher up in the path, + // find the deepest one. + const packageFolderNameI = path.lastIndexOf('packages'); + + if (packageFolderNameI === -1) { + return null; + } + + // Find the path token immediately after the `packages` folder. + const [packageFolderName] = path + .substring(packageFolderNameI + 'packages'.length + 1) + .split('/'); + + if (packageFolderName === '') { + return null; + } + + return `@deephaven/${packageFolderName}`; +} + +/** + * Custom eslint rule that forbids importing from the same `@deephaven` package + * that owns the module. Note that this rule is only useful within the `@deephaven` + * web-client-ui monorepo. + */ +module.exports = { + meta: { + type: 'problem', + docs: { + description: + 'Forbid importing from the same `@deephaven` package that owns this module.', + }, + }, + create: function create(context) { + const filePath = context.getFilename(); + + const packageName = deriveDeephavenPackageNameFromPath(filePath); + if (packageName == null) { + return {}; + } + + return { + ImportDeclaration(node) { + if (node.source.value.includes(packageName)) { + context.report({ + node, + message: + 'Forbid importing from the same `@deephaven` package that owns this module.', + }); + } + }, + }; + }, +}; diff --git a/packages/jsapi-utils/src/SessionUtils.ts b/packages/jsapi-utils/src/SessionUtils.ts index 264dc5cf87..f1577b32e6 100644 --- a/packages/jsapi-utils/src/SessionUtils.ts +++ b/packages/jsapi-utils/src/SessionUtils.ts @@ -5,12 +5,9 @@ import type { IdeConnection, IdeSession, } from '@deephaven/jsapi-types'; -import { - requestParentResponse, - SESSION_DETAILS_REQUEST, -} from '@deephaven/jsapi-utils'; import Log from '@deephaven/log'; import shortid from 'shortid'; +import { requestParentResponse, SESSION_DETAILS_REQUEST } from './MessageUtils'; import NoConsolesError, { isNoConsolesError } from './NoConsolesError'; const log = Log.module('SessionUtils');