Migrate delete-project plugin to polymer 3

Feature: Issue 12535
Change-Id: I6f92b9d874c32b67c5c3f274b9a83e6795655ba3
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..ebc77fb 100644
--- a/BUILD
+++ b/BUILD
@@ -1,4 +1,5 @@
 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/bzl:plugin.bzl",
@@ -29,7 +30,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 +38,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(
diff --git a/gr-delete-repo/gr-delete-repo.js b/gr-delete-repo/gr-delete-repo.js
index a4d7740..9afef3c 100644
--- a/gr-delete-repo/gr-delete-repo.js
+++ b/gr-delete-repo/gr-delete-repo.js
@@ -1,68 +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.
+ */
 
-  class GrDeleteRepo extends Polymer.Element {
-    static get is() { return 'gr-delete-repo'; }
+import {htmlTemplate} from './gr-delete-repo_html.js';
 
-    static get properties() {
-      return {
-        repoName: String,
-        config: Object,
-        action: Object,
-        actionId: String,
-      };
-    }
+class GrDeleteRepo extends Polymer.Element {
+  /** @returns {string} name of the component */
+  static get is() { return 'gr-delete-repo'; }
 
-    connectedCallback() {
-      super.connectedCallback();
-      this.actionId = this.plugin.getPluginName() + '~delete';
-      this.action = this.config.actions[this.actionId];
-      this.hidden = !this.action;
-    }
+  /** @returns {?} template for this component */
+  static get template() { return htmlTemplate; }
 
-    _handleCommandTap() {
-      this.$.deleteRepoOverlay.open();
-    }
-
-    _handleCloseDeleteRepo() {
-      this.$.deleteRepoOverlay.close();
-    }
-
-    _handleDeleteRepo() {
-      const endpoint = '/projects/' +
-          encodeURIComponent(this.repoName) + '/' +
-          this.actionId;
-
-      const json = {
-        force: this.$.forceDeleteOpenChangesCheckBox.checked,
-        preserve: this.$.preserveGitRepoCheckBox.checked,
-      };
-
-      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');
-          });
-    }
+  /**
+   * Defines properties of the component
+   *
+   * @returns {?}
+   */
+  static get properties() {
+    return {
+      repoName: String,
+      config: Object,
+      action: Object,
+      actionId: String,
+    };
   }
 
-  customElements.define(GrDeleteRepo.is, GrDeleteRepo);
-})();
+  connectedCallback() {
+    super.connectedCallback();
+    this.actionId = this.plugin.getPluginName() + '~delete';
+    this.action = this.config.actions[this.actionId];
+    this.hidden = !this.action;
+  }
+
+  _handleCommandTap() {
+    this.$.deleteRepoOverlay.open();
+  }
+
+  _handleCloseDeleteRepo() {
+    this.$.deleteRepoOverlay.close();
+  }
+
+  _handleDeleteRepo() {
+    const endpoint = '/projects/' +
+        encodeURIComponent(this.repoName) + '/' +
+        this.actionId;
+
+    const json = {
+      force: this.$.forceDeleteOpenChangesCheckBox.checked,
+      preserve: this.$.preserveGitRepoCheckBox.checked,
+    };
+
+    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');
+        });
+  }
+}
+
+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..86133a7
--- /dev/null
+++ b/package.json
@@ -0,0 +1,26 @@
+{
+  "name": "delete-project",
+  "description": "Delete project plugin",
+  "browser": true,
+  "scripts": {
+    "eslint": "../../node_modules/eslint/bin/eslint.js --ext .html,.js ./gr-delete-repo/",
+    "eslintfix": "npm run eslint -- --fix"
+  },
+  "devDependencies": {
+    "@bazel/rollup": "^1.1.0",
+    "@webcomponents/shadycss": "^1.9.2",
+    "@webcomponents/webcomponentsjs": "^1.3.3",
+    "bower": "^1.8.8",
+    "es6-promise": "^3.3.1",
+    "eslint": "^6.6.0",
+    "eslint-config-google": "^0.13.0",
+    "eslint-plugin-html": "^6.0.0",
+    "eslint-plugin-import": "^2.20.1",
+    "eslint-plugin-jsdoc": "^19.2.0",
+    "moment": "^2.24.0",
+    "polymer-bridges": "file:../../polymer-bridges/",
+    "polymer-cli": "^1.9.11"
+  },
+  "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/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"));
   }
 }