Merge branch 'stable-3.1'

* stable-3.1:
  Upgrade bazlets to latest stable-2.16 to build with 2.16.18 API
  Bump Bazel version to 3.1.0

Change-Id: I28a5ac8be5fd1abf909f4fd4f177c7589028c066
diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/.eslintignore
diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..a43acc6
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,167 @@
+{
+  "extends": ["eslint:recommended", "google"],
+  "parserOptions": {
+    "ecmaVersion": 8,
+    "sourceType": "module"
+  },
+  "env": {
+    "browser": true,
+    "es6": true
+  },
+  "globals": {
+    "__dirname": false,
+    "app": false,
+    "page": false,
+    "Polymer": false,
+    "process": false,
+    "require": false,
+    "Gerrit": false,
+    "Promise": false,
+    "assert": false,
+    "test": false,
+    "flushAsynchronousOperations": false
+  },
+  "rules": {
+    "arrow-parens": ["error", "as-needed"],
+    "block-spacing": ["error", "always"],
+    "brace-style": ["error", "1tbs", { "allowSingleLine": true }],
+    "camelcase": "off",
+    "comma-dangle": ["error", {
+      "arrays": "always-multiline",
+      "objects": "always-multiline",
+      "imports": "always-multiline",
+      "exports": "always-multiline",
+      "functions": "never"
+    }],
+    "eol-last": "off",
+    "indent": ["error", 2, {
+      "MemberExpression": 2,
+      "FunctionDeclaration": {"body": 1, "parameters": 2},
+      "FunctionExpression": {"body": 1, "parameters": 2},
+      "CallExpression": {"arguments": 2 },
+      "ArrayExpression": 1,
+      "ObjectExpression": 1,
+      "SwitchCase": 1
+    }],
+    "keyword-spacing": ["error", { "after": true, "before": true }],
+    "lines-between-class-members": ["error", "always"],
+    "max-len": [
+      "error",
+      80,
+      2,
+      {
+        "ignoreComments": true,
+        "ignorePattern": "^import .*;$"
+      }
+    ],
+    "new-cap": ["error", { "capIsNewExceptions": ["Polymer", "LegacyElementMixin", "GestureEventListeners", "LegacyDataMixin"] }],
+    "no-console": "off",
+    "no-multiple-empty-lines": [ "error", { "max": 1 } ],
+    "no-prototype-builtins": "off",
+    "no-redeclare": "off",
+    "no-restricted-syntax": [
+      "error",
+      {
+        "selector": "ExpressionStatement > CallExpression > MemberExpression[object.name='test'][property.name='only']",
+        "message": "Remove test.only."
+      },
+      {
+        "selector": "ExpressionStatement > CallExpression > MemberExpression[object.name='suite'][property.name='only']",
+        "message": "Remove suite.only."
+      }
+    ],
+    "no-undef": "off",
+    "no-useless-escape": "off",
+    "no-var": "error",
+    "object-shorthand": ["error", "always"],
+    "padding-line-between-statements": [
+      "error",
+      {
+        "blankLine": "always",
+        "prev": "class",
+        "next": "*"
+      },
+      {
+        "blankLine": "always",
+        "prev": "*",
+        "next": "class"
+      }
+    ],
+    "prefer-arrow-callback": "error",
+    "prefer-const": "error",
+    "prefer-spread": "error",
+    "quote-props": ["error", "consistent-as-needed"],
+    "require-jsdoc": "off",
+    "semi": [2, "always"],
+    "template-curly-spacing": "error",
+    "valid-jsdoc": "off",
+
+    "require-jsdoc": 0,
+    "valid-jsdoc": 0,
+    "jsdoc/check-alignment": 2,
+    "jsdoc/check-examples": 0,
+    "jsdoc/check-indentation": 0,
+    "jsdoc/check-param-names": 0,
+    "jsdoc/check-syntax": 0,
+    "jsdoc/check-tag-names": 0,
+    "jsdoc/check-types": 0,
+    "jsdoc/implements-on-classes": 2,
+    "jsdoc/match-description": 0,
+    "jsdoc/newline-after-description": 2,
+    "jsdoc/no-types": 0,
+    "jsdoc/no-undefined-types": 0,
+    "jsdoc/require-description": 0,
+    "jsdoc/require-description-complete-sentence": 0,
+    "jsdoc/require-example": 0,
+    "jsdoc/require-hyphen-before-param-description": 0,
+    "jsdoc/require-jsdoc": 0,
+    "jsdoc/require-param": 0,
+    "jsdoc/require-param-description": 0,
+    "jsdoc/require-param-name": 2,
+    "jsdoc/require-param-type": 2,
+    "jsdoc/require-returns": 0,
+    "jsdoc/require-returns-check": 0,
+    "jsdoc/require-returns-description": 0,
+    "jsdoc/require-returns-type": 2,
+    "jsdoc/valid-types": 2,
+    "jsdoc/require-file-overview": ["error", {
+      "tags": {
+        "license": {
+          "mustExist": true,
+          "preventDuplicates": true
+        }
+      }
+    }],
+    "import/named": 2,
+    "import/no-unresolved": 2,
+    "import/no-self-import": 2,
+    // The no-cycle rule is slow, because it doesn't cache dependencies.
+    // Disable it.
+    "import/no-cycle": 0,
+    "import/no-useless-path-segments": 2,
+    "import/no-unused-modules": 2,
+    "import/no-default-export": 2
+  },
+  "plugins": [
+    "html",
+    "jsdoc",
+    "import"
+  ],
+  "settings": {
+    "html/report-bad-indent": "error"
+  },
+  "overrides": [
+    {
+      "files": ["*_html.js", "*-styles.js", "externs.js"],
+      "rules": {
+        "max-len": "off"
+      }
+    },
+    {
+      "files": ["*.html"],
+      "rules": {
+        "jsdoc/require-file-overview": "off"
+      }
+    }
+  ]
+}
diff --git a/BUILD b/BUILD
index 819cac0..7008531 100644
--- a/BUILD
+++ b/BUILD
@@ -1,5 +1,7 @@
 load("@rules_java//java:defs.bzl", "java_library")
