From 760db946dd9c3b23af69f2036b7a8c11e38307b4 Mon Sep 17 00:00:00 2001 From: Valery Bugakov Date: Mon, 22 May 2023 04:05:45 -0700 Subject: [PATCH] bazel: implement custom ESLint Bazel rule (#52062) - Upgraded `aspect_bazel_lib`, `aspect_rules_js` and `aspect_rules_ts` to the latest versions. - Ran [bazel run //.aspect/bazelrc:update_aspect_bazelrc_presets](https://github.com/sourcegraph/sourcegraph/pull/52062/commits/40a74223852218a7bf6145818ca6c7e1ad15e4b0) - Added `eslint_config` macro for client package eslint configuration `js_library` targets. - Implemented the custom ESLint rule, which copies `srcs` with dependencies and **declarations** to the Bazel to lint them. This way, we maintain the ability to do type-aware linting in Bazel. - Added a custom ESLint formatter used in Bazel to print out relative paths in ESLint reports. In the follow-up PR, I will look into improvements suggested by @alexeagle that should allow us to convert ESLint build targets into test targets and gracefully manage linting failures. ## Test plan 1. CI 2. `bazel build $(bazel query 'kind("_eslint_test_with_types", //client/...)')` --- .aspect/bazelrc/ci.bazelrc | 10 +- .aspect/bazelrc/performance.bazelrc | 14 -- .eslintignore | 6 + .eslintrc.js | 4 + BUILD.bazel | 63 +++++++++ WORKSPACE | 38 ++++-- client/app-shell/.eslintignore | 1 + client/app-shell/.eslintrc.js | 1 - client/app-shell/BUILD.bazel | 8 +- client/app-shell/src/app-shell.tsx | 12 +- client/branded/BUILD.bazel | 3 + client/browser/BUILD.bazel | 7 + client/build-config/BUILD.bazel | 3 + client/client-api/BUILD.bazel | 3 + client/codeintellify/BUILD.bazel | 3 + client/cody-cli/BUILD.bazel | 3 + client/cody-shared/.eslintignore | 2 + client/cody-shared/BUILD.bazel | 5 +- client/cody-slack/BUILD.bazel | 3 + client/cody-ui/BUILD.bazel | 3 + client/cody-web/BUILD.bazel | 3 + client/cody/.eslintrc.js | 7 +- client/cody/BUILD.bazel | 12 +- client/cody/scripts/.eslintrc.js | 10 -- client/cody/test/integration/.eslintrc.js | 10 -- client/cody/test/integration/BUILD.bazel | 1 + client/cody/test/integration/tsconfig.json | 2 +- client/common/BUILD.bazel | 3 + client/eslint-plugin-wildcard/BUILD.bazel | 13 +- client/extension-api-types/.eslintignore | 2 + client/extension-api-types/BUILD.bazel | 7 +- client/extension-api/.eslintignore | 2 + client/extension-api/BUILD.bazel | 11 +- client/http-client/BUILD.bazel | 3 + client/jetbrains/BUILD.bazel | 3 + client/observability-client/BUILD.bazel | 3 + client/observability-server/BUILD.bazel | 3 + client/shared/BUILD.bazel | 3 + client/storybook/BUILD.bazel | 3 + client/template-parser/BUILD.bazel | 3 + client/testing/BUILD.bazel | 3 + client/vscode/.eslintignore | 4 +- client/vscode/BUILD.bazel | 3 + client/web/.eslintrc.js | 1 + client/web/BUILD.bazel | 14 +- client/web/src/integration/BUILD.bazel | 1 + client/wildcard/BUILD.bazel | 5 +- .../Button/story/ButtonVariants.tsx | 8 +- dev/defs.bzl | 22 ++- dev/eslint.bzl | 122 +++++++++++++++++ dev/js_lib.bzl | 126 ++++++++++++++++++ eslint-relative-formatter.js | 103 ++++++++++++++ package.json | 2 + pnpm-lock.yaml | 6 + 54 files changed, 638 insertions(+), 78 deletions(-) create mode 100644 client/app-shell/.eslintignore create mode 100644 client/cody-shared/.eslintignore delete mode 100644 client/cody/scripts/.eslintrc.js delete mode 100644 client/cody/test/integration/.eslintrc.js create mode 100644 client/extension-api-types/.eslintignore create mode 100644 client/extension-api/.eslintignore create mode 100644 dev/eslint.bzl create mode 100644 dev/js_lib.bzl create mode 100644 eslint-relative-formatter.js diff --git a/.aspect/bazelrc/ci.bazelrc b/.aspect/bazelrc/ci.bazelrc index 5c17b99f6e7c..4d91ee0932a2 100644 --- a/.aspect/bazelrc/ci.bazelrc +++ b/.aspect/bazelrc/ci.bazelrc @@ -3,7 +3,7 @@ # or split up into multiple test targets with sharding or manually. # Set this flag to exclude targets that have their timeout set to eternal (>15m) from running on CI. # Docs: https://bazel.build/docs/user-manual#test-timeout-filters -build --test_timeout_filters=-eternal +test --test_timeout_filters=-eternal # Set this flag to enable re-tries of failed tests on CI. # When any test target fails, try one or more times. This applies regardless of whether the "flaky" @@ -16,7 +16,7 @@ build --test_timeout_filters=-eternal # is more likely to get fixed. # Note that when passing after the first attempt, Bazel will give a special "FLAKY" status. # Docs: https://bazel.build/docs/user-manual#flaky-test-attempts -build --flaky_test_attempts=2 +test --flaky_test_attempts=2 # Announce all announces command options read from the bazelrc file(s) when starting up at the # beginning of each Bazel invocation. This is very useful on CI to be able to inspect what Bazel rc @@ -29,9 +29,11 @@ build --announce_rc # Docs: https://bazel.build/docs/user-manual#show-timestamps build --show_timestamps -# Only show progress every 5 seconds on CI. +# Only show progress every 60 seconds on CI. +# We want to find a compromise between printing often enough to show that the build isn't stuck, +# but not so often that we produce a long log file that requires a lot of scrolling. # https://bazel.build/reference/command-line-reference#flag--show_progress_rate_limit -build --show_progress_rate_limit=5 +build --show_progress_rate_limit=60 # Use cursor controls in screen output. # Docs: https://bazel.build/docs/user-manual#curses diff --git a/.aspect/bazelrc/performance.bazelrc b/.aspect/bazelrc/performance.bazelrc index 9b9903abb9e9..fff4c7c5eed2 100644 --- a/.aspect/bazelrc/performance.bazelrc +++ b/.aspect/bazelrc/performance.bazelrc @@ -1,17 +1,3 @@ -# Merkle tree calculations will be memoized to improve the remote cache hit checking speed. The -# memory foot print of the cache is controlled by `--experimental_remote_merkle_tree_cache_size`. -# Docs: https://bazel.build/reference/command-line-reference#flag--experimental_remote_merkle_tree_cache -build --experimental_remote_merkle_tree_cache -query --experimental_remote_merkle_tree_cache - -# The number of Merkle trees to memoize to improve the remote cache hit checking speed. Even though -# the cache is automatically pruned according to Java's handling of soft references, out-of-memory -# errors can occur if set too high. If set to 0 the cache size is unlimited. Optimal value varies -# depending on project's size. -# Docs: https://bazel.build/reference/command-line-reference#flag--experimental_remote_merkle_tree_cache_size -build --experimental_remote_merkle_tree_cache_size=1000 -query --experimental_remote_merkle_tree_cache_size=1000 - # Speed up all builds by not checking if output files have been modified. Lets you make changes to # the output tree without triggering a build for local debugging. For example, you can modify # [rules_js](https://github.com/aspect-build/rules_js) 3rd party npm packages in the output tree diff --git a/.eslintignore b/.eslintignore index 344a5b571636..b9059e2b36a0 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,7 @@ +**/graphql-operations.ts +**/node_modules/** out/ +dist/ src/schema/* src/graphql-operations.ts GH2SG.bookmarklet.js @@ -7,3 +10,6 @@ svelte.config.js vite.config.ts playwright.config.ts .vscode-test +**/*.json +**/*.d.ts +eslint-relative-formatter.js diff --git a/.eslintrc.js b/.eslintrc.js index 06e40f0c5106..693c431c1b84 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -221,6 +221,10 @@ See https://handbook.sourcegraph.com/community/faq#is-all-of-sourcegraph-open-so 'never', { schema: 'always', + scss: 'always', + css: 'always', + yaml: 'always', + svg: 'always', }, ], 'import/order': 'off', diff --git a/BUILD.bazel b/BUILD.bazel index fece32fe8a12..34a3cc8b2fae 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -7,6 +7,7 @@ load("@io_bazel_rules_go//proto:compiler.bzl", "go_proto_compiler") load("@io_bazel_rules_go//proto/wkt:well_known_types.bzl", "WELL_KNOWN_TYPES_APIV2") load("@npm//:defs.bzl", "npm_link_all_packages") load("//dev/linters/staticcheck:analyzers.bzl", "STATIC_CHECK_ANALYZERS") +load("@npm//:eslint/package_json.bzl", eslint_bin = "bin") # Gazelle config # @@ -24,6 +25,12 @@ package(default_visibility = ["//visibility:public"]) npm_link_all_packages(name = "node_modules") +eslint_bin.eslint_binary( + name = "eslint", + testonly = True, + visibility = ["//visibility:public"], +) + js_library( name = "prettier_config_js", srcs = ["prettier.config.js"], @@ -41,6 +48,15 @@ ts_config( ], ) +ts_config( + name = "tsconfig-eslint", + src = "tsconfig.eslint.json", + visibility = ["//visibility:public"], + deps = [ + ":tsconfig", + ], +) + gazelle_binary( name = "gazelle-buf", languages = [ @@ -206,6 +222,53 @@ js_library( visibility = ["//visibility:public"], ) +js_library( + name = "eslint-relative-formatter", + srcs = [ + "eslint-relative-formatter.js", + ], + deps = [ + ":node_modules/chalk", + ":node_modules/strip-ansi", + ":node_modules/text-table", + ], +) + +js_library( + name = "eslint_config", + testonly = True, + srcs = [ + ".eslintrc.js", + ], + data = [ + ".eslintignore", + ":eslint-relative-formatter", + ], + deps = [ + ":node_modules/@sourcegraph/eslint-config", + ":node_modules/@sourcegraph/eslint-plugin-sourcegraph", + ":node_modules/@sourcegraph/eslint-plugin-wildcard", + ":node_modules/@typescript-eslint/eslint-plugin", + ":node_modules/eslint-config-prettier", + ":node_modules/eslint-import-resolver-node", + ":node_modules/eslint-plugin-ban", + ":node_modules/eslint-plugin-etc", + ":node_modules/eslint-plugin-import", + ":node_modules/eslint-plugin-jest-dom", + ":node_modules/eslint-plugin-jsdoc", + ":node_modules/eslint-plugin-jsx-a11y", + ":node_modules/eslint-plugin-monorepo", + ":node_modules/eslint-plugin-react", + ":node_modules/eslint-plugin-react-hooks", + ":node_modules/eslint-plugin-rxjs", + ":node_modules/eslint-plugin-unicorn", + ":node_modules/eslint-plugin-unused-imports", + ":node_modules/react", # required to resolve the react version for eslint-plugin-react + ":tsconfig-eslint", + "//:node_modules/tslib", + ], +) + # Go # nogo config diff --git a/WORKSPACE b/WORKSPACE index 31e68e9bfd3e..d1871e11e85a 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -15,23 +15,23 @@ bazel_skylib_workspace() http_archive( name = "aspect_bazel_lib", - sha256 = "2518c757715d4f5fc7cc7e0a68742dd1155eaafc78fb9196b8a18e13a738cea2", - strip_prefix = "bazel-lib-1.28.0", - url = "https://github.com/aspect-build/bazel-lib/releases/download/v1.28.0/bazel-lib-v1.28.0.tar.gz", + sha256 = "0da75299c5a52737b2ac39458398b3f256e41a1a6748e5457ceb3a6225269485", + strip_prefix = "bazel-lib-1.31.2", + url = "https://github.com/aspect-build/bazel-lib/releases/download/v1.31.2/bazel-lib-v1.31.2.tar.gz", ) http_archive( name = "aspect_rules_js", - sha256 = "3e237129b3554373a80c681c4b47348f91c294ff32d4bc8f8297f40511a4eb6c", - strip_prefix = "rules_js-1.25.4", - url = "https://github.com/aspect-build/rules_js/releases/download/v1.25.4/rules_js-v1.25.4.tar.gz", + sha256 = "08061ba5e5e7f4b1074538323576dac819f9337a0c7d75aee43afc8ae7cb6e18", + strip_prefix = "rules_js-1.26.1", + url = "https://github.com/aspect-build/rules_js/releases/download/v1.26.1/rules_js-v1.26.1.tar.gz", ) http_archive( name = "aspect_rules_ts", - sha256 = "02480b6a1cd12516edf364e678412e9da10445fe3f1070c014ac75e922c969ea", - strip_prefix = "rules_ts-1.3.1", - url = "https://github.com/aspect-build/rules_ts/releases/download/v1.3.1/rules_ts-v1.3.1.tar.gz", + sha256 = "ace5b609603d9b5b875d56c9c07182357c4ee495030f40dcefb10d443ba8c208", + strip_prefix = "rules_ts-1.4.0", + url = "https://github.com/aspect-build/rules_ts/releases/download/v1.4.0/rules_ts-v1.4.0.tar.gz", ) http_archive( @@ -95,6 +95,26 @@ npm_translate_lock( npm_package_target_name = "{dirname}_pkg", npmrc = "//:.npmrc", pnpm_lock = "//:pnpm-lock.yaml", + # Required for ESLint test targets. + # See https://github.com/aspect-build/rules_js/issues/239 + # See `public-hoist-pattern[]=*eslint*` in the `.npmrc` of this monorepo. + public_hoist_packages = { + "@typescript-eslint/eslint-plugin": [""], + "@typescript-eslint/parser@5.56.0_qxbo2xm47qt6fxnlmgbosp4hva": [""], + "eslint-config-prettier": [""], + "eslint-plugin-ban": [""], + "eslint-plugin-etc": [""], + "eslint-plugin-import": [""], + "eslint-plugin-jest-dom": [""], + "eslint-plugin-jsdoc": [""], + "eslint-plugin-jsx-a11y": [""], + "eslint-plugin-react@7.32.1_eslint_8.34.0": [""], + "eslint-plugin-react-hooks": [""], + "eslint-plugin-rxjs": [""], + "eslint-plugin-unicorn": [""], + "eslint-plugin-unused-imports": [""], + "eslint-import-resolver-node": [""], + }, verify_node_modules_ignored = "//:.bazelignore", ) diff --git a/client/app-shell/.eslintignore b/client/app-shell/.eslintignore new file mode 100644 index 000000000000..849ddff3b7ec --- /dev/null +++ b/client/app-shell/.eslintignore @@ -0,0 +1 @@ +dist/ diff --git a/client/app-shell/.eslintrc.js b/client/app-shell/.eslintrc.js index fab2e04e1c3c..9c55988d7986 100644 --- a/client/app-shell/.eslintrc.js +++ b/client/app-shell/.eslintrc.js @@ -12,7 +12,6 @@ module.exports = { { files: 'dev/**/*.ts', rules: { - 'no-console': 'off', 'no-sync': 'off', }, }, diff --git a/client/app-shell/BUILD.bazel b/client/app-shell/BUILD.bazel index e1ab772b6439..1eb01a3ae180 100644 --- a/client/app-shell/BUILD.bazel +++ b/client/app-shell/BUILD.bazel @@ -1,9 +1,12 @@ load("@aspect_rules_ts//ts:defs.bzl", "ts_config") load("@npm//:defs.bzl", "npm_link_all_packages") load("//dev:defs.bzl", "ts_project") +load("//dev:eslint.bzl", "eslint_config") npm_link_all_packages(name = "node_modules") +eslint_config() + ts_config( name = "tsconfig", src = "tsconfig.json", @@ -19,5 +22,8 @@ ts_project( name = "app-shell", srcs = ["src/app-shell.tsx"], tsconfig = ":tsconfig", - deps = ["//:node_modules/@tauri-apps/api"], + deps = [ + ":node_modules/@sourcegraph/common", + "//:node_modules/@tauri-apps/api", + ], ) diff --git a/client/app-shell/src/app-shell.tsx b/client/app-shell/src/app-shell.tsx index 97b4b3411a26..96fd472ba140 100644 --- a/client/app-shell/src/app-shell.tsx +++ b/client/app-shell/src/app-shell.tsx @@ -1,6 +1,8 @@ import { listen, Event } from '@tauri-apps/api/event' import { invoke } from '@tauri-apps/api/tauri' +import { logger } from '@sourcegraph/common' + // Sourcegraph desktop app entrypoint. There are two: // // * app-shell.tsx: before the Go backend has started, this is served. If the Go backend crashes, @@ -21,7 +23,7 @@ async function getLaunchPathFromTauri(): Promise { async function launchWithSignInUrl(url: string): Promise { const launchPath = await getLaunchPathFromTauri() if (launchPath) { - console.log('Using launch path:', launchPath) + logger.log('Using launch path:', launchPath) url = addRedirectParamToSignInUrl(url, launchPath) } window.location.href = url @@ -35,16 +37,16 @@ const appShellReady = (payload: AppShellReadyPayload): void => { if (!payload) { return } - console.log('app-shell-ready', payload) + logger.log('app-shell-ready', payload) launchWithSignInUrl(payload.sign_in_url).catch(error => console.error(`failed to launch with sign-in URL: ${error}`) ) } listen('app-shell-ready', (event: Event) => appShellReady(event.payload)) - .then(() => console.log('registered app-shell-ready handler')) - .catch(error => console.error(`failed to register app-shell-ready handler: ${error}`)) + .then(() => logger.log('registered app-shell-ready handler')) + .catch(error => logger.error(`failed to register app-shell-ready handler: ${error}`)) await invoke('app_shell_loaded') .then(payload => appShellReady(payload as AppShellReadyPayload)) - .catch(error => console.error(`failed to inform Tauri app_shell_loaded: ${error}`)) + .catch(error => logger.error(`failed to inform Tauri app_shell_loaded: ${error}`)) diff --git a/client/branded/BUILD.bazel b/client/branded/BUILD.bazel index 8490560c160b..78d4edb178c4 100644 --- a/client/branded/BUILD.bazel +++ b/client/branded/BUILD.bazel @@ -1,6 +1,7 @@ load("@npm//:defs.bzl", "npm_link_all_packages") load("//dev:defs.bzl", "jest_test", "npm_package", "sass", "ts_project") load("//client/shared/dev:tools.bzl", "module_style_typings") +load("//dev:eslint.bzl", "eslint_config") # TODO(bazel): storybook build # gazelle:exclude **/*.story.{ts,tsx} @@ -11,6 +12,8 @@ load("@aspect_rules_ts//ts:defs.bzl", "ts_config") npm_link_all_packages(name = "node_modules") +eslint_config() + ts_config( name = "tsconfig", src = "tsconfig.json", diff --git a/client/browser/BUILD.bazel b/client/browser/BUILD.bazel index c0f99ff57c61..4b8af13d092d 100644 --- a/client/browser/BUILD.bazel +++ b/client/browser/BUILD.bazel @@ -7,6 +7,7 @@ load("//client/shared/dev:generate_graphql_operations.bzl", "generate_graphql_op load("//client/shared/dev:build_code_intel_extensions.bzl", "build_code_intel_extensions") load("//client/shared/dev:tools.bzl", "module_style_typings") load("@aspect_bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory") +load("//dev:eslint.bzl", "eslint_config") # TODO(bazel): storybook build # gazelle:exclude **/*.story.{ts,tsx} @@ -21,6 +22,12 @@ load("@aspect_bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory") npm_link_all_packages(name = "node_modules") +eslint_config( + deps = [ + "//client/browser/src/end-to-end:tsconfig", + ], +) + ts_config( name = "tsconfig", src = "tsconfig.json", diff --git a/client/build-config/BUILD.bazel b/client/build-config/BUILD.bazel index 860bd1e61852..a0aa939930ae 100644 --- a/client/build-config/BUILD.bazel +++ b/client/build-config/BUILD.bazel @@ -1,12 +1,15 @@ load("@aspect_rules_ts//ts:defs.bzl", "ts_config") load("@npm//:defs.bzl", "npm_link_all_packages") load("//dev:defs.bzl", "npm_package", "ts_project") +load("//dev:eslint.bzl", "eslint_config") # TODO(bazel): currently handled with #keep # gazelle:js_resolve **/esbuild/* //client/build-config/src/esbuild npm_link_all_packages(name = "node_modules") +eslint_config() + ts_config( name = "tsconfig", src = "tsconfig.json", diff --git a/client/client-api/BUILD.bazel b/client/client-api/BUILD.bazel index 24874d5b33db..29498486c795 100644 --- a/client/client-api/BUILD.bazel +++ b/client/client-api/BUILD.bazel @@ -1,9 +1,12 @@ load("@npm//:defs.bzl", "npm_link_all_packages") load("//dev:defs.bzl", "npm_package", "ts_project") load("@aspect_rules_ts//ts:defs.bzl", "ts_config") +load("//dev:eslint.bzl", "eslint_config") npm_link_all_packages(name = "node_modules") +eslint_config() + ts_config( name = "tsconfig", src = "tsconfig.json", diff --git a/client/codeintellify/BUILD.bazel b/client/codeintellify/BUILD.bazel index 9cab04b2461d..ab675d1eaafa 100644 --- a/client/codeintellify/BUILD.bazel +++ b/client/codeintellify/BUILD.bazel @@ -1,9 +1,12 @@ load("@aspect_rules_ts//ts:defs.bzl", "ts_config") load("@npm//:defs.bzl", "npm_link_all_packages") load("//dev:defs.bzl", "jest_test", "npm_package", "ts_project") +load("//dev:eslint.bzl", "eslint_config") npm_link_all_packages(name = "node_modules") +eslint_config() + ts_config( name = "tsconfig", src = "tsconfig.json", diff --git a/client/cody-cli/BUILD.bazel b/client/cody-cli/BUILD.bazel index 2fc0644d43fe..fb40ee3f14e6 100644 --- a/client/cody-cli/BUILD.bazel +++ b/client/cody-cli/BUILD.bazel @@ -1,9 +1,12 @@ load("@aspect_rules_ts//ts:defs.bzl", "ts_config") load("@npm//:defs.bzl", "npm_link_all_packages") load("//dev:defs.bzl", "ts_project") +load("//dev:eslint.bzl", "eslint_config") npm_link_all_packages(name = "node_modules") +eslint_config() + ts_config( name = "tsconfig", src = "tsconfig.json", diff --git a/client/cody-shared/.eslintignore b/client/cody-shared/.eslintignore new file mode 100644 index 000000000000..3cf525c29b07 --- /dev/null +++ b/client/cody-shared/.eslintignore @@ -0,0 +1,2 @@ +/dist/ +/out/ diff --git a/client/cody-shared/BUILD.bazel b/client/cody-shared/BUILD.bazel index ddb93497ce58..27e51b03ec7a 100644 --- a/client/cody-shared/BUILD.bazel +++ b/client/cody-shared/BUILD.bazel @@ -1,10 +1,13 @@ load("@aspect_rules_ts//ts:defs.bzl", "ts_config") load("@npm//:defs.bzl", "npm_link_all_packages") load("//dev:defs.bzl", "npm_package", "ts_project") +load("//dev:eslint.bzl", "eslint_config") + +# gazelle:js_resolve vscode //:node_modules/@vscode npm_link_all_packages(name = "node_modules") -# gazelle:js_resolve vscode //:node_modules/@vscode +eslint_config() ts_config( name = "tsconfig", diff --git a/client/cody-slack/BUILD.bazel b/client/cody-slack/BUILD.bazel index f83522e9ace6..bedd5d1a15db 100644 --- a/client/cody-slack/BUILD.bazel +++ b/client/cody-slack/BUILD.bazel @@ -2,9 +2,12 @@ load("@aspect_rules_js//npm:defs.bzl", "npm_package") load("@aspect_rules_ts//ts:defs.bzl", "ts_config") load("@npm//:defs.bzl", "npm_link_all_packages") load("//dev:defs.bzl", "ts_project") +load("//dev:eslint.bzl", "eslint_config") npm_link_all_packages(name = "node_modules") +eslint_config() + ts_config( name = "tsconfig", src = "tsconfig.json", diff --git a/client/cody-ui/BUILD.bazel b/client/cody-ui/BUILD.bazel index 76f5eb5a3cba..3a76c8940d22 100644 --- a/client/cody-ui/BUILD.bazel +++ b/client/cody-ui/BUILD.bazel @@ -1,11 +1,14 @@ load("@aspect_rules_ts//ts:defs.bzl", "ts_config") load("@npm//:defs.bzl", "npm_link_all_packages") load("//dev:defs.bzl", "npm_package", "ts_project") +load("//dev:eslint.bzl", "eslint_config") # gazelle:js_ignore_imports **/*.css npm_link_all_packages(name = "node_modules") +eslint_config() + ts_config( name = "tsconfig", src = "tsconfig.json", diff --git a/client/cody-web/BUILD.bazel b/client/cody-web/BUILD.bazel index a29c3b31423d..589f99609224 100644 --- a/client/cody-web/BUILD.bazel +++ b/client/cody-web/BUILD.bazel @@ -1,11 +1,14 @@ load("//dev:defs.bzl", "ts_project") load("@aspect_rules_ts//ts:defs.bzl", "ts_config") load("@npm//:defs.bzl", "npm_link_all_packages") +load("//dev:eslint.bzl", "eslint_config") # gazelle:js_ignore_imports **/*.css npm_link_all_packages(name = "node_modules") +eslint_config() + ts_config( name = "tsconfig", src = "tsconfig.json", diff --git a/client/cody/.eslintrc.js b/client/cody/.eslintrc.js index d9b3e9ab62c1..c1db82275d60 100644 --- a/client/cody/.eslintrc.js +++ b/client/cody/.eslintrc.js @@ -1,11 +1,16 @@ // @ts-check const baseConfig = require('../../.eslintrc') + module.exports = { extends: '../../.eslintrc.js', parserOptions: { ...baseConfig.parserOptions, - project: [__dirname + '/tsconfig.json'], + project: [ + __dirname + '/tsconfig.json', + __dirname + '/test/integration/tsconfig.json', + __dirname + '/scripts/tsconfig.json', + ], }, overrides: baseConfig.overrides, rules: { diff --git a/client/cody/BUILD.bazel b/client/cody/BUILD.bazel index 8570191d1f86..1e33b1b31ded 100644 --- a/client/cody/BUILD.bazel +++ b/client/cody/BUILD.bazel @@ -1,14 +1,22 @@ load("@aspect_rules_ts//ts:defs.bzl", "ts_config") load("@npm//:defs.bzl", "npm_link_all_packages") load("//dev:defs.bzl", "ts_project") - -npm_link_all_packages(name = "node_modules") +load("//dev:eslint.bzl", "eslint_config") # gazelle:js_resolve vscode //:node_modules/@vscode # gazelle:js_files src/**/*.{ts,tsx} # gazelle:js_files webviews/**/*.{ts,tsx} # gazelle:js_ignore_imports **/*.css +npm_link_all_packages(name = "node_modules") + +eslint_config( + deps = [ + "//client/cody/scripts:tsconfig", + "//client/cody/test/integration:tsconfig", + ], +) + ts_config( name = "tsconfig", src = "tsconfig.json", diff --git a/client/cody/scripts/.eslintrc.js b/client/cody/scripts/.eslintrc.js deleted file mode 100644 index 565faebc965c..000000000000 --- a/client/cody/scripts/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// eslint-disable-next-line import/extensions -const baseConfig = require('../.eslintrc.js') -module.exports = { - extends: '../.eslintrc.js', - parserOptions: { - ...baseConfig.parserOptions, - project: [__dirname + '/tsconfig.json'], - }, - overrides: baseConfig.overrides, -} diff --git a/client/cody/test/integration/.eslintrc.js b/client/cody/test/integration/.eslintrc.js deleted file mode 100644 index 2f274980f694..000000000000 --- a/client/cody/test/integration/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -// @ts-check - -const baseConfig = require('../../.eslintrc') -module.exports = { - extends: '../../.eslintrc.js', - parserOptions: { - ...baseConfig.parserOptions, - project: [__dirname + '/tsconfig.json'], - }, -} diff --git a/client/cody/test/integration/BUILD.bazel b/client/cody/test/integration/BUILD.bazel index 1893c047f59e..cffb3167308f 100644 --- a/client/cody/test/integration/BUILD.bazel +++ b/client/cody/test/integration/BUILD.bazel @@ -12,6 +12,7 @@ load("//dev:defs.bzl", "ts_project") ts_config( name = "tsconfig", src = "tsconfig.json", + visibility = ["//client:__subpackages__"], deps = [ "//client/cody:tsconfig", ], diff --git a/client/cody/test/integration/tsconfig.json b/client/cody/test/integration/tsconfig.json index 66ec62303c2f..8366f74a42cc 100644 --- a/client/cody/test/integration/tsconfig.json +++ b/client/cody/test/integration/tsconfig.json @@ -9,6 +9,6 @@ "jsx": "react-jsx", }, "references": [{ "path": "../../" }], - "include": ["*", "**/*", ".eslintrc.js", "../fixtures/mock-server.ts"], + "include": ["*", "**/*", "../fixtures/mock-server.ts"], "exclude": ["out", "testdata"], } diff --git a/client/common/BUILD.bazel b/client/common/BUILD.bazel index 6df7ee2b2f4f..91e7d1de1cb6 100644 --- a/client/common/BUILD.bazel +++ b/client/common/BUILD.bazel @@ -1,9 +1,12 @@ load("@aspect_rules_ts//ts:defs.bzl", "ts_config") load("@npm//:defs.bzl", "npm_link_all_packages") load("//dev:defs.bzl", "jest_test", "npm_package", "ts_project") +load("//dev:eslint.bzl", "eslint_config") npm_link_all_packages(name = "node_modules") +eslint_config() + ts_config( name = "tsconfig", src = "tsconfig.json", diff --git a/client/eslint-plugin-wildcard/BUILD.bazel b/client/eslint-plugin-wildcard/BUILD.bazel index 39bac756d42c..50717cbc74f7 100644 --- a/client/eslint-plugin-wildcard/BUILD.bazel +++ b/client/eslint-plugin-wildcard/BUILD.bazel @@ -1,11 +1,20 @@ load("@npm//:defs.bzl", "npm_link_all_packages") load("//dev:defs.bzl", "npm_package") +load("@aspect_rules_js//js:defs.bzl", "js_library") -# gazelle:js_files **/*.ts +# gazelle:js_files **/*.js npm_link_all_packages(name = "node_modules") +js_library( + name = "eslint-plugin-wildcard_lib", + srcs = glob(["lib/**/*.js"]), +) + npm_package( name = "eslint-plugin-wildcard_pkg", - srcs = ["package.json"], + srcs = [ + "eslint-plugin-wildcard_lib", # keep + "package.json", + ], ) diff --git a/client/extension-api-types/.eslintignore b/client/extension-api-types/.eslintignore new file mode 100644 index 000000000000..3cf525c29b07 --- /dev/null +++ b/client/extension-api-types/.eslintignore @@ -0,0 +1,2 @@ +/dist/ +/out/ diff --git a/client/extension-api-types/BUILD.bazel b/client/extension-api-types/BUILD.bazel index afc6de76f045..7f033124c686 100644 --- a/client/extension-api-types/BUILD.bazel +++ b/client/extension-api-types/BUILD.bazel @@ -1,12 +1,17 @@ load("@aspect_rules_ts//ts:defs.bzl", "ts_config") load("@npm//:defs.bzl", "npm_link_all_packages") load("//dev:defs.bzl", "npm_package") +load("//dev:eslint.bzl", "eslint_config") package(default_visibility = ["//visibility:public"]) # .dts-only library done manually # gazelle:js disabled +npm_link_all_packages(name = "node_modules") + +eslint_config() + ts_config( name = "tsconfig", src = "tsconfig.json", @@ -17,8 +22,6 @@ ts_config( ], ) -npm_link_all_packages(name = "node_modules") - npm_package( name = "extension-api-types_pkg", srcs = [ diff --git a/client/extension-api/.eslintignore b/client/extension-api/.eslintignore new file mode 100644 index 000000000000..3cf525c29b07 --- /dev/null +++ b/client/extension-api/.eslintignore @@ -0,0 +1,2 @@ +/dist/ +/out/ diff --git a/client/extension-api/BUILD.bazel b/client/extension-api/BUILD.bazel index dd17b9960b42..89435e2876e1 100644 --- a/client/extension-api/BUILD.bazel +++ b/client/extension-api/BUILD.bazel @@ -1,13 +1,18 @@ load("@aspect_rules_ts//ts:defs.bzl", "ts_config") load("@npm//:defs.bzl", "npm_link_all_packages") load("//dev:defs.bzl", "npm_package") - -package(default_visibility = ["//visibility:public"]) +load("//dev:eslint.bzl", "eslint_config") # Disable gazelle for the js/dts-only package # gazelle:js disabled # gazelle:js_files **/*.{ts,tsx} +package(default_visibility = ["//visibility:public"]) + +npm_link_all_packages(name = "node_modules") + +eslint_config() + ts_config( name = "tsconfig", src = "tsconfig.json", @@ -17,8 +22,6 @@ ts_config( ], ) -npm_link_all_packages(name = "node_modules") - npm_package( name = "extension-api_pkg", srcs = [ diff --git a/client/http-client/BUILD.bazel b/client/http-client/BUILD.bazel index db06a8df5a2e..1f82372f8ca0 100644 --- a/client/http-client/BUILD.bazel +++ b/client/http-client/BUILD.bazel @@ -1,6 +1,7 @@ load("@aspect_rules_ts//ts:defs.bzl", "ts_config") load("@npm//:defs.bzl", "npm_link_all_packages") load("//dev:defs.bzl", "jest_test", "npm_package", "ts_project") +load("//dev:eslint.bzl", "eslint_config") npm_link_all_packages(name = "node_modules") @@ -79,3 +80,5 @@ jest_test( ":http-client_tests", ], ) + +eslint_config() diff --git a/client/jetbrains/BUILD.bazel b/client/jetbrains/BUILD.bazel index c5bfe67ebe9b..a8546767cd15 100644 --- a/client/jetbrains/BUILD.bazel +++ b/client/jetbrains/BUILD.bazel @@ -1,12 +1,15 @@ load("@aspect_rules_js//js:defs.bzl", "js_library") load("@aspect_rules_ts//ts:defs.bzl", "ts_config") load("@npm//:defs.bzl", "npm_link_all_packages") +load("//dev:eslint.bzl", "eslint_config") # dts-only done manually # gazelle:js disabled npm_link_all_packages(name = "node_modules") +eslint_config() + ts_config( name = "tsconfig", src = "tsconfig.json", diff --git a/client/observability-client/BUILD.bazel b/client/observability-client/BUILD.bazel index a53c2e845fd5..6c471f7413e8 100644 --- a/client/observability-client/BUILD.bazel +++ b/client/observability-client/BUILD.bazel @@ -1,9 +1,12 @@ load("@aspect_rules_ts//ts:defs.bzl", "ts_config") load("@npm//:defs.bzl", "npm_link_all_packages") load("//dev:defs.bzl", "jest_test", "npm_package", "ts_project") +load("//dev:eslint.bzl", "eslint_config") npm_link_all_packages(name = "node_modules") +eslint_config() + ts_config( name = "tsconfig", src = "tsconfig.json", diff --git a/client/observability-server/BUILD.bazel b/client/observability-server/BUILD.bazel index 70d86572e897..350a7f6f153d 100644 --- a/client/observability-server/BUILD.bazel +++ b/client/observability-server/BUILD.bazel @@ -1,9 +1,12 @@ load("@aspect_rules_ts//ts:defs.bzl", "ts_config") load("@npm//:defs.bzl", "npm_link_all_packages") load("//dev:defs.bzl", "jest_test", "ts_project") +load("//dev:eslint.bzl", "eslint_config") npm_link_all_packages(name = "node_modules") +eslint_config() + ts_config( name = "tsconfig", src = "tsconfig.json", diff --git a/client/shared/BUILD.bazel b/client/shared/BUILD.bazel index f8a9bcbfe35a..952b773b38e3 100644 --- a/client/shared/BUILD.bazel +++ b/client/shared/BUILD.bazel @@ -5,6 +5,7 @@ load("//dev:defs.bzl", "jest_test", "npm_package", "sass", "ts_project") load("//client/shared/dev:generate_schema.bzl", "generate_schema") load("//client/shared/dev:generate_graphql_operations.bzl", "generate_graphql_operations") load("//client/shared/dev:tools.bzl", "module_style_typings") +load("//dev:eslint.bzl", "eslint_config") # TODO(bazel): storybook build # gazelle:exclude **/*.story.{ts,tsx} @@ -15,6 +16,8 @@ load("//client/shared/dev:tools.bzl", "module_style_typings") npm_link_all_packages(name = "node_modules") +eslint_config() + ts_config( name = "tsconfig", src = "tsconfig.json", diff --git a/client/storybook/BUILD.bazel b/client/storybook/BUILD.bazel index b52d44e17043..b49ad01f80bf 100644 --- a/client/storybook/BUILD.bazel +++ b/client/storybook/BUILD.bazel @@ -1,6 +1,7 @@ load("@npm//:defs.bzl", "npm_link_all_packages") load("//dev:defs.bzl", "npm_package", "sass", "ts_project") load("//client/shared/dev:tools.bzl", "module_style_typings") +load("//dev:eslint.bzl", "eslint_config") # gazelle:js_resolve **/*.module.scss :module_style_typings @@ -8,6 +9,8 @@ load("@aspect_rules_ts//ts:defs.bzl", "ts_config") npm_link_all_packages(name = "node_modules") +eslint_config() + ts_config( name = "tsconfig", src = "tsconfig.json", diff --git a/client/template-parser/BUILD.bazel b/client/template-parser/BUILD.bazel index 02db923469a4..85b9d045f4ac 100644 --- a/client/template-parser/BUILD.bazel +++ b/client/template-parser/BUILD.bazel @@ -1,9 +1,12 @@ load("@aspect_rules_ts//ts:defs.bzl", "ts_config") load("@npm//:defs.bzl", "npm_link_all_packages") load("//dev:defs.bzl", "npm_package", "ts_project") +load("//dev:eslint.bzl", "eslint_config") npm_link_all_packages(name = "node_modules") +eslint_config() + ts_config( name = "tsconfig", src = "tsconfig.json", diff --git a/client/testing/BUILD.bazel b/client/testing/BUILD.bazel index 2be7779db511..d90421089334 100644 --- a/client/testing/BUILD.bazel +++ b/client/testing/BUILD.bazel @@ -1,9 +1,12 @@ load("@aspect_rules_ts//ts:defs.bzl", "ts_config") load("@npm//:defs.bzl", "npm_link_all_packages") load("//dev:defs.bzl", "npm_package", "ts_project") +load("//dev:eslint.bzl", "eslint_config") npm_link_all_packages(name = "node_modules") +eslint_config() + ts_config( name = "tsconfig", src = "tsconfig.json", diff --git a/client/vscode/.eslintignore b/client/vscode/.eslintignore index 59ff3646eaf5..d344db7454c7 100644 --- a/client/vscode/.eslintignore +++ b/client/vscode/.eslintignore @@ -1,4 +1,6 @@ -src/graphql-operations.ts +**/graphql-operations.ts +graphql-operations.ts +**/node_modules/** dist/ package.json src/vendor/*.js diff --git a/client/vscode/BUILD.bazel b/client/vscode/BUILD.bazel index aec5d061c924..17c4c323aca7 100644 --- a/client/vscode/BUILD.bazel +++ b/client/vscode/BUILD.bazel @@ -4,6 +4,7 @@ load("@npm//:defs.bzl", "npm_link_all_packages") load("//dev:defs.bzl", "sass", "ts_project") load("//client/shared/dev:generate_graphql_operations.bzl", "generate_graphql_operations") load("//client/shared/dev:tools.bzl", "module_style_typings") +load("//dev:eslint.bzl", "eslint_config") # TODO(bazel): webpack workers? # gazelle:js_ignore_imports **/*.worker.ts @@ -13,6 +14,8 @@ load("//client/shared/dev:tools.bzl", "module_style_typings") npm_link_all_packages(name = "node_modules") +eslint_config() + ts_config( name = "tsconfig", src = "tsconfig.json", diff --git a/client/web/.eslintrc.js b/client/web/.eslintrc.js index 7f92ae15c569..1fe486f40deb 100644 --- a/client/web/.eslintrc.js +++ b/client/web/.eslintrc.js @@ -1,4 +1,5 @@ const baseConfig = require('../../.eslintrc') + module.exports = { extends: '../../.eslintrc.js', parserOptions: { diff --git a/client/web/BUILD.bazel b/client/web/BUILD.bazel index e286aba09a0f..9dc8736002ba 100644 --- a/client/web/BUILD.bazel +++ b/client/web/BUILD.bazel @@ -6,6 +6,8 @@ load("//client/shared/dev:generate_graphql_operations.bzl", "generate_graphql_op load("//client/shared/dev:tools.bzl", "module_style_typings") load("//dev:defs.bzl", "jest_test", "npm_package", "sass", "ts_project") load("//dev:webpack.bzl", "webpack_bundle", "webpack_devserver", "webpack_web_app") +load("//dev:eslint.bzl", "eslint_config") +load("@aspect_rules_ts//ts:defs.bzl", "ts_config") # TODO(bazel): storybook build # gazelle:exclude **/*.story.{ts,tsx} @@ -18,10 +20,16 @@ load("//dev:webpack.bzl", "webpack_bundle", "webpack_devserver", "webpack_web_ap # gazelle:js_ignore_imports **/*.worker.ts # gazelle:js_ignore_imports **/SourcegraphWebApp.scss -load("@aspect_rules_ts//ts:defs.bzl", "ts_config") - npm_link_all_packages(name = "node_modules") +eslint_config( + deps = [ + "//client/web/src/end-to-end:tsconfig", + "//client/web/src/integration:tsconfig", + "//client/web/src/regression:tsconfig", + ], +) + ts_config( name = "tsconfig", src = "tsconfig.json", @@ -2008,10 +2016,10 @@ ENTERPRISE_BUNDLE_DATA_DEPS = BUNDLE_DATA_DEPS + [ webpack_bundle( name = "bundle" if prod else "bundle-dev", srcs = BUNDLE_DATA_DEPS + [ + ":web_lib", "//:babel_config", "//:browserslist", "//:package_json", - ":web_lib", ], entry_points = { "src/main.js": "app", diff --git a/client/web/src/integration/BUILD.bazel b/client/web/src/integration/BUILD.bazel index 65793c7c2a92..158e862a1184 100644 --- a/client/web/src/integration/BUILD.bazel +++ b/client/web/src/integration/BUILD.bazel @@ -10,6 +10,7 @@ load("//dev:mocha.bzl", "mocha_test") ts_config( name = "tsconfig", src = "tsconfig.json", + visibility = ["//client:__subpackages__"], deps = [ "//client/shared/src/testing:tsconfig", "//client/web:tsconfig", diff --git a/client/wildcard/BUILD.bazel b/client/wildcard/BUILD.bazel index c5b24496db79..8ee26aed3541 100644 --- a/client/wildcard/BUILD.bazel +++ b/client/wildcard/BUILD.bazel @@ -3,6 +3,7 @@ load("@aspect_rules_ts//ts:defs.bzl", "ts_config") load("@npm//:defs.bzl", "npm_link_all_packages") load("//dev:defs.bzl", "jest_test", "npm_package", "sass", "ts_project") load("//client/shared/dev:tools.bzl", "module_style_typings") +load("//dev:eslint.bzl", "eslint_config") # TODO(bazel): storybook build # gazelle:exclude **/*.story.{ts,tsx} @@ -10,6 +11,8 @@ load("//client/shared/dev:tools.bzl", "module_style_typings") # gazelle:js_resolve **/*.module.scss :module_style_typings # gazelle:js_ignore_imports **/global-styles/index.scss +npm_link_all_packages(name = "node_modules") + ts_config( name = "tsconfig", src = "tsconfig.json", @@ -21,7 +24,7 @@ ts_config( ], ) -npm_link_all_packages(name = "node_modules") +eslint_config() module_style_typings( name = "module_style_typings", diff --git a/client/wildcard/src/components/Button/story/ButtonVariants.tsx b/client/wildcard/src/components/Button/story/ButtonVariants.tsx index 4d9562cae9d7..95e7251087ea 100644 --- a/client/wildcard/src/components/Button/story/ButtonVariants.tsx +++ b/client/wildcard/src/components/Button/story/ButtonVariants.tsx @@ -4,6 +4,8 @@ import { startCase } from 'lodash' import 'storybook-addon-designs' +import { logger } from '@sourcegraph/common' + import { Icon } from '../../Icon' import { Button, ButtonProps } from '../Button' import { BUTTON_VARIANTS } from '../constants' @@ -24,15 +26,15 @@ export const ButtonVariants: React.FunctionComponent {variants.map(variant => ( - - - diff --git a/dev/defs.bzl b/dev/defs.bzl index bc92adf0ad76..5ce329f5eecd 100644 --- a/dev/defs.bzl +++ b/dev/defs.bzl @@ -5,23 +5,38 @@ load("@bazel_skylib//rules:expand_template.bzl", "expand_template") load("@aspect_rules_js//npm:defs.bzl", _npm_package = "npm_package") load("@aspect_rules_ts//ts:defs.bzl", _ts_project = "ts_project") load("@aspect_rules_jest//jest:defs.bzl", _jest_test = "jest_test") +load("//dev:eslint.bzl", "eslint_test_with_types", "get_client_package_path") load(":sass.bzl", _sass = "sass") load(":babel.bzl", _babel = "babel") sass = _sass -def ts_project(name, deps = [], use_preset_env = True, **kwargs): +# TODO move this to `ts_project.bzl` +def ts_project(name, srcs = [], deps = [], use_preset_env = True, **kwargs): """A wrapper around ts_project Args: name: A unique name for this target + srcs: A list of source files + deps: A list of dependencies use_preset_env: Controls if we transpile TS sources with babel-preset-env **kwargs: Additional arguments to pass to ts_project """ + + # Add the ESLint test target which lints all srcs of the `ts_project`. + eslint_test_with_types( + name = "%s_eslint" % name, + srcs = srcs, + deps = deps, + testonly = True, + binary = "//:eslint", + config = "//{}:eslint_config".format(get_client_package_path()), + ) + deps = deps + [ "//:node_modules/tslib", ] @@ -39,6 +54,7 @@ def ts_project(name, deps = [], use_preset_env = True, **kwargs): # Default arguments for ts_project. _ts_project( name = name, + srcs = srcs, deps = deps, # tsconfig options, default to the root @@ -78,7 +94,7 @@ def npm_package(name, srcs = [], **kwargs): """ replace_prefixes = kwargs.pop("replace_prefixes", {}) - package_type = kwargs.pop("type", "module") + package_type = kwargs.pop("type", "commonjs") # Modifications to package.json # TODO(bazel): remove when package.json can be updated in source @@ -94,7 +110,7 @@ def npm_package(name, srcs = [], **kwargs): out = updated_pkg_json, substitutions = { "src/index.ts": "src/index.js", - "\"name\"": "\"type\": \"%s\",\n \"name\"" % package_type, + # "\"name\"": "\"type\": \"%s\",\n \"name\"" % package_type, }, ) diff --git a/dev/eslint.bzl b/dev/eslint.bzl new file mode 100644 index 000000000000..b1713b17cd94 --- /dev/null +++ b/dev/eslint.bzl @@ -0,0 +1,122 @@ +load("@aspect_bazel_lib//lib:copy_to_bin.bzl", "copy_files_to_bin_actions") +load("//dev:js_lib.bzl", "gather_files_from_js_providers", "gather_runfiles") +load("@aspect_rules_js//js:defs.bzl", "js_library") +load("@aspect_rules_js//js:providers.bzl", "JsInfo") +load("@bazel_skylib//rules:build_test.bzl", "build_test") + +def get_client_package_path(): + # Used to reference the `eslint_config` target in the client package + # We assume that eslint config files are located at `client/` + return "/".join(native.package_name().split("/")[:2]) + +def eslint_config(deps = []): + js_library( + name = "eslint_config", + testonly = True, + srcs = [".eslintrc.js"], + data = [ + ".eslintignore", + "package.json", + ":tsconfig", + ], + deps = [ + "//:eslint_config", + ] + deps, + visibility = ["//{}:__subpackages__".format(get_client_package_path())], + ) + +def _custom_eslint_impl(ctx): + copied_srcs = copy_files_to_bin_actions(ctx, ctx.files.srcs) + + inputs_depset = depset( + copied_srcs, + transitive = [gather_files_from_js_providers( + targets = [ctx.attr.config] + ctx.attr.deps, + include_sources = False, + include_transitive_sources = False, + # We have to include declarations because we need to lint the types. + include_declarations = True, + include_npm_linked_packages = True, + )], + ) + + runfiles = gather_runfiles( + ctx = ctx, + sources = [], + data = [ctx.attr.config], + deps = [], + ) + + args = ctx.actions.args() + + # TODO: add context on why it doesn't work well with `overrides` globs. + # args.add("--no-eslintrc") + # args.add_all(["--config", get_path(ctx.files.config[0])]) + + # Ignore warnings and fail only on errors. + args.add("--quiet") + + # Use the custom formatter to ouput relative paths. + args.add_all(["--format", "./{}".format(ctx.files.formatter[0].short_path)]) + + # Specify the files to lint. + args.add_all([s.short_path for s in copied_srcs]) + + # Declare the output file for the eslint output. + output = ctx.actions.declare_file(ctx.attr.output) + # args.add_all(["--output-file", output.short_path]) + + env = { + "BAZEL_BINDIR": ctx.bin_dir.path, + # "JS_BINARY__LOG_DEBUG": "1", + # "JS_BINARY__LOG_INFO": "1", + # "JS_BINARY__LOG_ERROR": "1", + # "JS_BINARY__SILENT_ON_SUCCESS": "0", + "JS_BINARY__STDOUT_OUTPUT_FILE": output.path, + "JS_BINARY__STDERR_OUTPUT_FILE": output.path, + # "JS_BINARY__EXPECTED_EXIT_CODE": "0", + # "JS_BINARY__EXIT_CODE_OUTPUT_FILE": output.path, + } + + ctx.actions.run( + env = env, + inputs = inputs_depset, + outputs = [output], + executable = ctx.executable.binary, + arguments = [args], + mnemonic = "ESLint", + ) + + return [ + DefaultInfo( + files = depset([output]), + runfiles = runfiles, + ), + OutputGroupInfo( + output = depset([output]), + runfiles = runfiles.files, + ), + ] + +_eslint_test_with_types = rule( + implementation = _custom_eslint_impl, + attrs = { + "srcs": attr.label_list(allow_files = True), + "deps": attr.label_list(default = [], providers = [JsInfo]), + "config": attr.label(allow_single_file = True), + "formatter": attr.label(allow_single_file = True, default = Label("//:eslint-relative-formatter")), + "binary": attr.label(executable = True, cfg = "exec", allow_files = True), + "output": attr.string(), + }, +) + +def eslint_test_with_types(name, **kwargs): + lint_name = "%s_lint" % name + + build_test(name, targets = [lint_name]) + + _eslint_test_with_types( + name = lint_name, + output = "%s-output.txt" % name, + **kwargs + ) diff --git a/dev/js_lib.bzl b/dev/js_lib.bzl new file mode 100644 index 000000000000..0ac09265d59e --- /dev/null +++ b/dev/js_lib.bzl @@ -0,0 +1,126 @@ +"""`js_binary` helper functions. Copied from rules_js internals. +""" + +load("@aspect_rules_js//js:providers.bzl", "JsInfo") +load("@aspect_rules_js//npm:providers.bzl", "NpmPackageStoreInfo") + +def gather_files_from_js_providers( + targets, + include_sources, + include_transitive_sources, + include_declarations, + include_npm_linked_packages): + """Gathers files from JsInfo and NpmPackageStoreInfo providers. + + Args: + targets: list of target to gather from + include_sources: see js_filegroup documentation + include_transitive_sources: see js_filegroup documentation + include_declarations: see js_filegroup documentation + include_npm_linked_packages: see js_filegroup documentation + + Returns: + A depset of files + """ + + files_depsets = [] + + files_depsets.extend([ + target[DefaultInfo].default_runfiles.files + for target in targets + if DefaultInfo in target and hasattr(target[DefaultInfo], "default_runfiles") + ]) + + if include_sources: + files_depsets.extend([ + target[JsInfo].sources + for target in targets + if JsInfo in target and hasattr(target[JsInfo], "sources") + ]) + + if include_transitive_sources: + files_depsets.extend([ + target[JsInfo].transitive_sources + for target in targets + if JsInfo in target and hasattr(target[JsInfo], "transitive_sources") + ]) + + if include_declarations: + files_depsets.extend([ + target[JsInfo].transitive_declarations + for target in targets + if JsInfo in target and hasattr(target[JsInfo], "transitive_declarations") + ]) + + if include_npm_linked_packages: + files_depsets.extend([ + target[JsInfo].transitive_npm_linked_package_files + for target in targets + if JsInfo in target and hasattr(target[JsInfo], "transitive_npm_linked_package_files") + ]) + files_depsets.extend([ + target[NpmPackageStoreInfo].transitive_files + for target in targets + if NpmPackageStoreInfo in target and hasattr(target[NpmPackageStoreInfo], "transitive_files") + ]) + + # print(files_depsets) + + return depset([], transitive = files_depsets) + +def gather_runfiles(ctx, sources, data, deps): + """Creates a runfiles object containing files in `sources`, default outputs from `data` and transitive runfiles from `data` & `deps`. + + As a defense in depth against `data` & `deps` targets not supplying all required runfiles, also + gathers the transitive sources & transitive npm linked packages from the `JsInfo` & + `NpmPackageStoreInfo` providers of `data` & `deps` targets. + + See https://bazel.build/extending/rules#runfiles for more info on providing runfiles in build rules. + + Args: + ctx: the rule context + + sources: list or depset of files which should be included in runfiles + + data: list of data targets; default outputs and transitive runfiles are gather from these targets + + See https://bazel.build/reference/be/common-definitions#typical.data and + https://bazel.build/concepts/dependencies#data-dependencies for more info and guidance + on common usage of the `data` attribute in build rules. + + deps: list of dependency targets; only transitive runfiles are gather from these targets + + Returns: + A [runfiles](https://bazel.build/rules/lib/runfiles) object created with [ctx.runfiles](https://bazel.build/rules/lib/ctx#runfiles). + """ + + # Includes sources + if type(sources) == "list": + sources = depset(sources) + transitive_files_depsets = [sources] + + # Gather the default outputs of data targets + transitive_files_depsets.extend([ + target[DefaultInfo].files + for target in data + ]) + + # Gather the transitive sources & transitive npm linked packages from the JsInfo & + # NpmPackageStoreInfo providers of data & deps targets. + transitive_files_depsets.append(gather_files_from_js_providers( + targets = data + deps, + include_sources = True, + include_transitive_sources = True, + include_declarations = False, + include_npm_linked_packages = True, + )) + + # Merge the above with the transitive runfiles of data & deps. + runfiles = ctx.runfiles( + transitive_files = depset(transitive = transitive_files_depsets), + ).merge_all([ + target[DefaultInfo].default_runfiles + for target in data + deps + ]) + + return runfiles diff --git a/eslint-relative-formatter.js b/eslint-relative-formatter.js new file mode 100644 index 000000000000..d74bb674f8b9 --- /dev/null +++ b/eslint-relative-formatter.js @@ -0,0 +1,103 @@ +// The default ESLint formatter with relative paths. +// Forked from https://github.com/eslint/eslint/blob/main/lib/cli-engine/formatters/stylish.js + +const path = require('path') +const chalk = require('chalk') +const stripAnsi = require('strip-ansi') +const table = require('text-table') + +function pluralize(word, count) { + return count === 1 ? word : `${word}s` +} + +module.exports = function (results) { + let output = '\n', + errorCount = 0, + warningCount = 0, + fixableErrorCount = 0, + fixableWarningCount = 0, + summaryColor = 'yellow' + + results.forEach(result => { + const messages = result.messages + + if (messages.length === 0) { + return + } + + errorCount += result.errorCount + warningCount += result.warningCount + fixableErrorCount += result.fixableErrorCount + fixableWarningCount += result.fixableWarningCount + + // This is the only line that is different from the original stylish formatter. + output += `${chalk.underline(path.relative(process.cwd(), result.filePath))}\n` + + output += `${table( + messages.map(message => { + let messageType + + if (message.fatal || message.severity === 2) { + messageType = chalk.red('error') + summaryColor = 'red' + } else { + messageType = chalk.yellow('warning') + } + + return [ + '', + message.line || 0, + message.column || 0, + messageType, + message.message.replace(/([^ ])\.$/u, '$1'), + chalk.dim(message.ruleId || ''), + ] + }), + { + align: ['', 'r', 'l'], + stringLength(str) { + return stripAnsi(str).length + }, + } + ) + .split('\n') + .map(el => el.replace(/(\d+)\s+(\d+)/u, (m, p1, p2) => chalk.dim(`${p1}:${p2}`))) + .join('\n')}\n\n` + }) + + const total = errorCount + warningCount + + if (total > 0) { + output += chalk[summaryColor].bold( + [ + '\u2716 ', + total, + pluralize(' problem', total), + ' (', + errorCount, + pluralize(' error', errorCount), + ', ', + warningCount, + pluralize(' warning', warningCount), + ')\n', + ].join('') + ) + + if (fixableErrorCount > 0 || fixableWarningCount > 0) { + output += chalk[summaryColor].bold( + [ + ' ', + fixableErrorCount, + pluralize(' error', fixableErrorCount), + ' and ', + fixableWarningCount, + pluralize(' warning', fixableWarningCount), + ' potentially fixable with the `--fix` option.\n', + ].join('') + ) + } + } + + // Resets output color, for prevent change on top level + return total > 0 ? chalk.reset(output) : '' +} diff --git a/package.json b/package.json index 704088402027..69d5637736d4 100644 --- a/package.json +++ b/package.json @@ -320,11 +320,13 @@ "storybook-dark-mode": "^2.0.4", "stream-browserify": "^3.0.0", "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", "style-loader": "^3.3.1", "stylelint": "^14.3.0", "svgo": "^2.7.0", "term-size": "^2.2.0", "terser-webpack-plugin": "^5.3.6", + "text-table": "^0.2.0", "ts-loader": "^9.4.2", "ts-node": "^10.7.0", "typed-scss-modules": "^4.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b40d47af7eae..d2988a1485b2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1151,6 +1151,9 @@ importers: string-width: specifier: ^4.2.0 version: 4.2.3 + strip-ansi: + specifier: ^6.0.1 + version: 6.0.1 style-loader: specifier: ^3.3.1 version: 3.3.1(webpack@5.75.0) @@ -1166,6 +1169,9 @@ importers: terser-webpack-plugin: specifier: ^5.3.6 version: 5.3.6(esbuild@0.17.14)(webpack@5.75.0) + text-table: + specifier: ^0.2.0 + version: 0.2.0 ts-loader: specifier: ^9.4.2 version: 9.4.2(typescript@5.0.2)(webpack@5.75.0)