Add `prettier` support for //scouting

This patch doesn't actually enable `prettier`. The next patch will
enable it and reformat the files in //scouting.

We can expand coverage to other directories in future patches.

Signed-off-by: Philipp Schrader <philipp.schrader@gmail.com>
Change-Id: Ib3dae3aa921ab11b3d5deea692e71185d5fa2a23
diff --git a/.prettierrc.json b/.prettierrc.json
new file mode 100644
index 0000000..4cb66e7
--- /dev/null
+++ b/.prettierrc.json
@@ -0,0 +1,17 @@
+{
+  "htmlWhitespaceSensitivity": "ignore",
+  "overrides": [
+    {
+      "files": "*.js",
+      "options": {
+        "singleQuote": true
+      }
+    },
+    {
+      "files": "*.ts",
+      "options": {
+        "singleQuote": true
+      }
+    }
+  ]
+}
diff --git a/package.json b/package.json
index d16aed7..b716d1a 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,7 @@
     "karma-requirejs": "1.1.0",
     "karma-sourcemap-loader": "0.3.8",
     "protractor": "7.0.0",
+    "prettier": "2.6.1",
     "requirejs": "2.3.6",
     "rollup": "2.66.1",
     "@rollup/plugin-node-resolve": "13.1.3",
diff --git a/tools/lint/BUILD b/tools/lint/BUILD
index 8d9b150..743c852 100644
--- a/tools/lint/BUILD
+++ b/tools/lint/BUILD
@@ -24,6 +24,18 @@
 )
 
 sh_binary(
+    name = "prettier",
+    srcs = ["prettier.sh"],
+    data = [
+        "@npm//prettier/bin:prettier",
+    ],
+    target_compatible_with = ["@platforms//cpu:x86_64"],
+    deps = [
+        "@bazel_tools//tools/bash/runfiles",
+    ],
+)
+
+sh_binary(
     name = "run-ci",
     srcs = [
         "run-ci.sh",
@@ -31,6 +43,7 @@
     data = [
         ":buildifier",
         ":gofmt",
+        ":prettier",
         "//:gazelle-runner",
         "//tools/go:mirror_go_repos",
         "//tools/go:tweak_gazelle_go_deps",
diff --git a/tools/lint/prettier.sh b/tools/lint/prettier.sh
new file mode 100755
index 0000000..dc29522
--- /dev/null
+++ b/tools/lint/prettier.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+# --- begin runfiles.bash initialization v2 ---
+# Copy-pasted from the Bazel Bash runfiles library v2.
+set -uo pipefail; f=bazel_tools/tools/bash/runfiles/runfiles.bash
+source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \
+  source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \
+  source "$0.runfiles/$f" 2>/dev/null || \
+  source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
+  source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
+  { echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e
+# --- end runfiles.bash initialization v2 ---
+
+PRETTIER="$(rlocation npm/prettier/bin/prettier.sh)"
+PRETTIER="$(readlink -f "${PRETTIER}")"
+readonly PRETTIER
+
+# Find the files to format at the root of the repo. For some reason, the
+# prettier binary (probably because of the rules_nodejs wrapper), breaks when
+# invoked from a directory outside of the runfiles tree.
+pushd "${BUILD_WORKSPACE_DIRECTORY}"
+
+# Find web-related files in the repo.
+# TODO(phil): Support more than just //scouting.
+web_files=($(git ls-tree --name-only --full-tree -r @ \
+    | grep '^scouting/' \
+    | (grep \
+        -e '\.ts$' \
+        -e '\.js$' \
+        -e '\.css$' \
+        -e '\.html$' \
+        || :)))
+
+web_files_abs_paths=($(for file in "${web_files[@]}"; do
+  readlink -f "${file}"
+done))
+
+CONFIG_FILE="$(readlink -f .prettierrc.json)"
+readonly CONFIG_FILE
+
+# Go back to the runfiles directory so that prettier doesn't break.
+popd
+
+# If we have any web-related files, format them.
+if ((${#web_files_abs_paths[@]} > 0)); then
+    "${PRETTIER}" \
+        --config "${CONFIG_FILE}" \
+        --write \
+        "${web_files_abs_paths[@]}"
+fi
diff --git a/tools/lint/run-ci.sh b/tools/lint/run-ci.sh
index 32f110a..25c6d0c 100755
--- a/tools/lint/run-ci.sh
+++ b/tools/lint/run-ci.sh
@@ -64,6 +64,12 @@
     ./tools/lint/buildifier
 }
 
+prettier() {
+    # TODO(phil): Uncomment this and delete the "true" statement.
+    #./tools/lint/prettier
+    true
+}
+
 git_status_is_clean() {
     cd "${BUILD_WORKSPACE_DIRECTORY}"
     if ! git diff --quiet; then
@@ -81,6 +87,7 @@
     tweak_gazelle_go_deps
     clean_up_go_mirrors
     buildifier
+    prettier
     git_status_is_clean  # This must the last linter.
 )
 
diff --git a/yarn.lock b/yarn.lock
index e76092f..8c5720f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2713,6 +2713,11 @@
   resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
   integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
 
+prettier@2.6.1:
+  version "2.6.1"
+  resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.1.tgz#d472797e0d7461605c1609808e27b80c0f9cfe17"
+  integrity sha512-8UVbTBYGwN37Bs9LERmxCPjdvPxlEowx2urIL6urHzdb3SDq4B/Z6xLFCblrSnE4iKWcS6ziJ3aOYrc1kz/E2A==
+
 process-nextick-args@~2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"