+load("@npm_bazel_rollup//:index.bzl", "rollup_bundle")
 load("//tools/bzl:junit.bzl", "junit_tests")
+load("//tools/js:eslint.bzl", "eslint")
 load(
     "//tools/bzl:plugin.bzl",
     "PLUGIN_DEPS",
@@ -29,7 +31,7 @@
     outs = ["gr-delete-repo-static.jar"],
     cmd = " && ".join([
         "mkdir $$TMP/static",
-        "cp -r $(locations :gr-delete-repo) $$TMP/static",
+        "cp $(locations :gr-delete-repo) $$TMP/static",
         "cd $$TMP",
         "zip -Drq $$ROOT/$@ -g .",
     ]),
@@ -37,11 +39,19 @@
 
 polygerrit_plugin(
     name = "gr-delete-repo",
-    srcs = glob([
-        "gr-delete-repo/*.html",
-        "gr-delete-repo/*.js",
-    ]),
-    app = "plugin.html",
+    app = "delete-project-bundle.js",
+    plugin_name = "delete-project",
+)
+
+rollup_bundle(
+    name = "delete-project-bundle",
+    srcs = glob(["gr-delete-repo/*.js"]),
+    entry_point = "gr-delete-repo/plugin.js",
+    rollup_bin = "//tools/node_tools:rollup-bin",
+    sourcemap = "hidden",
+    deps = [
+        "@tools_npm//rollup-plugin-node-resolve",
+    ],
 )
 
 junit_tests(
@@ -63,3 +73,24 @@
         "@mockito//jar",
     ],
 )
+
+# Define the eslinter for the plugin
+# The eslint macro creates 2 rules: lint_test and lint_bin
+eslint(
+    name = "lint",
+    srcs = glob([
+        "gr-delete-repo/**/*.js",
+    ]),
+    config = ".eslintrc.json",
+    data = [],
+    extensions = [
+        ".js",
+    ],
+    ignore = ".eslintignore",
+    plugins = [
+        "@npm//eslint-config-google",
+        "@npm//eslint-plugin-html",
+        "@npm//eslint-plugin-import",
+        "@npm//eslint-plugin-jsdoc",
+    ],
+)
diff --git a/gr-delete-repo/gr-delete-repo.js b/gr-delete-repo/gr-delete-repo.js
index 8055070..9afef3c 100644
--- a/gr-delete-repo/gr-delete-repo.js
+++ b/gr-delete-repo/gr-delete-repo.js
@@ -1,63 +1,79 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-(function() {
-  'use strict';
+/**
+ * @license
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
-  Polymer({
-    is: 'gr-delete-repo',
+import {htmlTemplate} from './gr-delete-repo_html.js';
 
-    properties: {
+class GrDeleteRepo extends Polymer.Element {
+  /** @returns {string} name of the component */
+  static get is() { return 'gr-delete-repo'; }
+
+  /** @returns {?} template for this component */
+  static get template() { return htmlTemplate; }
+
+  /**
+   * Defines properties of the component
+   *
+   * @returns {?}
+   */
+  static get properties() {
+    return {
       repoName: String,
       config: Object,
       action: Object,
       actionId: String,
-    },
+    };
+  }
 
-    attached() {
-      this.actionId = this.plugin.getPluginName() + '~delete';
-      this.action = this.config.actions[this.actionId];
-      this.hidden = !this.action;
-    },
+  connectedCallback() {
+    super.connectedCallback();
+    this.actionId = this.plugin.getPluginName() + '~delete';
+    this.action = this.config.actions[this.actionId];
+    this.hidden = !this.action;
+  }
 
-    _handleCommandTap() {
-      this.$.deleteRepoOverlay.open();
-    },
+  _handleCommandTap() {
+    this.$.deleteRepoOverlay.open();
+  }
 
-    _handleCloseDeleteRepo() {
-      this.$.deleteRepoOverlay.close();
-    },
+  _handleCloseDeleteRepo() {
+    this.$.deleteRepoOverlay.close();
+  }
 
-    _handleDeleteRepo() {
-      const endpoint = '/projects/' +
-          encodeURIComponent(this.repoName) + '/' +
-          this.actionId;
+  _handleDeleteRepo() {
+    const endpoint = '/projects/' +
+        encodeURIComponent(this.repoName) + '/' +
+        this.actionId;
 
-      const json = {
-        force: this.$.forceDeleteOpenChangesCheckBox.checked,
-        preserve: this.$.preserveGitRepoCheckBox.checked
-      };
+    const json = {
+      force: this.$.forceDeleteOpenChangesCheckBox.checked,
+      preserve: this.$.preserveGitRepoCheckBox.checked,
+    };
 
-      const errFn = response => {
-        this.fire('page-error', {response});
-      };
+    const errFn = response => {
+      this.fire('page-error', {response});
+    };
 
-      return this.plugin.restApi().send(
-          this.action.method, endpoint, json, errFn)
-            .then(r => {
-              this.plugin.restApi().invalidateReposCache();
-              Gerrit.Nav.navigateToRelativeUrl('/admin/repos');
-      });
-    },
-  });
-})();
+    return this.plugin.restApi().send(
+        this.action.method, endpoint, json, errFn)
+        .then(r => {
+          this.plugin.restApi().invalidateReposCache();
+          Gerrit.Nav.navigateToRelativeUrl('/admin/repos');
+        });
+  }
+}
+
+customElements.define(GrDeleteRepo.is, GrDeleteRepo);
diff --git a/gr-delete-repo/gr-delete-repo.html b/gr-delete-repo/gr-delete-repo_html.js
similarity index 65%
rename from gr-delete-repo/gr-delete-repo.html
rename to gr-delete-repo/gr-delete-repo_html.js
index c2a4a1b..31962d8 100644
--- a/gr-delete-repo/gr-delete-repo.html
+++ b/gr-delete-repo/gr-delete-repo_html.js
@@ -1,21 +1,21 @@
-<!--
-Copyright (C) 2018 The Android Open Source Project
+/**
+ * @license
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
-
-<dom-module id="gr-delete-repo">
-  <template>
+export const htmlTemplate = Polymer.html`
     <style include="gr-form-styles"></style>
     <gr-repo-command
         title="[[action.label]]"
@@ -52,6 +52,4 @@
         </div>
       </gr-dialog>
     </gr-overlay>
-  </template>
-  <script src="gr-delete-repo.js"></script>
-</dom-module>
+`;
diff --git a/gr-delete-repo/plugin.js b/gr-delete-repo/plugin.js
new file mode 100644
index 0000000..2e22d59
--- /dev/null
+++ b/gr-delete-repo/plugin.js
@@ -0,0 +1,22 @@
+/**
+ * @license
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import './gr-delete-repo.js';
+
+Gerrit.install(plugin => {
+  plugin.registerCustomComponent(
+      'repo-command', 'gr-delete-repo');
+});
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..5a46795
--- /dev/null
+++ b/package.json
@@ -0,0 +1,13 @@
+{
+  "name": "delete-project",
+  "description": "Delete project plugin",
+  "browser": true,
+  "scripts": {
+    "safe_bazelisk": "if which bazelisk >/dev/null; then bazel_bin=bazelisk; else bazel_bin=bazel; fi && $bazel_bin",
+    "eslint": "npm run safe_bazelisk test :lint_test",
+    "eslintfix": "npm run safe_bazelisk run :lint_bin -- -- --fix $(pwd)"
+  },
+  "devDependencies": {},
+  "license": "Apache-2.0",
+  "private": true
+}
diff --git a/plugin.html b/plugin.html
deleted file mode 100644
index 7118174..0000000
--- a/plugin.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
-Copyright (C) 2018 The Android Open Source Project
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
--->
-
-<link rel="import" href="./gr-delete-repo/gr-delete-repo.html">
-
-<dom-module id="delete-repo">
-  <script>
-    Gerrit.install(function(plugin) {
-        plugin.registerCustomComponent(
-            'repo-command', 'gr-delete-repo');
-    });
-  </script>
-</dom-module>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeletePreconditions.java b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeletePreconditions.java
index 05842c4..417c968 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeletePreconditions.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/DeletePreconditions.java
@@ -39,7 +39,7 @@
 import com.google.gerrit.server.query.change.InternalChangeQuery;
 import com.google.gerrit.server.restapi.project.ListChildProjects;
 import com.google.gerrit.server.submit.MergeOpRepoManager;
-import com.google.gerrit.server.submit.SubmoduleException;
+import com.google.gerrit.server.submit.SubmoduleConflictException;
 import com.google.gerrit.server.submit.SubmoduleOp;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -165,7 +165,7 @@
     } catch (RepositoryNotFoundException e) {
       // we're trying to delete the repository,
       // so this exception should not stop us
-    } catch (IOException | SubmoduleException e) {
+    } catch (IOException | SubmoduleConflictException e) {
       throw new CannotDeleteProjectException("Project is subscribed by other projects.", e);
     }
   }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/HideProject.java b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/HideProject.java
index c5bf8ce..7656726 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/HideProject.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/HideProject.java
@@ -84,7 +84,7 @@
   }
 
   private void createProjectIfMissing(String projectName) throws IOException, RestApiException {
-    if (projectCache.get(Project.nameKey(projectName)) == null) {
+    if (!projectCache.get(Project.nameKey(projectName)).isPresent()) {
       try {
         createProject.apply(TopLevelResource.INSTANCE, IdString.fromDecoded(projectName), null);
       } catch (RestApiException | ConfigInvalidException | PermissionBackendException e) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/HttpModule.java b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/HttpModule.java
index 5066685..a41a374 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/deleteproject/HttpModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/deleteproject/HttpModule.java
@@ -23,6 +23,6 @@
   @Override
   protected void configureServlets() {
     DynamicSet.bind(binder(), WebUiPlugin.class)
-        .toInstance(new JavaScriptPlugin("gr-delete-repo.html"));
+        .toInstance(new JavaScriptPlugin("delete-project.html"));
   }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/deleteproject/DeleteProjectIT.java b/src/test/java/com/googlesource/gerrit/plugins/deleteproject/DeleteProjectIT.java
index d83fac7..4960816 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/deleteproject/DeleteProjectIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/deleteproject/DeleteProjectIT.java
@@ -19,15 +19,16 @@
 import static com.google.gerrit.acceptance.GitUtil.pushHead;
 import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
 import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static com.google.gerrit.server.project.ProjectCache.illegalState;
 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
 
 import com.google.common.base.Joiner;
-import com.google.gerrit.acceptance.GerritConfig;
 import com.google.gerrit.acceptance.LightweightPluginDaemonTest;
 import com.google.gerrit.acceptance.RestResponse;
 import com.google.gerrit.acceptance.TestPlugin;
 import com.google.gerrit.acceptance.UseLocalDisk;
 import com.google.gerrit.acceptance.UseSsh;
+import com.google.gerrit.acceptance.config.GerritConfig;
 import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
 import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
 import com.google.gerrit.common.data.Permission;
@@ -173,7 +174,7 @@
     String cmd = createDeleteCommand("--preserve-git-repository", project.get());
     adminSshSession.exec(cmd);
 
-    ProjectConfig cfg = projectCache.checkedGet(project).getConfig();
+    ProjectConfig cfg = projectCache.get(project).orElseThrow(illegalState(project)).getConfig();
     ProjectState state = cfg.getProject().getState();
 
     assertThat(state).isEqualTo(ProjectState.HIDDEN);