]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Migrate client to eslint
authorChocobozzz <me@florianbigard.com>
Tue, 17 Aug 2021 12:42:53 +0000 (14:42 +0200)
committerChocobozzz <me@florianbigard.com>
Wed, 18 Aug 2021 06:35:06 +0000 (08:35 +0200)
215 files changed:
client/.eslintrc.json [new file with mode: 0644]
client/angular.json
client/e2e/local-protractor.conf.js
client/e2e/protractor.conf.js
client/e2e/tsconfig.json [moved from client/e2e/tsconfig.e2e.json with 100% similarity]
client/package.json
client/src/app/+about/about-follows/about-follows.component.ts
client/src/app/+about/about-instance/about-instance.component.ts
client/src/app/+about/about-instance/contact-admin-modal.component.ts
client/src/app/+about/about-routing.module.ts
client/src/app/+accounts/account-video-channels/account-video-channels.component.ts
client/src/app/+accounts/account-videos/account-videos.component.ts
client/src/app/+accounts/accounts.component.html
client/src/app/+accounts/accounts.component.ts
client/src/app/+admin/config/edit-custom-config/edit-basic-configuration.component.ts
client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.ts
client/src/app/+admin/config/edit-custom-config/edit-homepage.component.ts
client/src/app/+admin/follows/following-list/following-list.component.ts
client/src/app/+admin/follows/shared/redundancy-checkbox.component.ts
client/src/app/+admin/follows/video-redundancies-list/video-redundancies-list.component.ts
client/src/app/+admin/moderation/video-block-list/video-block-list.component.html
client/src/app/+admin/moderation/video-block-list/video-block-list.component.ts
client/src/app/+admin/moderation/video-comment-list/video-comment-list.component.ts
client/src/app/+admin/plugins/plugin-search/plugin-search.component.html
client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.ts
client/src/app/+admin/plugins/shared/plugin-api.service.ts
client/src/app/+admin/system/jobs/job.service.ts
client/src/app/+admin/system/logs/logs.service.ts
client/src/app/+admin/users/user-edit/user-create.component.ts
client/src/app/+admin/users/user-edit/user-edit.ts
client/src/app/+admin/users/user-edit/user-password.component.ts
client/src/app/+admin/users/user-edit/user-update.component.ts
client/src/app/+admin/users/user-list/user-list.component.ts
client/src/app/+login/login.component.html
client/src/app/+login/login.component.ts
client/src/app/+my-account/my-account-applications/my-account-applications.component.ts
client/src/app/+my-account/my-account-settings/my-account-change-email/my-account-change-email.component.ts
client/src/app/+my-account/my-account-settings/my-account-change-password/my-account-change-password.component.ts
client/src/app/+my-account/my-account-settings/my-account-danger-zone/my-account-danger-zone.component.ts
client/src/app/+my-account/my-account-settings/my-account-settings.component.ts
client/src/app/+my-account/my-account.component.ts
client/src/app/+my-library/+my-video-channels/my-video-channel-create.component.ts
client/src/app/+my-library/+my-video-channels/my-video-channel-update.component.ts
client/src/app/+my-library/+my-video-channels/my-video-channels.component.ts
client/src/app/+my-library/my-history/my-history.component.ts
client/src/app/+my-library/my-library.component.ts
client/src/app/+my-library/my-ownership/my-accept-ownership/my-accept-ownership.component.ts
client/src/app/+my-library/my-video-playlists/my-video-playlist-create.component.ts
client/src/app/+my-library/my-video-playlists/my-video-playlist-elements.component.ts
client/src/app/+my-library/my-video-playlists/my-video-playlist-update.component.ts
client/src/app/+my-library/my-videos/modals/video-change-ownership.component.ts
client/src/app/+my-library/my-videos/my-videos.component.ts
client/src/app/+remote-interaction/remote-interaction.component.ts
client/src/app/+reset-password/reset-password.component.ts
client/src/app/+signup/+register/custom-stepper.component.ts
client/src/app/+signup/+register/register.component.ts
client/src/app/+signup/+verify-account/verify-account-email/verify-account-email.component.ts
client/src/app/+video-channels/video-channel-videos/video-channel-videos.component.ts
client/src/app/+video-channels/video-channels.component.html
client/src/app/+video-channels/video-channels.component.ts
client/src/app/+videos/+video-edit/shared/i18n-primeng-calendar.service.ts
client/src/app/+videos/+video-edit/shared/video-caption-add-modal.component.ts
client/src/app/+videos/+video-edit/shared/video-edit-utils.ts
client/src/app/+videos/+video-edit/shared/video-edit.component.ts
client/src/app/+videos/+video-edit/video-add-components/drag-drop.directive.ts
client/src/app/+videos/+video-edit/video-add-components/video-go-live.component.ts
client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.html
client/src/app/+videos/+video-edit/video-add-components/video-import-torrent.component.ts
client/src/app/+videos/+video-edit/video-add-components/video-import-url.component.ts
client/src/app/+videos/+video-edit/video-add-components/video-send.ts
client/src/app/+videos/+video-edit/video-add-components/video-upload.component.html
client/src/app/+videos/+video-edit/video-add-components/video-upload.component.ts
client/src/app/+videos/+video-edit/video-update.component.ts
client/src/app/+videos/+video-edit/video-update.resolver.ts
client/src/app/+videos/+video-watch/player-styles.component.ts
client/src/app/+videos/+video-watch/shared/comment/video-comment-add.component.ts
client/src/app/+videos/+video-watch/shared/comment/video-comment.component.html
client/src/app/+videos/+video-watch/shared/comment/video-comment.component.ts
client/src/app/+videos/+video-watch/shared/comment/video-comments.component.ts
client/src/app/+videos/+video-watch/shared/information/video-alert.component.ts
client/src/app/+videos/+video-watch/shared/metadata/video-description.component.html
client/src/app/+videos/+video-watch/shared/playlist/video-watch-playlist.component.ts
client/src/app/+videos/+video-watch/shared/recommendations/recommended-videos.component.html
client/src/app/+videos/+video-watch/shared/recommendations/recommended-videos.component.ts
client/src/app/+videos/+video-watch/shared/timestamp-route-transformer.directive.ts
client/src/app/+videos/+video-watch/video-watch.component.ts
client/src/app/+videos/video-list/overview/overview.service.ts
client/src/app/+videos/video-list/trending/video-trending-header.component.ts
client/src/app/+videos/video-list/trending/video-trending.component.ts
client/src/app/+videos/video-list/video-local.component.ts
client/src/app/app.component.ts
client/src/app/core/auth/auth.service.ts
client/src/app/core/hotkeys/hotkeys.component.ts
client/src/app/core/menu/menu.service.ts
client/src/app/core/plugins/hooks.service.ts
client/src/app/core/plugins/plugin.service.ts
client/src/app/core/renderer/markdown.service.ts
client/src/app/core/rest/rest-extractor.service.ts
client/src/app/core/routing/custom-reuse-strategy.ts
client/src/app/core/routing/menu-guard.service.ts
client/src/app/core/routing/meta-guard.service.ts
client/src/app/core/routing/preload-selected-modules-list.ts
client/src/app/core/scoped-tokens/scoped-tokens.service.ts
client/src/app/core/server/server.service.ts
client/src/app/core/theme/theme.service.ts
client/src/app/core/users/user.service.ts
client/src/app/header/search-typeahead.component.ts
client/src/app/helpers/locales/oc.ts
client/src/app/helpers/utils.ts
client/src/app/menu/language-chooser.component.ts
client/src/app/menu/menu.component.ts
client/src/app/modal/custom-modal.component.ts
client/src/app/shared/form-validators/abuse-validators.ts
client/src/app/shared/form-validators/custom-config-validators.ts
client/src/app/shared/form-validators/form-validator.model.ts
client/src/app/shared/form-validators/host-validators.ts
client/src/app/shared/form-validators/instance-validators.ts
client/src/app/shared/form-validators/login-validators.ts
client/src/app/shared/form-validators/reset-password-validators.ts
client/src/app/shared/form-validators/user-validators.ts
client/src/app/shared/form-validators/video-block-validators.ts
client/src/app/shared/form-validators/video-captions-validators.ts
client/src/app/shared/form-validators/video-channel-validators.ts
client/src/app/shared/form-validators/video-comment-validators.ts
client/src/app/shared/form-validators/video-ownership-change-validators.ts
client/src/app/shared/form-validators/video-playlist-validators.ts
client/src/app/shared/form-validators/video-validators.ts
client/src/app/shared/shared-abuse-list/abuse-list-table.component.ts
client/src/app/shared/shared-abuse-list/moderation-comment-modal.component.ts
client/src/app/shared/shared-abuse-list/processed-abuse.model.ts
client/src/app/shared/shared-actor-image-edit/actor-avatar-edit.component.ts
client/src/app/shared/shared-actor-image-edit/actor-banner-edit.component.ts
client/src/app/shared/shared-actor-image/actor-avatar.component.ts
client/src/app/shared/shared-custom-markup/peertube-custom-tags/channel-miniature-markup.component.ts
client/src/app/shared/shared-custom-markup/peertube-custom-tags/video-miniature-markup.component.ts
client/src/app/shared/shared-forms/form-reactive.ts
client/src/app/shared/shared-forms/form-validator.service.ts
client/src/app/shared/shared-forms/reactive-file.component.ts
client/src/app/shared/shared-icons/global-icon.component.ts
client/src/app/shared/shared-instance/instance-about-accordion.component.ts
client/src/app/shared/shared-instance/instance-follow.service.ts
client/src/app/shared/shared-instance/instance-statistics.component.ts
client/src/app/shared/shared-instance/instance.service.ts
client/src/app/shared/shared-instance/shared-instance.module.ts
client/src/app/shared/shared-main/account/account.model.ts
client/src/app/shared/shared-main/account/actor.model.ts
client/src/app/shared/shared-main/angular/autofocus.directive.ts
client/src/app/shared/shared-main/angular/link.component.ts
client/src/app/shared/shared-main/angular/peertube-template.directive.ts
client/src/app/shared/shared-main/feeds/syndication.model.ts
client/src/app/shared/shared-main/misc/help.component.ts
client/src/app/shared/shared-main/misc/list-overflow.component.ts
client/src/app/shared/shared-main/misc/simple-search-input.component.ts
client/src/app/shared/shared-main/misc/top-menu-dropdown.component.ts
client/src/app/shared/shared-main/users/user-notification.service.ts
client/src/app/shared/shared-main/users/user-quota.component.ts
client/src/app/shared/shared-main/video-channel/video-channel.model.ts
client/src/app/shared/shared-main/video-channel/video-channel.service.ts
client/src/app/shared/shared-main/video/redundancy.service.ts
client/src/app/shared/shared-main/video/video-edit.model.ts
client/src/app/shared/shared-main/video/video.model.ts
client/src/app/shared/shared-main/video/video.service.ts
client/src/app/shared/shared-moderation/abuse.service.ts
client/src/app/shared/shared-moderation/account-blocklist.component.ts
client/src/app/shared/shared-moderation/blocklist.service.ts
client/src/app/shared/shared-moderation/server-blocklist.component.ts
client/src/app/shared/shared-moderation/video-block.component.ts
client/src/app/shared/shared-support-modal/support-modal.component.ts
client/src/app/shared/shared-user-settings/user-video-settings.component.ts
client/src/app/shared/shared-user-subscription/remote-subscribe.component.ts
client/src/app/shared/shared-user-subscription/subscribe-button.component.ts
client/src/app/shared/shared-user-subscription/user-subscription.service.ts
client/src/app/shared/shared-video-comment/video-comment.model.ts
client/src/app/shared/shared-video-comment/video-comment.service.ts
client/src/app/shared/shared-video-miniature/abstract-video-list.ts
client/src/app/shared/shared-video-miniature/video-download.component.ts
client/src/app/shared/shared-video-miniature/video-list-header.component.ts
client/src/app/shared/shared-video-miniature/videos-selection.component.ts
client/src/app/shared/shared-video-playlist/video-add-to-playlist.component.ts
client/src/app/shared/shared-video-playlist/video-playlist-element.model.ts
client/src/app/shared/shared-video-playlist/video-playlist.model.ts
client/src/app/shared/shared-video-playlist/video-playlist.service.ts
client/src/assets/player/p2p-media-loader/hls-plugin.ts
client/src/assets/player/p2p-media-loader/segment-validator.ts
client/src/assets/player/peertube-player-manager.ts
client/src/assets/player/peertube-plugin.ts
client/src/assets/player/peertube-videojs-typings.ts
client/src/assets/player/stats/stats-card.ts
client/src/assets/player/translations-manager.ts
client/src/assets/player/upnext/end-card.ts
client/src/assets/player/utils.ts
client/src/assets/player/videojs-components/p2p-info-button.ts
client/src/assets/player/videojs-components/resolution-menu-item.ts
client/src/assets/player/videojs-components/settings-dialog.ts
client/src/assets/player/videojs-components/settings-menu-item.ts
client/src/assets/player/videojs-components/settings-panel-child.ts
client/src/assets/player/videojs-components/settings-panel.ts
client/src/assets/player/webtorrent/peertube-chunk-store.ts
client/src/assets/player/webtorrent/video-renderer.ts
client/src/assets/player/webtorrent/webtorrent-plugin.ts
client/src/polyfills.ts
client/src/root-helpers/bytes.ts
client/src/root-helpers/peertube-web-storage.ts
client/src/root-helpers/plugins-manager.ts
client/src/standalone/player/definitions.ts
client/src/standalone/player/events.ts
client/src/standalone/player/player.ts
client/src/standalone/videos/embed-api.ts
client/src/standalone/videos/embed.ts
client/src/standalone/videos/test-embed.ts
client/src/types/register-client-option.model.ts
client/src/typings.d.ts
client/tsconfig.json
client/tslint.json [deleted file]
client/yarn.lock

diff --git a/client/.eslintrc.json b/client/.eslintrc.json
new file mode 100644 (file)
index 0000000..81b5037
--- /dev/null
@@ -0,0 +1,169 @@
+{
+  "root": true,
+  "ignorePatterns": [
+    "projects/**/*",
+    "node_modules/"
+  ],
+  "overrides": [
+    {
+      "files": [
+        "*.ts"
+      ],
+      "parserOptions": {
+        "project": [
+          "tsconfig.json",
+          "e2e/tsconfig.json"
+        ],
+        "createDefaultProgram": true
+      },
+      "extends": [
+        "../.eslintrc.json",
+        "plugin:@angular-eslint/ng-cli-compat",
+        "plugin:@angular-eslint/ng-cli-compat--formatting-add-on",
+        "plugin:@angular-eslint/template/process-inline-templates"
+      ],
+      "rules": {
+        "lines-between-class-members": "off",
+        "@typescript-eslint/lines-between-class-members": [ "off" ],
+        "arrow-body-style": "off",
+        "import/no-webpack-loader-syntax": "off",
+        "no-underscore-dangle": "off",
+        "node/no-callback-literal": "off",
+        "@angular-eslint/component-selector": [
+          "error",
+          {
+            "type": [ "element", "attribute" ],
+            "prefix": "my",
+            "style": "kebab-case"
+          }
+        ],
+        "@angular-eslint/directive-selector": [
+          "error",
+          {
+            "type": [ "element", "attribute" ],
+            "prefix": "my",
+            "style": "camelCase"
+          }
+        ],
+        "@typescript-eslint/no-this-alias": [
+          "error",
+          {
+            "allowDestructuring": true,
+            "allowedNames": ["self", "player"]
+          }
+        ],
+        "@typescript-eslint/prefer-readonly": "off",
+        "@angular-eslint/use-component-view-encapsulation": "error",
+        "prefer-arrow/prefer-arrow-functions": "off",
+        "@typescript-eslint/await-thenable": "error",
+        "@typescript-eslint/consistent-type-definitions": "off",
+        "@typescript-eslint/dot-notation": "off",
+        "@typescript-eslint/explicit-member-accessibility": [
+          "off",
+          {
+            "accessibility": "explicit"
+          }
+        ],
+        "@typescript-eslint/member-ordering": [
+          "off"
+        ],
+        "@typescript-eslint/member-delimiter-style": [
+          "error",
+          {
+            "multiline": {
+              "delimiter": "none",
+              "requireLast": true
+            },
+            "singleline": {
+              "delimiter": "comma",
+              "requireLast": false
+            }
+          }
+        ],
+        "@typescript-eslint/prefer-for-of": "off",
+        "@typescript-eslint/no-empty-function": "error",
+        "@typescript-eslint/no-floating-promises": "off",
+        "@typescript-eslint/no-inferrable-types": "error",
+        "@typescript-eslint/no-shadow": [
+          "off",
+          {
+            "hoist": "all"
+          }
+        ],
+        "@typescript-eslint/no-unnecessary-qualifier": "error",
+        "@typescript-eslint/no-unnecessary-type-assertion": "error",
+        "@typescript-eslint/no-unused-expressions": [
+          "error",
+          {
+            "allowTaggedTemplates": true,
+            "allowShortCircuit": true
+          }
+        ],
+        "@typescript-eslint/quotes": [
+          "error",
+          "single",
+          {
+            "avoidEscape": true,
+            "allowTemplateLiterals": true
+          }
+        ],
+        "@typescript-eslint/semi": [
+          "error",
+          "never"
+        ],
+        "brace-style": [
+          "error",
+          "1tbs"
+        ],
+        "comma-dangle": "error",
+        "curly": [
+          "error",
+          "multi-line"
+        ],
+        "dot-notation": "off",
+        "no-useless-return": "off",
+        "indent": "off",
+        "no-bitwise": "off",
+        "no-console": "off",
+        "no-return-assign": "off",
+        "no-constant-condition": "error",
+        "no-control-regex": "error",
+        "no-duplicate-imports": "error",
+        "no-empty": "error",
+        "no-empty-function": [
+          "error",
+          { "allow": [ "constructors" ] }
+        ],
+        "no-invalid-regexp": "error",
+        "no-multiple-empty-lines": "error",
+        "no-redeclare": "error",
+        "no-regex-spaces": "error",
+        "no-return-await": "error",
+        "no-shadow": "off",
+        "no-unused-expressions": "error",
+        "semi": "error",
+        "space-before-function-paren": [
+          "error",
+          "always"
+        ],
+        "space-in-parens": [
+          "error",
+          "never"
+        ],
+        "object-shorthand": [
+          "error",
+          "properties"
+        ]
+      }
+    },
+    {
+      "files": [
+        "*.html"
+      ],
+      "extends": [
+        "plugin:@angular-eslint/template/recommended"
+      ],
+      "rules": {}
+    }
+  ]
+}
index 60a630c895cc4f8edc2d9de9ee53360259f10dee..556df6bbd2ce0f4e8bbce531765510322406e8ac 100644 (file)
               ]
             },
             "ar-locale": {
-              "localize": ["ar"],
+              "localize": [
+                "ar"
+              ],
               "budgets": [
                 {
                   "type": "anyComponentStyle",
           }
         },
         "lint": {
-          "builder": "@angular-devkit/build-angular:tslint",
+          "builder": "@angular-eslint/builder:lint",
           "options": {
-            "tsConfig": [
-              "tsconfig.json"
-            ],
-            "exclude": [
-              "**/node_modules/**"
+            "lintFilePatterns": [
+              "src/**/*.ts",
+              "src/**/*.html"
             ]
           }
         },
               "protractorConfig": "e2e/local-protractor.conf.js"
             }
           }
-        },
-        "lint": {
-          "builder": "@angular-devkit/build-angular:tslint",
-          "options": {
-            "tsConfig": [
-              "e2e/tsconfig.e2e.json"
-            ],
-            "exclude": [
-              "**/node_modules/**"
-            ]
-          }
         }
       }
     }
index 5080f2528cedeb112579952d42ad84ca9b14f258..d0d4ae9a38ff140da92a1587d7f089760cb7b8a7 100644 (file)
@@ -49,7 +49,7 @@ exports.config = {
 
   onPrepare() {
     require('ts-node').register({
-      project: require('path').join(__dirname, './tsconfig.e2e.json')
+      project: require('path').join(__dirname, './tsconfig.json')
     })
     jasmine.getEnv().addReporter(new SpecReporter({   spec:  {  displayStacktrace: true    }  }))
   }
index e5a23e16a65d45be1cf614b9b819250b62eb589a..ff0ae83e7e76eea27c9f59dd8d4454ca7361f240 100644 (file)
@@ -83,7 +83,7 @@ exports.config = {
 
   onPrepare() {
     require('ts-node').register({
-      project: require('path').join(__dirname, './tsconfig.e2e.json')
+      project: require('path').join(__dirname, './tsconfig.json')
     })
     jasmine.getEnv().addReporter(new SpecReporter({
       spec: { displayStacktrace: 'raw' }
index 1a04106f4b9247ffe0b8169e7786cd69d7ae765e..bfbb99dba5f618872d0b182175d775c13e6b5036 100644 (file)
   },
   "scripts": {
     "lint": "npm run lint-ts && npm run lint-scss",
-    "lint-ts": "tslint --project ./tsconfig.json -c ./tslint.json 'src/app/**/*.ts' 'src/standalone/**/*.ts'",
+    "lint-ts": "eslint --ext .ts src/standalone/**/*.ts && npm run ng lint",
     "lint-scss": "stylelint 'src/**/*.scss'",
     "webpack": "webpack",
-    "tslint": "tslint",
+    "eslint": "eslint",
     "ng": "ng",
     "webpack-bundle-analyzer": "webpack-bundle-analyzer",
     "webdriver-manager": "webdriver-manager",
   "typings": "*.d.ts",
   "devDependencies": {
     "@angular-devkit/build-angular": "^12.0.0",
+    "@angular-eslint/builder": "12.3.1",
+    "@angular-eslint/eslint-plugin": "12.3.1",
+    "@angular-eslint/eslint-plugin-template": "12.3.1",
+    "@angular-eslint/schematics": "12.3.1",
+    "@angular-eslint/template-parser": "12.3.1",
     "@angular/animations": "^12.0.0",
     "@angular/cdk": "^12.0.0",
     "@angular/cli": "^12.0.0",
     "@types/sha.js": "^2.4.0",
     "@types/video.js": "^7.3.8",
     "@types/webtorrent": "^0.109.0",
+    "@typescript-eslint/eslint-plugin": "4.28.2",
+    "@typescript-eslint/parser": "4.28.2",
     "angular2-hotkeys": "^2.1.2",
     "angularx-qrcode": "11.0.0",
     "bootstrap": "^4.1.3",
     "buffer": "^6.0.3",
     "cache-chunk-store": "^3.0.0",
     "chart.js": "^2.9.3",
-    "codelyzer": "^6.0.0",
     "core-js": "^3.1.4",
     "css-loader": "^6.2.0",
     "debug": "^4.3.1",
     "dexie": "^3.0.0",
+    "eslint": "^7.26.0",
+    "eslint-plugin-import": "latest",
+    "eslint-plugin-jsdoc": "latest",
+    "eslint-plugin-prefer-arrow": "latest",
     "focus-visible": "^5.0.2",
     "hls.js": "^1.0.7",
     "html-loader": "^2.1.2",
     "terser-webpack-plugin": "^5.1.2",
     "ts-loader": "^9.2.2",
     "tslib": "^2.0.0",
-    "tslint": "~6.1.0",
-    "tslint-angular": "^3.0.2",
-    "tslint-config-standard": "^9.0.0",
     "typescript": "~4.3.4",
     "video.js": "^7",
     "videojs-contextmenu-pt": "^5.4.1",
index 1dcb6396c7c2acd41a9a33c73229e703ecafec68..a352726810eae74537f555c5fdd28a86dbd8cc48 100644 (file)
@@ -90,7 +90,7 @@ export class AboutFollowsComponent implements OnInit {
   private loadMoreFollowers (reset = false) {
     const pagination = this.restService.componentPaginationToRestPagination(this.followersPagination)
 
-    this.followService.getFollowers({ pagination: pagination, sort: this.sort, state: 'accepted' })
+    this.followService.getFollowers({ pagination, sort: this.sort, state: 'accepted' })
         .subscribe({
           next: resultList => {
             if (reset) this.followers = []
index f86df5b67098f67f013b3e8093c866a8cb52e901..0826bbc5a198bfb59c0b50b0a1ab48b9fdf9e6f2 100644 (file)
@@ -95,6 +95,6 @@ export class AboutInstanceComponent implements OnInit, AfterViewChecked {
   onClickCopyLink (anchor: HTMLAnchorElement) {
     const link = anchor.href
     copyToClipboard(link)
-    this.notifier.success(link, $localize `Link copied`)
+    this.notifier.success(link, $localize`Link copied`)
   }
 }
index cbc75988129b50fd1030339eecba8ef76da68583..fab9cfc4b079d5fcedd16fbde4d458fae96d4d13 100644 (file)
@@ -77,10 +77,10 @@ export class ContactAdminModalComponent extends FormReactive implements OnInit {
   }
 
   sendForm () {
-    const fromName = this.form.value[ 'fromName' ]
-    const fromEmail = this.form.value[ 'fromEmail' ]
-    const subject = this.form.value[ 'subject' ]
-    const body = this.form.value[ 'body' ]
+    const fromName = this.form.value['fromName']
+    const fromEmail = this.form.value['fromEmail']
+    const subject = this.form.value['subject']
+    const body = this.form.value['body']
 
     this.instanceService.contactAdministrator(fromEmail, fromName, subject, body)
         .subscribe({
index 3974231e338e3da190d4c911c567810f0cd3f32c..cf5570d31399c8cfe165476d798e7161bcd41e3f 100644 (file)
@@ -3,7 +3,6 @@ import { RouterModule, Routes } from '@angular/router'
 import { AboutFollowsComponent } from '@app/+about/about-follows/about-follows.component'
 import { AboutInstanceComponent } from '@app/+about/about-instance/about-instance.component'
 import { AboutInstanceResolver } from '@app/+about/about-instance/about-instance.resolver'
-import { ContactAdminModalComponent } from '@app/+about/about-instance/contact-admin-modal.component'
 import { AboutPeertubeComponent } from '@app/+about/about-peertube/about-peertube.component'
 import { AboutComponent } from './about.component'
 
index e146a5cd2c4075b53ab19d08f88faab160d7f3a9..f6df388570b837930e907d598044101b6590d1ab 100644 (file)
@@ -1,10 +1,10 @@
 import { from, Subject, Subscription } from 'rxjs'
 import { concatMap, map, switchMap, tap } from 'rxjs/operators'
 import { Component, OnDestroy, OnInit } from '@angular/core'
-import { ComponentPagination, hasMoreItems, MarkdownService, ScreenService, User, UserService } from '@app/core'
+import { ComponentPagination, hasMoreItems, MarkdownService, User, UserService } from '@app/core'
 import { Account, AccountService, Video, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main'
-import { NSFWPolicyType, VideoSortField } from '@shared/models'
 import { MiniatureDisplayOptions } from '@app/shared/shared-video-miniature'
+import { NSFWPolicyType, VideoSortField } from '@shared/models'
 
 @Component({
   selector: 'my-account-video-channels',
@@ -87,7 +87,9 @@ export class AccountVideoChannelsComponent implements OnInit, OnDestroy {
 
     this.videoChannelService.listAccountVideoChannels(options)
       .pipe(
-        tap(res => this.channelPagination.totalItems = res.total),
+        tap(res => {
+          this.channelPagination.totalItems = res.total
+        }),
         switchMap(res => from(res.data)),
         concatMap(videoChannel => {
           const options = {
@@ -113,14 +115,14 @@ export class AccountVideoChannelsComponent implements OnInit, OnDestroy {
   }
 
   getVideosOf (videoChannel: VideoChannel) {
-    const obj = this.videos[ videoChannel.id ]
+    const obj = this.videos[videoChannel.id]
     if (!obj) return []
 
     return obj.videos
   }
 
   getTotalVideosOf (videoChannel: VideoChannel) {
-    const obj = this.videos[ videoChannel.id ]
+    const obj = this.videos[videoChannel.id]
     if (!obj) return undefined
 
     return obj.total
index 75af45e9091c97e0ead52bd0606007dcca9874ec..4ab6d21473c43a239f599dd0a947d4664e6b5902 100644 (file)
@@ -1,5 +1,5 @@
 import { forkJoin, Subscription } from 'rxjs'
-import { first, tap } from 'rxjs/operators'
+import { first } from 'rxjs/operators'
 import { Component, ComponentFactoryResolver, OnDestroy, OnInit } from '@angular/core'
 import { ActivatedRoute, Router } from '@angular/router'
 import { AuthService, ConfirmService, LocalStorageService, Notifier, ScreenService, ServerService, UserService } from '@app/core'
index cb3e79a18c8a4275c42c67702e8191f5d79bfedf..0906992fefadea29a807cdd86f6243c995ae53da 100644 (file)
       <a [routerLink]="item.routerLink" routerLinkActive="active" class="title-page">{{ item.label }}</a>
     </ng-template>
 
-    <list-overflow [hidden]="hideMenu" [items]="links" [itemTemplate]="linkTemplate"></list-overflow>
+    <my-list-overflow [hidden]="hideMenu" [items]="links" [itemTemplate]="linkTemplate"></my-list-overflow>
 
-    <simple-search-input
+    <my-simple-search-input
       [alwaysShow]="!isInSmallView()" (searchChanged)="searchChanged($event)"
       (inputDisplayChanged)="onSearchInputDisplayChanged($event)" name="search-videos"
       i18n-iconTitle icon-title="Search account videos"
       i18n-placeholder placeholder="Search account videos"
-    ></simple-search-input>
+    ></my-simple-search-input>
   </div>
 
   <router-outlet (activate)="onOutletLoaded($event)"></router-outlet>
index 25eb13588d8710d7f1fc1ecf74a3e316142aab5c..733cff8d51284429b322adcd011f2e89525fea11 100644 (file)
@@ -61,7 +61,7 @@ export class AccountsComponent implements OnInit, OnDestroy {
   ngOnInit () {
     this.routeSub = this.route.params
                         .pipe(
-                          map(params => params[ 'accountId' ]),
+                          map(params => params['accountId']),
                           distinctUntilChanged(),
                           switchMap(accountId => this.accountService.getAccount(accountId)),
                           tap(account => this.onAccount(account)),
@@ -72,7 +72,9 @@ export class AccountsComponent implements OnInit, OnDestroy {
                           ]))
                         )
                         .subscribe({
-                          next: videoChannels => this.videoChannels = videoChannels.data,
+                          next: videoChannels => {
+                            this.videoChannels = videoChannels.data
+                          },
 
                           error: err => this.notifier.error(err.message)
                         })
@@ -176,7 +178,9 @@ export class AccountsComponent implements OnInit, OnDestroy {
     if (user.hasRight(UserRight.MANAGE_USERS)) {
       this.userService.getUser(account.userId)
         .subscribe({
-          next: accountUser => this.accountUser = accountUser,
+          next: accountUser => {
+            this.accountUser = accountUser
+          },
 
           error: err => this.notifier.error(err.message)
         })
@@ -209,6 +213,8 @@ export class AccountsComponent implements OnInit, OnDestroy {
         itemsPerPage: 0
       },
       sort: '-publishedAt'
-    }).subscribe(res => this.accountVideosCount = res.total)
+    }).subscribe(res => {
+      this.accountVideosCount = res.total
+    })
   }
 }
index 671e734ac80562464f2bbbcd49814408112a1df3..7a8258820cfcc635e2a0b7c8ae24d16d470358c8 100644 (file)
@@ -97,7 +97,7 @@ export class EditBasicConfigurationComponent implements OnInit, OnChanges {
       .pipe(pairwise())
       .subscribe(([ oldValue, newValue ]) => {
         if (oldValue !== true && newValue === true) {
-          // tslint:disable:max-line-length
+          /* eslint-disable max-len */
           this.signupAlertMessage = $localize`You enabled signup: we automatically enabled the "Block new videos automatically" checkbox of the "Videos" section just below.`
 
           this.form.patchValue({
index 538fa6f145d6405e1c2e955c17666b4e694ede13..be1a992894e422b81cb4a9fb18dfa1e5158d391f 100644 (file)
@@ -277,7 +277,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
 
           // Reload general configuration
           this.serverService.resetConfig()
-            .subscribe(config => this.serverConfig = config)
+            .subscribe(config => {
+              this.serverConfig = config
+            })
 
           this.updateForm()
 
index 1923ede395bc65302f7fd0206d8d9a46b629a4c8..dc834da142ad3ff2a0ac470aafe6cc4b5dd07e68 100644 (file)
@@ -1,4 +1,4 @@
-import { Component, Input, OnInit } from '@angular/core'
+import { Component, Input } from '@angular/core'
 import { FormGroup } from '@angular/forms'
 import { CustomMarkupService } from '@app/shared/shared-custom-markup'
 
index cf0225098cdcc0c9219b8e3d549865d25cc7904b..383f66ffde6baf3f1b22c51eff04841a6398525f 100644 (file)
@@ -2,7 +2,6 @@ import { SortMeta } from 'primeng/api'
 import { Component, OnInit, ViewChild } from '@angular/core'
 import { ConfirmService, Notifier, RestPagination, RestTable } from '@app/core'
 import { InstanceFollowService } from '@app/shared/shared-instance'
-import { BatchDomainsModalComponent } from '@app/shared/shared-moderation'
 import { ActorFollow } from '@shared/models'
 import { FollowModalComponent } from './follow-modal.component'
 
@@ -22,7 +21,7 @@ export class FollowingListComponent extends RestTable implements OnInit {
     private notifier: Notifier,
     private confirmService: ConfirmService,
     private followService: InstanceFollowService
-    ) {
+  ) {
     super()
   }
 
index 47c402510e844ff56bb132b9a320b5a136eaa840..95f8473db106a6c6fe072c96a3e1b52b83de1993 100644 (file)
@@ -14,7 +14,7 @@ export class RedundancyCheckboxComponent {
   constructor (
     private notifier: Notifier,
     private redundancyService: RedundancyService
-    ) { }
+  ) { }
 
   updateRedundancyState () {
     this.redundancyService.updateRedundancy(this.host, this.redundancyAllowed)
index 4c691269a1cfde957624fc50b6236706df58e86a..7ffed83e8a53147311dd02b39a805c0a1cd19ae0 100644 (file)
@@ -21,7 +21,7 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit
   pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
   displayType: VideoRedundanciesTarget = 'my-videos'
 
-  redundanciesGraphsData: { stats: VideosRedundancyStats, graphData: object, options: object }[] = []
+  redundanciesGraphsData: { stats: VideosRedundancyStats, graphData: any, options: any }[] = []
 
   noRedundancies = false
 
@@ -32,7 +32,7 @@ export class VideoRedundanciesListComponent extends RestTable implements OnInit
     private confirmService: ConfirmService,
     private redundancyService: RedundancyService,
     private serverService: ServerService
-    ) {
+  ) {
     super()
 
     this.bytesPipe = new BytesPipe()
index d89c8f24445c2754eb36617e90584b57c16bc715..3a8df1f07e6972f025dbe35df99c04f34faa964e 100644 (file)
@@ -21,7 +21,7 @@
 
   <ng-template pTemplate="header">
     <tr>
-      <th style="width: 40px"></th>
+      <th style="width: 40px;"></th>
       <th style="width: 150px;"></th>
       <th i18n pSortableColumn="name">Video <p-sortIcon field="name"></p-sortIcon></th>
       <th style="width: 100px;" i18n>Sensitive</th>
@@ -54,7 +54,7 @@
             </div>
             <div class="table-video-text">
               <div>
-                <my-global-icon i18n-title title="The video was blocked due to automatic blocking of new videos" *ngIf="videoBlock.type == 2" iconName="robot"></my-global-icon>
+                <my-global-icon i18n-title title="The video was blocked due to automatic blocking of new videos" *ngIf="videoBlock.type === 2" iconName="robot"></my-global-icon>
                 {{ videoBlock.video.name }}
               </div>
               <div class="text-muted">by {{ videoBlock.video.channel?.displayName }} on {{ videoBlock.video.channel?.host }} </div>
index adef16975bdad4b40781fc93737a5091eb71875e..3edcb1c638d4bf7034c970a9df34f9b2f7910c75 100644 (file)
@@ -28,11 +28,11 @@ export class VideoBlockListComponent extends RestTable implements OnInit {
 
   inputFilters: AdvancedInputFilter[] = [
     {
-      queryParams: { 'search': 'type:auto' },
+      queryParams: { search: 'type:auto' },
       label: $localize`Automatic blocks`
     },
     {
-      queryParams: { 'search': 'type:manual' },
+      queryParams: { search: 'type:manual' },
       label: $localize`Manual blocks`
     }
   ]
index 4904bcc25aaf8247f2d6002bcdae7086606a2322..512ceffd98bab85a6fc043c0969c50e86c1b9329 100644 (file)
@@ -44,11 +44,11 @@ export class VideoCommentListComponent extends RestTable implements OnInit {
 
   inputFilters: AdvancedInputFilter[] = [
     {
-      queryParams: { 'search': 'local:true' },
+      queryParams: { search: 'local:true' },
       label: $localize`Local comments`
     },
     {
-      queryParams: { 'search': 'local:false' },
+      queryParams: { search: 'local:false' },
       label: $localize`Remote comments`
     }
   ]
@@ -66,7 +66,7 @@ export class VideoCommentListComponent extends RestTable implements OnInit {
     private videoCommentService: VideoCommentService,
     private markdownRenderer: MarkdownService,
     private bulkService: BulkService
-    ) {
+  ) {
     super()
 
     this.videoCommentActions = [
index 8d8f12c485f018dd84bece29b8aa8ad0de7a5839..a41c7d7005484717a80a15a282c3775c27e3ceeb 100644 (file)
@@ -3,7 +3,7 @@
 </div>
 
 <div class="search-bar">
-  <input type="text" (input)="onSearchChange($event)" i18n-placeholder placeholder="Search..." autofocus />
+  <input type="text" (input)="onSearchChange($event)" i18n-placeholder placeholder="Search..." myAutofocus />
 </div>
 
 <div class="alert alert-info" i18n *ngIf="pluginInstalled">
index 10fb5291157249c08dabb144fc4b1afa2403eb21..402bef1ea7beef1e4836309a3b1a9d6dbeb49d7a 100644 (file)
@@ -103,8 +103,8 @@ export class PluginShowInstalledComponent extends FormReactive implements OnInit
     const settingsValues: any = {}
 
     for (const setting of this.registeredSettings) {
-      buildOptions[ setting.name ] = null
-      settingsValues[ setting.name ] = this.getSetting(setting.name)
+      buildOptions[setting.name] = null
+      settingsValues[setting.name] = this.getSetting(setting.name)
     }
 
     this.buildForm(buildOptions)
@@ -117,7 +117,7 @@ export class PluginShowInstalledComponent extends FormReactive implements OnInit
   private getSetting (name: string) {
     const settings = this.plugin.settings
 
-    if (settings && settings[name] !== undefined) return settings[name]
+    if (settings?.[name] !== undefined) return settings[name]
 
     const registered = this.registeredSettings.find(r => r.name === name)
 
index d91fccc09364e501b384db949d3c8c1609a2b327..c4f480caed4fb418d3f4df889a7a03b55245eb1f 100644 (file)
@@ -1,10 +1,8 @@
-import { Observable } from 'rxjs'
-import { catchError, map, switchMap } from 'rxjs/operators'
+import { catchError } from 'rxjs/operators'
 import { HttpClient, HttpParams } from '@angular/common/http'
 import { Injectable } from '@angular/core'
 import { ComponentPagination, RestExtractor, RestService } from '@app/core'
 import { PluginService } from '@app/core/plugins/plugin.service'
-import { peertubeTranslate } from '@shared/core-utils/i18n'
 import {
   InstallOrUpdatePlugin,
   ManagePlugin,
index 4b4a8914fdd5543776351be93853e790ffc5624a..6c4a07469f8b65f96952d5d45250a8a67c0d67c7 100644 (file)
@@ -20,9 +20,9 @@ export class JobService {
   ) {}
 
   getJobs (options: {
-    jobState?: JobStateClient,
-    jobType: JobTypeClient,
-    pagination: RestPagination,
+    jobState?: JobStateClient
+    jobType: JobTypeClient
+    pagination: RestPagination
     sort: SortMeta
   }): Observable<ResultList<Job>> {
     const { jobState, jobType, pagination, sort } = options
@@ -32,7 +32,7 @@ export class JobService {
 
     if (jobType !== 'all') params = params.append('jobType', jobType)
 
-    return this.authHttp.get<ResultList<Job>>(JobService.BASE_JOB_URL + `/${jobState ? jobState : ''}`, { params })
+    return this.authHttp.get<ResultList<Job>>(JobService.BASE_JOB_URL + `/${jobState || ''}`, { params })
                .pipe(
                  map(res => {
                    return this.restExtractor.convertResultListDateToHuman(res, [ 'createdAt', 'processedOn', 'finishedOn' ])
index 69439a179e302cd765684ae8e8e3b2f22e43807b..0c222cad2f12cc11d6191b23b1d681f2642049e5 100644 (file)
@@ -18,9 +18,9 @@ export class LogsService {
   ) {}
 
   getLogs (options: {
-    isAuditLog: boolean,
-    startDate: string,
-    level?: LogLevel,
+    isAuditLog: boolean
+    startDate: string
+    level?: LogLevel
     endDate?: string
   }): Observable<any[]> {
     const { isAuditLog, startDate } = options
index 8403db91a8b444b563550d57b19d0defab759be9..b61b22fd0fb06ba9ebe0043309ff1996051167d5 100644 (file)
@@ -33,7 +33,7 @@ export class UserCreateComponent extends UserEdit implements OnInit {
     private router: Router,
     private notifier: Notifier,
     private userService: UserService
-    ) {
+  ) {
     super()
 
     this.buildQuotaOptions()
@@ -78,7 +78,9 @@ export class UserCreateComponent extends UserEdit implements OnInit {
           this.router.navigate([ '/admin/users/list' ])
         },
 
-        error: err => this.error = err.message
+        error: err => {
+          this.error = err.message
+        }
       })
   }
 
index ae1f79ba0c100dceedb989270cbe028e3ca82d6f..af5e674a7df718b6df9c282c44090f0ab16ebec5 100644 (file)
@@ -7,7 +7,7 @@ import { HTMLServerConfig, UserAdminFlag, UserRole, VideoResolution } from '@sha
 import { SelectOptionsItem } from '../../../../types/select-options-item.model'
 
 @Directive()
-// tslint:disable-next-line: directive-class-suffix
+// eslint-disable-next-line @angular-eslint/directive-class-suffix
 export abstract class UserEdit extends FormReactive implements OnInit {
   videoQuotaOptions: SelectOptionsItem[] = []
   videoQuotaDailyOptions: SelectOptionsItem[] = []
index 7c42b92414139d2ba0446cf3dcdbfde1779bc676..42bf20de14a6a5745207a1aaef45e7331fc8f354 100644 (file)
@@ -20,7 +20,7 @@ export class UserPasswordComponent extends FormReactive implements OnInit {
     protected formValidatorService: FormValidatorService,
     private notifier: Notifier,
     private userService: UserService
-    ) {
+  ) {
     super()
   }
 
@@ -39,7 +39,9 @@ export class UserPasswordComponent extends FormReactive implements OnInit {
       .subscribe({
         next: () => this.notifier.success($localize`Password changed for user ${this.username}.`),
 
-        error: err => this.error = err.message
+        error: err => {
+          this.error = err.message
+        }
       })
   }
 
index 2128ba4fdd70b031673443a5972feeb986a4fd7b..42599a17e59a76b7bd88cf216d897b41ae9bee4d 100644 (file)
@@ -33,7 +33,7 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
     private router: Router,
     private notifier: Notifier,
     private userService: UserService
-    ) {
+  ) {
     super()
 
     this.buildQuotaOptions()
@@ -63,7 +63,9 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
         .subscribe({
           next: user => this.onUserFetched(user),
 
-          error: err => this.error = err.message
+          error: err => {
+            this.error = err.message
+          }
         })
     })
   }
@@ -91,7 +93,9 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
           this.router.navigate([ '/admin/users/list' ])
         },
 
-        error: err => this.error = err.message
+        error: err => {
+          this.error = err.message
+        }
       })
   }
 
@@ -114,7 +118,9 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
           this.notifier.success($localize`An email asking for password reset has been sent to ${this.user.username}.`)
         },
 
-        error: err => this.error = err.message
+        error: err => {
+          this.error = err.message
+        }
       })
   }
 
index d4406549a15b8d729cc7e51bce3a7d02c0a64b21..39caf5ed519787c9f93ad44cdd5cbc2cc71a73c4 100644 (file)
@@ -36,7 +36,7 @@ export class UserListComponent extends RestTable implements OnInit {
 
   inputFilters: AdvancedInputFilter[] = [
     {
-      queryParams: { 'search': 'banned:true' },
+      queryParams: { search: 'banned:true' },
       label: $localize`Banned users`
     }
   ]
index 27793ff0cc9345447614f5f8914aff1bd2e440ac..0be67368ebbe1c6c96cd7a90ff210168aa937e8f 100644 (file)
@@ -21,7 +21,7 @@
               <label i18n for="username">User</label>
               <input
                 type="text" id="username" i18n-placeholder placeholder="Username or email address" required tabindex="1"
-                formControlName="username" class="form-control" [ngClass]="{ 'input-error': formErrors['username'] }" autofocus
+                formControlName="username" class="form-control" [ngClass]="{ 'input-error': formErrors['username'] }" myAutofocus
               >
             </div>
 
index 16876afd6d930748819cc522d4085b4b71ba5f57..1fa4bd3b5f4092b08e6ad106b5a7e9d937ec8a82 100644 (file)
@@ -46,7 +46,7 @@ export class LoginComponent extends FormReactive implements OnInit, AfterViewIni
     private redirectService: RedirectService,
     private notifier: Notifier,
     private hooks: HooksService
-    ) {
+  ) {
     super()
   }
 
index 6873c7d40139f0396481e4c81fa6b008a41a871e..e88cdd228697e3df9d9e02fbbd0f6b841cbfce01 100644 (file)
@@ -36,6 +36,7 @@ export class MyAccountApplicationsComponent implements OnInit {
 
   async renewToken () {
     const res = await this.confirmService.confirm(
+      // eslint-disable-next-line max-len
       $localize`Renewing the token will disallow previously configured clients from retrieving the feed until they use the new token. Proceed?`,
       $localize`Renew token`
     )
index 08bc5b425480fae705163cf693da15c44f89ecb8..9b87daa40ef11d33871bc2810cddb1204cbaaf8d 100644 (file)
@@ -28,7 +28,7 @@ export class MyAccountChangeEmailComponent extends FormReactive implements OnIni
   ngOnInit () {
     this.buildForm({
       'new-email': USER_EMAIL_VALIDATOR,
-      'password': USER_PASSWORD_VALIDATOR
+      password: USER_PASSWORD_VALIDATOR
     })
 
     this.user = this.authService.getUser()
@@ -38,8 +38,8 @@ export class MyAccountChangeEmailComponent extends FormReactive implements OnIni
     this.error = null
     this.success = null
 
-    const password = this.form.value[ 'password' ]
-    const email = this.form.value[ 'new-email' ]
+    const password = this.form.value['password']
+    const email = this.form.value['new-email']
 
     forkJoin([
       this.serverService.getConfig(),
index f91b2f37b18d6889d973765a9a5a8dc4d496874f..47e54dc23110dbb3fc0febbaaaf122cf928b40a6 100644 (file)
@@ -1,7 +1,11 @@
 import { filter } from 'rxjs/operators'
 import { Component, OnInit } from '@angular/core'
 import { AuthService, Notifier, UserService } from '@app/core'
-import { USER_CONFIRM_PASSWORD_VALIDATOR, USER_PASSWORD_VALIDATOR, USER_EXISTING_PASSWORD_VALIDATOR } from '@app/shared/form-validators/user-validators'
+import {
+  USER_CONFIRM_PASSWORD_VALIDATOR,
+  USER_EXISTING_PASSWORD_VALIDATOR,
+  USER_PASSWORD_VALIDATOR
+} from '@app/shared/form-validators/user-validators'
 import { FormReactive, FormValidatorService } from '@app/shared/shared-forms'
 import { User } from '@shared/models'
 
@@ -19,7 +23,7 @@ export class MyAccountChangePasswordComponent extends FormReactive implements On
     private notifier: Notifier,
     private authService: AuthService,
     private userService: UserService
-    ) {
+  ) {
     super()
   }
 
@@ -35,13 +39,13 @@ export class MyAccountChangePasswordComponent extends FormReactive implements On
     const confirmPasswordControl = this.form.get('new-confirmed-password')
 
     confirmPasswordControl.valueChanges
-                          .pipe(filter(v => v !== this.form.value[ 'new-password' ]))
+                          .pipe(filter(v => v !== this.form.value['new-password']))
                           .subscribe(() => confirmPasswordControl.setErrors({ matchPassword: true }))
   }
 
   changePassword () {
-    const currentPassword = this.form.value[ 'current-password' ]
-    const newPassword = this.form.value[ 'new-password' ]
+    const currentPassword = this.form.value['current-password']
+    const newPassword = this.form.value['new-password']
 
     this.userService.changePassword(currentPassword, newPassword)
       .subscribe({
index 5005cb630426ada7c21ef830e0ee9707150f659e..4a46f1ad9072558f6e3aade03f6d39809aca50d1 100644 (file)
@@ -15,10 +15,11 @@ export class MyAccountDangerZoneComponent {
     private userService: UserService,
     private confirmService: ConfirmService,
     private redirectService: RedirectService
-    ) { }
+  ) { }
 
   async deleteMe () {
     const res = await this.confirmService.confirmWithInput(
+      // eslint-disable-next-line max-len
       $localize`Are you sure you want to delete your account? This will delete all your data, including channels, videos and comments. Content cached by other servers and other third-parties might make longer to be deleted.`,
       $localize`Type your username to confirm`,
       this.user.username,
index fc7635f38736cce6afab9533fbed967f4705bb58..a5bcb6496d9074f8ee75e7b64ed247eb95cf1a41 100644 (file)
@@ -19,7 +19,7 @@ export class MyAccountSettingsComponent implements OnInit, AfterViewChecked {
     private userService: UserService,
     private authService: AuthService,
     private notifier: Notifier
-    ) {}
+  ) {}
 
   get userInformationLoaded () {
     return this.authService.userInformationLoaded
index eaf8a72e92d0fe2b19b8d14b495055a9d90f28ff..450454ca2659940d83d7b24da3e71e604da32a2b 100644 (file)
@@ -13,7 +13,7 @@ export class MyAccountComponent implements OnInit {
 
   constructor (
     private screenService: ScreenService
-    ) { }
+  ) { }
 
   get isBroadcastMessageDisplayed () {
     return this.screenService.isBroadcastMessageDisplayed
index d983aacd919fddb4e216c38300eabb364169423f..fd00720d817cbb7f00928ba48f274c4c18c7ef0b 100644 (file)
@@ -31,7 +31,7 @@ export class MyVideoChannelCreateComponent extends MyVideoChannelEdit implements
     private notifier: Notifier,
     private router: Router,
     private videoChannelService: VideoChannelService
-    ) {
+  ) {
     super()
   }
 
@@ -64,7 +64,7 @@ export class MyVideoChannelCreateComponent extends MyVideoChannelEdit implements
           this.authService.refreshUserInformation()
 
           this.notifier.success($localize`Video channel ${videoChannelCreate.displayName} created.`)
-          this.router.navigate(['/my-library', 'video-channels'])
+          this.router.navigate([ '/my-library', 'video-channels' ])
         },
 
         error: err => {
index e8bfbf9ce7e7bb4fecd7e06f857fd9379f95177f..f9521b8b51afae1b852519be77d1a9e55effc9c3 100644 (file)
@@ -66,7 +66,9 @@ export class MyVideoChannelUpdateComponent extends MyVideoChannelEdit implements
             })
           },
 
-          error: err => this.error = err.message
+          error: err => {
+            this.error = err.message
+          }
         })
     })
   }
@@ -96,7 +98,9 @@ export class MyVideoChannelUpdateComponent extends MyVideoChannelEdit implements
           this.router.navigate([ '/my-library', 'video-channels' ])
         },
 
-        error: err => this.error = err.message
+        error: err => {
+          this.error = err.message
+        }
       })
   }
 
index 8b665fd5803da719505261b3ff792bf10c51413f..303f783fd32496f19502a835685019c5efad2365 100644 (file)
@@ -121,16 +121,16 @@ channel with the same name (${videoChannel.name})!`,
         display: false
       },
       scales: {
-        xAxes: [{
+        xAxes: [ {
           display: false
-        }],
-        yAxes: [{
+        } ],
+        yAxes: [ {
           display: false,
           ticks: {
             min: Math.max(0, this.videoChannelsMinimumDailyViews - (3 * this.videoChannelsMaximumDailyViews / 100)),
             max: Math.max(1, this.videoChannelsMaximumDailyViews)
           }
-        }]
+        } ]
       },
       layout: {
         padding: {
index fe6e863467c9b852631ed437bb4c741f2b7c8de8..a72d22e1c4b1a44f4a5db1150f27be4ebecc8c64 100644 (file)
@@ -1,4 +1,3 @@
-import { Subject } from 'rxjs'
 import { tap } from 'rxjs/operators'
 import { Component, ComponentFactoryResolver, OnInit, ViewChild } from '@angular/core'
 import { ActivatedRoute, Router } from '@angular/router'
@@ -109,9 +108,9 @@ export class MyHistoryComponent implements OnInit, DisableForReuseHook {
     this.userService.updateMyProfile({ videosHistoryEnabled: this.videosHistoryEnabled })
       .subscribe({
         next: () => {
-          const message = this.videosHistoryEnabled === true ?
-            $localize`Videos history is enabled` :
-            $localize`Videos history is disabled`
+          const message = this.videosHistoryEnabled === true
+            ? $localize`Videos history is enabled`
+            $localize`Videos history is disabled`
 
           this.notifier.success(message)
 
index 2ee3559a78da116aa3c437df72e1d66fba4221ec..16a7f63e330597cc0e3586603f502d8f178879c5 100644 (file)
@@ -17,7 +17,7 @@ export class MyLibraryComponent implements OnInit {
     private serverService: ServerService,
     private authService: AuthService,
     private screenService: ScreenService
-    ) { }
+  ) { }
 
   get isBroadcastMessageDisplayed () {
     return this.screenService.isBroadcastMessageDisplayed
index b92377bca39bfab07255a78e6e4f411bdfff4338..764da2369c5a9062132c3537764f8c8983ce3092 100644 (file)
@@ -29,7 +29,7 @@ export class MyAcceptOwnershipComponent extends FormReactive implements OnInit {
     private notifier: Notifier,
     private authService: AuthService,
     private modalService: NgbModal
-    ) {
+  ) {
     super()
   }
 
index 3e3c3c8785306f4605587c7dbc815f2acdae609d..8bc78b2dbc3fd37ca1c5cab56ccebf7bade50026 100644 (file)
@@ -29,7 +29,7 @@ export class MyVideoPlaylistCreateComponent extends MyVideoPlaylistEdit implemen
     private router: Router,
     private videoPlaylistService: VideoPlaylistService,
     private serverService: ServerService
-    ) {
+  ) {
     super()
   }
 
@@ -78,7 +78,9 @@ export class MyVideoPlaylistCreateComponent extends MyVideoPlaylistEdit implemen
           this.router.navigate([ '/my-library', 'video-playlists' ])
         },
 
-        error: err => this.error = err.message
+        error: err => {
+          this.error = err.message
+        }
       })
   }
 
index 6aff5dbd71bf7d5a07212d1f3e5c8885f96de399..d6959a50ec13aac140e5b57a8f1458a81e7326e6 100644 (file)
@@ -57,7 +57,7 @@ export class MyVideoPlaylistElementsComponent implements OnInit, OnDestroy {
     ]
 
     this.paramsSub = this.route.params.subscribe(routeParams => {
-      this.videoPlaylistId = routeParams[ 'videoPlaylistId' ]
+      this.videoPlaylistId = routeParams['videoPlaylistId']
       this.loadElements()
 
       this.loadPlaylistInfo()
@@ -145,8 +145,6 @@ export class MyVideoPlaylistElementsComponent implements OnInit, OnDestroy {
    * we add a delay to prevent unwanted drag&drop.
    *
    * @see {@link https://github.com/Chocobozzz/PeerTube/issues/2078}
-   *
-   * @returns {null|number} Null for no delay, or a number in milliseconds.
    */
   getDragStartDelay (): null | number {
     if (this.screenService.isInTouchScreen()) {
index a3f54279b8f2ab08ef9e549068d7afd5fff31ac0..06ac3ad50aae64dbe777b55dd260d30e11fd0d2d 100644 (file)
@@ -65,14 +65,16 @@ export class MyVideoPlaylistUpdateComponent extends MyVideoPlaylistEdit implemen
                            })
                          )
                          .subscribe({
-                           next: ([ videoPlaylistToUpdate, videoPlaylistPrivacies]) => {
+                           next: ([ videoPlaylistToUpdate, videoPlaylistPrivacies ]) => {
                              this.videoPlaylistToUpdate = videoPlaylistToUpdate
                              this.videoPlaylistPrivacies = videoPlaylistPrivacies
 
                              this.hydrateFormFromPlaylist()
                            },
 
-                           error: err => this.error = err.message
+                           error: err => {
+                             this.error = err.message
+                           }
                          })
   }
 
@@ -99,7 +101,9 @@ export class MyVideoPlaylistUpdateComponent extends MyVideoPlaylistEdit implemen
           this.router.navigate([ '/my-library', 'video-playlists' ])
         },
 
-        error: err => this.error = err.message
+        error: err => {
+          this.error = err.message
+        }
       })
   }
 
index 8c1f94bf3634d991ebaa3b2427ed82966f35f370..960c9a4f73f0ba31e627187fb76f5319ddf4812d 100644 (file)
@@ -25,7 +25,7 @@ export class VideoChangeOwnershipComponent extends FormReactive implements OnIni
     private notifier: Notifier,
     private userService: UserService,
     private modalService: NgbModal
-    ) {
+  ) {
     super()
   }
 
@@ -49,7 +49,9 @@ export class VideoChangeOwnershipComponent extends FormReactive implements OnIni
     const query = event.query
     this.userService.autocomplete(query)
       .subscribe({
-        next: usernames => this.usernamePropositions = usernames,
+        next: usernames => {
+          this.usernamePropositions = usernames
+        },
 
         error: err => this.notifier.error(err.message)
       })
index 4f9b71cc6052c2fa36b53627aa97af6f09319b5d..edb9392ddda855502e29228ee100a84525236e86 100644 (file)
@@ -49,7 +49,7 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook {
 
   inputFilters: AdvancedInputFilter[] = [
     {
-      queryParams: { 'search': 'isLive:true' },
+      queryParams: { search: 'isLive:true' },
       label: $localize`Only live videos`
     }
   ]
@@ -107,7 +107,7 @@ export class MyVideosComponent implements OnInit, DisableForReuseHook {
 
   async deleteSelectedVideos () {
     const toDeleteVideosIds = Object.keys(this.selection)
-                                    .filter(k => this.selection[ k ] === true)
+                                    .filter(k => this.selection[k] === true)
                                     .map(k => parseInt(k, 10))
 
     const res = await this.confirmService.confirm(
index 293f7edad5506655b7de9f7d720366a5b52e27a5..775cc580c03f3fd4a8bc53d8cd52677cf33ce0db 100644 (file)
@@ -7,7 +7,7 @@ import { SearchService } from '@app/shared/shared-search'
 @Component({
   selector: 'my-remote-interaction',
   templateUrl: './remote-interaction.component.html',
-  styleUrls: ['./remote-interaction.component.scss']
+  styleUrls: [ './remote-interaction.component.scss' ]
 })
 export class RemoteInteractionComponent implements OnInit {
   error = ''
index a1c04147bead55069493f78d98289485264d4a74..11c5110fd73bacb31cbc4fc851faf40fc379f981 100644 (file)
@@ -21,7 +21,7 @@ export class ResetPasswordComponent extends FormReactive implements OnInit {
     private notifier: Notifier,
     private router: Router,
     private route: ActivatedRoute
-    ) {
+  ) {
     super()
   }
 
index 5a80895f9a81b99aa0f7d845735c04c33a68bc00..3b7ba40e8ae6ce9af57c7ca8bdde2f6325ffd042 100644 (file)
@@ -1,5 +1,5 @@
-import { Component } from '@angular/core'
 import { CdkStep, CdkStepper } from '@angular/cdk/stepper'
+import { Component } from '@angular/core'
 
 @Component({
   selector: 'my-custom-stepper',
@@ -14,13 +14,13 @@ export class CustomStepperComponent extends CdkStepper {
   }
 
   isCompleted (step: CdkStep) {
-    return step.stepControl && step.stepControl.dirty && step.stepControl.valid
+    return step.stepControl?.dirty && step.stepControl.valid
   }
 
   isAccessible (index: number) {
     const stepsCompletedMap = this.steps.map(step => this.isCompleted(step))
     return index === 0
       ? true
-      : stepsCompletedMap[ index - 1 ]
+      : stepsCompletedMap[index - 1]
   }
 }
index 056442107c8a706dea3cc979263259f68cf6f1da..d8ac39c7c6b158642fd812fc78abea56d0700c04 100644 (file)
@@ -49,8 +49,7 @@ export class RegisterComponent implements OnInit {
     private authService: AuthService,
     private userService: UserService,
     private hooks: HooksService
-    ) {
-  }
+  ) { }
 
   get requiresEmailVerification () {
     return this.serverConfig.signup.requiresEmailVerification
@@ -138,11 +137,15 @@ export class RegisterComponent implements OnInit {
               this.success = $localize`You are now logged in as ${body.username}!`
             },
 
-            error: err => this.error = err.message
+            error: err => {
+              this.error = err.message
+            }
           })
       },
 
-      error: err => this.error = err.message
+      error: err => {
+        this.error = err.message
+      }
     })
   }
 }
index 457a0abe0e7db882b1300e6e5944c59bd7f8f0f8..827ec7652d0700f9b0c5036c94cb1ffb0d8a0797 100644 (file)
@@ -20,7 +20,7 @@ export class VerifyAccountEmailComponent implements OnInit {
     private authService: AuthService,
     private notifier: Notifier,
     private route: ActivatedRoute
-    ) {
+  ) {
   }
 
   ngOnInit () {
index d83fc1324432f911bbd9554e79351076c6807c96..ef8fd79b9961e723fce242d4e904735f33102f0a 100644 (file)
@@ -1,5 +1,5 @@
 import { forkJoin, Subscription } from 'rxjs'
-import { first, tap } from 'rxjs/operators'
+import { first } from 'rxjs/operators'
 import { Component, ComponentFactoryResolver, OnDestroy, OnInit } from '@angular/core'
 import { ActivatedRoute, Router } from '@angular/router'
 import { AuthService, ConfirmService, LocalStorageService, Notifier, ScreenService, ServerService, UserService } from '@app/core'
index b4d81fe39d595ce7078a8933579d878ca620c8eb..064fbb6f5b6e30a60319aa622051abe279620090 100644 (file)
       <a [routerLink]="item.routerLink" routerLinkActive="active" class="title-page">{{ item.label }}</a>
     </ng-template>
 
-    <list-overflow [items]="links" [itemTemplate]="linkTemplate"></list-overflow>
+    <my-list-overflow [items]="links" [itemTemplate]="linkTemplate"></my-list-overflow>
   </div>
 
   <router-outlet></router-outlet>
index 6479644f130b9df82892c32a23e1135254f7f64a..272fc41d95ef08e5155cb6d6281a7d243c012242 100644 (file)
@@ -44,7 +44,7 @@ export class VideoChannelsComponent implements OnInit, OnDestroy {
   ngOnInit () {
     this.routeSub = this.route.params
                         .pipe(
-                          map(params => params[ 'videoChannelName' ]),
+                          map(params => params['videoChannelName']),
                           distinctUntilChanged(),
                           switchMap(videoChannelName => this.videoChannelService.getVideoChannel(videoChannelName)),
                           catchError(err => this.restExtractor.redirectTo404IfNotFound(err, 'other', [
@@ -64,9 +64,9 @@ export class VideoChannelsComponent implements OnInit, OnDestroy {
 
     this.hotkeys = [
       new Hotkey('S', (event: KeyboardEvent): boolean => {
-        this.subscribeButton.subscribed ?
-          this.subscribeButton.unsubscribe() :
-          this.subscribeButton.subscribe()
+        if (this.subscribeButton.subscribed) this.subscribeButton.unsubscribe()
+        else this.subscribeButton.subscribe()
+
         return false
       }, undefined, $localize`Subscribe to the account`)
     ]
index 34848b0362c19a9e40822184670f966ec188c384..4b201ac7467b2325af78b11617e19403c77a53b7 100644 (file)
@@ -73,7 +73,7 @@ export class I18nPrimengCalendarService {
   }
 
   getTimezone () {
-    const gmt = new Date().toString().match(/([A-Z]+[\+-][0-9]+)/)[1]
+    const gmt = new Date().toString().match(/([A-Z]+[+-][0-9]+)/)[1]
     const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone
 
     return `${timezone} - ${gmt}`
index 875911b91cdad2bb33572d84478e25d6ea9d3cc0..98d66ff00db633afd7afec111a144b3342a11f13 100644 (file)
@@ -66,18 +66,18 @@ export class VideoCaptionAddModalComponent extends FormReactive implements OnIni
   isReplacingExistingCaption () {
     if (this.closingModal === true) return false
 
-    const languageId = this.form.value[ 'language' ]
+    const languageId = this.form.value['language']
 
-    return languageId && this.existingCaptions.indexOf(languageId) !== -1
+    return languageId && this.existingCaptions.includes(languageId)
   }
 
   async addCaption () {
-    const languageId = this.form.value[ 'language' ]
+    const languageId = this.form.value['language']
     const languageObject = this.videoCaptionLanguages.find(l => l.id === languageId)
 
     this.captionAdded.emit({
       language: languageObject,
-      captionfile: this.form.value[ 'captionfile' ]
+      captionfile: this.form.value['captionfile']
     })
 
     this.hide()
index 3a7dbed36ef4d9d1d85b54b638293466dc962a5d..db1ef8d73c5374698c2c2df7d30105d596a04c47 100644 (file)
@@ -24,7 +24,7 @@ function hydrateFormFromVideo (formGroup: FormGroup, video: VideoEdit, thumbnail
       .then(response => response.blob())
       .then(data => {
         formGroup.patchValue({
-          [ obj.name ]: data
+          [obj.name]: data
         })
       })
   }
index 90a0e8f52b4259734062dc12673545095abdbf34..366c93a7936ac26d22185675e6ee31372df18308 100644 (file)
@@ -233,7 +233,7 @@ export class VideoEditComponent implements OnInit, OnDestroy {
 
   async deleteCaption (caption: VideoCaptionEdit) {
     // Caption recovers his former state
-    if (caption.action && this.initialVideoCaptions.indexOf(caption.language.id) !== -1) {
+    if (caption.action && this.initialVideoCaptions.includes(caption.language.id)) {
       caption.action = undefined
       return
     }
@@ -297,7 +297,7 @@ export class VideoEditComponent implements OnInit, OnDestroy {
 
   private trackPrivacyChange () {
     // We will update the schedule input and the wait transcoding checkbox validators
-    this.form.controls[ 'privacy' ]
+    this.form.controls['privacy']
       .valueChanges
       .pipe(map(res => parseInt(res.toString(), 10)))
       .subscribe(
@@ -336,12 +336,12 @@ export class VideoEditComponent implements OnInit, OnDestroy {
 
   private trackChannelChange () {
     // We will update the "support" field depending on the channel
-    this.form.controls[ 'channelId' ]
+    this.form.controls['channelId']
       .valueChanges
       .pipe(map(res => parseInt(res.toString(), 10)))
       .subscribe(
         newChannelId => {
-          const oldChannelId = parseInt(this.form.value[ 'channelId' ], 10)
+          const oldChannelId = parseInt(this.form.value['channelId'], 10)
 
           // Not initialized yet
           if (isNaN(newChannelId)) return
@@ -350,7 +350,7 @@ export class VideoEditComponent implements OnInit, OnDestroy {
 
           // Wait support field update
           setTimeout(() => {
-            const currentSupport = this.form.value[ 'support' ]
+            const currentSupport = this.form.value['support']
 
             // First time we set the channel?
             if (isNaN(oldChannelId)) {
index 7b1a38c6212b2b63b968740392f756217f10db4f..7c35e6b84f51ddd8208bad05e7189f5d37872afc 100644 (file)
@@ -1,26 +1,26 @@
 import { Directive, Output, EventEmitter, HostBinding, HostListener } from '@angular/core'
 
 @Directive({
-  selector: '[dragDrop]'
+  selector: '[myDragDrop]'
 })
 export class DragDropDirective {
   @Output() fileDropped = new EventEmitter<FileList>()
 
   @HostBinding('class.dragover') dragover = false
 
-  @HostListener('dragover', ['$event']) onDragOver (e: Event) {
+  @HostListener('dragover', [ '$event' ]) onDragOver (e: Event) {
     e.preventDefault()
     e.stopPropagation()
     this.dragover = true
   }
 
-  @HostListener('dragleave', ['$event']) public onDragLeave (e: Event) {
+  @HostListener('dragleave', [ '$event' ]) public onDragLeave (e: Event) {
     e.preventDefault()
     e.stopPropagation()
     this.dragover = false
   }
 
-  @HostListener('drop', ['$event']) public ondrop (e: DragEvent) {
+  @HostListener('drop', [ '$event' ]) public ondrop (e: DragEvent) {
     e.preventDefault()
     e.stopPropagation()
     this.dragover = false
index 30c79594dd1800c40003e36825d02691afbb0250..1b9447e03343641effbfc2dc60aaafe1382d84e0 100644 (file)
@@ -41,7 +41,7 @@ export class VideoGoLiveComponent extends VideoSend implements OnInit, AfterView
     private liveVideoService: LiveVideoService,
     private router: Router,
     private hooks: HooksService
-    ) {
+  ) {
     super()
   }
 
index 20a7538db01002d5e40fcb70062c9f79b792e6b2..0f1a94c84b8a215d2c578f289fc42832d2dbdea2 100644 (file)
@@ -1,4 +1,4 @@
-<div *ngIf="!hasImportedVideo" class="upload-video-container" dragDrop (fileDropped)="setTorrentFile($event)">
+<div *ngIf="!hasImportedVideo" class="upload-video-container" myDragDrop (fileDropped)="setTorrentFile($event)">
   <div class="first-step-block">
     <my-global-icon class="upload-icon" iconName="upload" aria-hidden="true"></my-global-icon>
 
index fef1f5d65a353b5a6fb8414d8f5a8f04cdce0cf0..87e47683f71cdaed76d3ea500349850778c6c15e 100644 (file)
@@ -5,7 +5,7 @@ import { scrollToTop } from '@app/helpers'
 import { FormValidatorService } from '@app/shared/shared-forms'
 import { VideoCaptionService, VideoEdit, VideoImportService, VideoService } from '@app/shared/shared-main'
 import { LoadingBarService } from '@ngx-loading-bar/core'
-import { PeerTubeProblemDocument, ServerErrorCode, VideoPrivacy, VideoUpdate } from '@shared/models'
+import { PeerTubeProblemDocument, ServerErrorCode, VideoUpdate } from '@shared/models'
 import { hydrateFormFromVideo } from '../shared/video-edit-utils'
 import { VideoSend } from './video-send'
 
@@ -43,7 +43,7 @@ export class VideoImportTorrentComponent extends VideoSend implements OnInit, Af
     private router: Router,
     private videoImportService: VideoImportService,
     private hooks: HooksService
-    ) {
+  ) {
     super()
   }
 
index e1893b28f141bc21fab6cde47cfdd0597d22d126..3487c1adfa539e6eb2f08c00881554a86e9a558d 100644 (file)
@@ -59,7 +59,7 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, AfterV
   }
 
   isTargetUrlValid () {
-    return this.targetUrl && this.targetUrl.match(/https?:\/\//)
+    return this.targetUrl?.match(/https?:\/\//)
   }
 
   importVideo () {
index ce8de049d85d0b5a51c755ee36c76b3da758434c..efa8c85a3bd2d44fc8f118eac15d0bf8db220e60 100644 (file)
@@ -9,7 +9,7 @@ import { LoadingBarService } from '@ngx-loading-bar/core'
 import { HTMLServerConfig, VideoConstant, VideoPrivacy } from '@shared/models'
 
 @Directive()
-// tslint:disable-next-line: directive-class-suffix
+// eslint-disable-next-line @angular-eslint/directive-class-suffix
 export abstract class VideoSend extends FormReactive implements OnInit {
   userVideoChannels: SelectChannelItem[] = []
   videoPrivacies: VideoConstant<VideoPrivacy>[] = []
index 14cd06fcfa2e05f09cf422bffb305295a5f0424e..db494a02f7ae1e2e456bbf7b1e638501a11817e1 100644 (file)
@@ -1,4 +1,4 @@
-<div *ngIf="!isUploadingVideo" class="upload-video-container" dragDrop (fileDropped)="onFileDropped($event)">
+<div *ngIf="!isUploadingVideo" class="upload-video-container" myDragDrop (fileDropped)="onFileDropped($event)">
   <div class="first-step-block">
     <my-global-icon class="upload-icon" iconName="upload" aria-hidden="true"></my-global-icon>
 
index b8cb4fa1e5904b52c6c6fbdf399c13d5c53c989e..dee2bb57a9206f555b88d3f4fb8866f0d364f93a 100644 (file)
@@ -128,7 +128,7 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
 
   onUploadVideoOngoing (state: UploadState) {
     switch (state.status) {
-      case 'error':
+      case 'error': {
         const error = state.response?.error || 'Unknow error'
 
         this.handleUploadError({
@@ -143,6 +143,7 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
           url: state.url
         })
         break
+      }
 
       case 'cancelled':
         this.isUploadingVideo = false
@@ -323,6 +324,7 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
       const videoQuotaUsedBytes = bytePipes.transform(this.userVideoQuotaUsed, 0)
       const videoQuotaBytes = bytePipes.transform(videoQuota, 0)
 
+      // eslint-disable-next-line max-len
       const msg = $localize`Your video quota is exceeded with this video (video size: ${videoSizeBytes}, used: ${videoQuotaUsedBytes}, quota: ${videoQuotaBytes})`
       this.notifier.error(msg)
 
@@ -341,6 +343,7 @@ export class VideoUploadComponent extends VideoSend implements OnInit, OnDestroy
       const videoSizeBytes = bytePipes.transform(videofile.size, 0)
       const quotaUsedDailyBytes = bytePipes.transform(this.userVideoQuotaUsedDaily, 0)
       const quotaDailyBytes = bytePipes.transform(videoQuotaDaily, 0)
+      // eslint-disable-next-line max-len
       const msg = $localize`Your daily video quota is exceeded with this video (video size: ${videoSizeBytes}, used: ${quotaUsedDailyBytes}, quota: ${quotaDailyBytes})`
       this.notifier.error(msg)
 
index 95336dc756add64437fad5e172a6c28470ab2f08..9bef60133d7c853a9c3b2d3176bdf30e3d8073f9 100644 (file)
@@ -38,7 +38,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
     private loadingBar: LoadingBarService,
     private videoCaptionService: VideoCaptionService,
     private liveVideoService: LiveVideoService
-    ) {
+  ) {
     super()
   }
 
@@ -119,8 +119,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
   }
 
   update () {
-    if (this.checkForm() === false
-      || this.isUpdatingVideo === true) {
+    if (this.checkForm() === false || this.isUpdatingVideo === true) {
       return
     }
 
index 9172b78a88154a890b5711bb7de6cc0a4baf4b2f..91e76b7fe31f09b3cc7562ec029eb97fa81d5d2f 100644 (file)
@@ -18,7 +18,7 @@ export class VideoUpdateResolver implements Resolve<any> {
   }
 
   resolve (route: ActivatedRouteSnapshot) {
-    const uuid: string = route.params[ 'uuid' ]
+    const uuid: string = route.params['uuid']
 
     return this.videoService.getVideo({ videoId: uuid })
                .pipe(
@@ -42,8 +42,8 @@ export class VideoUpdateResolver implements Resolve<any> {
         ),
 
       video.isLive
-          ? this.liveVideoService.getVideoLive(video.id)
-          : of(undefined)
+        ? this.liveVideoService.getVideoLive(video.id)
+        : of(undefined)
     ]
   }
 }
index 9b1672a8c37089d284ef20519222b09d554f8fd3..448a0ee96db835fd906c42b2123bb005c65645f4 100644 (file)
@@ -8,7 +8,7 @@ import { Component, ViewEncapsulation } from '@angular/core'
   selector: 'my-player-styles',
   template: '',
   styleUrls: [ './player-styles.component.scss' ],
-  // tslint:disable:use-component-view-encapsulation
+  /* eslint-disable @angular-eslint/use-component-view-encapsulation */
   encapsulation: ViewEncapsulation.None
 })
 export class PlayerStylesComponent {
index ac65f72604076bd0c0b30a5d5196ab7406bddeaa..71fb127f6c5cc8c6204bd705d38048efd42ae3f0 100644 (file)
@@ -25,7 +25,7 @@ import { VideoCommentCreate } from '@shared/models'
 @Component({
   selector: 'my-video-comment-add',
   templateUrl: './video-comment-add.component.html',
-  styleUrls: ['./video-comment-add.component.scss']
+  styleUrls: [ './video-comment-add.component.scss' ]
 })
 export class VideoCommentAddComponent extends FormReactive implements OnChanges, OnInit {
   @Input() user: User
@@ -64,7 +64,7 @@ export class VideoCommentAddComponent extends FormReactive implements OnChanges,
     for (const emojiMarkupName in emojiMarkupObjectList) {
       if (emojiMarkupName) {
         const emoji = emojiMarkupObjectList[emojiMarkupName]
-        emojiMarkupArrayList.push([emoji, emojiMarkupName])
+        emojiMarkupArrayList.push([ emoji, emojiMarkupName ])
       }
     }
 
@@ -91,7 +91,7 @@ export class VideoCommentAddComponent extends FormReactive implements OnChanges,
     // Not initialized yet
     if (!this.form) return
 
-    if (changes.textValue && changes.textValue.currentValue && changes.textValue.currentValue !== changes.textValue.previousValue) {
+    if (changes.textValue?.currentValue && changes.textValue.currentValue !== changes.textValue.previousValue) {
       this.patchTextValue(changes.textValue.currentValue, true)
     }
   }
index d8b944b354d9e55a8ff4dedd0fb8958098563f70..d0e9bcd2975b7803488292981704e0f5095c27d7 100644 (file)
@@ -29,7 +29,7 @@
           class="comment-html"
           [innerHTML]="sanitizedCommentHTML"
           (timestampClicked)="handleTimestampClicked($event)"
-          timestampRouteTransformer
+          myTimestampRouteTransformer
         ></div>
 
         <div class="comment-actions">
index f858f4750756b36a8a98b5b93dc0f50ee157fb60..5bbcffe8293483a0dc9a7e3cabc53ae269a47d73 100644 (file)
@@ -10,7 +10,7 @@ import { User, UserRight } from '@shared/models'
 @Component({
   selector: 'my-video-comment',
   templateUrl: './video-comment.component.html',
-  styleUrls: ['./video-comment.component.scss']
+  styleUrls: [ './video-comment.component.scss' ]
 })
 export class VideoCommentComponent implements OnInit, OnChanges {
   @ViewChild('commentReportModal') commentReportModal: CommentReportComponent
index 72866b8741cc46451c753ac38d5cf795b156121a..17e0af3bc91797a1468683cbaf9e816ac523a338 100644 (file)
@@ -9,7 +9,7 @@ import { VideoComment, VideoCommentService, VideoCommentThreadTree } from '@app/
 @Component({
   selector: 'my-video-comments',
   templateUrl: './video-comments.component.html',
-  styleUrls: ['./video-comments.component.scss']
+  styleUrls: [ './video-comments.component.scss' ]
 })
 export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
   @ViewChild('commentHighlightBlock') commentHighlightBlock: ElementRef
@@ -200,7 +200,11 @@ export class VideoCommentsComponent implements OnInit, OnChanges, OnDestroy {
   }
 
   async onWantedToRedraft (commentToRedraft: VideoComment) {
-    const confirm = await this.onWantedToDelete(commentToRedraft, $localize`Delete and re-draft`, $localize`Do you really want to delete and re-draft this comment?`)
+    const confirm = await this.onWantedToDelete(
+      commentToRedraft,
+      $localize`Delete and re-draft`,
+      $localize`Do you really want to delete and re-draft this comment?`
+    )
 
     if (confirm) {
       this.inReplyToCommentId = commentToRedraft.inReplyToCommentId
index 0072492ac5b81f4a156e8e8ae9e4086d20c04b68..257d463b4710ae690370c4f77f5a76481dc577c9 100644 (file)
@@ -23,7 +23,7 @@ export class VideoAlertComponent {
   }
 
   hasVideoScheduledPublication () {
-    return this.video && this.video.scheduledUpdate !== undefined
+    return this.video?.scheduledUpdate !== undefined
   }
 
   isWaitingForLive () {
index 57f682899ed235b58ec34b79a23957aded75fdb1..2cfaad8f6b6dc7f171aae33ec5861d5fd41adecd 100644 (file)
@@ -3,7 +3,7 @@
     class="video-info-description-html"
     [innerHTML]="videoHTMLDescription"
     (timestampClicked)="onTimestampClicked($event)"
-    timestampRouteTransformer
+    myTimestampRouteTransformer
   ></div>
 
   <div class="video-info-description-more" *ngIf="completeDescriptionShown === false && video.description?.length >= 250" (click)="showMoreDescription()">
index f0f7966b17ebe2a6f6fc3dec8f4be861ba5a608e..78b3af4a70aa0ea821683adb24b5319f63b2514c 100644 (file)
@@ -39,7 +39,7 @@ export class VideoWatchPlaylistComponent {
     private notifier: Notifier,
     private videoPlaylist: VideoPlaylistService,
     private localStorageService: LocalStorageService,
-    private sessionStorageService: SessionStorageService,
+    private sessionStorage: SessionStorageService,
     private router: Router
   ) {
     // defaults to true
@@ -50,7 +50,7 @@ export class VideoWatchPlaylistComponent {
     this.setAutoPlayNextVideoPlaylistSwitchText()
 
     // defaults to false
-    this.loopPlaylist = this.sessionStorageService.getItem(VideoWatchPlaylistComponent.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST) === 'true'
+    this.loopPlaylist = this.sessionStorage.getItem(VideoWatchPlaylistComponent.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST) === 'true'
     this.setLoopPlaylistSwitchText()
   }
 
@@ -145,7 +145,7 @@ export class VideoWatchPlaylistComponent {
 
     const start = previous.startTimestamp
     const stop = previous.stopTimestamp
-    this.router.navigate([],{ queryParams: { playlistPosition: previous.position, start, stop } })
+    this.router.navigate([], { queryParams: { playlistPosition: previous.position, start, stop } })
   }
 
   findPlaylistVideo (position: number, type: 'previous' | 'next'): VideoPlaylistElement {
@@ -163,7 +163,7 @@ export class VideoWatchPlaylistComponent {
     }
 
     const found = this.playlistElements.find(e => e.position === position)
-    if (found && found.video) return found
+    if (found?.video) return found
 
     const newPosition = type === 'previous'
       ? position - 1
@@ -178,7 +178,7 @@ export class VideoWatchPlaylistComponent {
 
     const start = next.startTimestamp
     const stop = next.stopTimestamp
-    this.router.navigate([],{ queryParams: { playlistPosition: next.position, start, stop } })
+    this.router.navigate([], { queryParams: { playlistPosition: next.position, start, stop } })
   }
 
   switchAutoPlayNextVideoPlaylist () {
index e1040feadfa04217458a631df9c847730cba3ad6..bbfcab2aee7243e7337b78e75c63c326c92f2c96 100644 (file)
@@ -20,7 +20,7 @@
       >
       </my-video-miniature>
 
-      <hr *ngIf="!playlist && i == 0 && length > 1" />
+      <hr *ngIf="!playlist && i === 0 && length > 1" />
     </ng-container>
   </ng-container>
 </div>
index 7f3703c084f15f38d0d0e8a081fa77ebf5959735..dfc296d154ff606b83454f0d048a34cb37419395 100644 (file)
@@ -51,7 +51,7 @@ export class RecommendedVideosComponent implements OnInit, OnChanges {
     } else {
       this.autoPlayNextVideo = this.sessionStorageService.getItem(UserLocalStorageKeys.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO) === 'true'
 
-      this.sessionStorageService.watch([UserLocalStorageKeys.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO]).subscribe(
+      this.sessionStorageService.watch([ UserLocalStorageKeys.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO ]).subscribe(
         () => {
           this.autoPlayNextVideo = this.sessionStorageService.getItem(UserLocalStorageKeys.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO) === 'true'
         }
index 45e023695889448d1c29535be8736e7e909c2e06..91fe5bf5d34c3e631e0aa23532b082795ac7eb1c 100644 (file)
@@ -1,12 +1,12 @@
 import { Directive, EventEmitter, HostListener, Output } from '@angular/core'
 
 @Directive({
-  selector: '[timestampRouteTransformer]'
+  selector: '[myTimestampRouteTransformer]'
 })
 export class TimestampRouteTransformerDirective {
   @Output() timestampClicked = new EventEmitter<number>()
 
-  @HostListener('click', ['$event'])
+  @HostListener('click', [ '$event' ])
   public onClick ($event: Event) {
     const target = $event.target as HTMLLinkElement
 
@@ -21,10 +21,10 @@ export class TimestampRouteTransformerDirective {
     const ngxLinkParams = new URLSearchParams(ngxLink.search)
     if (ngxLinkParams.has('start') !== true) return
 
-    const separators = ['h', 'm', 's']
+    const separators = [ 'h', 'm', 's' ]
     const start = ngxLinkParams
       .get('start')
-      .match(new RegExp('(\\d{1,9}[' + separators.join('') + '])','g')) // match digits before any given separator
+      .match(new RegExp('(\\d{1,9}[' + separators.join('') + '])', 'g')) // match digits before any given separator
       .map(t => {
         if (t.includes('h')) return parseInt(t, 10) * 3600
         if (t.includes('m')) return parseInt(t, 10) * 60
index 85100b653798d89c24b254dfa023544cba3b7551..2007bdecbdb72b7788e9f1c13991e9550d2eb7d0 100644 (file)
@@ -195,10 +195,10 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
 
   private loadRouteParams () {
     this.paramsSub = this.route.params.subscribe(routeParams => {
-      const videoId = routeParams[ 'videoId' ]
+      const videoId = routeParams['videoId']
       if (videoId) return this.loadVideo(videoId)
 
-      const playlistId = routeParams[ 'playlistId' ]
+      const playlistId = routeParams['playlistId']
       if (playlistId) return this.loadPlaylist(playlistId)
     })
   }
@@ -206,7 +206,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
   private loadRouteQuery () {
     this.queryParamsSub = this.route.queryParams.subscribe(queryParams => {
       // Handle the ?playlistPosition
-      const positionParam = queryParams[ 'playlistPosition' ] ?? 1
+      const positionParam = queryParams['playlistPosition'] ?? 1
 
       this.playlistPosition = positionParam === 'last'
         ? -1 // Handle the "last" index
@@ -219,7 +219,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
 
       this.videoWatchPlaylist.updatePlaylistIndex(this.playlistPosition)
 
-      const start = queryParams[ 'start' ]
+      const start = queryParams['start']
       if (this.player && start) this.player.currentTime(parseInt(start, 10))
     })
   }
@@ -237,7 +237,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
       'filter:api.video-watch.video.get.result'
     )
 
-    forkJoin([ videoObs, this.videoCaptionService.listCaptions(videoId)])
+    forkJoin([ videoObs, this.videoCaptionService.listCaptions(videoId) ])
       .subscribe({
         next: ([ video, captionsResult ]) => {
           const queryParams = this.route.snapshot.queryParams
@@ -292,6 +292,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
       const originUrl = errorBody.originUrl + (window.location.search ?? '')
 
       const res = await this.confirmService.confirm(
+        // eslint-disable-next-line max-len
         $localize`This video is not available on this instance. Do you want to be redirected on the origin instance: <a href="${originUrl}">${originUrl}</a>?`,
         $localize`Redirection`
       )
@@ -312,7 +313,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
     if (!errorMessage) return
 
     // Display a message in the video player instead of a notification
-    if (errorMessage.indexOf('from xs param') !== -1) {
+    if (errorMessage.includes('from xs param')) {
       this.flushPlayer()
       this.remoteServerDown = true
 
@@ -466,7 +467,6 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
 
     if (this.nextVideoUUID) {
       this.router.navigate([ '/w', this.nextVideoUUID ])
-      return
     }
   }
 
@@ -483,14 +483,14 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
 
   private isAutoPlayNext () {
     return (
-      (this.user && this.user.autoPlayNextVideo) ||
+      (this.user?.autoPlayNextVideo) ||
       this.anonymousUser.autoPlayNextVideo
     )
   }
 
   private isPlaylistAutoPlayNext () {
     return (
-      (this.user && this.user.autoPlayNextVideoPlaylist) ||
+      (this.user?.autoPlayNextVideoPlaylist) ||
       this.anonymousUser.autoPlayNextVideoPlaylist
     )
   }
@@ -508,9 +508,9 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
   }
 
   private buildPlayerManagerOptions (params: {
-    video: VideoDetails,
-    videoCaptions: VideoCaption[],
-    urlOptions: CustomizationOptions & { playerMode: PlayerMode },
+    video: VideoDetails
+    videoCaptions: VideoCaption[]
+    urlOptions: CustomizationOptions & { playerMode: PlayerMode }
     user?: AuthUser
   }) {
     const { video, videoCaptions, urlOptions, user } = params
@@ -573,10 +573,12 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
 
         language: this.localeId,
 
-        userWatching: user && user.videosHistoryEnabled === true ? {
-          url: this.videoService.getUserWatchingVideoUrl(video.uuid),
-          authorizationHeader: this.authService.getRequestHeaderValue()
-        } : undefined,
+        userWatching: user && user.videosHistoryEnabled === true
+          ? {
+            url: this.videoService.getUserWatchingVideoUrl(video.uuid),
+            authorizationHeader: this.authService.getRequestHeaderValue()
+          }
+          : undefined,
 
         serverUrl: environment.apiUrl,
 
@@ -704,9 +706,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
     if (this.isUserLoggedIn()) {
       this.hotkeys = this.hotkeys.concat([
         new Hotkey('shift+s', () => {
-          this.subscribeButton.isSubscribedToAll()
-            ? this.subscribeButton.unsubscribe()
-            : this.subscribeButton.subscribe()
+          if (this.subscribeButton.isSubscribedToAll()) this.subscribeButton.unsubscribe()
+          else this.subscribeButton.subscribe()
 
           return false
         }, undefined, $localize`Subscribe to the account`)
index 3aa64ebc81c1390b7bc88860f89bb3b9bdc4ecae..12d2aa1cb451d4fc03c9a346fcdd17f905f6d8c7 100644 (file)
@@ -43,7 +43,7 @@ export class OverviewService {
 
     // Build videos objects
     for (const key of Object.keys(serverVideosOverview)) {
-      for (const object of serverVideosOverview[ key ]) {
+      for (const object of serverVideosOverview[key]) {
         observables.push(
           of(object.videos)
             .pipe(
index 6c2b32a4fb92dbbabb60304022bd35facb088cca..c94655c747c2df41308ef2149b33f7c5eb689211 100644 (file)
@@ -15,7 +15,7 @@ interface VideoTrendingHeaderItem {
 }
 
 @Component({
-  selector: 'video-trending-title-page',
+  selector: 'my-video-trending-title-page',
   styleUrls: [ './video-trending-header.component.scss' ],
   templateUrl: './video-trending-header.component.html'
 })
index ebec672f3954465051609045b5ebb51afe07b09c..085f29a8bd36aad37165f8837ba1ff10a2eb01b1 100644 (file)
@@ -63,7 +63,7 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit,
 
         if (oldSort !== this.sort) this.reloadVideos()
       }
-    )
+      )
   }
 
   ngOnDestroy () {
@@ -97,12 +97,12 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit,
 
   getInjector () {
     return Injector.create({
-      providers: [{
+      providers: [ {
         provide: 'data',
         useValue: {
           model: this.defaultSort
         }
-      }]
+      } ]
     })
   }
 
index 4be8cd6b5d5ef27545dc30a65f9a005ca4f0d30b..b576883d1f460b54706acae112cecd8779979e60 100644 (file)
@@ -5,7 +5,7 @@ import { HooksService } from '@app/core/plugins/hooks.service'
 import { immutableAssign } from '@app/helpers'
 import { VideoService } from '@app/shared/shared-main'
 import { AbstractVideoList } from '@app/shared/shared-video-miniature'
-import { UserRight, VideoFilter, VideoSortField } from '@shared/models'
+import { VideoFilter, VideoSortField } from '@shared/models'
 
 @Component({
   selector: 'my-videos-local',
index c1e2961820a255a08f8664669bfccb6b6df67ef3..ed5cc53d9c0c611c5d85a916bb6f675990a68aba 100644 (file)
@@ -243,7 +243,7 @@ export class AppComponent implements OnInit, AfterViewInit {
     // Inject JS
     if (this.serverConfig.instance.customizations.javascript) {
       try {
-        // tslint:disable:no-eval
+        /* eslint-disable no-eval */
         eval(this.serverConfig.instance.customizations.javascript)
       } catch (err) {
         console.error('Cannot eval custom JavaScript.', err)
@@ -294,7 +294,7 @@ export class AppComponent implements OnInit, AfterViewInit {
 
   private initHotkeys () {
     this.hotkeysService.add([
-      new Hotkey(['/', 's'], (event: KeyboardEvent): boolean => {
+      new Hotkey([ '/', 's' ], (event: KeyboardEvent): boolean => {
         document.getElementById('search-video').focus()
         return false
       }, undefined, $localize`Focus the search bar`),
index 5da66c981da42d25e892a48933b6fe5a2a4d1708..79239a17a2136c107307fba93d4dfa3f1babd813 100644 (file)
@@ -48,7 +48,7 @@ export class AuthService {
     private hotkeysService: HotkeysService,
     private restExtractor: RestExtractor,
     private router: Router
-    ) {
+  ) {
     this.loginChanged = new Subject<AuthStatus>()
     this.loginChangedSource = this.loginChanged.asObservable()
 
@@ -206,7 +206,9 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
     this.refreshingTokenObservable = this.http.post<UserRefreshToken>(AuthService.BASE_TOKEN_URL, body, { headers })
                                          .pipe(
                                            map(res => this.handleRefreshToken(res)),
-                                           tap(() => { this.refreshingTokenObservable = null }),
+                                           tap(() => {
+                                             this.refreshingTokenObservable = null
+                                           }),
                                            catchError(err => {
                                              this.refreshingTokenObservable = null
 
index 315e1a25e26782b18e034159bc6004b1b995cd3e..60b7516aac7d08e0e64da36681c94d13def96f11 100644 (file)
@@ -3,8 +3,8 @@ import { Subscription } from 'rxjs'
 import { Component, Input, OnDestroy, OnInit } from '@angular/core'
 
 @Component({
-  selector : 'my-hotkeys-cheatsheet',
-  templateUrl : './hotkeys.component.html',
+  selector: 'my-hotkeys-cheatsheet',
+  templateUrl: './hotkeys.component.html',
   styleUrls: [ './hotkeys.component.scss' ]
 })
 export class CheatSheetComponent implements OnInit, OnDestroy {
@@ -16,7 +16,7 @@ export class CheatSheetComponent implements OnInit, OnDestroy {
 
   constructor (
     private hotkeysService: HotkeysService
-    ) {}
+  ) {}
 
   public ngOnInit (): void {
     this.subscription = this.hotkeysService.cheatSheetToggle.subscribe((isOpen) => {
index 0b8d0191e15dfe113aacce005c5cab250c106886..f0dc4fcaafe96a2d37975324ac9f07452cd010e9 100644 (file)
@@ -25,7 +25,7 @@ export type MenuSection = {
 export class MenuService {
   isMenuDisplayed = true
   isMenuChangedByUser = false
-  menuWidth = 240  // should be kept equal to $menu-width
+  menuWidth = 240 // should be kept equal to $menu-width
 
   constructor (
     private screenService: ScreenService
@@ -55,7 +55,7 @@ export class MenuService {
     // On touch screens, lock body scroll and display content overlay when memu is opened
     if (this.isMenuDisplayed) {
       document.body.classList.add('menu-open')
-      this.screenService.onFingerSwipe('left', () => { this.setMenuDisplay(false) })
+      this.screenService.onFingerSwipe('left', () => this.setMenuDisplay(false))
       return
     }
 
index ddde198d2ffb0fe2c7ec7ab0fd7023259a6ba36c..062083fd155db48a1749e4c4dafba77e521c2fee 100644 (file)
@@ -27,9 +27,8 @@ export class HooksService {
     })
   }
 
-  wrapObsFun
-    <P, R, H1 extends ClientFilterHookName, H2 extends ClientFilterHookName>
-    (fun: ObservableFunction<P, R>, params: P, scope: PluginClientScope, hookParamName: H1, hookResultName: H2) {
+  wrapObsFun <P, R, H1 extends ClientFilterHookName, H2 extends ClientFilterHookName>
+  (fun: ObservableFunction<P, R>, params: P, scope: PluginClientScope, hookParamName: H1, hookResultName: H2) {
     return from(this.pluginService.ensurePluginsAreLoaded(scope))
       .pipe(
         mergeMap(() => this.wrapObjectWithoutScopeLoad(params, hookParamName)),
@@ -38,9 +37,8 @@ export class HooksService {
       )
   }
 
-  async wrapFun
-    <P, R, H1 extends ClientFilterHookName, H2 extends ClientFilterHookName>
-    (fun: RawFunction<P, R>, params: P, scope: PluginClientScope, hookParamName: H1, hookResultName: H2) {
+  async wrapFun <P, R, H1 extends ClientFilterHookName, H2 extends ClientFilterHookName>
+  (fun: RawFunction<P, R>, params: P, scope: PluginClientScope, hookParamName: H1, hookResultName: H2) {
     await this.pluginService.ensurePluginsAreLoaded(scope)
 
     const newParams = await this.wrapObjectWithoutScopeLoad(params, hookParamName)
index 774c039647afb1dbb4e00e1822a5c5f51b2b0e23..89391c2c5d0eda2f915fb5c4124764c1073105a0 100644 (file)
@@ -188,7 +188,7 @@ export class PluginService implements ClientHook {
         if (!this.authService.isLoggedIn()) return undefined
 
         const value = this.authService.getRequestHeaderValue()
-        return { 'Authorization': value }
+        return { Authorization: value }
       },
 
       notifier: {
@@ -198,10 +198,10 @@ export class PluginService implements ClientHook {
       },
 
       showModal: (input: {
-        title: string,
-        content: string,
-        close?: boolean,
-        cancel?: { value: string, action?: () => void },
+        title: string
+        content: string
+        close?: boolean
+        cancel?: { value: string, action?: () => void }
         confirm?: { value: string, action?: () => void }
       }) => {
         this.zone.run(() => this.customModal.show(input))
index 36258ca9872f6f1a471b9ac83c8b57f5341a0148..a81d995347739bf934c63ba63f47945ef1e71e84 100644 (file)
@@ -103,20 +103,20 @@ export class MarkdownService {
     const { name, markdown, withEmoji, additionalAllowedTags } = options
     if (!markdown) return ''
 
-    const config = this.parsersConfig[ name ]
-    if (!this.markdownParsers[ name ]) {
-      this.markdownParsers[ name ] = await this.createMarkdownIt(config)
+    const config = this.parsersConfig[name]
+    if (!this.markdownParsers[name]) {
+      this.markdownParsers[name] = await this.createMarkdownIt(config)
 
       if (withEmoji) {
         if (!this.emojiModule) {
           this.emojiModule = (await import('markdown-it-emoji/light')).default
         }
 
-        this.markdownParsers[ name ].use(this.emojiModule)
+        this.markdownParsers[name].use(this.emojiModule)
       }
     }
 
-    let html = this.markdownParsers[ name ].render(markdown)
+    let html = this.markdownParsers[name].render(markdown)
     html = this.avoidTruncatedTags(html)
 
     if (config.escape) return this.htmlRenderer.toSafeHtml(html, additionalAllowedTags)
@@ -156,7 +156,7 @@ export class MarkdownService {
       if (relIndex < 0) token.attrPush([ 'rel', 'noopener noreferrer' ])
       else token.attrs[relIndex][1] = 'noopener noreferrer'
 
-      // pass token to default renderer.
+      // pass token to default renderer.*
       return defaultRender(tokens, index, options, env, self)
     }
   }
@@ -164,7 +164,7 @@ export class MarkdownService {
   private avoidTruncatedTags (html: string) {
     return html.replace(/\*\*?([^*]+)$/, '$1')
       .replace(/<a[^>]+>([^<]+)<\/a>\s*...((<\/p>)|(<\/li>)|(<\/strong>))?$/mi, '$1...')
-      .replace(/\[[^\]]+\]\(([^\)]+)$/m, '$1')
+      .replace(/\[[^\]]+\]\(([^)]+)$/m, '$1')
       .replace(/\s?\[[^\]]+\]?[.]{3}<\/p>$/m, '...</p>')
   }
 }
index 29a56ba39203cad2130f1c58c15bc7a24a87460f..dd4a78de5ef1aea3cb27a2b969b27668a5aecad5 100644 (file)
@@ -13,15 +13,16 @@ export class RestExtractor {
     return true
   }
 
-  applyToResultListData <T> (result: ResultList<T>, fun: Function, additionalArgs?: any[]): ResultList<T> {
+  applyToResultListData <T, A, U> (
+    result: ResultList<T>,
+    fun: (data: T, ...args: A[]) => U,
+    additionalArgs: A[] = []
+  ): ResultList<U> {
     const data: T[] = result.data
-    const newData: T[] = []
-
-    data.forEach(d => newData.push(fun.apply(this, [ d ].concat(additionalArgs))))
 
     return {
       total: result.total,
-      data: newData
+      data: data.map(d => fun.apply(this, [ d, ...additionalArgs ]))
     }
   }
 
@@ -29,8 +30,10 @@ export class RestExtractor {
     return this.applyToResultListData(result, this.convertDateToHuman, [ fieldsToConvert ])
   }
 
-  convertDateToHuman (target: { [ id: string ]: string }, fieldsToConvert: string[]) {
-    fieldsToConvert.forEach(field => target[field] = dateToHuman(target[field]))
+  convertDateToHuman (target: any, fieldsToConvert: string[]) {
+    fieldsToConvert.forEach(field => {
+      target[field] = dateToHuman(target[field])
+    })
 
     return target
   }
@@ -46,7 +49,7 @@ export class RestExtractor {
       errorMessage = err.error
     } else if (err.status !== undefined) {
       // A server-side error occurred.
-      if (err.error && err.error.errors) {
+      if (err.error?.errors) {
         const errors = err.error.errors
         const errorsArray: string[] = []
 
@@ -55,9 +58,10 @@ export class RestExtractor {
         })
 
         errorMessage = errorsArray.join('. ')
-      } else if (err.error && err.error.error) {
+      } else if (err.error?.error) {
         errorMessage = err.error.error
       } else if (err.status === HttpStatusCode.PAYLOAD_TOO_LARGE_413) {
+        // eslint-disable-next-line max-len
         errorMessage = $localize`Media is too large for the server. Please contact you administrator if you want to increase the limit size.`
       } else if (err.status === HttpStatusCode.TOO_MANY_REQUESTS_429) {
         const secondsLeft = err.headers.get('retry-after')
@@ -71,7 +75,7 @@ export class RestExtractor {
         errorMessage = $localize`Server error. Please retry later.`
       }
 
-      errorMessage = errorMessage ? errorMessage : 'Unknown error.'
+      errorMessage = errorMessage || 'Unknown error.'
       console.error(`Backend returned code ${err.status}, errorMessage is: ${errorMessage}`)
     } else {
       console.error(err)
@@ -93,7 +97,7 @@ export class RestExtractor {
   }
 
   redirectTo404IfNotFound (obj: { status: number }, type: 'video' | 'other', status = [ HttpStatusCode.NOT_FOUND_404 ]) {
-    if (obj && obj.status && status.indexOf(obj.status) !== -1) {
+    if (obj?.status && status.includes(obj.status)) {
       // Do not use redirectService to avoid circular dependencies
       this.router.navigate([ '/404' ], { state: { type, obj }, skipLocationChange: true })
     }
index c0f9f04e02605aa91db18f23f23436fff9c998c5..c2510f1df3f9376eab1de2bbc4aec940925f258c 100644 (file)
@@ -1,5 +1,5 @@
-import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router'
 import { Injectable } from '@angular/core'
+import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router'
 
 @Injectable()
 export class CustomReuseStrategy implements RouteReuseStrategy {
@@ -78,6 +78,6 @@ export class CustomReuseStrategy implements RouteReuseStrategy {
   }
 
   private isReuseEnabled (route: ActivatedRouteSnapshot) {
-    return route.data.reuse && route.data.reuse.enabled && route.queryParams[ 'a-state' ]
+    return route.data.reuse?.enabled && route.queryParams['a-state']
   }
 }
index c4e64d434afc525b3742046a0aaa359ebb74c8cc..8c5bbfde92d7052844791d4b35e42f879bdc6429 100644 (file)
@@ -17,33 +17,43 @@ abstract class MenuGuard implements CanActivate, CanDeactivate<any> {
     if (!this.screen.isInMobileView() && this.screen.isInMediumView()) {
       this.menu.setMenuDisplay(this.display)
     }
+
     return true
   }
 }
 
 @Injectable()
 export class OpenMenuGuard extends MenuGuard {
-  constructor (menu: MenuService, screen: ScreenService) { super(menu, screen, true) }
+  constructor (menu: MenuService, screen: ScreenService) {
+    super(menu, screen, true)
+  }
 }
 
 @Injectable()
 export class OpenMenuAlwaysGuard extends MenuGuard {
-  constructor (menu: MenuService, screen: ScreenService) { super(menu, screen, true) }
+  constructor (menu: MenuService, screen: ScreenService) {
+    super(menu, screen, true)
+  }
 
   canActivate (): boolean {
     this.menu.setMenuDisplay(this.display)
+
     return true
   }
 }
 
 @Injectable()
 export class CloseMenuGuard extends MenuGuard {
-  constructor (menu: MenuService, screen: ScreenService) { super(menu, screen, false) }
+  constructor (menu: MenuService, screen: ScreenService) {
+    super(menu, screen, false)
+  }
 }
 
 @Injectable()
 export class CloseMenuAlwaysGuard extends MenuGuard {
-  constructor (menu: MenuService, screen: ScreenService) { super(menu, screen, false) }
+  constructor (menu: MenuService, screen: ScreenService) {
+    super(menu, screen, false)
+  }
 
   canActivate (): boolean {
     this.menu.setMenuDisplay(this.display)
index bedb3450e6b34eb980ab730749362ba6d5b45a37..851404959ca1dd65be2a9d0f14aaf74fc9fd7c0f 100644 (file)
@@ -1,5 +1,5 @@
 import { Injectable } from '@angular/core'
-import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, RouterStateSnapshot } from '@angular/router'
+import { ActivatedRouteSnapshot, CanActivate, CanActivateChild } from '@angular/router'
 import { MetaService } from './meta.service'
 
 @Injectable()
index b494a40bc151c57b30d07e60ab10e207771fdb31..b5c3195b012631e4dbbd288036ddb6efb272f1c8 100644 (file)
@@ -6,7 +6,7 @@ import { Injectable } from '@angular/core'
 @Injectable()
 export class PreloadSelectedModulesList implements PreloadingStrategy {
 
-  preload (route: Route, load: Function): Observable<any> {
+  preload (route: Route, load: () => Observable<any>): Observable<any> {
     if (!route.data || !route.data.preload) return ofObservable(null)
 
     if (typeof route.data.preload === 'number') {
index 8e3697c31ae86abec4dcb455fd77ac316fd52d4e..038e5031c6b79ae3499e17633e379856a5d8bfbc 100644 (file)
@@ -1,9 +1,8 @@
-import { Injectable } from '@angular/core'
+import { catchError } from 'rxjs/operators'
 import { HttpClient } from '@angular/common/http'
-import { environment } from '../../../environments/environment'
-import { AuthService } from '../auth'
+import { Injectable } from '@angular/core'
 import { ScopedToken } from '@shared/models/users/user-scoped-token'
-import { catchError } from 'rxjs/operators'
+import { environment } from '../../../environments/environment'
 import { RestExtractor } from '../rest'
 
 @Injectable()
index 8f041a14780d441e7b3283cc902707393a0cded6..d019421390ee6686b183c1487138f0b7f1bacf0b 100644 (file)
@@ -4,7 +4,7 @@ import { HttpClient } from '@angular/common/http'
 import { Inject, Injectable, LOCALE_ID } from '@angular/core'
 import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers'
 import { getCompleteLocale, isDefaultLocale, peertubeTranslate } from '@shared/core-utils/i18n'
-import { HTMLServerConfig, SearchTargetType, ServerConfig, ServerStats, VideoConstant } from '@shared/models'
+import { HTMLServerConfig, ServerConfig, ServerStats, VideoConstant } from '@shared/models'
 import { environment } from '../../../environments/environment'
 
 @Injectable()
@@ -171,7 +171,7 @@ export class ServerService {
                  map(({ data, translations }) => {
                    const hashToPopulate: VideoConstant<T>[] = Object.keys(data)
                                                                     .map(dataKey => {
-                                                                      const label = data[ dataKey ]
+                                                                      const label = data[dataKey]
 
                                                                       const id = attributeName === 'languages'
                                                                         ? dataKey as T
index c355487980385eae7ef9c3e6c15bc658d9b32ff1..c9e6fa7005475ca85b5e4e6812b80d32224372d6 100644 (file)
@@ -95,8 +95,8 @@ export class ThemeService {
   private loadTheme (name: string) {
     const links = document.getElementsByTagName('link')
     for (let i = 0; i < links.length; i++) {
-      const link = links[ i ]
-      if (link.getAttribute('rel').indexOf('style') !== -1 && link.getAttribute('title')) {
+      const link = links[i]
+      if (link.getAttribute('rel').includes('style') && link.getAttribute('title')) {
         link.disabled = link.getAttribute('title') !== name
       }
     }
index 47db985e12f00036234131e7b665a5b5b3efc005..a8a774eca5811a21caea01354b6ea19fc8a762af 100644 (file)
@@ -35,7 +35,7 @@ export class UserService {
     private restService: RestService,
     private localStorageService: LocalStorageService,
     private sessionStorageService: SessionStorageService
-    ) { }
+  ) { }
 
   changePassword (currentPassword: string, newPassword: string) {
     const url = UserService.BASE_USERS_URL + 'me'
@@ -266,7 +266,7 @@ export class UserService {
 
   getUserWithCache (userId: number) {
     if (!this.userCache[userId]) {
-      this.userCache[ userId ] = this.getUser(userId).pipe(shareReplay())
+      this.userCache[userId] = this.getUser(userId).pipe(shareReplay())
     }
 
     return this.userCache[userId]
index b5d76c70e1143ff45d7369bd9c445ec8783460f1..e10baea2b967a5a98ba0f5bf84a7b214144dad4b 100644 (file)
@@ -199,7 +199,7 @@ export class SearchTypeaheadComponent implements OnInit, AfterViewChecked, OnDes
   }
 
   private loadUserLanguagesIfNeeded (queryParams: any) {
-    if (queryParams && queryParams.languageOneOf) return of(queryParams)
+    if (queryParams?.languageOneOf) return of(queryParams)
 
     return this.authService.userInformationLoaded
                .pipe(
index d3b2e84078c2491d27190b8ef9f9ebc5c7214e55..716b76df1e33a343678363ec1159616ca60d131e 100644 (file)
@@ -12,16 +12,16 @@ function plural (n: number): number {
 
 export default [
   'oc',
-  [['a. m.', 'p. m.'], u, u],
+  [ [ 'a. m.', 'p. m.' ], u, u ],
   u,
   [
-    ['dg', 'dl', 'dm', 'dc', 'dj', 'dv', 'ds'], ['dg.', 'dl.', 'dm.', 'dc.', 'dj.', 'dv.', 'ds.'],
-    ['dimenge', 'diluns', 'dimars', 'dimècres', 'dijòus', 'divendres', 'dissabte'],
-    ['dg.', 'dl.', 'dm.', 'dc.', 'dj.', 'dv.', 'ds.']
+    [ 'dg', 'dl', 'dm', 'dc', 'dj', 'dv', 'ds' ], [ 'dg.', 'dl.', 'dm.', 'dc.', 'dj.', 'dv.', 'ds.' ],
+    [ 'dimenge', 'diluns', 'dimars', 'dimècres', 'dijòus', 'divendres', 'dissabte' ],
+    [ 'dg.', 'dl.', 'dm.', 'dc.', 'dj.', 'dv.', 'ds.' ]
   ],
   u,
   [
-    ['GN', 'FB', 'MÇ', 'AB', 'MA', 'JN', 'JL', 'AG', 'ST', 'OC', 'NV', 'DC'],
+    [ 'GN', 'FB', 'MÇ', 'AB', 'MA', 'JN', 'JL', 'AG', 'ST', 'OC', 'NV', 'DC' ],
     [
       'de gen.', 'de febr.', 'de març', 'd’abr.', 'de mai', 'de junh', 'de jul.', 'd’ag.',
       'de set.', 'd’oct.', 'de nov.', 'de dec.'
@@ -32,7 +32,7 @@ export default [
     ]
   ],
   [
-    ['GN', 'FB', 'MÇ', 'AB', 'MA', 'JN', 'JL', 'AG', 'ST', 'OC', 'NV', 'DC'],
+    [ 'GN', 'FB', 'MÇ', 'AB', 'MA', 'JN', 'JL', 'AG', 'ST', 'OC', 'NV', 'DC' ],
     [
       'gen.', 'febr.', 'març', 'abr.', 'mai', 'junh', 'jul.', 'ag.', 'set.', 'oct.', 'nov.',
       'dec.'
@@ -42,62 +42,62 @@ export default [
       'novembre', 'decembre'
     ]
   ],
-  [['aC', 'dC'], u, ['abans Jèsus-Crist', 'aprèp Jèsus-Crist']],
+  [ [ 'aC', 'dC' ], u, [ 'abans Jèsus-Crist', 'aprèp Jèsus-Crist' ] ],
   1,
-  [6, 0],
-  ['d/M/yy', 'd MMM y', 'd MMMM \'de\' y', 'EEEE, d MMMM \'de\' y'],
-  ['H:mm', 'H:mm:ss', 'H:mm:ss z', 'H:mm:ss zzzz'],
-  ['{1} {0}', '{1}, {0}', '{1} \'a\' \'les\' {0}', u],
-  [',', '.', ';', '%', '+', '-', 'E', '×', '‰', '∞', 'NaN', ':'],
-  ['#,##0.###', '#,##0%', '#,##0.00 Â¤', '#E0'],
+  [ 6, 0 ],
+  [ 'd/M/yy', 'd MMM y', 'd MMMM \'de\' y', 'EEEE, d MMMM \'de\' y' ],
+  [ 'H:mm', 'H:mm:ss', 'H:mm:ss z', 'H:mm:ss zzzz' ],
+  [ '{1} {0}', '{1}, {0}', '{1} \'a\' \'les\' {0}', u ],
+  [ ',', '.', ';', '%', '+', '-', 'E', '×', '‰', '∞', 'NaN', ':' ],
+  [ '#,##0.###', '#,##0%', '#,##0.00 Â¤', '#E0' ],
   'EUR',
   '€',
   'euro',
   {
-    'ARS': ['$AR', '$'],
-    'AUD': ['$AU', '$'],
-    'BEF': ['FB'],
-    'BMD': ['$BM', '$'],
-    'BND': ['$BN', '$'],
-    'BZD': ['$BZ', '$'],
-    'CAD': ['$CA', '$'],
-    'CLP': ['$CL', '$'],
-    'CNY': [u, 'Â¥'],
-    'COP': ['$CO', '$'],
-    'CYP': ['£CY'],
-    'EGP': [u, '£E'],
-    'FJD': ['$FJ', '$'],
-    'FKP': ['£FK', '£'],
-    'FRF': ['F'],
-    'GBP': ['£GB', '£'],
-    'GIP': ['£GI', '£'],
-    'HKD': [u, '$'],
-    'IEP': ['£IE'],
-    'ILP': ['£IL'],
-    'ITL': ['₤IT'],
-    'JPY': [u, 'Â¥'],
-    'KMF': [u, 'FC'],
-    'LBP': ['£LB', '£L'],
-    'MTP': ['£MT'],
-    'MXN': ['$MX', '$'],
-    'NAD': ['$NA', '$'],
-    'NIO': [u, '$C'],
-    'NZD': ['$NZ', '$'],
-    'RHD': ['$RH'],
-    'RON': [u, 'L'],
-    'RWF': [u, 'FR'],
-    'SBD': ['$SB', '$'],
-    'SGD': ['$SG', '$'],
-    'SRD': ['$SR', '$'],
-    'TOP': [u, '$T'],
-    'TTD': ['$TT', '$'],
-    'TWD': [u, 'NT$'],
-    'USD': ['$US', '$'],
-    'UYU': ['$UY', '$'],
-    'WST': ['$WS'],
-    'XCD': [u, '$'],
-    'XPF': ['FCFP'],
-    'ZMW': [u, 'Kw']
+    ARS: [ '$AR', '$' ],
+    AUD: [ '$AU', '$' ],
+    BEF: [ 'FB' ],
+    BMD: [ '$BM', '$' ],
+    BND: [ '$BN', '$' ],
+    BZD: [ '$BZ', '$' ],
+    CAD: [ '$CA', '$' ],
+    CLP: [ '$CL', '$' ],
+    CNY: [ u, 'Â¥' ],
+    COP: [ '$CO', '$' ],
+    CYP: [ '£CY' ],
+    EGP: [ u, '£E' ],
+    FJD: [ '$FJ', '$' ],
+    FKP: [ '£FK', '£' ],
+    FRF: [ 'F' ],
+    GBP: [ '£GB', '£' ],
+    GIP: [ '£GI', '£' ],
+    HKD: [ u, '$' ],
+    IEP: [ '£IE' ],
+    ILP: [ '£IL' ],
+    ITL: [ '₤IT' ],
+    JPY: [ u, 'Â¥' ],
+    KMF: [ u, 'FC' ],
+    LBP: [ '£LB', '£L' ],
+    MTP: [ '£MT' ],
+    MXN: [ '$MX', '$' ],
+    NAD: [ '$NA', '$' ],
+    NIO: [ u, '$C' ],
+    NZD: [ '$NZ', '$' ],
+    RHD: [ '$RH' ],
+    RON: [ u, 'L' ],
+    RWF: [ u, 'FR' ],
+    SBD: [ '$SB', '$' ],
+    SGD: [ '$SG', '$' ],
+    SRD: [ '$SR', '$' ],
+    TOP: [ u, '$T' ],
+    TTD: [ '$TT', '$' ],
+    TWD: [ u, 'NT$' ],
+    USD: [ '$US', '$' ],
+    UYU: [ '$UY', '$' ],
+    WST: [ '$WS' ],
+    XCD: [ u, '$' ],
+    XPF: [ 'FCFP' ],
+    ZMW: [ u, 'Kw' ]
   },
   'ltr',
   plural
index edcaf50e00b4b77a3f0d74e6f76fc52c0813b4c4..8636f3a5527bd4efcc26043864b3b3c76b232ce6 100644 (file)
@@ -10,7 +10,7 @@ import { AuthService } from '../core/auth'
 // Thanks: https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
 function getParameterByName (name: string, url: string) {
   if (!url) url = window.location.href
-  name = name.replace(/[\[\]]/g, '\\$&')
+  name = name.replace(/[[\]]/g, '\\$&')
 
   const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)')
   const results = regex.exec(url)
@@ -110,10 +110,10 @@ function objectToFormData (obj: any, form?: FormData, namespace?: string) {
       continue
     }
 
-    if (obj[key] !== null && typeof obj[ key ] === 'object' && !(obj[ key ] instanceof File)) {
-      objectToFormData(obj[ key ], fd, formKey)
+    if (obj[key] !== null && typeof obj[key] === 'object' && !(obj[key] instanceof File)) {
+      objectToFormData(obj[key], fd, formKey)
     } else {
-      fd.append(formKey, obj[ key ])
+      fd.append(formKey, obj[key])
     }
   }
 
@@ -159,7 +159,7 @@ function scrollToTop (behavior: 'auto' | 'smooth' = 'auto') {
 function isInViewport (el: HTMLElement) {
   const bounding = el.getBoundingClientRect()
   return (
-      bounding.top >= 0 &&
+    bounding.top >= 0 &&
       bounding.left >= 0 &&
       bounding.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
       bounding.right <= (window.innerWidth || document.documentElement.clientWidth)
index e15aeff201ddc4c9be1c5a92a92fc21504821230..b42e41855902228dc91a5f7a2f4a3257bdcea3b4 100644 (file)
@@ -18,7 +18,7 @@ export class LanguageChooserComponent {
     @Inject(LOCALE_ID) private localeId: string
   ) {
     const l = Object.keys(I18N_LOCALES)
-                    .map(k => ({ id: k, label: I18N_LOCALES[k] , iso: getShortLocale(k) }))
+                    .map(k => ({ id: k, label: I18N_LOCALES[k], iso: getShortLocale(k) }))
 
     this.languages = sortBy(l, 'label')
   }
index 627a8712fdaf55bc8861ce953a590103b88802be..97f07c956edcb8d612f05dfa604cd06150ba802c 100644 (file)
@@ -28,7 +28,7 @@ const logger = debug('peertube:menu:MenuComponent')
 @Component({
   selector: 'my-menu',
   templateUrl: './menu.component.html',
-  styleUrls: ['./menu.component.scss']
+  styleUrls: [ './menu.component.scss' ]
 })
 export class MenuComponent implements OnInit {
   @ViewChild('languageChooserModal', { static: true }) languageChooserModal: LanguageChooserComponent
index a98579085a2269e19bb20583916357eb70695ead..559230e04b19b884c7a67d9705105362159c5dd5 100644 (file)
@@ -22,10 +22,10 @@ export class CustomModalComponent {
   ) { }
 
   show (input: {
-    title: string,
-    content: string,
-    close?: boolean,
-    cancel?: { value: string, action?: () => void },
+    title: string
+    content: string
+    close?: boolean
+    cancel?: { value: string, action?: () => void }
     confirm?: { value: string, action?: () => void }
   }) {
     if (this.modalRef instanceof NgbModalRef && this.modalService.hasOpenModals()) {
index 75bfacf01de28972ed2d56949a9be1b0b8da9143..8d3c411b43fa0fadf0edb1b407ba252af0630652 100644 (file)
@@ -2,28 +2,28 @@ import { Validators } from '@angular/forms'
 import { BuildFormValidator } from './form-validator.model'
 
 export const ABUSE_REASON_VALIDATOR: BuildFormValidator = {
-  VALIDATORS: [Validators.required, Validators.minLength(2), Validators.maxLength(3000)],
+  VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(3000) ],
   MESSAGES: {
-    'required': $localize`Report reason is required.`,
-    'minlength': $localize`Report reason must be at least 2 characters long.`,
-    'maxlength': $localize`Report reason cannot be more than 3000 characters long.`
+    required: $localize`Report reason is required.`,
+    minlength: $localize`Report reason must be at least 2 characters long.`,
+    maxlength: $localize`Report reason cannot be more than 3000 characters long.`
   }
 }
 
 export const ABUSE_MODERATION_COMMENT_VALIDATOR: BuildFormValidator = {
-  VALIDATORS: [Validators.required, Validators.minLength(2), Validators.maxLength(3000)],
+  VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(3000) ],
   MESSAGES: {
-    'required': $localize`Moderation comment is required.`,
-    'minlength': $localize`Moderation comment must be at least 2 characters long.`,
-    'maxlength': $localize`Moderation comment cannot be more than 3000 characters long.`
+    required: $localize`Moderation comment is required.`,
+    minlength: $localize`Moderation comment must be at least 2 characters long.`,
+    maxlength: $localize`Moderation comment cannot be more than 3000 characters long.`
   }
 }
 
 export const ABUSE_MESSAGE_VALIDATOR: BuildFormValidator = {
-  VALIDATORS: [Validators.required, Validators.minLength(2), Validators.maxLength(3000)],
+  VALIDATORS: [ Validators.required, Validators.minLength(2), Validators.maxLength(3000) ],
   MESSAGES: {
-    'required': $localize`Abuse message is required.`,
-    'minlength': $localize`Abuse message must be at least 2 characters long.`,
-    'maxlength': $localize`Abuse message cannot be more than 3000 characters long.`
+    required: $localize`Abuse message is required.`,
+    minlength: $localize`Abuse message must be at least 2 characters long.`,
+    maxlength: $localize`Abuse message cannot be more than 3000 characters long.`
   }
 }
index 1ed5700ff7fe8a68a555a8051cf7b62d700e25ef..fbf423d08a4db6c6591301820c135484d7417eee 100644 (file)
@@ -2,120 +2,120 @@ import { Validators } from '@angular/forms'
 import { BuildFormValidator } from './form-validator.model'
 
 export const INSTANCE_NAME_VALIDATOR: BuildFormValidator = {
-  VALIDATORS: [Validators.required],
+  VALIDATORS: [ Validators.required ],
   MESSAGES: {
-    'required': $localize`Instance name is required.`
+    required: $localize`Instance name is required.`
   }
 }
 
 export const INSTANCE_SHORT_DESCRIPTION_VALIDATOR: BuildFormValidator = {
-  VALIDATORS: [Validators.max(250)],
+  VALIDATORS: [ Validators.max(250) ],
   MESSAGES: {
-    'max': $localize`Short description should not be longer than 250 characters.`
+    max: $localize`Short description should not be longer than 250 characters.`
   }
 }
 
 export const SERVICES_TWITTER_USERNAME_VALIDATOR: BuildFormValidator = {
-  VALIDATORS: [Validators.required],
+  VALIDATORS: [ Validators.required ],
   MESSAGES: {
-    'required': $localize`Twitter username is required.`
+    required: $localize`Twitter username is required.`
   }
 }
 
 export const CACHE_PREVIEWS_SIZE_VALIDATOR: BuildFormValidator = {
-  VALIDATORS: [Validators.required, Validators.min(1), Validators.pattern('[0-9]+')],
+  VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ],
   MESSAGES: {
-    'required': $localize`Previews cache size is required.`,
-    'min': $localize`Previews cache size must be greater than 1.`,
-    'pattern': $localize`Previews cache size must be a number.`
+    required: $localize`Previews cache size is required.`,
+    min: $localize`Previews cache size must be greater than 1.`,
+    pattern: $localize`Previews cache size must be a number.`
   }
 }
 
 export const CACHE_CAPTIONS_SIZE_VALIDATOR: BuildFormValidator = {
-  VALIDATORS: [Validators.required, Validators.min(1), Validators.pattern('[0-9]+')],
+  VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ],
   MESSAGES: {
-    'required': $localize`Captions cache size is required.`,
-    'min': $localize`Captions cache size must be greater than 1.`,
-    'pattern': $localize`Captions cache size must be a number.`
+    required: $localize`Captions cache size is required.`,
+    min: $localize`Captions cache size must be greater than 1.`,
+    pattern: $localize`Captions cache size must be a number.`
   }
 }
 
 export const SIGNUP_LIMIT_VALIDATOR: BuildFormValidator = {
-  VALIDATORS: [Validators.required, Validators.min(-1), Validators.pattern('-?[0-9]+')],
+  VALIDATORS: [ Validators.required, Validators.min(-1), Validators.pattern('-?[0-9]+') ],
   MESSAGES: {
-    'required': $localize`Signup limit is required.`,
-    'min': $localize`Signup limit must be greater than 1. Use -1 to disable it.`,
-    'pattern': $localize`Signup limit must be a number.`
+    required: $localize`Signup limit is required.`,
+    min: $localize`Signup limit must be greater than 1. Use -1 to disable it.`,
+    pattern: $localize`Signup limit must be a number.`
   }
 }
 
 export const SIGNUP_MINIMUM_AGE_VALIDATOR: BuildFormValidator = {
-  VALIDATORS: [Validators.required, Validators.min(1), Validators.pattern('[0-9]+')],
+  VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ],
   MESSAGES: {
-    'required': $localize`Signup minimum age is required.`,
-    'min': $localize`Signup minimum age must be greater than 1.`,
-    'pattern': $localize`Signup minimum age must be a number.`
+    required: $localize`Signup minimum age is required.`,
+    min: $localize`Signup minimum age must be greater than 1.`,
+    pattern: $localize`Signup minimum age must be a number.`
   }
 }
 
 export const ADMIN_EMAIL_VALIDATOR: BuildFormValidator = {
-  VALIDATORS: [Validators.required, Validators.email],
+  VALIDATORS: [ Validators.required, Validators.email ],
   MESSAGES: {
-    'required': $localize`Admin email is required.`,
-    'email': $localize`Admin email must be valid.`
+    required: $localize`Admin email is required.`,
+    email: $localize`Admin email must be valid.`
   }
 }
 
 export const TRANSCODING_THREADS_VALIDATOR: BuildFormValidator = {
-  VALIDATORS: [Validators.required, Validators.min(0)],
+  VALIDATORS: [ Validators.required, Validators.min(0) ],
   MESSAGES: {
-    'required': $localize`Transcoding threads is required.`,
-    'min': $localize`Transcoding threads must be greater or equal to 0.`
+    required: $localize`Transcoding threads is required.`,
+    min: $localize`Transcoding threads must be greater or equal to 0.`
   }
 }
 
 export const MAX_LIVE_DURATION_VALIDATOR: BuildFormValidator = {
-  VALIDATORS: [Validators.required, Validators.min(-1)],
+  VALIDATORS: [ Validators.required, Validators.min(-1) ],
   MESSAGES: {
-    'required': $localize`Max live duration is required.`,
-    'min': $localize`Max live duration should be greater or equal to -1.`
+    required: $localize`Max live duration is required.`,
+    min: $localize`Max live duration should be greater or equal to -1.`
   }
 }
 
 export const MAX_INSTANCE_LIVES_VALIDATOR: BuildFormValidator = {
-  VALIDATORS: [Validators.required, Validators.min(-1)],
+  VALIDATORS: [ Validators.required, Validators.min(-1) ],
   MESSAGES: {
-    'required': $localize`Max instance lives is required.`,
-    'min': $localize`Max instance lives should be greater or equal to -1.`
+    required: $localize`Max instance lives is required.`,
+    min: $localize`Max instance lives should be greater or equal to -1.`
   }
 }
 
 export const MAX_USER_LIVES_VALIDATOR: BuildFormValidator = {
-  VALIDATORS: [Validators.required, Validators.min(-1)],
+  VALIDATORS: [ Validators.required, Validators.min(-1) ],
   MESSAGES: {
-    'required': $localize`Max user lives is required.`,
-    'min': $localize`Max user lives should be greater or equal to -1.`
+    required: $localize`Max user lives is required.`,
+    min: $localize`Max user lives should be greater or equal to -1.`
   }
 }
 
 export const CONCURRENCY_VALIDATOR: BuildFormValidator = {
-  VALIDATORS: [Validators.required, Validators.min(1)],
+  VALIDATORS: [ Validators.required, Validators.min(1) ],
   MESSAGES: {
-    'required': $localize`Concurrency is required.`,
-    'min': $localize`Concurrency should be greater or equal to 1.`
+    required: $localize`Concurrency is required.`,
+    min: $localize`Concurrency should be greater or equal to 1.`
   }
 }
 
 export const INDEX_URL_VALIDATOR: BuildFormValidator = {
-  VALIDATORS: [Validators.pattern(/^https:\/\//)],
+  VALIDATORS: [ Validators.pattern(/^https:\/\//) ],
   MESSAGES: {
-    'pattern': $localize`Index URL should be a URL`
+    pattern: $localize`Index URL should be a URL`
   }
 }
 
 export const SEARCH_INDEX_URL_VALIDATOR: BuildFormValidator = {
-  VALIDATORS: [Validators.pattern(/^https?:\/\//)],
+  VALIDATORS: [ Validators.pattern(/^https?:\/\//) ],
   MESSAGES: {
-    'pattern': $localize`Search index URL should be a URL`
+    pattern: $localize`Search index URL should be a URL`
   }
 }
index 07b1ea075e58e5b5150777054f2aab2635358462..6f2472ccded1942403e019ffd6addf0ed5b6193f 100644 (file)
@@ -1,7 +1,7 @@
 import { ValidatorFn } from '@angular/forms'
 
 export type BuildFormValidator = {
-  VALIDATORS: ValidatorFn[],
+  VALIDATORS: ValidatorFn[]
   MESSAGES: { [ name: string ]: string }
 }
 
index 6f410a50a7c5b8aa8e8e3a0959c686145ae2ed85..3d9c476b5ff30870330423cb8e9a463e882d5f34 100644 (file)
@@ -4,7 +4,7 @@ import { BuildFormValidator } from './form-validator.model'
 export function validateHost (value: string) {
   // Thanks to http://stackoverflow.com/a/106223
   const HOST_REGEXP = new RegExp(
-    '^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$'
+    '^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]).)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])$'
   )
 
   return HOST_REGEXP.test(value)
@@ -32,7 +32,7 @@ const validHosts: ValidatorFn = (control: AbstractControl) => {
   if (errors.length === 0) return null
 
   return {
-    'validHosts': {
+    validHosts: {
       reason: 'invalid',
       value: errors.join('. ') + '.'
     }
@@ -55,7 +55,7 @@ const validHostsOrHandles: ValidatorFn = (control: AbstractControl) => {
   if (errors.length === 0) return null
 
   return {
-    'validHostsOrHandles': {
+    validHostsOrHandles: {
       reason: 'invalid',
       value: errors.join('. ') + '.'
     }
@@ -80,7 +80,7 @@ export const unique: ValidatorFn = (control: AbstractControl) => {
   }
 
   return {
-    'unique': {
+    unique: {
       reason: 'invalid'
     }
   }
@@ -89,17 +89,17 @@ export const unique: ValidatorFn = (control: AbstractControl) => {
 export const UNIQUE_HOSTS_VALIDATOR: BuildFormValidator = {
   VALIDATORS: [ Validators.required, validHosts, unique ],
   MESSAGES: {
-    'required': $localize`Domain is required.`,
-    'validHosts': $localize`Hosts entered are invalid.`,
-    'unique': $localize`Hosts entered contain duplicates.`
+    required: $localize`Domain is required.`,
+    validHosts: $localize`Hosts entered are invalid.`,
+    unique: $localize`Hosts entered contain duplicates.`
   }
 }
 
 export const UNIQUE_HOSTS_OR_HANDLE_VALIDATOR: BuildFormValidator = {
   VALIDATORS: [ Validators.required, validHostsOrHandles, unique ],
   MESSAGES: {
-    'required': $localize`Domain is required.`,
-    'validHostsOrHandles': $localize`Hosts or handles are invalid.`,
-    'unique': $localize`Hosts or handles contain duplicates.`
+    required: $localize`Domain is required.`,
+    validHostsOrHandles: $localize`Hosts or handles are invalid.`,
+    unique: $localize`Hosts or handles contain duplicates.`
   }
 }
index a72e28ba0f2de2e7ac31169a2ed39db5ee053fd9..4b1f0d0481dd55c0cadfea90ff7ff5529e407a61 100644 (file)
@@ -2,10 +2,10 @@ import { Validators } from '@angular/forms'
 import { BuildFormValidator } from './form-validator.model'
 
 export const FROM_EMAIL_VALIDATOR: BuildFormValidator = {
-  VALIDATORS: [Validators.required, Validators.email],
+  VALIDATORS: [ Validators.required, Validators.email ],
   MESSAGES: {
-    'required': $localize`Email is required.`,
-    'email': $localize`Email must be valid.`
+    required: $localize`Email is required.`,
+    email: $localize`Email must be valid.`
   }
 }
 
@@ -16,9 +16,9 @@ export const FROM_NAME_VALIDATOR: BuildFormValidator = {
     Validators.maxLength(120)
   ],
   MESSAGES: {
-    'required': $localize`Your name is required.`,
-    'minlength': $localize`Your name must be at least 1 character long.`,
-    'maxlength': $localize`Your name cannot be more than 120 characters long.`
+    required: $localize`Your name is required.`,
+    minlength: $localize`Your name must be at least 1 character long.`,
+    maxlength: $localize`Your name cannot be more than 120 characters long.`
   }
 }
 
@@ -29,9 +29,9 @@ export const SUBJECT_VALIDATOR: BuildFormValidator = {
     Validators.maxLength(120)
   ],
   MESSAGES: {
-    'required': $localize`A subject is required.`,
-    'minlength': $localize`The subject must be at least 1 character long.`,
-    'maxlength': $localize`The subject cannot be more than 120 characters long.`
+    required: $localize`A subject is required.`,
+    minlength: $localize`The subject must be at least 1 character long.`,
+    maxlength: $localize`The subject cannot be more than 120 characters long.`
   }
 }
 
@@ -42,8 +42,8 @@ export const BODY_VALIDATOR: BuildFormValidator = {
     Validators.maxLength(5000)
   ],
   MESSAGES: {
-    'required': $localize`A message is required.`,
-    'minlength': $localize`The message must be at least 3 characters long.`,
-    'maxlength': $localize`The message cannot be more than 5000 characters long.`
+    required: $localize`A message is required.`,
+    minlength: $localize`The message must be at least 3 characters long.`,
+    maxlength: $localize`The message cannot be more than 5000 characters long.`
   }
 }
index 1ceae1be3a60b2995d92adbfe527be95ccc492f2..5b911ac47c88df268f3777837273d1da5b5b2058 100644 (file)
@@ -6,7 +6,7 @@ export const LOGIN_USERNAME_VALIDATOR: BuildFormValidator = {
     Validators.required
   ],
   MESSAGES: {
-    'required': $localize`Username is required.`
+    required: $localize`Username is required.`
   }
 }
 
@@ -15,6 +15,6 @@ export const LOGIN_PASSWORD_VALIDATOR: BuildFormValidator = {
     Validators.required
   ],
   MESSAGES: {
-    'required': $localize`Password is required.`
+    required: $localize`Password is required.`
   }
 }
index b87f2eab9b42288fd8f839f568ffdbf59d7cbcec..70617a56217b662b4b64a127cdfce6611bdce516 100644 (file)
@@ -6,6 +6,6 @@ export const RESET_PASSWORD_CONFIRM_VALIDATOR: BuildFormValidator = {
     Validators.required
   ],
   MESSAGES: {
-    'required': $localize`Confirmation of the password is required.`
+    required: $localize`Confirmation of the password is required.`
   }
 }
index 976c97b872f981c6d71a0f010be66000e5a5e06f..6d0dea64e4db1270f09bfcbf0c9938f4f629460a 100644 (file)
@@ -11,10 +11,10 @@ export const USER_USERNAME_VALIDATOR: BuildFormValidator = {
     Validators.pattern(new RegExp(`^${USER_USERNAME_REGEX_CHARACTERS}*$`))
   ],
   MESSAGES: {
-    'required': $localize`Username is required.`,
-    'minlength': $localize`Username must be at least 1 character long.`,
-    'maxlength': $localize`Username cannot be more than 50 characters long.`,
-    'pattern': $localize`Username should be lowercase alphanumeric; dots and underscores are allowed.`
+    required: $localize`Username is required.`,
+    minlength: $localize`Username must be at least 1 character long.`,
+    maxlength: $localize`Username cannot be more than 50 characters long.`,
+    pattern: $localize`Username should be lowercase alphanumeric; dots and underscores are allowed.`
   }
 }
 
@@ -26,18 +26,18 @@ export const USER_CHANNEL_NAME_VALIDATOR: BuildFormValidator = {
     Validators.pattern(/^[a-z0-9][a-z0-9._]*$/)
   ],
   MESSAGES: {
-    'required': $localize`Channel name is required.`,
-    'minlength': $localize`Channel name must be at least 1 character long.`,
-    'maxlength': $localize`Channel name cannot be more than 50 characters long.`,
-    'pattern': $localize`Channel name should be lowercase, and can contain only alphanumeric characters, dots and underscores.`
+    required: $localize`Channel name is required.`,
+    minlength: $localize`Channel name must be at least 1 character long.`,
+    maxlength: $localize`Channel name cannot be more than 50 characters long.`,
+    pattern: $localize`Channel name should be lowercase, and can contain only alphanumeric characters, dots and underscores.`
   }
 }
 
 export const USER_EMAIL_VALIDATOR: BuildFormValidator = {
   VALIDATORS: [ Validators.required, Validators.email ],
   MESSAGES: {
-    'required': $localize`Email is required.`,
-    'email': $localize`Email must be valid.`
+    required: $localize`Email is required.`,
+    email: $localize`Email must be valid.`
   }
 }
 
@@ -47,8 +47,8 @@ export const USER_HANDLE_VALIDATOR: BuildFormValidator = {
     Validators.pattern(/@.+/)
   ],
   MESSAGES: {
-    'required': $localize`Handle is required.`,
-    'pattern': $localize`Handle must be valid (eg. chocobozzz@example.com).`
+    required: $localize`Handle is required.`,
+    pattern: $localize`Handle must be valid (eg. chocobozzz@example.com).`
   }
 }
 
@@ -57,7 +57,7 @@ export const USER_EXISTING_PASSWORD_VALIDATOR: BuildFormValidator = {
     Validators.required
   ],
   MESSAGES: {
-    'required': $localize`Password is required.`
+    required: $localize`Password is required.`
   }
 }
 
@@ -68,9 +68,9 @@ export const USER_PASSWORD_VALIDATOR: BuildFormValidator = {
     Validators.maxLength(255)
   ],
   MESSAGES: {
-    'required': $localize`Password is required.`,
-    'minlength': $localize`Password must be at least 6 characters long.`,
-    'maxlength': $localize`Password cannot be more than 255 characters long.`
+    required: $localize`Password is required.`,
+    minlength: $localize`Password must be at least 6 characters long.`,
+    maxlength: $localize`Password cannot be more than 255 characters long.`
   }
 }
 
@@ -80,37 +80,37 @@ export const USER_PASSWORD_OPTIONAL_VALIDATOR: BuildFormValidator = {
     Validators.maxLength(255)
   ],
   MESSAGES: {
-    'minlength': $localize`Password must be at least 6 characters long.`,
-    'maxlength': $localize`Password cannot be more than 255 characters long.`
+    minlength: $localize`Password must be at least 6 characters long.`,
+    maxlength: $localize`Password cannot be more than 255 characters long.`
   }
 }
 
 export const USER_CONFIRM_PASSWORD_VALIDATOR: BuildFormValidator = {
   VALIDATORS: [],
   MESSAGES: {
-    'matchPassword': $localize`The new password and the confirmed password do not correspond.`
+    matchPassword: $localize`The new password and the confirmed password do not correspond.`
   }
 }
 
 export const USER_VIDEO_QUOTA_VALIDATOR: BuildFormValidator = {
   VALIDATORS: [ Validators.required, Validators.min(-1) ],
   MESSAGES: {
-    'required': $localize`Video quota is required.`,
-    'min': $localize`Quota must be greater than -1.`
+    required: $localize`Video quota is required.`,
+    min: $localize`Quota must be greater than -1.`
   }
 }
 export const USER_VIDEO_QUOTA_DAILY_VALIDATOR: BuildFormValidator = {
   VALIDATORS: [ Validators.required, Validators.min(-1) ],
   MESSAGES: {
-    'required': $localize`Daily upload limit is required.`,
-    'min': $localize`Daily upload limit must be greater than -1.`
+    required: $localize`Daily upload limit is required.`,
+    min: $localize`Daily upload limit must be greater than -1.`
   }
 }
 
 export const USER_ROLE_VALIDATOR: BuildFormValidator = {
   VALIDATORS: [ Validators.required ],
   MESSAGES: {
-    'required': $localize`User role is required.`
+    required: $localize`User role is required.`
   }
 }
 
@@ -122,15 +122,15 @@ export const USER_DESCRIPTION_VALIDATOR: BuildFormValidator = {
     Validators.maxLength(1000)
   ],
   MESSAGES: {
-    'minlength': $localize`Description must be at least 3 characters long.`,
-    'maxlength': $localize`Description cannot be more than 1000 characters long.`
+    minlength: $localize`Description must be at least 3 characters long.`,
+    maxlength: $localize`Description cannot be more than 1000 characters long.`
   }
 }
 
 export const USER_TERMS_VALIDATOR: BuildFormValidator = {
   VALIDATORS: [ Validators.requiredTrue ],
   MESSAGES: {
-    'required': $localize`You must agree with the instance terms in order to register on it.`
+    required: $localize`You must agree with the instance terms in order to register on it.`
   }
 }
 
@@ -140,8 +140,8 @@ export const USER_BAN_REASON_VALIDATOR: BuildFormValidator = {
     Validators.maxLength(250)
   ],
   MESSAGES: {
-    'minlength': $localize`Ban reason must be at least 3 characters long.`,
-    'maxlength': $localize`Ban reason cannot be more than 250 characters long.`
+    minlength: $localize`Ban reason must be at least 3 characters long.`,
+    maxlength: $localize`Ban reason cannot be more than 250 characters long.`
   }
 }
 
@@ -152,9 +152,9 @@ function buildDisplayNameValidator (required: boolean) {
       Validators.maxLength(120)
     ],
     MESSAGES: {
-      'required': $localize`Display name is required.`,
-      'minlength': $localize`Display name must be at least 1 character long.`,
-      'maxlength': $localize`Display name cannot be more than 50 characters long.`
+      required: $localize`Display name is required.`,
+      minlength: $localize`Display name must be at least 1 character long.`,
+      maxlength: $localize`Display name cannot be more than 50 characters long.`
     }
   }
 
index d3974aefe0c41c166b87b10161c82c32202d96cb..cd2791b76199922b08a3ad8b7b2414047ed7ef95 100644 (file)
@@ -4,7 +4,7 @@ import { BuildFormValidator } from './form-validator.model'
 export const VIDEO_BLOCK_REASON_VALIDATOR: BuildFormValidator = {
   VALIDATORS: [ Validators.minLength(2), Validators.maxLength(300) ],
   MESSAGES: {
-    'minlength': $localize`Block reason must be at least 2 characters long.`,
-    'maxlength': $localize`Block reason cannot be more than 300 characters long.`
+    minlength: $localize`Block reason must be at least 2 characters long.`,
+    maxlength: $localize`Block reason cannot be more than 300 characters long.`
   }
 }
index 9742d292575c06efde6d2ff5a8608143d7e80f3b..a162164227a64072b1f437912788d1ee1860d381 100644 (file)
@@ -4,13 +4,13 @@ import { BuildFormValidator } from './form-validator.model'
 export const VIDEO_CAPTION_LANGUAGE_VALIDATOR: BuildFormValidator = {
   VALIDATORS: [ Validators.required ],
   MESSAGES: {
-    'required': $localize`Video caption language is required.`
+    required: $localize`Video caption language is required.`
   }
 }
 
 export const VIDEO_CAPTION_FILE_VALIDATOR: BuildFormValidator = {
   VALIDATORS: [ Validators.required ],
   MESSAGES: {
-    'required': $localize`Video caption file is required.`
+    required: $localize`Video caption file is required.`
   }
 }
index ba502ed01069214a3b4518e02011ecd8631bbb04..48f5b1a2cc20bb028d62e977532630d019724d15 100644 (file)
@@ -7,10 +7,10 @@ export const VIDEO_CHANNEL_NAME_VALIDATOR: BuildFormValidator = {
   VALIDATORS: USER_USERNAME_VALIDATOR.VALIDATORS,
 
   MESSAGES: {
-    'required': $localize`Name is required.`,
-    'minlength': $localize`Name must be at least 1 character long.`,
-    'maxlength': $localize`Name cannot be more than 50 characters long.`,
-    'pattern': $localize`Name should be lowercase alphanumeric; dots and underscores are allowed.`
+    required: $localize`Name is required.`,
+    minlength: $localize`Name must be at least 1 character long.`,
+    maxlength: $localize`Name cannot be more than 50 characters long.`,
+    pattern: $localize`Name should be lowercase alphanumeric; dots and underscores are allowed.`
   }
 }
 
@@ -21,9 +21,9 @@ export const VIDEO_CHANNEL_DISPLAY_NAME_VALIDATOR: BuildFormValidator = {
     Validators.maxLength(50)
   ],
   MESSAGES: {
-    'required': $localize`Display name is required.`,
-    'minlength': $localize`Display name must be at least 1 character long.`,
-    'maxlength': $localize`Display name cannot be more than 50 characters long.`
+    required: $localize`Display name is required.`,
+    minlength: $localize`Display name must be at least 1 character long.`,
+    maxlength: $localize`Display name cannot be more than 50 characters long.`
   }
 }
 
@@ -33,8 +33,8 @@ export const VIDEO_CHANNEL_DESCRIPTION_VALIDATOR: BuildFormValidator = {
     Validators.maxLength(1000)
   ],
   MESSAGES: {
-    'minlength': $localize`Description must be at least 3 characters long.`,
-    'maxlength': $localize`Description cannot be more than 1000 characters long.`
+    minlength: $localize`Description must be at least 3 characters long.`,
+    maxlength: $localize`Description cannot be more than 1000 characters long.`
   }
 }
 
@@ -44,7 +44,7 @@ export const VIDEO_CHANNEL_SUPPORT_VALIDATOR: BuildFormValidator = {
     Validators.maxLength(1000)
   ],
   MESSAGES: {
-    'minlength': $localize`Support text must be at least 3 characters long.`,
-    'maxlength': $localize`Support text cannot be more than 1000 characters long`
+    minlength: $localize`Support text must be at least 3 characters long.`,
+    maxlength: $localize`Support text cannot be more than 1000 characters long`
   }
 }
index c56564d3454297b7b0c880cfeb294f1294478024..9e8f95e7cfdec3edb7ba9b97705e317dc30eca96 100644 (file)
@@ -4,8 +4,8 @@ import { BuildFormValidator } from './form-validator.model'
 export const VIDEO_COMMENT_TEXT_VALIDATOR: BuildFormValidator = {
   VALIDATORS: [ Validators.required, Validators.minLength(1), Validators.maxLength(3000) ],
   MESSAGES: {
-    'required': $localize`Comment is required.`,
-    'minlength': $localize`Comment must be at least 2 characters long.`,
-    'maxlength': $localize`Comment cannot be more than 3000 characters long.`
+    required: $localize`Comment is required.`,
+    minlength: $localize`Comment must be at least 2 characters long.`,
+    maxlength: $localize`Comment cannot be more than 3000 characters long.`
   }
 }
index e1a2df8a64f00b23d1c0494f166900a185346018..3e7823c4912a1d27fed9c1b581e2c0db97d7ccc3 100644 (file)
@@ -4,21 +4,21 @@ import { BuildFormValidator } from './form-validator.model'
 export const OWNERSHIP_CHANGE_CHANNEL_VALIDATOR: BuildFormValidator = {
   VALIDATORS: [ Validators.required ],
   MESSAGES: {
-    'required': $localize`The channel is required.`
+    required: $localize`The channel is required.`
   }
 }
 
 export const OWNERSHIP_CHANGE_USERNAME_VALIDATOR: BuildFormValidator = {
   VALIDATORS: [ Validators.required, localAccountValidator ],
   MESSAGES: {
-    'required': $localize`The username is required.`,
-    'localAccountOnly': $localize`You can only transfer ownership to a local account`
+    required: $localize`The username is required.`,
+    localAccountOnly: $localize`You can only transfer ownership to a local account`
   }
 }
 
 function localAccountValidator (control: AbstractControl): ValidationErrors {
   if (control.value.includes('@')) {
-    return { 'localAccountOnly': true }
+    return { localAccountOnly: true }
   }
 
   return null
index 7e3d294586bbe7a6d4ec9b488e214e8d19337623..63af637a3712b38d3a80c0f6330c5adfa76edacd 100644 (file)
@@ -9,9 +9,9 @@ export const VIDEO_PLAYLIST_DISPLAY_NAME_VALIDATOR: BuildFormValidator = {
     Validators.maxLength(120)
   ],
   MESSAGES: {
-    'required': $localize`Display name is required.`,
-    'minlength': $localize`Display name must be at least 1 character long.`,
-    'maxlength': $localize`Display name cannot be more than 120 characters long.`
+    required: $localize`Display name is required.`,
+    minlength: $localize`Display name must be at least 1 character long.`,
+    maxlength: $localize`Display name cannot be more than 120 characters long.`
   }
 }
 
@@ -20,7 +20,7 @@ export const VIDEO_PLAYLIST_PRIVACY_VALIDATOR: BuildFormValidator = {
     Validators.required
   ],
   MESSAGES: {
-    'required': $localize`Privacy is required.`
+    required: $localize`Privacy is required.`
   }
 }
 
@@ -30,21 +30,21 @@ export const VIDEO_PLAYLIST_DESCRIPTION_VALIDATOR: BuildFormValidator = {
     Validators.maxLength(1000)
   ],
   MESSAGES: {
-    'minlength': $localize`Description must be at least 3 characters long.`,
-    'maxlength': $localize`Description cannot be more than 1000 characters long.`
+    minlength: $localize`Description must be at least 3 characters long.`,
+    maxlength: $localize`Description cannot be more than 1000 characters long.`
   }
 }
 
 export const VIDEO_PLAYLIST_CHANNEL_ID_VALIDATOR: BuildFormValidator = {
   VALIDATORS: [],
   MESSAGES: {
-    'required': $localize`The channel is required when the playlist is public.`
+    required: $localize`The channel is required when the playlist is public.`
   }
 }
 
 export function setPlaylistChannelValidator (channelControl: AbstractControl, privacy: VideoPlaylistPrivacy) {
   if (privacy.toString() === VideoPlaylistPrivacy.PUBLIC.toString()) {
-    channelControl.setValidators([Validators.required])
+    channelControl.setValidators([ Validators.required ])
   } else {
     channelControl.setValidators(null)
   }
index 1382a774782f7fd1431f8aeff78cae2fdaa07770..f96189aa248fead9d58b4dde6391f3f9c37cd803 100644 (file)
@@ -12,17 +12,17 @@ export const trimValidator: ValidatorFn = (control: FormControl) => {
 export const VIDEO_NAME_VALIDATOR: BuildFormValidator = {
   VALIDATORS: [ Validators.required, Validators.minLength(3), Validators.maxLength(120) ],
   MESSAGES: {
-    'required': $localize`Video name is required.`,
-    'minlength': $localize`Video name must be at least 3 characters long.`,
-    'maxlength': $localize`Video name cannot be more than 120 characters long.`,
-    'spaces': $localize`Video name has leading or trailing whitespace.`
+    required: $localize`Video name is required.`,
+    minlength: $localize`Video name must be at least 3 characters long.`,
+    maxlength: $localize`Video name cannot be more than 120 characters long.`,
+    spaces: $localize`Video name has leading or trailing whitespace.`
   }
 }
 
 export const VIDEO_PRIVACY_VALIDATOR: BuildFormValidator = {
   VALIDATORS: [ Validators.required ],
   MESSAGES: {
-    'required': $localize`Video privacy is required.`
+    required: $localize`Video privacy is required.`
   }
 }
 
@@ -49,46 +49,46 @@ export const VIDEO_IMAGE_VALIDATOR: BuildFormValidator = {
 export const VIDEO_CHANNEL_VALIDATOR: BuildFormValidator = {
   VALIDATORS: [ Validators.required ],
   MESSAGES: {
-    'required': $localize`Video channel is required.`
+    required: $localize`Video channel is required.`
   }
 }
 
 export const VIDEO_DESCRIPTION_VALIDATOR: BuildFormValidator = {
   VALIDATORS: [ Validators.minLength(3), Validators.maxLength(10000) ],
   MESSAGES: {
-    'minlength': $localize`Video description must be at least 3 characters long.`,
-    'maxlength': $localize`Video description cannot be more than 10000 characters long.`
+    minlength: $localize`Video description must be at least 3 characters long.`,
+    maxlength: $localize`Video description cannot be more than 10000 characters long.`
   }
 }
 
 export const VIDEO_TAG_VALIDATOR: BuildFormValidator = {
   VALIDATORS: [ Validators.minLength(2), Validators.maxLength(30) ],
   MESSAGES: {
-    'minlength': $localize`A tag should be more than 2 characters long.`,
-    'maxlength': $localize`A tag should be less than 30 characters long.`
+    minlength: $localize`A tag should be more than 2 characters long.`,
+    maxlength: $localize`A tag should be less than 30 characters long.`
   }
 }
 
 export const VIDEO_TAGS_ARRAY_VALIDATOR: BuildFormValidator = {
   VALIDATORS: [ Validators.maxLength(5), arrayTagLengthValidator() ],
   MESSAGES: {
-    'maxlength': $localize`A maximum of 5 tags can be used on a video.`,
-    'arrayTagLength': $localize`A tag should be more than 1 and less than 30 characters long.`
+    maxlength: $localize`A maximum of 5 tags can be used on a video.`,
+    arrayTagLength: $localize`A tag should be more than 1 and less than 30 characters long.`
   }
 }
 
 export const VIDEO_SUPPORT_VALIDATOR: BuildFormValidator = {
   VALIDATORS: [ Validators.minLength(3), Validators.maxLength(1000) ],
   MESSAGES: {
-    'minlength': $localize`Video support must be at least 3 characters long.`,
-    'maxlength': $localize`Video support cannot be more than 1000 characters long.`
+    minlength: $localize`Video support must be at least 3 characters long.`,
+    maxlength: $localize`Video support cannot be more than 1000 characters long.`
   }
 }
 
 export const VIDEO_SCHEDULE_PUBLICATION_AT_VALIDATOR: BuildFormValidator = {
   VALIDATORS: [ ],
   MESSAGES: {
-    'required': $localize`A date is required to schedule video update.`
+    required: $localize`A date is required to schedule video update.`
   }
 }
 
@@ -105,6 +105,6 @@ function arrayTagLengthValidator (min = 2, max = 30): ValidatorFn {
       return null
     }
 
-    return { 'arrayTagLength': true }
+    return { arrayTagLength: true }
   }
 }
index e3bf9e1fb48f53c731e069945cc978f3fadde843..33e9fd8de7d5e494d0cf77ed28877337afeab2e2 100644 (file)
@@ -39,23 +39,23 @@ export class AbuseListTableComponent extends RestTable implements OnInit {
 
   inputFilters: AdvancedInputFilter[] = [
     {
-      queryParams: { 'search': 'state:pending' },
+      queryParams: { search: 'state:pending' },
       label: $localize`Unsolved reports`
     },
     {
-      queryParams: { 'search': 'state:accepted' },
+      queryParams: { search: 'state:accepted' },
       label: $localize`Accepted reports`
     },
     {
-      queryParams: { 'search': 'state:rejected' },
+      queryParams: { search: 'state:rejected' },
       label: $localize`Refused reports`
     },
     {
-      queryParams: { 'search': 'videoIs:blacklisted' },
+      queryParams: { search: 'videoIs:blacklisted' },
       label: $localize`Reports with blocked videos`
     },
     {
-      queryParams: { 'search': 'videoIs:deleted' },
+      queryParams: { search: 'videoIs:deleted' },
       label: $localize`Reports with deleted videos`
     }
   ]
index 06f1555ea1a575f025fe33d2258b610fafc6592c..ccb0c5262ac111ecb0f4713f13b4a3756cf78ff1 100644 (file)
@@ -50,7 +50,7 @@ export class ModerationCommentModalComponent extends FormReactive implements OnI
   }
 
   async banUser () {
-    const moderationComment: string = this.form.value[ 'moderationComment' ]
+    const moderationComment: string = this.form.value['moderationComment']
 
     this.abuseService.updateAbuse(this.abuseToComment, { moderationComment })
         .subscribe({
index fce1a8db317000d921cdc1f0a34c35f1a2e0dd60..194d52a333dab782c809259197609217ddb3a564 100644 (file)
@@ -5,7 +5,7 @@ import { Account } from '@app/shared/shared-main'
 // Don't use an abuse model because we need external services to compute some properties
 // And this model is only used in this component
 export type ProcessedAbuse = AdminAbuse & {
-  moderationCommentHtml?: string,
+  moderationCommentHtml?: string
   reasonHtml?: string
   embedHtml?: SafeHtml
   updatedAt?: Date
index dc9b72ddb48d7e5f4a98c3ef11907bd3986cfe64..2c0e45e203537b560514f4e269fe1f0541acff70 100644 (file)
@@ -51,7 +51,7 @@ export class ActorAvatarEditComponent implements OnInit {
   onAvatarChange (input: HTMLInputElement) {
     this.avatarfileInput = new ElementRef(input)
 
-    const avatarfile = this.avatarfileInput.nativeElement.files[ 0 ]
+    const avatarfile = this.avatarfileInput.nativeElement.files[0]
     if (avatarfile.size > this.maxAvatarSize) {
       this.notifier.error('Error', $localize`This image is too large.`)
       return
index c3f10c055834dec2644fd2e8506f39dcd2467fb4..cba2c5db3bbab32d5b1eae88c4a48e27df38fe51 100644 (file)
@@ -40,14 +40,14 @@ export class ActorBannerEditComponent implements OnInit {
     this.maxBannerSize = config.banner.file.size.max
     this.bannerExtensions = config.banner.file.extensions.join(', ')
 
-    // tslint:disable:max-line-length
+    /* eslint-disable max-len */
     this.bannerFormat = $localize`ratio 6/1, recommended size: 1920x317, max size: ${getBytes(this.maxBannerSize)}, extensions: ${this.bannerExtensions}`
   }
 
   onBannerChange (input: HTMLInputElement) {
     this.bannerfileInput = new ElementRef(input)
 
-    const bannerfile = this.bannerfileInput.nativeElement.files[ 0 ]
+    const bannerfile = this.bannerfileInput.nativeElement.files[0]
     if (bannerfile.size > this.maxBannerSize) {
       this.notifier.error('Error', $localize`This image is too large.`)
       return
index b06c2bae6547cf6946e5da965fcccf24fcffdd36..a4adfd1b73925eceae4706f6a808b34c510854f1 100644 (file)
@@ -17,6 +17,8 @@ export type ActorAvatarSize = '18' | '25' | '32' | '34' | '36' | '40' | '100' |
   templateUrl: './actor-avatar.component.html'
 })
 export class ActorAvatarComponent {
+  private _title: string
+
   @Input() account: ActorInput
   @Input() channel: ActorInput
 
@@ -33,8 +35,6 @@ export class ActorAvatarComponent {
     this._title = value
   }
 
-  private _title: string
-
   get title () {
     if (this._title) return this._title
     if (this.account) return $localize`${this.account.name} (account page)`
@@ -50,22 +50,6 @@ export class ActorAvatarComponent {
     return ''
   }
 
-  getClass (type: 'avatar' | 'initial') {
-    const base = [ 'avatar' ]
-
-    if (this.size) base.push(`avatar-${this.size}`)
-
-    if (this.channel) base.push('channel')
-    else base.push('account')
-
-    if (type === 'initial' && this.initial) {
-      base.push('initial')
-      base.push(this.getColorTheme())
-    }
-
-    return base
-  }
-
   get defaultAvatarUrl () {
     if (this.channel) return VideoChannel.GET_DEFAULT_AVATAR_URL()
 
@@ -86,6 +70,22 @@ export class ActorAvatarComponent {
     return name.slice(0, 1)
   }
 
+  getClass (type: 'avatar' | 'initial') {
+    const base = [ 'avatar' ]
+
+    if (this.size) base.push(`avatar-${this.size}`)
+
+    if (this.channel) base.push('channel')
+    else base.push('account')
+
+    if (type === 'initial' && this.initial) {
+      base.push('initial')
+      base.push(this.getColorTheme())
+    }
+
+    return base
+  }
+
   hasActor () {
     return !!this.account || !!this.channel
   }
index 8c1357d7a1b93b8230cfe37df542ace346fec575..35b413b60abfee905cb15bd19fcb886c06bbcd84 100644 (file)
@@ -39,9 +39,13 @@ export class ChannelMiniatureMarkupComponent implements CustomMarkupComponent, O
   ngOnInit () {
     this.findInBulk.getChannel(this.name)
       .pipe(
-        tap(channel => this.channel = channel),
+        tap(channel => {
+          this.channel = channel
+        }),
         switchMap(() => from(this.markdown.textMarkdownToHTML(this.channel.description))),
-        tap(html => this.descriptionHTML = html),
+        tap(html => {
+          this.descriptionHTML = html
+        }),
         switchMap(() => this.loadVideosObservable()),
         finalize(() => this.loaded.emit(true))
       ).subscribe({
index 56b43d85e287668d69463d9cb3c48b6e11f827b8..7315126e0b936779834f5c0fade78335cf5043bb 100644 (file)
@@ -1,10 +1,10 @@
 import { finalize } from 'rxjs/operators'
 import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
 import { AuthService, Notifier } from '@app/core'
-import { Video, VideoService } from '../../shared-main'
+import { FindInBulkService } from '@app/shared/shared-search'
+import { Video } from '../../shared-main'
 import { MiniatureDisplayOptions } from '../../shared-video-miniature'
 import { CustomMarkupComponent } from './shared'
-import { FindInBulkService } from '@app/shared/shared-search'
 
 /*
  * Markup component that creates a video miniature only
index adf6cb8941dff297f0ff394f9e255d495bf51349..f2ce8236006eff90c34b614ddae751a6a657e333 100644 (file)
@@ -51,7 +51,7 @@ export abstract class FormReactive {
       }
 
       // clear previous error message (if any)
-      formErrors[ field ] = ''
+      formErrors[field] = ''
       const control = form.get(field)
 
       if (control.dirty) this.formChanged = true
@@ -59,9 +59,9 @@ export abstract class FormReactive {
       // Don't care if dirty on force check
       const isDirty = control.dirty || forceCheck === true
       if (control && isDirty && control.enabled && !control.valid) {
-        const messages = validationMessages[ field ]
+        const messages = validationMessages[field]
         for (const key of Object.keys(control.errors)) {
-          formErrors[ field ] += messages[ key ] + ' '
+          formErrors[field] += messages[key] + ' '
         }
       }
     }
index 41c8b76bde96e16b93bab17e4e8b76b6eb5b3f97..c0664de5f02bff696d78f66af8ed4ed4d426f5ad 100644 (file)
@@ -28,11 +28,11 @@ export class FormValidatorService {
         continue
       }
 
-      if (field && field.MESSAGES) validationMessages[name] = field.MESSAGES as { [ name: string ]: string }
+      if (field?.MESSAGES) validationMessages[name] = field.MESSAGES as { [ name: string ]: string }
 
       const defaultValue = defaultValues[name] || ''
 
-      if (field && field.VALIDATORS) group[name] = [ defaultValue, field.VALIDATORS ]
+      if (field?.VALIDATORS) group[name] = [ defaultValue, field.VALIDATORS ]
       else group[name] = [ defaultValue ]
     }
 
@@ -62,11 +62,11 @@ export class FormValidatorService {
         continue
       }
 
-      if (field && field.MESSAGES) validationMessages[name] = field.MESSAGES as { [ name: string ]: string }
+      if (field?.MESSAGES) validationMessages[name] = field.MESSAGES as { [ name: string ]: string }
 
       const defaultValue = defaultValues[name] || ''
 
-      if (field && field.VALIDATORS) form.addControl(name, new FormControl(defaultValue, field.VALIDATORS as ValidatorFn[]))
+      if (field?.VALIDATORS) form.addControl(name, new FormControl(defaultValue, field.VALIDATORS as ValidatorFn[]))
       else form.addControl(name, new FormControl(defaultValue))
     }
   }
index fac3dfb3a0f67d9cb34640202417faa39e29311c..9d27ad07a2b3be15a61bc78e924d9ad944df8501 100644 (file)
@@ -43,7 +43,7 @@ export class ReactiveFileComponent implements OnInit, ControlValueAccessor {
   }
 
   fileChange (event: any) {
-    if (event.target.files && event.target.files.length) {
+    if (event.target.files?.length) {
       const [ file ] = event.target.files
 
       if (file.size > this.maxFileSize) {
index a47f07fc3f1f51d5a41150edf625a6f6a71e7f0e..cb5f31c8e87b3e06ca861c3ac879ed4699c3726a 100644 (file)
@@ -3,77 +3,77 @@ import { HooksService } from '@app/core/plugins/hooks.service'
 
 const icons = {
   // misc icons
-  'npm': require('!!raw-loader?!../../../assets/images/misc/npm.svg').default,
-  'markdown': require('!!raw-loader?!../../../assets/images/misc/markdown.svg').default,
-  'language': require('!!raw-loader?!../../../assets/images/misc/language.svg').default,
+  npm: require('!!raw-loader?!../../../assets/images/misc/npm.svg').default,
+  markdown: require('!!raw-loader?!../../../assets/images/misc/markdown.svg').default,
+  language: require('!!raw-loader?!../../../assets/images/misc/language.svg').default,
   'video-lang': require('!!raw-loader?!../../../assets/images/misc/video-lang.svg').default,
-  'support': require('!!raw-loader?!../../../assets/images/misc/support.svg').default,
+  support: require('!!raw-loader?!../../../assets/images/misc/support.svg').default,
   'peertube-x': require('!!raw-loader?!../../../assets/images/misc/peertube-x.svg').default,
-  'robot': require('!!raw-loader?!../../../assets/images/misc/miscellaneous-services.svg').default, // material ui
-  'videos': require('!!raw-loader?!../../../assets/images/misc/video-library.svg').default, // material ui
-  'history': require('!!raw-loader?!../../../assets/images/misc/history.svg').default, // material ui
-  'subscriptions': require('!!raw-loader?!../../../assets/images/misc/subscriptions.svg').default, // material ui
+  robot: require('!!raw-loader?!../../../assets/images/misc/miscellaneous-services.svg').default, // material ui
+  videos: require('!!raw-loader?!../../../assets/images/misc/video-library.svg').default, // material ui
+  history: require('!!raw-loader?!../../../assets/images/misc/history.svg').default, // material ui
+  subscriptions: require('!!raw-loader?!../../../assets/images/misc/subscriptions.svg').default, // material ui
   'playlist-add': require('!!raw-loader?!../../../assets/images/misc/playlist-add.svg').default, // material ui
-  'follower': require('!!raw-loader?!../../../assets/images/misc/account-arrow-left.svg').default, // material ui
-  'following': require('!!raw-loader?!../../../assets/images/misc/account-arrow-right.svg').default, // material ui
-  'flame': require('!!raw-loader?!../../../assets/images/misc/flame.svg').default,
-  'local': require('!!raw-loader?!../../../assets/images/misc/local.svg').default,
+  follower: require('!!raw-loader?!../../../assets/images/misc/account-arrow-left.svg').default, // material ui
+  following: require('!!raw-loader?!../../../assets/images/misc/account-arrow-right.svg').default, // material ui
+  flame: require('!!raw-loader?!../../../assets/images/misc/flame.svg').default,
+  local: require('!!raw-loader?!../../../assets/images/misc/local.svg').default,
 
   // feather icons
-  'flag': require('!!raw-loader?!../../../assets/images/feather/flag.svg').default,
-  'playlists': require('!!raw-loader?!../../../assets/images/feather/list.svg').default,
-  'syndication': require('!!raw-loader?!../../../assets/images/feather/syndication.svg').default,
-  'help': require('!!raw-loader?!../../../assets/images/feather/help.svg').default,
-  'alert': require('!!raw-loader?!../../../assets/images/feather/alert.svg').default,
-  'globe': require('!!raw-loader?!../../../assets/images/feather/globe.svg').default,
-  'home': require('!!raw-loader?!../../../assets/images/feather/home.svg').default,
+  flag: require('!!raw-loader?!../../../assets/images/feather/flag.svg').default,
+  playlists: require('!!raw-loader?!../../../assets/images/feather/list.svg').default,
+  syndication: require('!!raw-loader?!../../../assets/images/feather/syndication.svg').default,
+  help: require('!!raw-loader?!../../../assets/images/feather/help.svg').default,
+  alert: require('!!raw-loader?!../../../assets/images/feather/alert.svg').default,
+  globe: require('!!raw-loader?!../../../assets/images/feather/globe.svg').default,
+  home: require('!!raw-loader?!../../../assets/images/feather/home.svg').default,
   'recently-added': require('!!raw-loader?!../../../assets/images/feather/recently-added.svg').default,
-  'trending': require('!!raw-loader?!../../../assets/images/feather/trending.svg').default,
-  'search': require('!!raw-loader?!../../../assets/images/feather/search.svg').default,
-  'upload': require('!!raw-loader?!../../../assets/images/feather/upload.svg').default,
-  'dislike': require('!!raw-loader?!../../../assets/images/feather/dislike.svg').default,
-  'like': require('!!raw-loader?!../../../assets/images/feather/like.svg').default,
-  'no': require('!!raw-loader?!../../../assets/images/feather/no.svg').default,
+  trending: require('!!raw-loader?!../../../assets/images/feather/trending.svg').default,
+  search: require('!!raw-loader?!../../../assets/images/feather/search.svg').default,
+  upload: require('!!raw-loader?!../../../assets/images/feather/upload.svg').default,
+  dislike: require('!!raw-loader?!../../../assets/images/feather/dislike.svg').default,
+  like: require('!!raw-loader?!../../../assets/images/feather/like.svg').default,
+  no: require('!!raw-loader?!../../../assets/images/feather/no.svg').default,
   'cloud-download': require('!!raw-loader?!../../../assets/images/feather/cloud-download.svg').default,
-  'clock': require('!!raw-loader?!../../../assets/images/feather/clock.svg').default,
-  'cog': require('!!raw-loader?!../../../assets/images/feather/cog.svg').default,
-  'delete': require('!!raw-loader?!../../../assets/images/feather/delete.svg').default,
-  'bell': require('!!raw-loader?!../../../assets/images/feather/bell.svg').default,
+  clock: require('!!raw-loader?!../../../assets/images/feather/clock.svg').default,
+  cog: require('!!raw-loader?!../../../assets/images/feather/cog.svg').default,
+  delete: require('!!raw-loader?!../../../assets/images/feather/delete.svg').default,
+  bell: require('!!raw-loader?!../../../assets/images/feather/bell.svg').default,
   'sign-out': require('!!raw-loader?!../../../assets/images/feather/log-out.svg').default,
   'sign-in': require('!!raw-loader?!../../../assets/images/feather/log-in.svg').default,
-  'download': require('!!raw-loader?!../../../assets/images/feather/download.svg').default,
+  download: require('!!raw-loader?!../../../assets/images/feather/download.svg').default,
   'ownership-change': require('!!raw-loader?!../../../assets/images/feather/share.svg').default,
-  'share': require('!!raw-loader?!../../../assets/images/feather/share-2.svg').default,
-  'channel': require('!!raw-loader?!../../../assets/images/feather/tv.svg').default,
-  'user': require('!!raw-loader?!../../../assets/images/feather/user.svg').default,
+  share: require('!!raw-loader?!../../../assets/images/feather/share-2.svg').default,
+  channel: require('!!raw-loader?!../../../assets/images/feather/tv.svg').default,
+  user: require('!!raw-loader?!../../../assets/images/feather/user.svg').default,
   'user-x': require('!!raw-loader?!../../../assets/images/feather/user-x.svg').default,
-  'users': require('!!raw-loader?!../../../assets/images/feather/users.svg').default,
+  users: require('!!raw-loader?!../../../assets/images/feather/users.svg').default,
   'user-add': require('!!raw-loader?!../../../assets/images/feather/user-plus.svg').default,
-  'add': require('!!raw-loader?!../../../assets/images/feather/plus-circle.svg').default,
+  add: require('!!raw-loader?!../../../assets/images/feather/plus-circle.svg').default,
   'cloud-error': require('!!raw-loader?!../../../assets/images/feather/cloud-off.svg').default,
-  'undo': require('!!raw-loader?!../../../assets/images/feather/corner-up-left.svg').default,
+  undo: require('!!raw-loader?!../../../assets/images/feather/corner-up-left.svg').default,
   'circle-tick': require('!!raw-loader?!../../../assets/images/feather/check-circle.svg').default,
   'more-horizontal': require('!!raw-loader?!../../../assets/images/feather/more-horizontal.svg').default,
   'more-vertical': require('!!raw-loader?!../../../assets/images/feather/more-vertical.svg').default,
-  'play': require('!!raw-loader?!../../../assets/images/feather/play.svg').default,
-  'p2p': require('!!raw-loader?!../../../assets/images/feather/airplay.svg').default,
-  'fullscreen': require('!!raw-loader?!../../../assets/images/feather/maximize.svg').default,
+  play: require('!!raw-loader?!../../../assets/images/feather/play.svg').default,
+  p2p: require('!!raw-loader?!../../../assets/images/feather/airplay.svg').default,
+  fullscreen: require('!!raw-loader?!../../../assets/images/feather/maximize.svg').default,
   'exit-fullscreen': require('!!raw-loader?!../../../assets/images/feather/minimize.svg').default,
-  'film': require('!!raw-loader?!../../../assets/images/feather/film.svg').default,
-  'edit': require('!!raw-loader?!../../../assets/images/feather/edit-2.svg').default,
-  'sensitive': require('!!raw-loader?!../../../assets/images/feather/eye.svg').default,
-  'unsensitive': require('!!raw-loader?!../../../assets/images/feather/eye-off.svg').default,
-  'refresh': require('!!raw-loader?!../../../assets/images/feather/refresh-cw.svg').default,
-  'command': require('!!raw-loader?!../../../assets/images/feather/command.svg').default,
-  'go': require('!!raw-loader?!../../../assets/images/feather/arrow-up-right.svg').default,
-  'cross': require('!!raw-loader?!../../../assets/images/feather/x.svg').default,
-  'tick': require('!!raw-loader?!../../../assets/images/feather/check.svg').default,
-  'columns': require('!!raw-loader?!../../../assets/images/feather/columns.svg').default,
-  'live': require('!!raw-loader?!../../../assets/images/feather/live.svg').default,
-  'repeat': require('!!raw-loader?!../../../assets/images/feather/repeat.svg').default,
+  film: require('!!raw-loader?!../../../assets/images/feather/film.svg').default,
+  edit: require('!!raw-loader?!../../../assets/images/feather/edit-2.svg').default,
+  sensitive: require('!!raw-loader?!../../../assets/images/feather/eye.svg').default,
+  unsensitive: require('!!raw-loader?!../../../assets/images/feather/eye-off.svg').default,
+  refresh: require('!!raw-loader?!../../../assets/images/feather/refresh-cw.svg').default,
+  command: require('!!raw-loader?!../../../assets/images/feather/command.svg').default,
+  go: require('!!raw-loader?!../../../assets/images/feather/arrow-up-right.svg').default,
+  cross: require('!!raw-loader?!../../../assets/images/feather/x.svg').default,
+  tick: require('!!raw-loader?!../../../assets/images/feather/check.svg').default,
+  columns: require('!!raw-loader?!../../../assets/images/feather/columns.svg').default,
+  live: require('!!raw-loader?!../../../assets/images/feather/live.svg').default,
+  repeat: require('!!raw-loader?!../../../assets/images/feather/repeat.svg').default,
   'message-circle': require('!!raw-loader?!../../../assets/images/feather/message-circle.svg').default,
-  'codesandbox': require('!!raw-loader?!../../../assets/images/feather/codesandbox.svg').default,
-  'award': require('!!raw-loader?!../../../assets/images/feather/award.svg').default
+  codesandbox: require('!!raw-loader?!../../../assets/images/feather/codesandbox.svg').default,
+  award: require('!!raw-loader?!../../../assets/images/feather/award.svg').default
 }
 
 export type GlobalIconName = keyof typeof icons
index 855c06aa195d68404e6edfc2e9217b66006ca6bb..1eb7b49b67ad2bdb48d14e07d5b24a3a2a15e02f 100644 (file)
@@ -7,7 +7,7 @@ import { About } from '@shared/models/server'
 @Component({
   selector: 'my-instance-about-accordion',
   templateUrl: './instance-about-accordion.component.html',
-  styleUrls: ['./instance-about-accordion.component.scss']
+  styleUrls: [ './instance-about-accordion.component.scss' ]
 })
 export class InstanceAboutAccordionComponent implements OnInit {
   @ViewChild('accordion', { static: true }) accordion: NgbAccordion
index af44020cf4ef1df622ea2fe64fa07b9d941e1864..a6799d3e18199ab1e9748f3ca1bc3911ed29fcde 100644 (file)
@@ -19,10 +19,10 @@ export class InstanceFollowService {
   }
 
   getFollowing (options: {
-    pagination: RestPagination,
-    sort: SortMeta,
-    search?: string,
-    actorType?: ActivityPubActorType,
+    pagination: RestPagination
+    sort: SortMeta
+    search?: string
+    actorType?: ActivityPubActorType
     state?: FollowState
   }): Observable<ResultList<ActorFollow>> {
     const { pagination, sort, search, state, actorType } = options
@@ -42,10 +42,10 @@ export class InstanceFollowService {
   }
 
   getFollowers (options: {
-    pagination: RestPagination,
-    sort: SortMeta,
-    search?: string,
-    actorType?: ActivityPubActorType,
+    pagination: RestPagination
+    sort: SortMeta
+    search?: string
+    actorType?: ActivityPubActorType
     state?: FollowState
   }): Observable<ResultList<ActorFollow>> {
     const { pagination, sort, search, state, actorType } = options
index 40aa8a4c086e0cdd1bff440216d36132390e74a6..0618efe699f0df3184cea7c572c22e1c2a8ea890 100644 (file)
@@ -17,6 +17,8 @@ export class InstanceStatisticsComponent implements OnInit {
 
   ngOnInit () {
     this.serverService.getServerStats()
-        .subscribe(res => this.serverStats = res)
+        .subscribe(res => {
+          this.serverStats = res
+        })
   }
 }
index 70e02217893c3f5df6eecf9c1d2b19a552c0eea0..0241f56eff208a5ee4744cf5d84c342d5715b6a2 100644 (file)
@@ -51,7 +51,7 @@ export class InstanceService {
     }
 
     for (const key of Object.keys(html)) {
-      html[ key ] = await this.markdownService.textMarkdownToHTML(about.instance[ key ])
+      html[key] = await this.markdownService.textMarkdownToHTML(about.instance[key])
     }
 
     return html
index 5efb95a7dd6137efa8e51604ed2f8271247c8983..13c681ab8fabadd2e9d592e22cd99672de00d8d7 100644 (file)
@@ -1,7 +1,6 @@
 
 import { NgModule } from '@angular/core'
 import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'
-import { SharedCustomMarkupModule } from '../shared-custom-markup'
 import { SharedMainModule } from '../shared-main/shared-main.module'
 import { FeatureBooleanComponent } from './feature-boolean.component'
 import { InstanceAboutAccordionComponent } from './instance-about-accordion.component'
index 7b5611f35a21e47654b880bcfd6a2bef1934209a..92606e7fa9810c382253538e187767dee8731f6e 100644 (file)
@@ -1,4 +1,4 @@
-import { Account as ServerAccount, Actor as ServerActor, ActorImage } from '@shared/models'
+import { Account as ServerAccount, ActorImage } from '@shared/models'
 import { Actor } from './actor.model'
 
 export class Account extends Actor implements ServerAccount {
index 2fccc472a64fe4e87bedf9ba14200f61c89005b3..082f44fb95b1f9e66418c6c62246ffe0e0ee35ae 100644 (file)
@@ -20,7 +20,7 @@ export abstract class Actor implements ServerActor {
   static GET_ACTOR_AVATAR_URL (actor: { avatar?: { url?: string, path: string } }) {
     if (actor?.avatar?.url) return actor.avatar.url
 
-    if (actor && actor.avatar) {
+    if (actor?.avatar) {
       const absoluteAPIUrl = getAbsoluteAPIUrl()
 
       return absoluteAPIUrl + actor.avatar.path
index 5f087d79d1b09f0349becb2cbcfa449bcc3e48f6..2da492ea11ef62c6cc4f25db05a3946040f79dbd 100644 (file)
@@ -1,7 +1,7 @@
 import { AfterViewInit, Directive, ElementRef } from '@angular/core'
 
 @Directive({
-  selector: '[autofocus]'
+  selector: '[myAutofocus]'
 })
 export class AutofocusDirective implements AfterViewInit {
   constructor (private host: ElementRef) { }
index 597a16871f5de71e484280c8473b046a94a93cb9..ecbd9151c944f80b2ff459d880f67eb5734e1666 100644 (file)
@@ -1,4 +1,4 @@
-import { Component, Input, ViewEncapsulation } from '@angular/core'
+import { Component, Input } from '@angular/core'
 
 @Component({
   selector: 'my-link',
index e04c25d9a20fbf0c1cb7ffbe9a65f5b95ddc3936..dc0cde12d2a8f243a54489aaecae569380f5aa6b 100644 (file)
@@ -1,6 +1,7 @@
 import { Directive, Input, TemplateRef } from '@angular/core'
 
 @Directive({
+  // eslint-disable-next-line @angular-eslint/directive-selector
   selector: '[ptTemplate]'
 })
 export class PeerTubeTemplateDirective <T extends string> {
index 2466ae7c6fe5a2da9eab3a8d8fce7f37179349af..cd6fbdb487a5b989549ae5174d732d9ac0d29553 100644 (file)
@@ -1,7 +1,7 @@
 import { FeedFormat } from '@shared/models'
 
 export interface Syndication {
-  format: FeedFormat,
-  label: string,
+  format: FeedFormat
+  label: string
   url: string
 }
index 76e255d9943eef584d979699e54a42275249c55b..37e2abd97b8b7026a28fdf41dbbc2e2ddb10e102 100644 (file)
@@ -71,18 +71,18 @@ export class HelpComponent implements OnInit, OnChanges, AfterContentInit {
   }
 
   private formatMarkdownSupport (rules: string[]) {
-    // tslint:disable:max-line-length
+    /* eslint-disable max-len */
     return $localize`<a href="https://en.wikipedia.org/wiki/Markdown#Example" target="_blank" rel="noopener noreferrer">Markdown</a> compatible that supports:` +
       this.createMarkdownList(rules)
   }
 
   private createMarkdownList (rules: string[]) {
     const rulesToText = {
-      'emphasis': $localize`Emphasis`,
-      'link': $localize`Links`,
-      'newline': $localize`New lines`,
-      'list': $localize`Lists`,
-      'image': $localize`Images`
+      emphasis: $localize`Emphasis`,
+      link: $localize`Links`,
+      newline: $localize`New lines`,
+      list: $localize`Lists`,
+      image: $localize`Images`
     }
 
     const bullets = rules.map(r => rulesToText[r])
index 144e0f1567370ac2bad8809d59edd88e22cc4e6c..fbc4810932c58891526763dc8695b8b7088da951 100644 (file)
@@ -22,7 +22,7 @@ export interface ListOverflowItem {
 }
 
 @Component({
-  selector: 'list-overflow',
+  selector: 'my-list-overflow',
   templateUrl: './list-overflow.component.html',
   styleUrls: [ './list-overflow.component.scss' ],
   changeDetection: ChangeDetectionStrategy.OnPush
@@ -65,7 +65,7 @@ export class ListOverflowComponent<T extends ListOverflowItem> implements AfterV
     let showItemsUntilIndexExcluded: number
     let accWidth = 0
 
-    for (const [index, el] of this.itemsRendered.toArray().entries()) {
+    for (const [ index, el ] of this.itemsRendered.toArray().entries()) {
       accWidth += el.nativeElement.getBoundingClientRect().width
       if (showItemsUntilIndexExcluded === undefined) {
         showItemsUntilIndexExcluded = (parentWidth < accWidth) ? index : undefined
index 224d71134535697db729c6034cd82b6c12c149ec..292ec4c82b046d60eea2b38b85f23f8e80d2aa98 100644 (file)
@@ -4,7 +4,7 @@ import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild }
 import { ActivatedRoute, Router } from '@angular/router'
 
 @Component({
-  selector: 'simple-search-input',
+  selector: 'my-simple-search-input',
   templateUrl: './simple-search-input.component.html',
   styleUrls: [ './simple-search-input.component.scss' ]
 })
index 2cafb6c55c6d68f50d17106b7e675afdeb7650b8..e7e34ce1ed30b782db85c3ce871279422e56e810 100644 (file)
@@ -66,7 +66,7 @@ export class TopMenuDropdownComponent implements OnInit, OnDestroy {
       .subscribe(() => this.updateChildLabels(window.location.pathname))
 
     this.hasIcons = this.menuEntries.some(
-      e => e.children && e.children.some(c => !!c.iconName)
+      e => e.children?.some(c => !!c.iconName)
     )
   }
 
index 9014b48a8d894ae43dbf5e829254956fa25da6fd..09fee87a30b7b9506478eb9066b103e19d287c83 100644 (file)
@@ -1,11 +1,11 @@
+import { SortMeta } from 'primeng/api'
 import { catchError, map, tap } from 'rxjs/operators'
 import { HttpClient, HttpParams } from '@angular/common/http'
 import { Injectable } from '@angular/core'
-import { ComponentPaginationLight, RestExtractor, RestService, User, PeerTubeSocket, AuthService } from '@app/core'
+import { AuthService, ComponentPaginationLight, PeerTubeSocket, RestExtractor, RestService } from '@app/core'
 import { ResultList, UserNotification as UserNotificationServer, UserNotificationSetting } from '@shared/models'
 import { environment } from '../../../../environments/environment'
 import { UserNotification } from './user-notification.model'
-import { SortMeta } from 'primeng/api'
 
 @Injectable()
 export class UserNotificationService {
@@ -23,7 +23,7 @@ export class UserNotificationService {
   listMyNotifications (parameters: {
     pagination: ComponentPaginationLight
     ignoreLoadingBar?: boolean
-    unread?: boolean,
+    unread?: boolean
     sort?: SortMeta
   }) {
     const { pagination, ignoreLoadingBar, unread, sort } = parameters
index b38619186016d89fb4156edc0a6057f9b3772fd1..5a95f120928245611a64bf81fa22baf58209867b 100644 (file)
@@ -6,7 +6,7 @@ import { BytesPipe } from '../angular'
 @Component({
   selector: 'my-user-quota',
   templateUrl: './user-quota.component.html',
-  styleUrls: ['./user-quota.component.scss']
+  styleUrls: [ './user-quota.component.scss' ]
 })
 
 export class UserQuotaComponent implements OnInit {
index a9dcf2fa29c352430d71f9aadf9de7771c73cc57..66d4cac687cede38ee3b9a0bf80520a2b6d1d793 100644 (file)
@@ -1,6 +1,5 @@
 import { getAbsoluteAPIUrl } from '@app/helpers'
 import { Account as ServerAccount, ActorImage, VideoChannel as ServerVideoChannel, ViewsPerDate } from '@shared/models'
-import { Account } from '../account/account.model'
 import { Actor } from '../account/actor.model'
 
 export class VideoChannel extends Actor implements ServerVideoChannel {
@@ -25,14 +24,14 @@ export class VideoChannel extends Actor implements ServerVideoChannel {
 
   viewsPerDay?: ViewsPerDate[]
 
-  static GET_ACTOR_AVATAR_URL (actor: object) {
+  static GET_ACTOR_AVATAR_URL (actor: { avatar?: { url?: string, path: string } }) {
     return Actor.GET_ACTOR_AVATAR_URL(actor)
   }
 
   static GET_ACTOR_BANNER_URL (channel: ServerVideoChannel) {
     if (channel?.banner?.url) return channel.banner.url
 
-    if (channel && channel.banner) {
+    if (channel?.banner) {
       const absoluteAPIUrl = getAbsoluteAPIUrl()
 
       return absoluteAPIUrl + channel.banner.path
index a89f1065adbe8a06ad0e6545d023d6b7866a6b1e..7560a35a8fa4b02ec5987f93dc568a71a479ec42 100644 (file)
@@ -15,6 +15,12 @@ export class VideoChannelService {
 
   videoChannelLoaded = new ReplaySubject<VideoChannel>(1)
 
+  constructor (
+    private authHttp: HttpClient,
+    private restService: RestService,
+    private restExtractor: RestExtractor
+  ) { }
+
   static extractVideoChannels (result: ResultList<VideoChannelServer>) {
     const videoChannels: VideoChannel[] = []
 
@@ -25,12 +31,6 @@ export class VideoChannelService {
     return { data: videoChannels, total: result.total }
   }
 
-  constructor (
-    private authHttp: HttpClient,
-    private restService: RestService,
-    private restExtractor: RestExtractor
-  ) { }
-
   getVideoChannel (videoChannelName: string) {
     return this.authHttp.get<VideoChannel>(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannelName)
                .pipe(
index 6e839e655f14a6161ffc4e3080c3403e526b544b..966d7fafd426e76efa2f87488cb5080eca6796ed 100644 (file)
@@ -30,8 +30,8 @@ export class RedundancyService {
   }
 
   listVideoRedundancies (options: {
-    pagination: RestPagination,
-    sort: SortMeta,
+    pagination: RestPagination
+    sort: SortMeta
     target?: VideoRedundanciesTarget
   }): Observable<ResultList<VideoRedundancy>> {
     const { pagination, sort, target } = options
index 757b686c01149d31326839cd11f73cfa3bc84769..ea04569421e883d52090ee569787c70a56be8bdc 100644 (file)
@@ -29,11 +29,11 @@ export class VideoEdit implements VideoUpdate {
 
   constructor (
     video?: Video & {
-      tags: string[],
-      commentsEnabled: boolean,
-      downloadEnabled: boolean,
-      support: string,
-      thumbnailUrl: string,
+      tags: string[]
+      commentsEnabled: boolean
+      downloadEnabled: boolean
+      support: string
+      thumbnailUrl: string
       previewUrl: string
     }) {
     if (video) {
@@ -64,7 +64,7 @@ export class VideoEdit implements VideoUpdate {
 
   patch (values: { [ id: string ]: any }) {
     Object.keys(values).forEach((key) => {
-      this[ key ] = values[ key ]
+      this[key] = values[key]
     })
 
     // If schedule publication, the video is private and will be changed to public privacy
index b7720c8d21da3617869c159a391f2d1be60a5f6a..7471a933b2006e3a6e9a8b22534b0f24752b606d 100644 (file)
@@ -100,7 +100,7 @@ export class Video implements VideoServerModel {
     return '/videos/update/' + video.uuid
   }
 
-  constructor (hash: VideoServerModel, translations = {}) {
+  constructor (hash: VideoServerModel, translations: { [ id: string ]: string } = {}) {
     const absoluteAPIUrl = getAbsoluteAPIUrl()
 
     this.createdAt = new Date(hash.createdAt.toString())
index 4a97719fa96858749800dabf244eddc4642766e0..60cc9d16012a0c9e13c78a6733813f5dea652bdf 100644 (file)
@@ -30,10 +30,10 @@ import { Video } from './video.model'
 
 export interface VideosProvider {
   getVideos (parameters: {
-    videoPagination: ComponentPaginationLight,
-    sort: VideoSortField,
-    filter?: VideoFilter,
-    categoryOneOf?: number[],
+    videoPagination: ComponentPaginationLight
+    sort: VideoSortField
+    filter?: VideoFilter
+    categoryOneOf?: number[]
     languageOneOf?: string[]
     nsfwPolicy: NSFWPolicyType
   }): Observable<ResultList<Video>>
@@ -145,8 +145,8 @@ export class VideoService implements VideosProvider {
   }
 
   getAccountVideos (parameters: {
-    account: Pick<Account, 'nameWithHost'>,
-    videoPagination: ComponentPaginationLight,
+    account: Pick<Account, 'nameWithHost'>
+    videoPagination: ComponentPaginationLight
     sort: VideoSortField
     nsfwPolicy?: NSFWPolicyType
     videoFilter?: VideoFilter
@@ -180,9 +180,9 @@ export class VideoService implements VideosProvider {
   }
 
   getVideoChannelVideos (parameters: {
-    videoChannel: Pick<VideoChannel, 'nameWithHost'>,
-    videoPagination: ComponentPaginationLight,
-    sort: VideoSortField,
+    videoChannel: Pick<VideoChannel, 'nameWithHost'>
+    videoPagination: ComponentPaginationLight
+    sort: VideoSortField
     nsfwPolicy?: NSFWPolicyType
     videoFilter?: VideoFilter
   }): Observable<ResultList<Video>> {
index bf98d4b36d6b294ba0aa7de041372f6595e844ab..f45b5c8e86e2367356f55ef6494da48a651f4fe6 100644 (file)
@@ -30,8 +30,8 @@ export class AbuseService {
   ) { }
 
   getAdminAbuses (options: {
-    pagination: RestPagination,
-    sort: SortMeta,
+    pagination: RestPagination
+    sort: SortMeta
     search?: string
   }): Observable<ResultList<AdminAbuse>> {
     const { pagination, sort, search } = options
@@ -51,8 +51,8 @@ export class AbuseService {
   }
 
   getUserAbuses (options: {
-    pagination: RestPagination,
-    sort: SortMeta,
+    pagination: RestPagination
+    sort: SortMeta
     search?: string
   }): Observable<ResultList<UserAbuse>> {
     const { pagination, sort, search } = options
@@ -74,7 +74,7 @@ export class AbuseService {
   reportVideo (parameters: AbuseCreate) {
     const url = AbuseService.BASE_ABUSE_URL
 
-    const body = omit(parameters, ['id'])
+    const body = omit(parameters, [ 'id' ])
 
     return this.authHttp.post(url, body)
       .pipe(
@@ -147,11 +147,13 @@ export class AbuseService {
       {
         id: 'spamOrMisleading',
         label: $localize`Spam, ad or false news`,
+        // eslint-disable-next-line max-len
         help: $localize`Contains marketing, spam, purposefully deceitful news, or otherwise misleading thumbnail/text/tags. Please provide reputable sources to report hoaxes.`
       },
       {
         id: 'privacy',
         label: $localize`Privacy breach or doxxing`,
+        // eslint-disable-next-line max-len
         help: $localize`Contains personal information that could be used to track, identify, contact or impersonate someone (e.g. name, address, phone number, email, or credit card details).`
       },
       {
@@ -162,6 +164,7 @@ export class AbuseService {
       {
         id: 'serverRules',
         label: $localize`Breaks server rules`,
+        // eslint-disable-next-line max-len
         description: $localize`Anything not included in the above that breaks the terms of service, code of conduct, or general rules in place on the server.`
       }
     ]
index 36f2a591b5086e22ff9e1728272b75b3af8d1cc6..9ed00bc12f211c0cad3d4a7f560f9588c5284629 100644 (file)
@@ -1,14 +1,13 @@
 import { SortMeta } from 'primeng/api'
 import { Directive, OnInit } from '@angular/core'
 import { Notifier, RestPagination, RestTable } from '@app/core'
-import { Account } from '@app/shared/shared-main'
 import { AccountBlock } from './account-block.model'
 import { BlocklistComponentType, BlocklistService } from './blocklist.service'
 
 @Directive()
-// tslint:disable-next-line: directive-class-suffix
+// eslint-disable-next-line @angular-eslint/directive-class-suffix
 export class GenericAccountBlocklistComponent extends RestTable implements OnInit {
-  // @ts-ignore: "Abstract methods can only appear within an abstract class"
+  // @ts-expect-error: "Abstract methods can only appear within an abstract class"
   abstract mode: BlocklistComponentType
 
   blockedAccounts: AccountBlock[] = []
@@ -23,7 +22,7 @@ export class GenericAccountBlocklistComponent extends RestTable implements OnIni
     super()
   }
 
-  // @ts-ignore: "Abstract methods can only appear within an abstract class"
+  // @ts-expect-error: "Abstract methods can only appear within an abstract class"
   abstract getIdentifier (): string
 
   ngOnInit () {
index de677a77b18ccde9d87470ab02495d1da8262128..db2a8c5846ea920cfda02045344f5c6f8cd6aa3b 100644 (file)
@@ -21,7 +21,7 @@ export class BlocklistService {
     private restService: RestService
   ) { }
 
-  /*********************** User -> Account blocklist ***********************/
+  /** ********************* User -> Account blocklist ***********************/
 
   getUserAccountBlocklist (options: { pagination: RestPagination, sort: SortMeta, search?: string }) {
     const { pagination, sort, search } = options
@@ -53,7 +53,7 @@ export class BlocklistService {
                .pipe(catchError(err => this.restExtractor.handleError(err)))
   }
 
-  /*********************** User -> Server blocklist ***********************/
+  /** ********************* User -> Server blocklist ***********************/
 
   getUserServerBlocklist (options: { pagination: RestPagination, sort: SortMeta, search?: string }) {
     const { pagination, sort, search } = options
@@ -84,7 +84,7 @@ export class BlocklistService {
                .pipe(catchError(err => this.restExtractor.handleError(err)))
   }
 
-  /*********************** Instance -> Account blocklist ***********************/
+  /** ********************* Instance -> Account blocklist ***********************/
 
   getInstanceAccountBlocklist (options: { pagination: RestPagination, sort: SortMeta, search?: string }) {
     const { pagination, sort, search } = options
@@ -116,7 +116,7 @@ export class BlocklistService {
                .pipe(catchError(err => this.restExtractor.handleError(err)))
   }
 
-  /*********************** Instance -> Server blocklist ***********************/
+  /** ********************* Instance -> Server blocklist ***********************/
 
   getInstanceServerBlocklist (options: { pagination: RestPagination, sort: SortMeta, search?: string }) {
     const { pagination, sort, search } = options
index da09b1d0e5820aab0e5e2b8fdc12a892c18e47eb..1ba7a1b4d732e33dffc07092d18d93722f055f83 100644 (file)
@@ -6,11 +6,11 @@ import { ServerBlock } from '@shared/models'
 import { BlocklistComponentType, BlocklistService } from './blocklist.service'
 
 @Directive()
-// tslint:disable-next-line: directive-class-suffix
+// eslint-disable-next-line @angular-eslint/directive-class-suffix
 export class GenericServerBlocklistComponent extends RestTable implements OnInit {
   @ViewChild('batchDomainsModal') batchDomainsModal: BatchDomainsModalComponent
 
-  // @ts-ignore: "Abstract methods can only appear within an abstract class"
+  // @ts-expect-error: "Abstract methods can only appear within an abstract class"
   public abstract mode: BlocklistComponentType
 
   blockedServers: ServerBlock[] = []
@@ -25,7 +25,7 @@ export class GenericServerBlocklistComponent extends RestTable implements OnInit
     super()
   }
 
-  // @ts-ignore: "Abstract methods can only appear within an abstract class"
+  // @ts-expect-error: "Abstract methods can only appear within an abstract class"
   public abstract getIdentifier (): string
 
   ngOnInit () {
@@ -34,8 +34,8 @@ export class GenericServerBlocklistComponent extends RestTable implements OnInit
 
   unblockServer (serverBlock: ServerBlock) {
     const operation = (host: string) => this.mode === BlocklistComponentType.Account
-        ? this.blocklistService.unblockServerByUser(host)
-        : this.blocklistService.unblockServerByInstance(host)
+      ? this.blocklistService.unblockServerByUser(host)
+      : this.blocklistService.unblockServerByInstance(host)
     const host = serverBlock.blockedServer.host
 
     operation(host).subscribe(
index bc69526201f523e208fb38c2df1ce5f2d3530190..f6c29dcfafafdc9c769dc12ff63c752c47ff3f36 100644 (file)
@@ -51,8 +51,8 @@ export class VideoBlockComponent extends FormReactive implements OnInit {
   }
 
   block () {
-    const reason = this.form.value[ 'reason' ] || undefined
-    const unfederate = this.video.isLocal ? this.form.value[ 'unfederate' ] : undefined
+    const reason = this.form.value['reason'] || undefined
+    const unfederate = this.video.isLocal ? this.form.value['unfederate'] : undefined
 
     this.videoBlocklistService.blockVideo(this.video.id, reason, unfederate)
         .subscribe({
index a0b9fada64dd157c6bc1a32995d1cfe53184da7f..08e997f7bfeb9b52cc5972069e941bfa9d124b11 100644 (file)
@@ -28,7 +28,9 @@ export class SupportModalComponent {
     const support = this.video?.support || this.videoChannel.support
 
     this.markdownService.enhancedMarkdownToHTML(support)
-      .then(r => this.htmlSupport = r)
+      .then(r => {
+        this.htmlSupport = r
+      })
 
     this.displayName = this.video
       ? this.video.channel.displayName
index 4aac60c2b4acb2624707e8c0f24e99cd7826691b..5d6e11c04776c13ddc9e8e335e3c80c9865be11e 100644 (file)
@@ -80,7 +80,7 @@ export class UserVideoSettingsComponent extends FormReactive implements OnInit,
   }
 
   updateDetails (onlyKeys?: string[]) {
-    const nsfwPolicy = this.form.value[ 'nsfwPolicy' ]
+    const nsfwPolicy = this.form.value['nsfwPolicy']
     const webTorrentEnabled = this.form.value['webTorrentEnabled']
     const autoPlayVideo = this.form.value['autoPlayVideo']
     const autoPlayNextVideo = this.form.value['autoPlayNextVideo']
index 66619952343be7296788178fbbe8d879f0b34ad7..a951134eb8223d39faf62deb2fb0d48716cd1219 100644 (file)
@@ -6,7 +6,7 @@ import { USER_HANDLE_VALIDATOR } from '../form-validators/user-validators'
 @Component({
   selector: 'my-remote-subscribe',
   templateUrl: './remote-subscribe.component.html',
-  styleUrls: ['./remote-subscribe.component.scss']
+  styleUrls: [ './remote-subscribe.component.scss' ]
 })
 export class RemoteSubscribeComponent extends FormReactive implements OnInit {
   @Input() uri: string
@@ -42,17 +42,21 @@ export class RemoteSubscribeComponent extends FormReactive implements OnInit {
     // Should not have CORS error because https://tools.ietf.org/html/rfc7033#section-5
     fetch(`${protocol}//${hostname}/.well-known/webfinger?resource=acct:${username}@${hostname}`)
       .then(response => response.json())
-      .then(data => new Promise((res, rej) => {
-        if (!data || Array.isArray(data.links) === false) return rej()
+      .then(data => {
+        if (!data || Array.isArray(data.links) === false) {
+          throw new Error('Not links in webfinger response')
+        }
 
         const link: { template: string } = data.links.find((link: any) => {
           return link && typeof link.template === 'string' && link.rel === 'http://ostatus.org/schema/1.0/subscribe'
         })
 
-        if (link && link.template.includes('{uri}')) {
-          res(link.template.replace('{uri}', encodeURIComponent(this.uri)))
+        if (link?.template.includes('{uri}')) {
+          return link.template.replace('{uri}', encodeURIComponent(this.uri))
         }
-      }))
+
+        throw new Error('No subscribe template in webfinger response')
+      })
       .then(window.open)
       .catch(err => {
         console.error(err)
index 796493bd976c6796212e534681c08b747746a50e..180bc05658568b60bf16ba63b68db733ec23f94d 100644 (file)
@@ -137,7 +137,7 @@ export class SubscribeButtonComponent implements OnInit, OnChanges {
           this.notifier.success(
             this.account
               ? $localize`Unsubscribed from all channels of ${this.account.nameWithHost}`
-              : $localize`Unsubscribed from ${this.videoChannels[ 0 ].nameWithHost}`,
+              : $localize`Unsubscribed from ${this.videoChannels[0].nameWithHost}`,
 
             $localize`Unsubscribed`
           )
@@ -157,7 +157,7 @@ export class SubscribeButtonComponent implements OnInit, OnChanges {
 
   subscribeStatus (subscribed: boolean) {
     const accumulator: string[] = []
-    for (const [key, value] of this.subscribed.entries()) {
+    for (const [ key, value ] of this.subscribed.entries()) {
       if (value === subscribed) accumulator.push(key)
     }
 
index bb44660d2f1a91e312c4912edb3f8fff0d1752c6..e4fc09b3684e3d5b036b4af12866cb7ecbc8a019 100644 (file)
@@ -46,8 +46,8 @@ export class UserSubscriptionService {
   }
 
   getUserSubscriptionVideos (parameters: {
-    videoPagination: ComponentPaginationLight,
-    sort: VideoSortField,
+    videoPagination: ComponentPaginationLight
+    sort: VideoSortField
     skipCount?: boolean
   }): Observable<ResultList<Video>> {
     const { videoPagination, sort, skipCount } = parameters
@@ -131,16 +131,16 @@ export class UserSubscriptionService {
 
   listenToSubscriptionCacheChange (nameWithHost: string) {
     if (nameWithHost in this.myAccountSubscriptionCacheObservable) {
-      return this.myAccountSubscriptionCacheObservable[ nameWithHost ]
+      return this.myAccountSubscriptionCacheObservable[nameWithHost]
     }
 
     const obs = this.existsObservable
                     .pipe(
-                      filter(existsResult => existsResult[ nameWithHost ] !== undefined),
-                      map(existsResult => existsResult[ nameWithHost ])
+                      filter(existsResult => existsResult[nameWithHost] !== undefined),
+                      map(existsResult => existsResult[nameWithHost])
                     )
 
-    this.myAccountSubscriptionCacheObservable[ nameWithHost ] = obs
+    this.myAccountSubscriptionCacheObservable[nameWithHost] = obs
     return obs
   }
 
@@ -150,16 +150,16 @@ export class UserSubscriptionService {
     if (nameWithHost in this.myAccountSubscriptionCache) {
       logger('Found cache for %d.', nameWithHost)
 
-      return of(this.myAccountSubscriptionCache[ nameWithHost ])
+      return of(this.myAccountSubscriptionCache[nameWithHost])
     }
 
     this.existsSubject.next(nameWithHost)
 
     logger('Fetching from network for %d.', nameWithHost)
     return this.existsObservable.pipe(
-      filter(existsResult => existsResult[ nameWithHost ] !== undefined),
-      map(existsResult => existsResult[ nameWithHost ]),
-      tap(result => this.myAccountSubscriptionCache[ nameWithHost ] = result)
+      filter(existsResult => existsResult[nameWithHost] !== undefined),
+      map(existsResult => existsResult[nameWithHost]),
+      tap(result => this.myAccountSubscriptionCache[nameWithHost] = result)
     )
   }
 
index ba0f57e8fe20d8b266752bd563674f18866b15bf..adab4cfbda43ede60833b4cdcba765b7d7aebfb5 100644 (file)
@@ -1,6 +1,10 @@
 import { getAbsoluteAPIUrl } from '@app/helpers'
-import { Account, Actor, Video } from '@app/shared/shared-main'
-import { Account as AccountInterface, VideoComment as VideoCommentServerModel, VideoCommentAdmin as VideoCommentAdminServerModel } from '@shared/models'
+import { Actor, Video } from '@app/shared/shared-main'
+import {
+  Account as AccountInterface,
+  VideoComment as VideoCommentServerModel,
+  VideoCommentAdmin as VideoCommentAdminServerModel
+} from '@shared/models'
 
 export class VideoComment implements VideoCommentServerModel {
   id: number
index 4f1452116a75fa6d0bb7db832fcc30078457ac89..5550c96e4a0dd1e59db628c8cdc7a5363fb50ed3 100644 (file)
@@ -37,8 +37,8 @@ export class VideoCommentService {
 
     return this.authHttp.post<{ comment: VideoCommentServerModel }>(url, normalizedComment)
                .pipe(
-                  map(data => this.extractVideoComment(data.comment)),
-                  catchError(err => this.restExtractor.handleError(err))
+                 map(data => this.extractVideoComment(data.comment)),
+                 catchError(err => this.restExtractor.handleError(err))
                )
   }
 
@@ -54,8 +54,8 @@ export class VideoCommentService {
   }
 
   getAdminVideoComments (options: {
-    pagination: RestPagination,
-    sort: SortMeta,
+    pagination: RestPagination
+    sort: SortMeta
     search?: string
   }): Observable<ResultList<VideoCommentAdmin>> {
     const { pagination, sort, search } = options
@@ -75,8 +75,8 @@ export class VideoCommentService {
   }
 
   getVideoCommentThreads (parameters: {
-    videoId: number | string,
-    componentPagination: ComponentPaginationLight,
+    videoId: number | string
+    componentPagination: ComponentPaginationLight
     sort: string
   }): Observable<ThreadsResultList<VideoComment>> {
     const { videoId, componentPagination, sort } = parameters
@@ -95,7 +95,7 @@ export class VideoCommentService {
   }
 
   getVideoThreadComments (parameters: {
-    videoId: number | string,
+    videoId: number | string
     threadId: number
   }): Observable<VideoCommentThreadTree> {
     const { videoId, threadId } = parameters
index d8b2ee17dd8328cb010418bf5cec6dc436e011fd..f12ae2ee58363710c6860c6ee74a6bea20260612 100644 (file)
@@ -42,7 +42,7 @@ enum GroupDate {
 }
 
 @Directive()
-// tslint:disable-next-line: directive-class-suffix
+// eslint-disable-next-line @angular-eslint/directive-class-suffix
 export abstract class AbstractVideoList implements OnInit, OnDestroy, AfterContentInit, DisableForReuseHook {
   @ViewChild('videoListHeader', { static: true, read: ViewContainerRef }) videoListHeader: ViewContainerRef
 
@@ -174,7 +174,7 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, AfterConte
   ngAfterContentInit () {
     if (this.videoListHeader) {
       // some components don't use the header: they use their own template, like my-history.component.html
-      this.setHeader.apply(this, [ this.HeaderComponent, this.headerComponentInjector ])
+      this.setHeader(this.HeaderComponent, this.headerComponentInjector)
     }
   }
 
@@ -278,7 +278,7 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, AfterConte
 
           if (currentGroupedDate !== period.value) {
             currentGroupedDate = period.value
-            this.groupedDates[ video.id ] = currentGroupedDate
+            this.groupedDates[video.id] = currentGroupedDate
           }
 
           break
@@ -302,13 +302,13 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, AfterConte
     i: Injector = this.headerComponentInjector
   ) {
     const injector = i || Injector.create({
-      providers: [{
+      providers: [ {
         provide: 'data',
         useValue: {
           titlePage: this.titlePage,
           titleTooltip: this.titleTooltip
         }
-      }]
+      } ]
     })
     const viewContainerRef = this.videoListHeader
     viewContainerRef.clear()
@@ -331,9 +331,9 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, AfterConte
   protected loadPageRouteParams (_queryParams: Params) { /* empty */ }
 
   protected loadRouteParams (queryParams: Params) {
-    this.sort = queryParams[ 'sort' ] as VideoSortField || this.defaultSort
-    this.categoryOneOf = queryParams[ 'categoryOneOf' ]
-    this.angularState = queryParams[ 'a-state' ]
+    this.sort = queryParams['sort'] as VideoSortField || this.defaultSort
+    this.categoryOneOf = queryParams['categoryOneOf']
+    this.angularState = queryParams['a-state']
 
     this.loadPageRouteParams(queryParams)
   }
index 28fefb9dddf12f4cb885eca731bea014f3ccc8fe..5328f5170d650d4465f6be35c8461bd53d9375ff 100644 (file)
@@ -210,10 +210,10 @@ export class VideoDownloadComponent {
 
   private getMetadataFormat (format: any) {
     const keyToTranslateFunction = {
-      'encoder': (value: string) => ({ label: $localize`Encoder`, value }),
-      'format_long_name': (value: string) => ({ label: $localize`Format name`, value }),
-      'size': (value: number) => ({ label: $localize`Size`, value: this.bytesPipe.transform(value, 2) }),
-      'bit_rate': (value: number) => ({
+      encoder: (value: string) => ({ label: $localize`Encoder`, value }),
+      format_long_name: (value: string) => ({ label: $localize`Format name`, value }),
+      size: (value: number) => ({ label: $localize`Size`, value: this.bytesPipe.transform(value, 2) }),
+      bit_rate: (value: number) => ({
         label: $localize`Bitrate`,
         value: `${this.numbersPipe.transform(value)}bps`
       })
@@ -234,9 +234,9 @@ export class VideoDownloadComponent {
     if (!stream) return undefined
 
     let keyToTranslateFunction = {
-      'codec_long_name': (value: string) => ({ label: $localize`Codec`, value }),
-      'profile': (value: string) => ({ label: $localize`Profile`, value }),
-      'bit_rate': (value: number) => ({
+      codec_long_name: (value: string) => ({ label: $localize`Codec`, value }),
+      profile: (value: string) => ({ label: $localize`Profile`, value }),
+      bit_rate: (value: number) => ({
         label: $localize`Bitrate`,
         value: `${this.numbersPipe.transform(value)}bps`
       })
@@ -244,15 +244,15 @@ export class VideoDownloadComponent {
 
     if (type === 'video') {
       keyToTranslateFunction = Object.assign(keyToTranslateFunction, {
-        'width': (value: number) => ({ label: $localize`Resolution`, value: `${value}x${stream.height}` }),
-        'display_aspect_ratio': (value: string) => ({ label: $localize`Aspect ratio`, value }),
-        'avg_frame_rate': (value: string) => ({ label: $localize`Average frame rate`, value }),
-        'pix_fmt': (value: string) => ({ label: $localize`Pixel format`, value })
+        width: (value: number) => ({ label: $localize`Resolution`, value: `${value}x${stream.height}` }),
+        display_aspect_ratio: (value: string) => ({ label: $localize`Aspect ratio`, value }),
+        avg_frame_rate: (value: string) => ({ label: $localize`Average frame rate`, value }),
+        pix_fmt: (value: string) => ({ label: $localize`Pixel format`, value })
       })
     } else {
       keyToTranslateFunction = Object.assign(keyToTranslateFunction, {
-        'sample_rate': (value: number) => ({ label: $localize`Sample rate`, value }),
-        'channel_layout': (value: number) => ({ label: $localize`Channel Layout`, value })
+        sample_rate: (value: number) => ({ label: $localize`Sample rate`, value }),
+        channel_layout: (value: number) => ({ label: $localize`Channel Layout`, value })
       })
     }
 
index 08a961be19b7c791e02611e915d01c500fde2fcc..fed69667204d6c5006e85fcad2686ba6a843395e 100644 (file)
@@ -11,7 +11,7 @@ export abstract class GenericHeaderComponent {
 
 @Component({
   selector: 'my-video-list-header',
-  // tslint:disable-next-line:use-component-view-encapsulation
+  // eslint-disable-next-line @angular-eslint/use-component-view-encapsulation
   encapsulation: ViewEncapsulation.None,
   templateUrl: './video-list-header.component.html'
 })
index d64ee9b981ecc3c94f84c5b935466614b403c8da..456b3692662daa3571519fcefffe8b75f75357b6 100644 (file)
@@ -108,7 +108,7 @@ export class VideosSelectionComponent extends AbstractVideoList implements OnIni
   }
 
   isInSelectionMode () {
-    return Object.keys(this._selection).some(k => this._selection[ k ] === true)
+    return Object.keys(this._selection).some(k => this._selection[k] === true)
   }
 
   generateSyndicationList () {
index df3aeddb740319340aab42f326d9520326f5e59b..5539305956394d2930aae4d19f845df6a869888f 100644 (file)
@@ -191,7 +191,7 @@ export class VideoAddToPlaylistComponent extends FormReactive implements OnInit,
   }
 
   createPlaylist () {
-    const displayName = this.form.value[ 'displayName' ]
+    const displayName = this.form.value['displayName']
 
     const videoPlaylistCreate: VideoPlaylistCreate = {
       displayName,
index f25f10ede5dd818cd0a344881477af29944cd7db..b661378bd80b51238816a4ec2f1e3775b4529a29 100644 (file)
@@ -11,7 +11,7 @@ export class VideoPlaylistElement implements ServerVideoPlaylistElement {
 
   video?: Video
 
-  constructor (hash: ServerVideoPlaylistElement, translations: {}) {
+  constructor (hash: ServerVideoPlaylistElement, translations: { [ id: string ]: string } = {}) {
     this.id = hash.id
     this.position = hash.position
     this.startTimestamp = hash.startTimestamp
index fcc2ce705dda5539b2c509f5bb6458060b6118a9..6b38d9ca351bc8814857e34bc8ae1d35c99ad2e7 100644 (file)
@@ -48,7 +48,7 @@ export class VideoPlaylist implements ServerVideoPlaylist {
     return buildPlaylistWatchPath({ shortUUID: playlist.shortUUID || playlist.uuid })
   }
 
-  constructor (hash: ServerVideoPlaylist, translations: {}) {
+  constructor (hash: ServerVideoPlaylist, translations: { [ id: string ]: string }) {
     const absoluteAPIUrl = getAbsoluteAPIUrl()
 
     this.id = hash.id
index a3f1393ff3318929d33fa462a93fe2fd0ca0d3fe..0a01af5937a465813dae4683530a0dd07ffb284c 100644 (file)
@@ -279,18 +279,18 @@ export class VideoPlaylistService {
   }
 
   listenToVideoPlaylistChange (videoId: number) {
-    if (this.videoExistsObservableCache[ videoId ]) {
-      return this.videoExistsObservableCache[ videoId ]
+    if (this.videoExistsObservableCache[videoId]) {
+      return this.videoExistsObservableCache[videoId]
     }
 
     const obs = this.videoExistsInPlaylistObservable
                     .pipe(
-                      map(existsResult => existsResult[ videoId ]),
+                      map(existsResult => existsResult[videoId]),
                       filter(r => !!r),
-                      tap(result => this.videoExistsCache[ videoId ] = result)
+                      tap(result => this.videoExistsCache[videoId] = result)
                     )
 
-    this.videoExistsObservableCache[ videoId ] = obs
+    this.videoExistsObservableCache[videoId] = obs
     return obs
   }
 
index 78f0944efe15c8cdd1003e337b13b8e8e8dd3801..a1b07aea6bfa9e2f4e40bf7e9c572b11b31cb05b 100644 (file)
@@ -13,6 +13,8 @@ type Metadata = {
   levels: Level[]
 }
 
+type HookFn = (player: videojs.Player, hljs: Hlsjs) => void
+
 const registerSourceHandler = function (vjs: typeof videojs) {
   if (!Hlsjs.isSupported()) {
     console.warn('Hls.js is not supported in this browser!')
@@ -82,7 +84,7 @@ const registerConfigPlugin = function (vjs: typeof videojs) {
 }
 
 class Html5Hlsjs {
-  private static readonly hooks: { [id: string]: Function[] } = {}
+  private static readonly hooks: { [id: string]: HookFn[] } = {}
 
   private readonly videoElement: HTMLVideoElement
   private readonly errorCounts: ErrorCounts = {}
@@ -131,7 +133,8 @@ class Html5Hlsjs {
           errorTxt = 'You aborted the video playback'
           break
         case mediaError.MEDIA_ERR_DECODE:
-          errorTxt = 'The video playback was aborted due to a corruption problem or because the video used features your browser did not support'
+          errorTxt = 'The video playback was aborted due to a corruption problem or because the video used features ' +
+                     'your browser did not support'
           this._handleMediaError(mediaError)
           break
         case mediaError.MEDIA_ERR_NETWORK:
@@ -182,58 +185,57 @@ class Html5Hlsjs {
     this.hls.destroy()
   }
 
-  static addHook (type: string, callback: Function) {
-    Html5Hlsjs.hooks[ type ] = this.hooks[ type ] || []
-    Html5Hlsjs.hooks[ type ].push(callback)
+  static addHook (type: string, callback: HookFn) {
+    Html5Hlsjs.hooks[type] = this.hooks[type] || []
+    Html5Hlsjs.hooks[type].push(callback)
   }
 
-  static removeHook (type: string, callback: Function) {
-    if (Html5Hlsjs.hooks[ type ] === undefined) return false
+  static removeHook (type: string, callback: HookFn) {
+    if (Html5Hlsjs.hooks[type] === undefined) return false
 
-    const index = Html5Hlsjs.hooks[ type ].indexOf(callback)
+    const index = Html5Hlsjs.hooks[type].indexOf(callback)
     if (index === -1) return false
 
-    Html5Hlsjs.hooks[ type ].splice(index, 1)
+    Html5Hlsjs.hooks[type].splice(index, 1)
 
     return true
   }
 
   private _executeHooksFor (type: string) {
-    if (Html5Hlsjs.hooks[ type ] === undefined) {
+    if (Html5Hlsjs.hooks[type] === undefined) {
       return
     }
 
     // ES3 and IE < 9
-    for (let i = 0; i < Html5Hlsjs.hooks[ type ].length; i++) {
-      Html5Hlsjs.hooks[ type ][ i ](this.player, this.hls)
+    for (let i = 0; i < Html5Hlsjs.hooks[type].length; i++) {
+      Html5Hlsjs.hooks[type][i](this.player, this.hls)
     }
   }
 
   private _handleMediaError (error: any) {
-    if (this.errorCounts[ Hlsjs.ErrorTypes.MEDIA_ERROR ] === 1) {
+    if (this.errorCounts[Hlsjs.ErrorTypes.MEDIA_ERROR] === 1) {
       console.info('trying to recover media error')
       this.hls.recoverMediaError()
       return
     }
 
-    if (this.errorCounts[ Hlsjs.ErrorTypes.MEDIA_ERROR ] === 2) {
+    if (this.errorCounts[Hlsjs.ErrorTypes.MEDIA_ERROR] === 2) {
       console.info('2nd try to recover media error (by swapping audio codec')
       this.hls.swapAudioCodec()
       this.hls.recoverMediaError()
       return
     }
 
-    if (this.errorCounts[ Hlsjs.ErrorTypes.MEDIA_ERROR ] > 2) {
+    if (this.errorCounts[Hlsjs.ErrorTypes.MEDIA_ERROR] > 2) {
       console.info('bubbling media error up to VIDEOJS')
       this.hls.destroy()
       this.tech.error = () => error
       this.tech.trigger('error')
-      return
     }
   }
 
   private _handleNetworkError (error: any) {
-    if (this.errorCounts[ Hlsjs.ErrorTypes.NETWORK_ERROR] <= 5) {
+    if (this.errorCounts[Hlsjs.ErrorTypes.NETWORK_ERROR] <= 5) {
       console.info('trying to recover network error')
 
       // Wait 1 second and retry
@@ -241,7 +243,7 @@ class Html5Hlsjs {
 
       // Reset error count on success
       this.hls.once(Hlsjs.Events.FRAG_LOADED, () => {
-        this.errorCounts[ Hlsjs.ErrorTypes.NETWORK_ERROR] = 0
+        this.errorCounts[Hlsjs.ErrorTypes.NETWORK_ERROR] = 0
       })
 
       return
@@ -259,8 +261,8 @@ class Html5Hlsjs {
     }
 
     // increment/set error count
-    if (this.errorCounts[ data.type ]) this.errorCounts[ data.type ] += 1
-    else this.errorCounts[ data.type ] = 1
+    if (this.errorCounts[data.type]) this.errorCounts[data.type] += 1
+    else this.errorCounts[data.type] = 1
 
     if (data.fatal) console.warn(error.message)
     else console.error(error.message, data)
@@ -300,7 +302,7 @@ class Html5Hlsjs {
     let isAuto = true
 
     for (let i = 0; i < qualityLevels.length; i++) {
-      if (!qualityLevels[ i ]._enabled) {
+      if (!qualityLevels[i]._enabled) {
         isAuto = false
         break
       }
@@ -316,7 +318,7 @@ class Html5Hlsjs {
     let selectedTrack: number
 
     for (selectedTrack = qualityLevels.length - 1; selectedTrack >= 0; selectedTrack--) {
-      if (qualityLevels[ selectedTrack ]._enabled) {
+      if (qualityLevels[selectedTrack]._enabled) {
         break
       }
     }
@@ -327,11 +329,11 @@ class Html5Hlsjs {
   private _handleQualityLevels () {
     if (!this.metadata) return
 
-    const qualityLevels = this.player.qualityLevels && this.player.qualityLevels()
+    const qualityLevels = this.player.qualityLevels?.()
     if (!qualityLevels) return
 
     for (let i = 0; i < this.metadata.levels.length; i++) {
-      const details = this.metadata.levels[ i ]
+      const details = this.metadata.levels[i]
       const representation: QualityLevelRepresentation = {
         id: i,
         width: details.width,
@@ -345,11 +347,11 @@ class Html5Hlsjs {
       representation.enabled = function (this: QualityLevels, level: number, toggle?: boolean) {
         // Brightcove switcher works TextTracks-style (enable tracks that it wants to ABR on)
         if (typeof toggle === 'boolean') {
-          this[ level ]._enabled = toggle
+          this[level]._enabled = toggle
           self._relayQualityChange(this)
         }
 
-        return this[ level ]._enabled
+        return this[level]._enabled
       }
 
       qualityLevels.addQualityLevel(representation)
@@ -395,7 +397,7 @@ class Html5Hlsjs {
     const playerAudioTracks = this.tech.audioTracks()
     for (let j = 0; j < playerAudioTracks.length; j++) {
       // FIXME: typings
-      if ((playerAudioTracks[ j ] as any).enabled) {
+      if ((playerAudioTracks[j] as any).enabled) {
         this.hls.audioTrack = j
         break
       }
@@ -412,8 +414,8 @@ class Html5Hlsjs {
         playerAudioTracks.addTrack(new this.vjs.AudioTrack({
           id: i.toString(),
           kind: 'alternative',
-          label: hlsAudioTracks[ i ].name || hlsAudioTracks[ i ].lang,
-          language: hlsAudioTracks[ i ].lang,
+          label: hlsAudioTracks[i].name || hlsAudioTracks[i].lang,
+          language: hlsAudioTracks[i].lang,
           enabled: i === this.hls.audioTrack
         }))
       }
@@ -430,8 +432,8 @@ class Html5Hlsjs {
   }
 
   private _isSameTextTrack (track1: TextTrack, track2: TextTrack) {
-    return this._getTextTrackLabel(track1) === this._getTextTrackLabel(track2)
-      && track1.kind === track2.kind
+    return this._getTextTrackLabel(track1) === this._getTextTrackLabel(track2) &&
+           track1.kind === track2.kind
   }
 
   private _updateSelectedTextTrack () {
@@ -439,16 +441,16 @@ class Html5Hlsjs {
     let activeTrack: TextTrack = null
 
     for (let j = 0; j < playerTextTracks.length; j++) {
-      if (playerTextTracks[ j ].mode === 'showing') {
-        activeTrack = playerTextTracks[ j ]
+      if (playerTextTracks[j].mode === 'showing') {
+        activeTrack = playerTextTracks[j]
         break
       }
     }
 
     const hlsjsTracks = this.videoElement.textTracks
     for (let k = 0; k < hlsjsTracks.length; k++) {
-      if (hlsjsTracks[ k ].kind === 'subtitles' || hlsjsTracks[ k ].kind === 'captions') {
-        hlsjsTracks[ k ].mode = activeTrack && this._isSameTextTrack(hlsjsTracks[ k ], activeTrack)
+      if (hlsjsTracks[k].kind === 'subtitles' || hlsjsTracks[k].kind === 'captions') {
+        hlsjsTracks[k].mode = activeTrack && this._isSameTextTrack(hlsjsTracks[k], activeTrack)
           ? 'showing'
           : 'disabled'
       }
@@ -460,11 +462,11 @@ class Html5Hlsjs {
     this.videoElement.removeEventListener('play', this.handlers.play)
   }
 
-  private _oneLevelObjClone (obj: object) {
+  private _oneLevelObjClone (obj: { [ id: string ]: any }) {
     const result = {}
     const objKeys = Object.keys(obj)
     for (let i = 0; i < objKeys.length; i++) {
-      result[ objKeys[ i ] ] = obj[ objKeys[ i ] ]
+      result[objKeys[i]] = obj[objKeys[i]]
     }
 
     return result
@@ -475,8 +477,8 @@ class Html5Hlsjs {
 
     // Filter out tracks that is displayable (captions or subtitles)
     for (let idx = 0; idx < textTracks.length; idx++) {
-      if (textTracks[ idx ].kind === 'subtitles' || textTracks[ idx ].kind === 'captions') {
-        displayableTracks.push(textTracks[ idx ])
+      if (textTracks[idx].kind === 'subtitles' || textTracks[idx].kind === 'captions') {
+        displayableTracks.push(textTracks[idx])
       }
     }
 
@@ -493,14 +495,14 @@ class Html5Hlsjs {
       let isAdded = false
 
       for (let jdx = 0; jdx < playerTextTracks.length; jdx++) {
-        if (this._isSameTextTrack(displayableTracks[ idx ], playerTextTracks[ jdx ])) {
+        if (this._isSameTextTrack(displayableTracks[idx], playerTextTracks[jdx])) {
           isAdded = true
           break
         }
       }
 
       if (!isAdded) {
-        const hlsjsTextTrack = displayableTracks[ idx ]
+        const hlsjsTextTrack = displayableTracks[idx]
         this.player.addRemoteTextTrack({
           kind: hlsjsTextTrack.kind as videojs.TextTrack.Kind,
           label: this._getTextTrackLabel(hlsjsTextTrack),
@@ -536,12 +538,12 @@ class Html5Hlsjs {
         const VTTCue = (window as any).VTTCue || (window as any).TextTrackCue
 
         for (let r = 0; r < captionScreen.rows.length; r++) {
-          row = captionScreen.rows[ r ]
+          row = captionScreen.rows[r]
           text = ''
 
           if (!row.isEmpty()) {
             for (let c = 0; c < row.chars.length; c++) {
-              text += row.chars[ c ].ucharj
+              text += row.chars[c].ucharj
             }
 
             cue = new VTTCue(startTime, endTime, text.trim())
@@ -552,7 +554,7 @@ class Html5Hlsjs {
               const configKeys = Object.keys(captionConfig)
 
               for (let k = 0; k < configKeys.length; k++) {
-                cue[ configKeys[ k ] ] = captionConfig[ configKeys[ k ] ]
+                cue[configKeys[k]] = captionConfig[configKeys[k]]
               }
             }
             track.addCue(cue)
@@ -567,7 +569,7 @@ class Html5Hlsjs {
     const techOptions = this.tech.options_ as HlsjsConfigHandlerOptions
     const srOptions_ = this.player.srOptions_
 
-    const hlsjsConfigRef = srOptions_ && srOptions_.hlsjsConfig || techOptions.hlsjsConfig
+    const hlsjsConfigRef = srOptions_?.hlsjsConfig || techOptions.hlsjsConfig
     // Hls.js will write to the reference thus change the object for later streams
     this.hlsjsConfig = hlsjsConfigRef ? this._oneLevelObjClone(hlsjsConfigRef) : {}
 
@@ -575,7 +577,7 @@ class Html5Hlsjs {
       this.hlsjsConfig.autoStartLoad = false
     }
 
-    const captionConfig = srOptions_ && srOptions_.captionConfig || techOptions.captionConfig
+    const captionConfig = srOptions_?.captionConfig || techOptions.captionConfig
     if (captionConfig) {
       this.hlsjsConfig.cueHandler = this._createCueHandler(captionConfig)
     }
index d0a4c4a3f361e1eb4019e359d78eb40860fc528d..f7f83a8a4f531a46c6377d3aa63db4f7161bed2d 100644 (file)
@@ -86,6 +86,7 @@ async function sha256Hex (data?: ArrayBuffer) {
 
   // Fallback for non HTTPS context
   const shaModule = (await import('sha.js') as any).default
+  // eslint-disable-next-line new-cap
   return new shaModule.sha256().update(Buffer.from(data)).digest('hex')
 }
 
@@ -97,7 +98,9 @@ function bufferToHex (buffer?: ArrayBuffer) {
   const h = '0123456789abcdef'
   const o = new Uint8Array(buffer)
 
-  o.forEach((v: any) => s += h[ v >> 4 ] + h[ v & 15 ])
+  o.forEach((v: any) => {
+    s += h[v >> 4] + h[v & 15]
+  })
 
   return s
 }
index c45e8f53edcde393eb629f555c93f265a6bb998c..f3c21fc4c13d8a4320f23682b314f8ffabcc02ca 100644 (file)
@@ -435,8 +435,6 @@ export class PeertubePlayerManager {
     const p2pMediaLoaderOptions = options.p2pMediaLoader
 
     const autoplay = this.getAutoPlayValue(commonOptions.autoplay) === 'play'
-      ? true
-      : false
 
     const webtorrent = {
       autoplay,
@@ -459,10 +457,10 @@ export class PeertubePlayerManager {
     theaterButton: boolean
     captions: boolean
 
-    nextVideo?: Function
+    nextVideo?: () => void
     hasNextVideo?: () => boolean
 
-    previousVideo?: Function
+    previousVideo?: () => void
     hasPreviousVideo?: () => boolean
   }) {
     const settingEntries = []
@@ -487,7 +485,7 @@ export class PeertubePlayerManager {
       }
 
       Object.assign(children, {
-        'previousVideoButton': buttonOptions
+        previousVideoButton: buttonOptions
       })
     }
 
@@ -505,35 +503,35 @@ export class PeertubePlayerManager {
       }
 
       Object.assign(children, {
-        'nextVideoButton': buttonOptions
+        nextVideoButton: buttonOptions
       })
     }
 
     Object.assign(children, {
-      'currentTimeDisplay': {},
-      'timeDivider': {},
-      'durationDisplay': {},
-      'liveDisplay': {},
+      currentTimeDisplay: {},
+      timeDivider: {},
+      durationDisplay: {},
+      liveDisplay: {},
 
-      'flexibleWidthSpacer': {},
-      'progressControl': {
+      flexibleWidthSpacer: {},
+      progressControl: {
         children: {
-          'seekBar': {
+          seekBar: {
             children: {
               [loadProgressBar]: {},
-              'mouseTimeDisplay': {},
-              'playProgressBar': {}
+              mouseTimeDisplay: {},
+              playProgressBar: {}
             }
           }
         }
       },
 
-      'p2PInfoButton': {},
+      p2PInfoButton: {},
 
-      'muteToggle': {},
-      'volumeControl': {},
+      muteToggle: {},
+      volumeControl: {},
 
-      'settingsButton': {
+      settingsButton: {
         setup: {
           maxHeightOffset: 40
         },
@@ -543,18 +541,18 @@ export class PeertubePlayerManager {
 
     if (options.peertubeLink === true) {
       Object.assign(children, {
-        'peerTubeLinkButton': { shortUUID: options.videoShortUUID } as PeerTubeLinkButtonOptions
+        peerTubeLinkButton: { shortUUID: options.videoShortUUID } as PeerTubeLinkButtonOptions
       })
     }
 
     if (options.theaterButton === true) {
       Object.assign(children, {
-        'theaterButton': {}
+        theaterButton: {}
       })
     }
 
     Object.assign(children, {
-      'fullscreenToggle': {}
+      fullscreenToggle: {}
     })
 
     return children
index ade8e2ee4bce6b207f379d705183e3cf6edcd1fc..b4841b235482513a25df8d52063699144949a8cb 100644 (file)
@@ -211,7 +211,7 @@ class PeerTubePlugin extends Plugin {
     const body = new URLSearchParams()
     body.append('currentTime', currentTime.toString())
 
-    const headers = new Headers({ 'Authorization': authorizationHeader })
+    const headers = new Headers({ Authorization: authorizationHeader })
 
     return fetch(url, { method: 'PUT', body, headers })
   }
index f0eb129d4153df394a56455544e866769d19d150..97828c8024931788f18a16fd2a6b234d8bcd80cb 100644 (file)
@@ -1,3 +1,6 @@
+// FIXME: lint
+/* eslint-disable @typescript-eslint/ban-types */
+
 import { HlsConfig, Level } from 'hls.js'
 import videojs from 'video.js'
 import { VideoFile, VideoPlaylist, VideoPlaylistElement } from '@shared/models'
@@ -93,7 +96,7 @@ type VideoJSCaption = {
 }
 
 type UserWatching = {
-  url: string,
+  url: string
   authorizationHeader: string
 }
 
@@ -166,7 +169,7 @@ type VideoJSPluginOptions = {
 }
 
 type LoadedQualityData = {
-  qualitySwitchCallback: Function,
+  qualitySwitchCallback: (resolutionId: number, type: 'video') => void
   qualityData: {
     video: {
       id: number
@@ -177,7 +180,7 @@ type LoadedQualityData = {
 }
 
 type ResolutionUpdateData = {
-  auto: boolean,
+  auto: boolean
   resolutionId: number
   id?: number
 }
index b271d052660fb129039ef5c82037a2e84e7e55df..a32f6fb979ad7d6b32bc8413588f925abada361c 100644 (file)
@@ -39,10 +39,6 @@ class StatsCard extends Component {
   intervalMs = 300
   playerNetworkInfo: PlayerNetworkInfo = {}
 
-  constructor (player: videojs.Player, options: StatsCardOptions) {
-    super(player, options)
-  }
-
   createEl () {
     const container = super.createEl('div', {
       className: 'vjs-stats-content',
@@ -81,9 +77,8 @@ class StatsCard extends Component {
   }
 
   toggle () {
-    this.updateInterval
-      ? this.hide()
-      : this.show()
+    if (this.updateInterval) this.hide()
+    else this.show()
   }
 
   show () {
index d5a09a31a71bfc71ee8c4088533a0d66a27e7220..8a6e67dda088355a027148f23863861d378605f6 100644 (file)
@@ -23,13 +23,13 @@ export class TranslationsManager {
 
     let p: Promise<any>
 
-    if (TranslationsManager.videojsLocaleCache[ path ]) {
-      p = Promise.resolve(TranslationsManager.videojsLocaleCache[ path ])
+    if (TranslationsManager.videojsLocaleCache[path]) {
+      p = Promise.resolve(TranslationsManager.videojsLocaleCache[path])
     } else {
       p = fetch(path + '/player.json')
         .then(res => res.json())
         .then(json => {
-          TranslationsManager.videojsLocaleCache[ path ] = json
+          TranslationsManager.videojsLocaleCache[path] = json
           return json
         })
         .catch(err => {
index 8fabfc3fdfd03de2e21f9a663922496690c0b3f2..61668e40790d5fdc82694b11b09987661204e535 100644 (file)
@@ -9,7 +9,9 @@ function getMainTemplate (options: any) {
     <div class="vjs-upnext-autoplay-icon">
       <svg height="100%" version="1.1" viewbox="0 0 98 98" width="100%">
         <circle class="vjs-upnext-svg-autoplay-circle" cx="49" cy="49" fill="#000" fill-opacity="0.8" r="48"></circle>
-        <circle class="vjs-upnext-svg-autoplay-ring" cx="-49" cy="49" fill-opacity="0" r="46.5" stroke="#FFFFFF" stroke-width="4" transform="rotate(-90)"></circle>
+        <circle class="vjs-upnext-svg-autoplay-ring" cx="-49" cy="49" fill-opacity="0" r="46.5"
+                stroke="#FFFFFF" stroke-width="4" transform="rotate(-90)"
+        ></circle>
         <polygon class="vjs-upnext-svg-autoplay-triangle" fill="#fff" points="32,27 72,49 32,71"></polygon></svg>
     </div>
     <span class="vjs-upnext-bottom">
@@ -22,7 +24,7 @@ function getMainTemplate (options: any) {
 }
 
 export interface EndCardOptions extends videojs.ComponentOptions {
-  next: Function,
+  next: () => void
   getTitle: () => string
   timeout: number
   cancelText: string
@@ -99,11 +101,11 @@ class EndCard extends Component {
     return container
   }
 
-  showCard (cb: Function) {
+  showCard (cb: (value: boolean) => void) {
     let timeout: any
 
-    this.autoplayRing.setAttribute('stroke-dasharray', '' + this.dashOffsetStart)
-    this.autoplayRing.setAttribute('stroke-dashoffset', '' + -this.dashOffsetStart)
+    this.autoplayRing.setAttribute('stroke-dasharray', `${this.dashOffsetStart}`)
+    this.autoplayRing.setAttribute('stroke-dashoffset', `${-this.dashOffsetStart}`)
 
     this.title.innerHTML = this.options_.getTitle()
 
@@ -123,7 +125,7 @@ class EndCard extends Component {
     })
 
     const goToPercent = (percent: number) => {
-      const newOffset = Math.max(-this.dashOffsetTotal, - this.dashOffsetStart - percent * this.dashOffsetTotal / 2 / 100)
+      const newOffset = Math.max(-this.dashOffsetTotal, -this.dashOffsetStart - percent * this.dashOffsetTotal / 2 / 100)
       this.autoplayRing.setAttribute('stroke-dashoffset', '' + newOffset)
     }
 
index f0a1b1aee246c7e5141a30a139e8560ad36d475e..f2e9adb14266136811a6ec7c84ac7f38cf112c57 100644 (file)
@@ -17,7 +17,7 @@ function isIOS () {
   // Detect iPad Desktop mode
   return !!(navigator.maxTouchPoints &&
       navigator.maxTouchPoints > 2 &&
-      /MacIntel/.test(navigator.platform))
+      navigator.platform.includes('MacIntel'))
 }
 
 function isSafari () {
index 81f9544f4476c22c4b7055b3170000e635d97447..07ed18989cd0f7f98316c42d6f656ea710570b0d 100644 (file)
@@ -96,11 +96,11 @@ class P2pInfoButton extends Button {
       }
       subDivWebtorrent.title += this.player().localize('Total uploaded: ') + totalUploaded.join(' ')
 
-      downloadSpeedNumber.textContent = downloadSpeed[ 0 ]
-      downloadSpeedUnit.textContent = ' ' + downloadSpeed[ 1 ]
+      downloadSpeedNumber.textContent = downloadSpeed[0]
+      downloadSpeedUnit.textContent = ' ' + downloadSpeed[1]
 
-      uploadSpeedNumber.textContent = uploadSpeed[ 0 ]
-      uploadSpeedUnit.textContent = ' ' + uploadSpeed[ 1 ]
+      uploadSpeedNumber.textContent = uploadSpeed[0]
+      uploadSpeedUnit.textContent = ' ' + uploadSpeed[1]
 
       peersNumber.textContent = numPeers.toString()
       peersText.textContent = ' ' + (numPeers > 1 ? this.player().localize('peers') : this.player_.localize('peer'))
index 73ad47d2b623addd18abfbe943d0f9ca298df248..c1f502600fb9389d52028bcc81859a0321219477 100644 (file)
@@ -6,7 +6,7 @@ const MenuItem = videojs.getComponent('MenuItem')
 export interface ResolutionMenuItemOptions extends videojs.MenuItemOptions {
   labels?: { [id: number]: string }
   id: number
-  callback: Function
+  callback: (resolutionId: number, type: 'video') => void
 }
 
 class ResolutionMenuItem extends MenuItem {
@@ -14,7 +14,7 @@ class ResolutionMenuItem extends MenuItem {
   private readonly label: string
   // Only used for the automatic item
   private readonly labels: { [id: number]: string }
-  private readonly callback: Function
+  private readonly callback: (resolutionId: number, type: 'video') => void
 
   private autoResolutionPossible: boolean
   private currentResolutionLabel: string
index 41911e7e8109c61115423029adfac9289214dacd..8cd98967f5a9ad6417095a3469ad5710aa0ce675 100644 (file)
@@ -12,8 +12,6 @@ class SettingsDialog extends Component {
   /**
    * Create the component's DOM element
    *
-   * @return {Element}
-   * @method createEl
    */
   createEl () {
     const uniqueId = this.id()
@@ -25,7 +23,7 @@ class SettingsDialog extends Component {
       innerHTML: '',
       tabIndex: -1
     }, {
-      'role': 'dialog',
+      role: 'dialog',
       'aria-labelledby': dialogLabelId,
       'aria-describedby': dialogDescriptionId
     })
index f1342f1792ed5a2da9f4835df5a37d6cf81d1002..1871d41f8c2eecb750d9f41a1a01d782c75742ec 100644 (file)
@@ -1,8 +1,8 @@
+import videojs from 'video.js'
 // Thanks to Yanko Shterev: https://github.com/yshterev/videojs-settings-menu
 import { toTitleCase } from '../utils'
-import videojs from 'video.js'
-import { SettingsButton } from './settings-menu-button'
 import { SettingsDialog } from './settings-dialog'
+import { SettingsButton } from './settings-menu-button'
 import { SettingsPanel } from './settings-panel'
 import { SettingsPanelChild } from './settings-panel-child'
 
@@ -57,7 +57,7 @@ class SettingsMenuItem extends MenuItem {
     const newOptions = Object.assign({}, options, { entry: options.menuButton, menuButton: this })
 
     this.subMenu = new SubMenuComponent(this.player(), newOptions) as any // FIXME: typings
-    const subMenuClass = this.subMenu.buildCSSClass().split(' ')[ 0 ]
+    const subMenuClass = this.subMenu.buildCSSClass().split(' ')[0]
     this.settingsSubMenuEl_.className += ' ' + subMenuClass
 
     this.eventHandlers()
@@ -104,7 +104,7 @@ class SettingsMenuItem extends MenuItem {
       target = event.currentTarget
     }
 
-    if (target && target.classList.contains('vjs-back-button')) {
+    if (target?.classList.contains('vjs-back-button')) {
       this.loadMainMenu()
       return
     }
@@ -121,8 +121,6 @@ class SettingsMenuItem extends MenuItem {
   /**
    * Create the component's DOM element
    *
-   * @return {Element}
-   * @method createEl
    */
   createEl () {
     const el = videojs.dom.createEl('li', {
@@ -198,14 +196,14 @@ class SettingsMenuItem extends MenuItem {
     const prefix = [ 'webkit', 'moz', 'MS', 'o', '' ]
 
     for (let p = 0; p < prefix.length; p++) {
-      if (!prefix[ p ]) {
+      if (!prefix[p]) {
         type = type.toLowerCase()
       }
 
       if (action === 'addEvent') {
-        element.addEventListener(prefix[ p ] + type, callback, false)
+        element.addEventListener(prefix[p] + type, callback, false)
       } else if (action === 'removeEvent') {
-        element.removeEventListener(prefix[ p ] + type, callback, false)
+        element.removeEventListener(prefix[p] + type, callback, false)
       }
     }
   }
@@ -292,7 +290,10 @@ class SettingsMenuItem extends MenuItem {
     // Thus we get the submenu value based on the labelEl of playbackRateMenuButton
     if (subMenu === 'PlaybackRateMenuButton') {
       const html = (this.subMenu as any).labelEl_.innerHTML
-      setTimeout(() => this.settingsSubMenuValueEl_.innerHTML = html, 250)
+
+      setTimeout(() => {
+        this.settingsSubMenuValueEl_.innerHTML = html
+      }, 250)
     } else {
       // Loop trough the submenu items to find the selected child
       for (const subMenuItem of this.subMenu.menu.children_) {
index d1582412c6e49006a35c35aba0eb613d62027fb6..161420c38b9f8b012147d0f409c73bb5e68f16f3 100644 (file)
@@ -4,10 +4,6 @@ const Component = videojs.getComponent('Component')
 
 class SettingsPanelChild extends Component {
 
-  constructor (player: videojs.Player, options?: videojs.ComponentOptions) {
-    super(player, options)
-  }
-
   createEl () {
     return super.createEl('div', {
       className: 'vjs-settings-panel-child',
index 1ad8bb1fc1de76a03aeeb1d2a86c7facaaf408b1..28b579bdd27b1f87bc6e2f147b0452a6d2cbd5cc 100644 (file)
@@ -4,10 +4,6 @@ const Component = videojs.getComponent('Component')
 
 class SettingsPanel extends Component {
 
-  constructor (player: videojs.Player, options?: videojs.ComponentOptions) {
-    super(player, options)
-  }
-
   createEl () {
     return super.createEl('div', {
       className: 'vjs-settings-panel',
index 66762bef8a8b1530c28977bb968ef80490e0c74a..93ca8e1d8486ba1713112a012d32dbc04f4a2980 100644 (file)
@@ -36,7 +36,7 @@ export class PeertubeChunkStore extends EventEmitter {
 
   chunkLength: number
 
-  private pendingPut: { id: number, buf: Buffer, cb: Function }[] = []
+  private pendingPut: { id: number, buf: Buffer, cb: (err?: Error) => void }[] = []
   // If the store is full
   private memoryChunks: { [ id: number ]: Buffer | true } = {}
   private databaseName: string
@@ -54,7 +54,7 @@ export class PeertubeChunkStore extends EventEmitter {
     this.databaseName = 'webtorrent-chunks-'
 
     if (!opts) opts = {}
-    if (opts.torrent && opts.torrent.infoHash) this.databaseName += opts.torrent.infoHash
+    if (opts.torrent?.infoHash) this.databaseName += opts.torrent.infoHash
     else this.databaseName += '-default'
 
     this.setMaxListeners(100)
@@ -106,7 +106,9 @@ export class PeertubeChunkStore extends EventEmitter {
       } catch (err) {
         console.log('Cannot bulk insert chunks. Store them in memory.', { err })
 
-        processing.forEach(p => this.memoryChunks[ p.id ] = p.buf)
+        processing.forEach(p => {
+          this.memoryChunks[p.id] = p.buf
+        })
       } finally {
         processing.forEach(p => p.cb())
       }
index c3cbea7256fba1ac5ab75a035435354db446d518..8fb3e9672dc1e0c80280c8aea03e4da2dbecd7ce 100644 (file)
@@ -3,7 +3,7 @@
 
 const MediaElementWrapper = require('mediasource')
 import { extname } from 'path'
-const videostream = require('videostream')
+const Videostream = require('videostream')
 
 const VIDEOSTREAM_EXTS = [
   '.m4a',
@@ -34,7 +34,7 @@ function renderMedia (file: any, elem: HTMLVideoElement, opts: RenderMediaOption
   let renderer: any
 
   try {
-    if (VIDEOSTREAM_EXTS.indexOf(extension) >= 0) {
+    if (VIDEOSTREAM_EXTS.includes(extension)) {
       renderer = useVideostream()
     } else {
       renderer = useMediaSource()
@@ -51,7 +51,7 @@ function renderMedia (file: any, elem: HTMLVideoElement, opts: RenderMediaOption
       return callback(err)
     })
     preparedElem.addEventListener('loadstart', onLoadStart)
-    return new videostream(file, preparedElem)
+    return new Videostream(file, preparedElem)
   }
 
   function useMediaSource (useVP9 = false) {
@@ -62,7 +62,7 @@ function renderMedia (file: any, elem: HTMLVideoElement, opts: RenderMediaOption
       preparedElem.removeEventListener('error', onError)
 
       // Try with vp9 before returning an error
-      if (codecs.indexOf('vp8') !== -1) return fallbackToMediaSource(true)
+      if (codecs.includes('vp8')) return fallbackToMediaSource(true)
 
       return callback(err)
     })
index 17d369c10ff1944f6db9cdd071a8fde8e1f36d38..0587ddee610a55857b3788fec6ec616b57def0e3 100644 (file)
@@ -17,8 +17,8 @@ import { renderVideo } from './video-renderer'
 const CacheChunkStore = require('cache-chunk-store')
 
 type PlayOptions = {
-  forcePlay?: boolean,
-  seek?: number,
+  forcePlay?: boolean
+  seek?: number
   delay?: number
 }
 
@@ -126,8 +126,8 @@ class WebTorrentPlugin extends Plugin {
   updateVideoFile (
     videoFile?: VideoFile,
     options: {
-      forcePlay?: boolean,
-      seek?: number,
+      forcePlay?: boolean
+      seek?: number
       delay?: number
     } = {},
     done: () => void = () => { /* empty */ }
@@ -248,7 +248,7 @@ class WebTorrentPlugin extends Plugin {
     magnetOrTorrentUrl: string,
     previousVideoFile: VideoFile,
     options: PlayOptions,
-    done: Function
+    done: (err?: Error) => void
   ) {
     if (!magnetOrTorrentUrl) return this.fallbackToHttp(options, done)
 
@@ -272,7 +272,7 @@ class WebTorrentPlugin extends Plugin {
         this.stopTorrent(oldTorrent)
 
         // We use a fake renderer so we download correct pieces of the next file
-        if (options.delay) this.renderFileInFakeElement(torrent.files[ 0 ], options.delay)
+        if (options.delay) this.renderFileInFakeElement(torrent.files[0], options.delay)
       }
 
       // Render the video in a few seconds? (on resolution change for example, we wait some seconds of the new video resolution)
@@ -288,7 +288,7 @@ class WebTorrentPlugin extends Plugin {
         if (options.seek) this.player.currentTime(options.seek)
 
         const renderVideoOptions = { autoplay: false, controls: true }
-        renderVideo(torrent.files[ 0 ], this.playerElement, renderVideoOptions, (err, renderer) => {
+        renderVideo(torrent.files[0], this.playerElement, renderVideoOptions, (err, renderer) => {
           this.renderer = renderer
 
           if (err) return this.fallbackToHttp(options, done)
@@ -321,7 +321,7 @@ class WebTorrentPlugin extends Plugin {
       if (err.message.indexOf('incorrect info hash') !== -1) {
         console.error('Incorrect info hash detected, falling back to torrent file.')
         const newOptions = { forcePlay: true, seek: options.seek }
-        return this.addTorrent(this.torrent[ 'xs' ], previousVideoFile, newOptions, done)
+        return this.addTorrent(this.torrent['xs'], previousVideoFile, newOptions, done)
       }
 
       // Remote instance is down
@@ -340,7 +340,7 @@ class WebTorrentPlugin extends Plugin {
     if (playPromise !== undefined) {
       return playPromise.then(() => done())
                         .catch((err: Error) => {
-                          if (err.message.indexOf('The play() request was interrupted by a call to pause()') !== -1) {
+                          if (err.message.includes('The play() request was interrupted by a call to pause()')) {
                             return
                           }
 
@@ -479,7 +479,7 @@ class WebTorrentPlugin extends Plugin {
   }
 
   private isPlayerWaiting () {
-    return this.player && this.player.hasClass('vjs-waiting')
+    return this.player?.hasClass('vjs-waiting')
   }
 
   private runTorrentInfoScheduler () {
@@ -513,7 +513,7 @@ class WebTorrentPlugin extends Plugin {
     }, this.CONSTANTS.INFO_SCHEDULER)
   }
 
-  private fallbackToHttp (options: PlayOptions, done?: Function) {
+  private fallbackToHttp (options: PlayOptions, done?: (err?: Error) => void) {
     const paused = this.player.paused()
 
     this.disableAutoResolution(true)
@@ -565,7 +565,7 @@ class WebTorrentPlugin extends Plugin {
   private stopTorrent (torrent: WebTorrent.Torrent) {
     torrent.pause()
     // Pause does not remove actual peers (in particular the webseed peer)
-    torrent.removePeer(torrent[ 'ws' ])
+    torrent.removePeer(torrent['ws'])
   }
 
   private renderFileInFakeElement (file: WebTorrent.TorrentFile, delay: number) {
index d862de8d8de7c69c53f43c65ec3fbf4e2d983df3..bbba3f05cd7026a76a081b36fc7d67d48b0d7343 100644 (file)
@@ -57,7 +57,7 @@ import 'core-js/es/object'
 /***************************************************************************************************
  * Zone JS is required by default for Angular itself.
  */
-// tslint:disable
+/* eslint-disable */
 import 'zone.js'  // Included with Angular CLI.
 
 /***************************************************************************************************
index ec8b55faae8e3db872e43f3ef2c724ca18756a71..bda786cc60ee341b16c007b873ebcedf821161c7 100644 (file)
@@ -1,4 +1,4 @@
-const dictionary: Array<{ max: number; type: string }> = [
+const dictionary: Array<{ max: number, type: string }> = [
   { max: 1024, type: 'B' },
   { max: 1048576, type: 'KB' },
   { max: 1073741824, type: 'MB' },
index d4cad8a20aa74f6c25ab24ede76117292c590691..68a2462de04837d5fa14ec080daa8397d4e5b87f 100644 (file)
@@ -5,7 +5,7 @@ const valuesMap = new Map()
 function proxify (instance: MemoryStorage) {
   return new Proxy(instance, {
     set: function (obj, prop: string | symbol, value) {
-      if (MemoryStorage.prototype.hasOwnProperty(prop)) {
+      if (Object.prototype.hasOwnProperty.call(MemoryStorage, prop)) {
         // FIXME: symbol typing issue https://github.com/microsoft/TypeScript/issues/1863
         instance[prop as any] = value
       } else {
@@ -14,7 +14,7 @@ function proxify (instance: MemoryStorage) {
       return true
     },
     get: function (target, name: string | symbol | number) {
-      if (MemoryStorage.prototype.hasOwnProperty(name)) {
+      if (Object.prototype.hasOwnProperty.call(MemoryStorage, name)) {
         // FIXME: symbol typing issue https://github.com/microsoft/TypeScript/issues/1863
         return instance[name as any]
       }
index d14ac4acd6728a120baaa3c9deb386da31fa766b..f1687d91d1c1f6c9c0b98f4d4157387e32717a9c 100644 (file)
@@ -155,7 +155,7 @@ class PluginsManager {
     try {
       if (!isReload) this.loadedScopes.push(scope)
 
-      const toLoad = this.scopes[ scope ]
+      const toLoad = this.scopes[scope]
       if (!Array.isArray(toLoad)) {
         this.loadingScopes[scope] = false
         this.pluginsLoaded[scope].next(true)
@@ -168,11 +168,11 @@ class PluginsManager {
       for (const pluginInfo of toLoad) {
         const clientScript = pluginInfo.clientScript
 
-        if (this.loadedScripts[ clientScript.script ]) continue
+        if (this.loadedScripts[clientScript.script]) continue
 
         promises.push(this.loadPlugin(pluginInfo))
 
-        this.loadedScripts[ clientScript.script ] = true
+        this.loadedScripts[clientScript.script] = true
       }
 
       await Promise.all(promises)
index cc5203ed534ce93c01197a3217b1f8c4c1c27b1a..495f1a98ce2b6877d7824bb3e49a16e7fc58a064 100644 (file)
@@ -21,5 +21,5 @@ export type PeerTubeTextTrack = {
   id: string
   label: string
   src: string
-  mode: 'showing' | 'disabled'
+  mode: TextTrackMode
 }
index 28a13c727b0b3f4c2f24ea03f93c60c4c0fab395..7a8e9dbec0e243e64c420f58b1fe6355a048b6b8 100644 (file)
@@ -1,7 +1,7 @@
 import { EventHandler } from './definitions'
 
 interface PlayerEventRegistrar {
-  registrations: Function[]
+  registrations: EventHandler<any>[]
 }
 
 interface PlayerEventRegistrationMap {
@@ -20,28 +20,28 @@ export class EventRegistrar {
 
   public registerTypes (names: string[]) {
     for (const name of names) {
-      this.eventRegistrations[ name ] = { registrations: [] }
+      this.eventRegistrations[name] = { registrations: [] }
     }
   }
 
   public fire<T> (name: string, event: T) {
-    this.eventRegistrations[ name ].registrations.forEach(x => x(event))
+    this.eventRegistrations[name].registrations.forEach(x => x(event))
   }
 
   public addListener<T> (name: string, handler: EventHandler<T>) {
-    if (!this.eventRegistrations[ name ]) {
+    if (!this.eventRegistrations[name]) {
       console.warn(`PeerTube: addEventListener(): The event '${name}' is not supported`)
       return false
     }
 
-    this.eventRegistrations[ name ].registrations.push(handler)
+    this.eventRegistrations[name].registrations.push(handler)
     return true
   }
 
   public removeListener<T> (name: string, handler: EventHandler<T>) {
-    if (!this.eventRegistrations[ name ]) return false
+    if (!this.eventRegistrations[name]) return false
 
-    this.eventRegistrations[ name ].registrations = this.eventRegistrations[ name ].registrations.filter(x => x === handler)
+    this.eventRegistrations[name].registrations = this.eventRegistrations[name].registrations.filter(x => x === handler)
 
     return true
   }
index 9776fda120297fbfd21966203cfd66f5eb65aeb1..bbe37a42b6bcd0ea65d8ca9dd52293817c5657de 100644 (file)
@@ -17,7 +17,7 @@ const PASSTHROUGH_EVENTS = [
  */
 export class PeerTubePlayer {
 
-  private eventRegistrar: EventRegistrar = new EventRegistrar()
+  private readonly eventRegistrar: EventRegistrar = new EventRegistrar()
   private channel: Channel.MessagingChannel
   private readyPromise: Promise<void>
 
@@ -31,8 +31,8 @@ export class PeerTubePlayer {
    * @param scope
    */
   constructor (
-    private embedElement: HTMLIFrameElement,
-    private scope?: string
+    private readonly embedElement: HTMLIFrameElement,
+    private readonly scope?: string
   ) {
     this.eventRegistrar.registerTypes(PASSTHROUGH_EVENTS)
 
@@ -90,6 +90,7 @@ export class PeerTubePlayer {
 
   /**
    * Tell the embed to change the audio volume
+   *
    * @param value A number from 0 to 1
    */
   async setVolume (value: number) {
@@ -98,14 +99,16 @@ export class PeerTubePlayer {
 
   /**
    * Get the current volume level in the embed.
+   *
    * @param value A number from 0 to 1
    */
   async getVolume (): Promise<number> {
-    return this.sendMessage<void, number>('getVolume')
+    return this.sendMessage<undefined, number>('getVolume')
   }
 
   /**
    * Tell the embed to change the current caption
+   *
    * @param value Caption id
    */
   async setCaption (value: string) {
@@ -116,11 +119,12 @@ export class PeerTubePlayer {
    * Get video captions
    */
   async getCaptions (): Promise<PeerTubeTextTrack[]> {
-    return this.sendMessage<void, PeerTubeTextTrack[]>('getCaptions')
+    return this.sendMessage<undefined, PeerTubeTextTrack[]>('getCaptions')
   }
 
   /**
    * Tell the embed to seek to a specific position (in seconds)
+   *
    * @param seconds
    */
   async seek (seconds: number) {
@@ -143,21 +147,21 @@ export class PeerTubePlayer {
    * resolutions change.
    */
   async getResolutions (): Promise<PeerTubeResolution[]> {
-    return this.sendMessage<void, PeerTubeResolution[]>('getResolutions')
+    return this.sendMessage<undefined, PeerTubeResolution[]>('getResolutions')
   }
 
   /**
    * Retrieve a list of available playback rates.
    */
   async getPlaybackRates (): Promise<number[]> {
-    return this.sendMessage<void, number[]>('getPlaybackRates')
+    return this.sendMessage<undefined, number[]>('getPlaybackRates')
   }
 
   /**
    * Get the current playback rate. Defaults to 1 (1x playback rate).
    */
   async getPlaybackRate (): Promise<number> {
-    return this.sendMessage<void, number>('getPlaybackRate')
+    return this.sendMessage<undefined, number>('getPlaybackRate')
   }
 
   /**
@@ -188,7 +192,7 @@ export class PeerTubePlayer {
    * Get video position currently played (starts from 1)
    */
   async getCurrentPosition () {
-    return this.sendMessage<void, number>('getCurrentPosition')
+    return this.sendMessage<undefined, number>('getCurrentPosition')
   }
 
   private constructChannel () {
@@ -201,8 +205,8 @@ export class PeerTubePlayer {
   }
 
   private prepareToBeReady () {
-    let readyResolve: Function
-    let readyReject: Function
+    let readyResolve: () => void
+    let readyReject: () => void
 
     this.readyPromise = new Promise<void>((res, rej) => {
       readyResolve = res
@@ -219,7 +223,8 @@ export class PeerTubePlayer {
   private sendMessage<TIn, TOut> (method: string, params?: TIn): Promise<TOut> {
     return new Promise<TOut>((resolve, reject) => {
       this.channel.call({
-        method, params,
+        method,
+        params,
         success: result => resolve(result),
         error: error => reject(error)
       })
@@ -228,4 +233,4 @@ export class PeerTubePlayer {
 }
 
 // put it on the window as well as the export
-(window[ 'PeerTubePlayer' ] as any) = PeerTubePlayer
+(window['PeerTubePlayer'] as any) = PeerTubePlayer
index 75174f2f8ef2a6eca1362ab52db7d3e7b8234001..b5c9da431e6f13cf056d75e20be2f03a3c0a3fbc 100644 (file)
@@ -13,7 +13,8 @@ export class PeerTubeEmbedApi {
   private isReady = false
   private resolutions: PeerTubeResolution[] = []
 
-  constructor (private embed: PeerTubeEmbed) {
+  constructor (private readonly embed: PeerTubeEmbed) {
+
   }
 
   initialize () {
@@ -45,7 +46,7 @@ export class PeerTubeEmbedApi {
     channel.bind('getResolutions', (txn, params) => this.resolutions)
 
     channel.bind('getCaptions', (txn, params) => this.getCaptions())
-    channel.bind('setCaption', (txn, id) => this.setCaption(id)),
+    channel.bind('setCaption', (txn, id) => this.setCaption(id))
 
     channel.bind('setPlaybackRate', (txn, playbackRate) => this.embed.player.playbackRate(playbackRate))
     channel.bind('getPlaybackRate', (txn, params) => this.embed.player.playbackRate())
@@ -79,14 +80,12 @@ export class PeerTubeEmbedApi {
   }
 
   private getCaptions (): PeerTubeTextTrack[] {
-    return this.embed.player.textTracks().tracks_.map(t => {
-      return {
-        id: t.id,
-        src: t.src,
-        label: t.label,
-        mode: t.mode as any
-      }
-    })
+    return this.embed.player.textTracks().tracks_.map(t => ({
+      id: t.id,
+      src: t.src,
+      label: t.label,
+      mode: t.mode
+    }))
   }
 
   private setCaption (id: string) {
index c9a4e541c1d088ff26e09eed0f26d74efec362c9..dad717108e1b83ddc5185c104f1ec14f9eabec0d 100644 (file)
@@ -64,17 +64,11 @@ export class PeerTubeEmbed {
   private playlistElements: VideoPlaylistElement[]
   private currentPlaylistElement: VideoPlaylistElement
 
-  private wrapperElement: HTMLElement
+  private readonly wrapperElement: HTMLElement
 
   private pluginsManager: PluginsManager
 
-  static async main () {
-    const videoContainerId = 'video-wrapper'
-    const embed = new PeerTubeEmbed(videoContainerId)
-    await embed.init()
-  }
-
-  constructor (private videoWrapperId: string) {
+  constructor (private readonly videoWrapperId: string) {
     this.wrapperElement = document.getElementById(this.videoWrapperId)
 
     try {
@@ -84,6 +78,12 @@ export class PeerTubeEmbed {
     }
   }
 
+  static async main () {
+    const videoContainerId = 'video-wrapper'
+    const embed = new PeerTubeEmbed(videoContainerId)
+    await embed.init()
+  }
+
   getVideoUrl (id: string) {
     return window.location.origin + '/api/v1/videos/' + id
   }
@@ -316,7 +316,7 @@ export class PeerTubeEmbed {
     while (total > elements.length && i < 10) {
       const result = await this.loadPlaylistElements(playlistId, elements.length)
 
-      const json = await result.json() as ResultList<VideoPlaylistElement>
+      const json = await result.json()
       total = json.total
 
       elements = elements.concat(json.data)
@@ -469,7 +469,7 @@ export class PeerTubeEmbed {
     // Issue when we parsed config from HTML, fallback to API
     if (!this.config) {
       this.config = await this.refreshFetch('/api/v1/config')
-                              .then(res => res.json())
+        .then(res => res.json())
     }
 
     const videoInfoPromise = videoResponse.json()
@@ -506,7 +506,7 @@ export class PeerTubeEmbed {
           this.currentPlaylistElement = videoPlaylistElement
 
           this.loadVideoAndBuildPlayer(this.currentPlaylistElement.video.uuid)
-            .catch(err => console.error(err))
+              .catch(err => console.error(err))
         }
       }
       : undefined
@@ -542,7 +542,9 @@ export class PeerTubeEmbed {
         isLive: videoInfo.isLive,
 
         playerElement: this.playerElement,
-        onPlayerElementChange: (element: HTMLVideoElement) => this.playerElement = element,
+        onPlayerElementChange: (element: HTMLVideoElement) => {
+          this.playerElement = element
+        },
 
         videoDuration: videoInfo.duration,
         enableHotkeys: true,
@@ -577,10 +579,13 @@ export class PeerTubeEmbed {
       })
     }
 
-    this.player = await PeertubePlayerManager.initialize(this.mode, options, (player: videojs.Player) => this.player = player)
+    this.player = await PeertubePlayerManager.initialize(this.mode, options, (player: videojs.Player) => {
+      this.player = player
+    })
+
     this.player.on('customError', (event: any, data: any) => this.handleError(data.err, serverTranslations))
 
-    window[ 'videojsPlayer' ] = this.player
+    window['videojsPlayer'] = this.player
 
     this.buildCSS()
 
@@ -656,7 +661,7 @@ export class PeerTubeEmbed {
       this.player.dispose()
       this.playerElement = null
       this.displayError('This video is not available because the remote instance is not responding.', translations)
-      return
+
     }
   }
 
@@ -694,9 +699,9 @@ export class PeerTubeEmbed {
 
   private async buildCaptions (serverTranslations: any, captionsResponse: Response): Promise<VideoJSCaption[]> {
     if (captionsResponse.ok) {
-      const { data } = (await captionsResponse.json()) as ResultList<VideoCaption>
+      const { data } = await captionsResponse.json()
 
-      return data.map(c => ({
+      return data.map((c: VideoCaption) => ({
         label: peertubeTranslate(c.language.label, serverTranslations),
         language: c.language.id,
         src: window.location.origin + c.captionPath
@@ -733,7 +738,7 @@ export class PeerTubeEmbed {
 
   private getResourceId () {
     const urlParts = window.location.pathname.split('/')
-    return urlParts[ urlParts.length - 1 ]
+    return urlParts[urlParts.length - 1]
   }
 
   private isPlaylistEmbed () {
@@ -751,7 +756,7 @@ export class PeerTubeEmbed {
   }
 
   private buildPeerTubeHelpers (translations?: { [ id: string ]: string }): RegisterClientHelpers {
-    function unimplemented (): any {
+    const unimplemented = () => {
       throw new Error('This helper is not implemented in embed.')
     }
 
@@ -780,9 +785,7 @@ export class PeerTubeEmbed {
         enhancedMarkdownToHTML: unimplemented
       },
 
-      translate: (value: string) => {
-        return Promise.resolve(peertubeTranslate(value, translations))
-      }
+      translate: (value: string) => Promise.resolve(peertubeTranslate(value, translations))
     }
   }
 }
index 6e035c0c94d26dc49080f411d2f5bc0767440c8e..066b3e0246b68c8280877bb42d8003b30745eb24 100644 (file)
@@ -4,11 +4,11 @@ import { PeerTubePlayer } from '../player/player'
 
 window.addEventListener('load', async () => {
   const urlParts = window.location.href.split('/')
-  const lastPart = urlParts[ urlParts.length - 1 ]
+  const lastPart = urlParts[urlParts.length - 1]
 
   const isPlaylist = window.location.pathname.startsWith('/video-playlists/')
 
-  const elementId = lastPart.indexOf('?') === -1 ? lastPart : lastPart.split('?')[ 0 ]
+  const elementId = lastPart.indexOf('?') === -1 ? lastPart : lastPart.split('?')[0]
 
   const iframe = document.createElement('iframe')
   iframe.src = isPlaylist
@@ -18,14 +18,14 @@ window.addEventListener('load', async () => {
   const mainElement = document.querySelector('#host')
   mainElement.appendChild(iframe)
 
-  console.log(`Document finished loading.`)
+  console.log('Document finished loading.')
   const player = new PeerTubePlayer(document.querySelector('iframe'))
 
-  window[ 'player' ] = player
+  window['player'] = player
 
-  console.log(`Awaiting player ready...`)
+  console.log('Awaiting player ready...')
   await player.ready
-  console.log(`Player is ready.`)
+  console.log('Player is ready.')
 
   const monitoredEvents = [
     'pause',
@@ -39,7 +39,9 @@ window.addEventListener('load', async () => {
     console.log(`PLAYER: now listening for event '${e}'`)
 
     player.getCurrentPosition()
-      .then(position => document.getElementById('playlist-position').innerHTML = position + '')
+      .then(position => {
+        document.getElementById('playlist-position').innerHTML = position + ''
+      })
   })
 
   let playbackRates: number[] = []
@@ -105,7 +107,7 @@ window.addEventListener('load', async () => {
 
   updateCaptions()
 
-  const updateResolutions = ((resolutions: PeerTubeResolution[]) => {
+  const updateResolutions = (resolutions: PeerTubeResolution[]) => {
     const resolutionListEl = document.querySelector('#resolution-list')
     resolutionListEl.innerHTML = ''
 
@@ -126,7 +128,7 @@ window.addEventListener('load', async () => {
         resolutionListEl.appendChild(itemEl)
       }
     })
-  })
+  }
 
   player.getResolutions().then(
     resolutions => updateResolutions(resolutions))
index 59bcbc5ffc79fd9384683631314d5a5d05c50ff3..3415ef08fa4839555e58b8879046e20e878bcf72 100644 (file)
@@ -30,16 +30,16 @@ export type RegisterClientHelpers = {
   getServerConfig: () => Promise<ServerConfig>
 
   notifier: {
-    info: (text: string, title?: string, timeout?: number) => void,
-    error: (text: string, title?: string, timeout?: number) => void,
+    info: (text: string, title?: string, timeout?: number) => void
+    error: (text: string, title?: string, timeout?: number) => void
     success: (text: string, title?: string, timeout?: number) => void
   }
 
   showModal: (input: {
-    title: string,
-    content: string,
-    close?: boolean,
-    cancel?: { value: string, action?: () => void },
+    title: string
+    content: string
+    close?: boolean
+    cancel?: { value: string, action?: () => void }
     confirm?: { value: string, action?: () => void }
   }) => void
 
index 24473aedea331c43dc5a7f5af7bd63ac8c2b81b0..ad21fdedf335781cf15fd487107b7b2ba22a6537 100644 (file)
@@ -1,4 +1,5 @@
 /* SystemJS module definition */
+// eslint-disable-next-line no-var
 declare var module: NodeModule
 
 interface NodeModule {
index fdf4dc777662c6f4d09fed11b81db9dab481c033..56e7b68ee448be28e046b6027bfaef00753fbe55 100644 (file)
@@ -53,7 +53,8 @@
   ],
   "exclude": [
     "../node_modules",
-    "../server"
+    "../server",
+    "node_modules"
   ],
   "angularCompilerOptions": {
     "strictInjectionParameters": true,
diff --git a/client/tslint.json b/client/tslint.json
deleted file mode 100644 (file)
index d998318..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-{
-  "extends": [ "tslint-angular", "tslint-config-standard" ],
-  "rules": {
-    "deprecation": {
-      "severity": "warning"
-    },
-    "no-inferrable-types": true,
-    "eofline": true,
-    "max-line-length": [true, 140],
-    "no-floating-promises": false, // Memory issues
-    "await-promise": [true, "PromiseLike"],
-    "member-ordering": [true, {
-      "order": [
-        "public-static-field",
-        "private-static-field",
-        "public-instance-field",
-        "private-instance-field",
-        "public-constructor",
-        "private-constructor",
-        "public-instance-method",
-        "protected-instance-method",
-        "private-instance-method"
-        ]}
-    ],
-    "variable-name": [
-      true,
-      "ban-keywords",
-      "check-format",
-      "allow-leading-underscore",
-      "allow-pascal-case",
-      "allow-trailing-underscore"
-    ],
-
-    "no-shadowed-variable": false,
-    "no-bitwise": false,
-    "max-classes-per-file": false,
-    "interface-over-type-literal": false
-  }
-}
index b2120b31b60ce0da4c978f6703d45437892f910d..75b7a620e5282c323605de14107f42f74cb965c6 100644 (file)
     ora "5.4.1"
     rxjs "6.6.7"
 
+"@angular-eslint/builder@12.3.1":
+  version "12.3.1"
+  resolved "https://registry.yarnpkg.com/@angular-eslint/builder/-/builder-12.3.1.tgz#e0a6768a9c7ee23623b1e363a23e59a333b6dabe"
+  integrity sha512-Py7Y9n0evIWGgEIrnMaLvalE1+Nw9HoY+fcYLYd9POfY0cug8j7hbkoPQ6rM7GzNHVcVsqEi/8C4DoF7RXXNRg==
+  dependencies:
+    "@nrwl/devkit" ">= 12.0.0 < 13.0.0"
+
+"@angular-eslint/eslint-plugin-template@12.3.1":
+  version "12.3.1"
+  resolved "https://registry.yarnpkg.com/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-12.3.1.tgz#33260cd5356df5db7e27719443eabcf6c214e32e"
+  integrity sha512-pz+nO64ma/9Sp2aeRnQ+Vktt7Fo1Lay/J+CG//3TIc3lYsoCTj4h42P6yCcxxJ9b4N7SUxMAnchA8eE5mJS4Ug==
+  dependencies:
+    "@typescript-eslint/experimental-utils" "4.28.2"
+    aria-query "^4.2.2"
+    axobject-query "^2.2.0"
+
+"@angular-eslint/eslint-plugin@12.3.1":
+  version "12.3.1"
+  resolved "https://registry.yarnpkg.com/@angular-eslint/eslint-plugin/-/eslint-plugin-12.3.1.tgz#00ac44d5986d86113d65a5373746e1fc22fd8702"
+  integrity sha512-KBm27onYggRcusA/BxuSkDGpVnIs8yG4ARio8ZAhe0H2XIRJTzJZ7oIBBjugDau03AGX3VMG6wAXailjJvsywg==
+  dependencies:
+    "@typescript-eslint/experimental-utils" "4.28.2"
+
+"@angular-eslint/schematics@12.3.1":
+  version "12.3.1"
+  resolved "https://registry.yarnpkg.com/@angular-eslint/schematics/-/schematics-12.3.1.tgz#99060a64fd6dd36ffb4c5e254b85562f4d0150d9"
+  integrity sha512-r1yZaqyO0oJhKDIWio3gH9TWpWEN8bUpiftgkkR6Yyc4hKBbiR5N4RQo5aZ5bnGytdW8QP8zq2k1yYBWjhEX7A==
+  dependencies:
+    "@angular-eslint/eslint-plugin" "12.3.1"
+    "@angular-eslint/eslint-plugin-template" "12.3.1"
+    ignore "5.1.8"
+    strip-json-comments "3.1.1"
+    tmp "0.2.1"
+
+"@angular-eslint/template-parser@12.3.1":
+  version "12.3.1"
+  resolved "https://registry.yarnpkg.com/@angular-eslint/template-parser/-/template-parser-12.3.1.tgz#561fe1bd3fb4a4d75cc55366d27818bca22204c9"
+  integrity sha512-6DkXqTaVEHZdcN3isHQ2CDoTsKLuJ5C1SYEOuFzOU1Zp85SvjxO92v6gPkFPKk0iQNVRmQS2XcKef6weehUUGA==
+  dependencies:
+    eslint-scope "^5.1.0"
+
 "@angular/animations@^12.0.0":
   version "12.2.0"
   resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-12.2.0.tgz#85e4e738bd72011067836b63bb8fc51a44646a7e"
     tslib "^2.2.0"
     yargs "^17.0.0"
 
-"@angular/compiler@9.0.0":
-  version "9.0.0"
-  resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-9.0.0.tgz#87e0bef4c369b6cadae07e3a4295778fc93799d5"
-  integrity sha512-ctjwuntPfZZT2mNj2NDIVu51t9cvbhl/16epc5xEwyzyDt76pX9UgwvY+MbXrf/C/FWwdtmNtfP698BKI+9leQ==
-
 "@angular/compiler@^12.0.0":
   version "12.2.0"
   resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-12.2.0.tgz#d1585f7b7a67b8393eef2aa4cd3ef47c9d7fc3c0"
   dependencies:
     tslib "^2.2.0"
 
-"@angular/core@9.0.0":
-  version "9.0.0"
-  resolved "https://registry.yarnpkg.com/@angular/core/-/core-9.0.0.tgz#227dc53e1ac81824f998c6e76000b7efc522641e"
-  integrity sha512-6Pxgsrf0qF9iFFqmIcWmjJGkkCaCm6V5QNnxMy2KloO3SDq6QuMVRbN9RtC8Urmo25LP+eZ6ZgYqFYpdD8Hd9w==
-
 "@angular/core@^12.0.0":
   version "12.2.0"
   resolved "https://registry.yarnpkg.com/@angular/core/-/core-12.2.0.tgz#29869aec3f624f6d4e1b5eedc25475cd5235a2e8"
   resolved "https://registry.yarnpkg.com/@assemblyscript/loader/-/loader-0.10.1.tgz#70e45678f06c72fa2e350e8553ec4a4d72b92e06"
   integrity sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==
 
+"@babel/code-frame@7.12.11":
+  version "7.12.11"
+  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f"
+  integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==
+  dependencies:
+    "@babel/highlight" "^7.10.4"
+
 "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.14.5", "@babel/code-frame@^7.8.3":
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb"
     "@babel/traverse" "^7.14.8"
     "@babel/types" "^7.14.8"
 
-"@babel/highlight@^7.14.5":
+"@babel/highlight@^7.10.4", "@babel/highlight@^7.14.5":
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9"
   integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==
     "@babel/types" "^7.4.4"
     esutils "^2.0.2"
 
+"@babel/runtime-corejs3@^7.10.2":
+  version "7.15.3"
+  resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.15.3.tgz#28754263988198f2a928c09733ade2fb4d28089d"
+  integrity sha512-30A3lP+sRL6ml8uhoJSs+8jwpKzbw8CqBvDc1laeptxPm5FahumJxirigcbD2qTs71Sonvj1cyZB0OKGAmxQ+A==
+  dependencies:
+    core-js-pure "^3.16.0"
+    regenerator-runtime "^0.13.4"
+
 "@babel/runtime@7.14.8", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4":
   version "7.14.8"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.8.tgz#7119a56f421018852694290b9f9148097391b446"
   dependencies:
     regenerator-runtime "^0.13.4"
 
+"@babel/runtime@^7.10.2":
+  version "7.15.3"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.3.tgz#2e1c2880ca118e5b2f9988322bd8a7656a32502b"
+  integrity sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA==
+  dependencies:
+    regenerator-runtime "^0.13.4"
+
 "@babel/template@7.14.5", "@babel/template@^7.14.5", "@babel/template@^7.8.3":
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4"
   resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz#90420f9f9c6d3987f176a19a7d8e764271a2f55d"
   integrity sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==
 
+"@es-joy/jsdoccomment@0.10.7":
+  version "0.10.7"
+  resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.10.7.tgz#bdb5dfcaa5cff8b5435f0c9f2777b533075a9c52"
+  integrity sha512-aNKZEoMESDzOBjKxCWrFuG50mcpMeKVBnBNko4+IZZ5t9zXYs8GT1KB0ZaOq1YUsKumDRc6YII/TQm309MJ0KQ==
+  dependencies:
+    comment-parser "1.2.3"
+    esquery "^1.4.0"
+    jsdoc-type-pratt-parser "1.1.1"
+
+"@eslint/eslintrc@^0.4.3":
+  version "0.4.3"
+  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c"
+  integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==
+  dependencies:
+    ajv "^6.12.4"
+    debug "^4.1.1"
+    espree "^7.3.0"
+    globals "^13.9.0"
+    ignore "^4.0.6"
+    import-fresh "^3.2.1"
+    js-yaml "^3.13.1"
+    minimatch "^3.0.4"
+    strip-json-comments "^3.1.1"
+
+"@humanwhocodes/config-array@^0.5.0":
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9"
+  integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==
+  dependencies:
+    "@humanwhocodes/object-schema" "^1.2.0"
+    debug "^4.1.1"
+    minimatch "^3.0.4"
+
+"@humanwhocodes/object-schema@^1.2.0":
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf"
+  integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==
+
 "@istanbuljs/schema@^0.1.2":
   version "0.1.3"
   resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98"
     node-gyp "^7.1.0"
     read-package-json-fast "^2.0.1"
 
+"@nrwl/devkit@>= 12.0.0 < 13.0.0":
+  version "12.7.1"
+  resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-12.7.1.tgz#aea0d8db1663fed44b5189452beae87bf432c60f"
+  integrity sha512-g88SWsXbiZDvL+4PWCBFlhzsg0niEJCxGDJ1EV+dLexAM3iD1aBn3EK2ygaF1sgabBNlJnUgbpU14/N5SexZuA==
+  dependencies:
+    "@nrwl/tao" "12.7.1"
+    ejs "^3.1.5"
+    ignore "^5.0.4"
+    rxjs "^6.5.4"
+    semver "7.3.4"
+    tslib "^2.0.0"
+
+"@nrwl/tao@12.7.1":
+  version "12.7.1"
+  resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-12.7.1.tgz#467b5b56f8e503d4a6acbd6eecd4ad51042d8d27"
+  integrity sha512-cQvrFwA+mEAiY3HwIuTjsO/yxgGEoffdUcsH6Oak3dcypkmTf4mVxqmuyzvJ5smt8cggg8qb7BEWowWs31folw==
+  dependencies:
+    chalk "4.1.0"
+    enquirer "~2.3.6"
+    fs-extra "^9.1.0"
+    jsonc-parser "3.0.0"
+    rxjs "^6.5.4"
+    rxjs-for-await "0.0.2"
+    semver "7.3.4"
+    tmp "~0.2.1"
+    tslib "^2.0.0"
+    yargs-parser "20.0.0"
+
 "@peertube/p2p-media-loader-core@^1.0.2":
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/@peertube/p2p-media-loader-core/-/p2p-media-loader-core-1.0.2.tgz#ef36a23df5a5393cc5d180a45dfb70d2c3ecff87"
   resolved "https://registry.yarnpkg.com/@types/jschannel/-/jschannel-1.0.2.tgz#a3649704909df330f6c9cfe72b2bd94f8490ce9f"
   integrity sha512-LhEz5aKmiWxHqOmGi31Y+6JGG3OqHkEjESV9U64eQt7mGOnZOVJUs44r4KEe5dmEvt4uGHEqxHtzQhwX2lU3eA==
 
-"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8":
+"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.8":
   version "7.0.9"
   resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
   integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==
   resolved "https://registry.yarnpkg.com/@types/xmldom/-/xmldom-0.1.31.tgz#519b647cfc66debf82cdf50e49763c8fdee553d6"
   integrity sha512-bVy7s0nvaR5D1mT1a8ZkByHWNOGb6Vn4yi5TWhEdmyKlAG+08SA7Md6+jH+tYmMLueAwNeWvHHpeKrr6S4c4BA==
 
+"@typescript-eslint/eslint-plugin@4.28.2":
+  version "4.28.2"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.2.tgz#7a8320f00141666813d0ae43b49ee8244f7cf92a"
+  integrity sha512-PGqpLLzHSxq956rzNGasO3GsAPf2lY9lDUBXhS++SKonglUmJypaUtcKzRtUte8CV7nruwnDxtLUKpVxs0wQBw==
+  dependencies:
+    "@typescript-eslint/experimental-utils" "4.28.2"
+    "@typescript-eslint/scope-manager" "4.28.2"
+    debug "^4.3.1"
+    functional-red-black-tree "^1.0.1"
+    regexpp "^3.1.0"
+    semver "^7.3.5"
+    tsutils "^3.21.0"
+
+"@typescript-eslint/experimental-utils@4.28.2":
+  version "4.28.2"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.2.tgz#4ebdec06a10888e9326e1d51d81ad52a361bd0b0"
+  integrity sha512-MwHPsL6qo98RC55IoWWP8/opTykjTp4JzfPu1VfO2Z0MshNP0UZ1GEV5rYSSnZSUI8VD7iHvtIPVGW5Nfh7klQ==
+  dependencies:
+    "@types/json-schema" "^7.0.7"
+    "@typescript-eslint/scope-manager" "4.28.2"
+    "@typescript-eslint/types" "4.28.2"
+    "@typescript-eslint/typescript-estree" "4.28.2"
+    eslint-scope "^5.1.1"
+    eslint-utils "^3.0.0"
+
+"@typescript-eslint/parser@4.28.2":
+  version "4.28.2"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.28.2.tgz#6aff11bf4b91eb67ca7517962eede951e9e2a15d"
+  integrity sha512-Q0gSCN51eikAgFGY+gnd5p9bhhCUAl0ERMiDKrTzpSoMYRubdB8MJrTTR/BBii8z+iFwz8oihxd0RAdP4l8w8w==
+  dependencies:
+    "@typescript-eslint/scope-manager" "4.28.2"
+    "@typescript-eslint/types" "4.28.2"
+    "@typescript-eslint/typescript-estree" "4.28.2"
+    debug "^4.3.1"
+
+"@typescript-eslint/scope-manager@4.28.2":
+  version "4.28.2"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.28.2.tgz#451dce90303a3ce283750111495d34c9c204e510"
+  integrity sha512-MqbypNjIkJFEFuOwPWNDjq0nqXAKZvDNNs9yNseoGBB1wYfz1G0WHC2AVOy4XD7di3KCcW3+nhZyN6zruqmp2A==
+  dependencies:
+    "@typescript-eslint/types" "4.28.2"
+    "@typescript-eslint/visitor-keys" "4.28.2"
+
+"@typescript-eslint/types@4.28.2":
+  version "4.28.2"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.2.tgz#e6b9e234e0e9a66c4d25bab881661e91478223b5"
+  integrity sha512-Gr15fuQVd93uD9zzxbApz3wf7ua3yk4ZujABZlZhaxxKY8ojo448u7XTm/+ETpy0V0dlMtj6t4VdDvdc0JmUhA==
+
+"@typescript-eslint/typescript-estree@4.28.2":
+  version "4.28.2"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.2.tgz#680129b2a285289a15e7c6108c84739adf3a798c"
+  integrity sha512-86lLstLvK6QjNZjMoYUBMMsULFw0hPHJlk1fzhAVoNjDBuPVxiwvGuPQq3fsBMCxuDJwmX87tM/AXoadhHRljg==
+  dependencies:
+    "@typescript-eslint/types" "4.28.2"
+    "@typescript-eslint/visitor-keys" "4.28.2"
+    debug "^4.3.1"
+    globby "^11.0.3"
+    is-glob "^4.0.1"
+    semver "^7.3.5"
+    tsutils "^3.21.0"
+
+"@typescript-eslint/visitor-keys@4.28.2":
+  version "4.28.2"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.2.tgz#bf56a400857bb68b59b311e6d0a5fbef5c3b5130"
+  integrity sha512-aT2B4PLyyRDUVUafXzpZFoc0C9t0za4BJAKP5sgWIhG+jHECQZUEjuQSCIwZdiJJ4w4cgu5r3Kh20SOdtEBl0w==
+  dependencies:
+    "@typescript-eslint/types" "4.28.2"
+    eslint-visitor-keys "^2.0.0"
+
 "@videojs/http-streaming@2.9.2":
   version "2.9.2"
   resolved "https://registry.yarnpkg.com/@videojs/http-streaming/-/http-streaming-2.9.2.tgz#47d33bb02bd9c1287200398b1e85d213dee814d0"
@@ -1950,11 +2138,21 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7:
     mime-types "~2.1.24"
     negotiator "0.6.2"
 
+acorn-jsx@^5.3.1:
+  version "5.3.2"
+  resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
+  integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
+
 acorn-walk@^8.0.0:
   version "8.1.1"
   resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.1.1.tgz#3ddab7f84e4a7e2313f6c414c5b7dac85f4e3ebc"
   integrity sha512-FbJdceMlPHEAWJOILDk1fXD8lnTlEIWFkqtfk+MvmL5q/qlHfN7GEHcsFZWt/Tea9jRNPWUZG4G976nqAAmU9w==
 
+acorn@^7.4.0:
+  version "7.4.1"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
+  integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
+
 acorn@^8.0.4, acorn@^8.4.1:
   version "8.4.1"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c"
@@ -2046,7 +2244,7 @@ ajv@8.6.2, ajv@^8.0.0, ajv@^8.0.1:
     require-from-string "^2.0.2"
     uri-js "^4.2.2"
 
-ajv@^6.1.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5:
+ajv@^6.1.0, ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5:
   version "6.12.6"
   resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
   integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@@ -2078,7 +2276,7 @@ angularx-qrcode@11.0.0:
     qrcode "1.4.2"
     tslib "^2.0.0"
 
-ansi-colors@4.1.1:
+ansi-colors@4.1.1, ansi-colors@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
   integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
@@ -2155,11 +2353,6 @@ anymatch@~3.1.2:
     normalize-path "^3.0.0"
     picomatch "^2.0.4"
 
-app-root-path@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.0.0.tgz#210b6f43873227e18a4b810a032283311555d5ad"
-  integrity sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw==
-
 aproba@^1.0.3:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
@@ -2185,13 +2378,13 @@ argparse@^2.0.1:
   resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
   integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
 
-aria-query@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc"
-  integrity sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=
+aria-query@^4.2.2:
+  version "4.2.2"
+  resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b"
+  integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==
   dependencies:
-    ast-types-flow "0.0.7"
-    commander "^2.11.0"
+    "@babel/runtime" "^7.10.2"
+    "@babel/runtime-corejs3" "^7.10.2"
 
 arr-diff@^4.0.0:
   version "4.0.0"
@@ -2218,6 +2411,17 @@ array-flatten@^2.1.0:
   resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099"
   integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==
 
+array-includes@^3.1.3:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.3.tgz#c7f619b382ad2afaf5326cddfdc0afc61af7690a"
+  integrity sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.3"
+    es-abstract "^1.18.0-next.2"
+    get-intrinsic "^1.1.1"
+    is-string "^1.0.5"
+
 array-union@^1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39"
@@ -2240,6 +2444,15 @@ array-unique@^0.3.2:
   resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
   integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
 
+array.prototype.flat@^1.2.4:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz#6ef638b43312bd401b4c6199fdec7e2dc9e9a123"
+  integrity sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==
+  dependencies:
+    call-bind "^1.0.0"
+    define-properties "^1.1.3"
+    es-abstract "^1.18.0-next.1"
+
 arrify@^1.0.0, arrify@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
@@ -2262,11 +2475,6 @@ assign-symbols@^1.0.0:
   resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
   integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
 
-ast-types-flow@0.0.7:
-  version "0.0.7"
-  resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad"
-  integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0=
-
 astral-regex@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
@@ -2282,6 +2490,11 @@ async-limiter@~1.0.0:
   resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
   integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
 
+async@0.9.x:
+  version "0.9.2"
+  resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d"
+  integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=
+
 async@^2.6.2:
   version "2.6.3"
   resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
@@ -2294,6 +2507,11 @@ asynckit@^0.4.0:
   resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
   integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
 
+at-least-node@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
+  integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
+
 atob@^2.1.2:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
@@ -2322,12 +2540,10 @@ aws4@^1.8.0:
   resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
   integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
 
-axobject-query@2.0.2:
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.0.2.tgz#ea187abe5b9002b377f925d8bf7d1c561adf38f9"
-  integrity sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==
-  dependencies:
-    ast-types-flow "0.0.7"
+axobject-query@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
+  integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==
 
 babel-loader@8.2.2:
   version "8.2.2"
@@ -2721,11 +2937,6 @@ bufferutil@^4.0.3:
   dependencies:
     node-gyp-build "^4.2.0"
 
-builtin-modules@^1.1.1:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
-  integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=
-
 builtin-status-codes@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
@@ -2857,6 +3068,14 @@ caseless@~0.12.0:
   resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
   integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
 
+chalk@4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a"
+  integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==
+  dependencies:
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
+
 chalk@^1.1.1, chalk@^1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
@@ -2868,7 +3087,7 @@ chalk@^1.1.1, chalk@^1.1.3:
     strip-ansi "^3.0.0"
     supports-color "^2.0.0"
 
-chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2:
+chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2:
   version "2.4.2"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
   integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -2877,7 +3096,7 @@ chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2:
     escape-string-regexp "^1.0.5"
     supports-color "^5.3.0"
 
-chalk@^4.1.0, chalk@^4.1.1:
+chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1:
   version "4.1.2"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
   integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
@@ -3108,26 +3327,6 @@ code-point-at@^1.0.0:
   resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
   integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
 
-codelyzer@^6.0.0:
-  version "6.0.2"
-  resolved "https://registry.yarnpkg.com/codelyzer/-/codelyzer-6.0.2.tgz#25d72eae641e8ff13ffd7d99b27c9c7ad5d7e135"
-  integrity sha512-v3+E0Ucu2xWJMOJ2fA/q9pDT/hlxHftHGPUay1/1cTgyPV5JTHFdO9hqo837Sx2s9vKBMTt5gO+lhF95PO6J+g==
-  dependencies:
-    "@angular/compiler" "9.0.0"
-    "@angular/core" "9.0.0"
-    app-root-path "^3.0.0"
-    aria-query "^3.0.0"
-    axobject-query "2.0.2"
-    css-selector-tokenizer "^0.7.1"
-    cssauron "^1.4.0"
-    damerau-levenshtein "^1.0.4"
-    rxjs "^6.5.3"
-    semver-dsl "^1.0.1"
-    source-map "^0.5.7"
-    sprintf-js "^1.1.2"
-    tslib "^1.10.0"
-    zone.js "~0.10.3"
-
 collection-visit@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
@@ -3182,7 +3381,7 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
   dependencies:
     delayed-stream "~1.0.0"
 
-commander@^2.11.0, commander@^2.12.1, commander@^2.20.0:
+commander@^2.20.0:
   version "2.20.3"
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
   integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
@@ -3202,6 +3401,11 @@ commander@^7.0.0, commander@^7.1.0:
   resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
   integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
 
+comment-parser@1.2.3:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.2.3.tgz#303a7eb99c9b2632efd594e183ccbd32042caf69"
+  integrity sha512-vnqDwBSXSsdAkGS5NjwMIPelE47q+UkEgWKHvCDNhVIIaQSUFY6sNnEYGzdoPGMdpV+7KR3ZkRd7oyWIjtuvJg==
+
 commondir@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
@@ -3336,6 +3540,11 @@ core-js-compat@^3.14.0, core-js-compat@^3.15.0:
     browserslist "^4.16.6"
     semver "7.0.0"
 
+core-js-pure@^3.16.0:
+  version "3.16.1"
+  resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.16.1.tgz#b997df2669c957a5b29f06e95813a171f993592e"
+  integrity sha512-TyofCdMzx0KMhi84mVRS8rL1XsRk2SPUNz2azmth53iRN0/08Uim9fdhQTaZTG1LqaXHYVci4RDHka6WrXfnvg==
+
 core-js@3.16.0, core-js@^3.1.4:
   version "3.16.0"
   resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.16.0.tgz#1d46fb33720bc1fa7f90d20431f36a5540858986"
@@ -3420,7 +3629,7 @@ cross-spawn@^6.0.0:
     shebang-command "^1.2.0"
     which "^1.2.9"
 
-cross-spawn@^7.0.3:
+cross-spawn@^7.0.2, cross-spawn@^7.0.3:
   version "7.0.3"
   resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
   integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
@@ -3513,14 +3722,6 @@ css-select@^4.1.3:
     domutils "^2.6.0"
     nth-check "^2.0.0"
 
-css-selector-tokenizer@^0.7.1:
-  version "0.7.3"
-  resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz#735f26186e67c749aaf275783405cf0661fae8f1"
-  integrity sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==
-  dependencies:
-    cssesc "^3.0.0"
-    fastparse "^1.1.2"
-
 css-tree@^1.1.2:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d"
@@ -3553,13 +3754,6 @@ css@^3.0.0:
     source-map "^0.6.1"
     source-map-resolve "^0.6.0"
 
-cssauron@^1.4.0:
-  version "1.4.0"
-  resolved "https://registry.yarnpkg.com/cssauron/-/cssauron-1.4.0.tgz#a6602dff7e04a8306dc0db9a551e92e8b5662ad8"
-  integrity sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=
-  dependencies:
-    through X.X.X
-
 cssdb@^4.4.0:
   version "4.4.0"
   resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-4.4.0.tgz#3bf2f2a68c10f5c6a08abd92378331ee803cddb0"
@@ -3642,11 +3836,6 @@ custom-event@~1.0.0:
   resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425"
   integrity sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=
 
-damerau-levenshtein@^1.0.4:
-  version "1.0.7"
-  resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.7.tgz#64368003512a1a6992593741a09a9d31a836f55d"
-  integrity sha512-VvdQIPGdWP0SqFXghj79Wf/5LArmreyMsGLa6FG6iC4t3j7j5s71TrwWmT/4akbDQIqjfACkLZmjXhA7g2oUZw==
-
 dashdash@^1.12.0:
   version "1.14.1"
   resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
@@ -3664,21 +3853,21 @@ date-format@^3.0.0:
   resolved "https://registry.yarnpkg.com/date-format/-/date-format-3.0.0.tgz#eb8780365c7d2b1511078fb491e6479780f3ad95"
   integrity sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w==
 
-debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
+debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9:
   version "2.6.9"
   resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
   integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
   dependencies:
     ms "2.0.0"
 
-debug@4, debug@4.3.2, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@~4.3.1:
+debug@4, debug@4.3.2, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@~4.3.1:
   version "4.3.2"
   resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
   integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
   dependencies:
     ms "2.1.2"
 
-debug@^3.1.0, debug@^3.1.1, debug@^3.2.6:
+debug@^3.1.0, debug@^3.1.1, debug@^3.2.6, debug@^3.2.7:
   version "3.2.7"
   resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
   integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
@@ -3729,6 +3918,11 @@ deep-equal@^1.0.1:
     object-keys "^1.1.1"
     regexp.prototype.flags "^1.2.0"
 
+deep-is@^0.1.3:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
+  integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
+
 deepmerge@^4.2.2:
   version "4.2.2"
   resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
@@ -3849,11 +4043,6 @@ di@^0.0.1:
   resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c"
   integrity sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=
 
-diff@^4.0.1:
-  version "4.0.2"
-  resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
-  integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
-
 dijkstrajs@^1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/dijkstrajs/-/dijkstrajs-1.0.2.tgz#2e48c0d3b825462afe75ab4ad5e829c8ece36257"
@@ -3886,13 +4075,19 @@ dns-txt@^2.0.2:
   dependencies:
     buffer-indexof "^1.0.0"
 
-doctrine@0.7.2:
-  version "0.7.2"
-  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-0.7.2.tgz#7cb860359ba3be90e040b26b729ce4bfa654c523"
-  integrity sha1-fLhgNZujvpDgQLJrcpzkv6ZUxSM=
+doctrine@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
+  integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==
+  dependencies:
+    esutils "^2.0.2"
+
+doctrine@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
+  integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
   dependencies:
-    esutils "^1.1.6"
-    isarray "0.0.1"
+    esutils "^2.0.2"
 
 dom-converter@^0.2.0:
   version "0.2.0"
@@ -4007,6 +4202,13 @@ ee-first@1.1.1:
   resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
   integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
 
+ejs@^3.1.5:
+  version "3.1.6"
+  resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.6.tgz#5bfd0a0689743bb5268b3550cceeebbc1702822a"
+  integrity sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw==
+  dependencies:
+    jake "^10.6.1"
+
 electron-to-chromium@^1.3.793:
   version "1.3.798"
   resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.798.tgz#12b0bb826ddf35486f2ca41c01be4bd6ad1b9b1e"
@@ -4089,6 +4291,13 @@ enhanced-resolve@^5.0.0, enhanced-resolve@^5.8.0:
     graceful-fs "^4.2.4"
     tapable "^2.2.0"
 
+enquirer@^2.3.5, enquirer@~2.3.6:
+  version "2.3.6"
+  resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
+  integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==
+  dependencies:
+    ansi-colors "^4.1.1"
+
 ent@~2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d"
@@ -4143,11 +4352,43 @@ error-ex@^1.2.0, error-ex@^1.3.1:
   dependencies:
     is-arrayish "^0.2.1"
 
+es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2, es-abstract@^1.18.2:
+  version "1.18.5"
+  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.5.tgz#9b10de7d4c206a3581fd5b2124233e04db49ae19"
+  integrity sha512-DDggyJLoS91CkJjgauM5c0yZMjiD1uK3KcaCeAmffGwZ+ODWzOkPN4QwRbsK5DOFf06fywmyLci3ZD8jLGhVYA==
+  dependencies:
+    call-bind "^1.0.2"
+    es-to-primitive "^1.2.1"
+    function-bind "^1.1.1"
+    get-intrinsic "^1.1.1"
+    has "^1.0.3"
+    has-symbols "^1.0.2"
+    internal-slot "^1.0.3"
+    is-callable "^1.2.3"
+    is-negative-zero "^2.0.1"
+    is-regex "^1.1.3"
+    is-string "^1.0.6"
+    object-inspect "^1.11.0"
+    object-keys "^1.1.1"
+    object.assign "^4.1.2"
+    string.prototype.trimend "^1.0.4"
+    string.prototype.trimstart "^1.0.4"
+    unbox-primitive "^1.0.1"
+
 es-module-lexer@^0.7.1:
   version "0.7.1"
   resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.7.1.tgz#c2c8e0f46f2df06274cdaf0dd3f3b33e0a0b267d"
   integrity sha512-MgtWFl5No+4S3TmhDmCz2ObFGm6lEpTnzbQi+Dd+pw4mlTIZTmM2iAs5gRlmx5zS9luzobCSBSI90JM/1/JgOw==
 
+es-to-primitive@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
+  integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==
+  dependencies:
+    is-callable "^1.1.4"
+    is-date-object "^1.0.1"
+    is-symbol "^1.0.2"
+
 es6-promise@^4.0.3:
   version "4.2.8"
   resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
@@ -4185,7 +4426,64 @@ escape-string-regexp@^4.0.0:
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
   integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
 
-eslint-scope@5.1.1:
+eslint-import-resolver-node@^0.3.5:
+  version "0.3.6"
+  resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd"
+  integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==
+  dependencies:
+    debug "^3.2.7"
+    resolve "^1.20.0"
+
+eslint-module-utils@^2.6.2:
+  version "2.6.2"
+  resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.2.tgz#94e5540dd15fe1522e8ffa3ec8db3b7fa7e7a534"
+  integrity sha512-QG8pcgThYOuqxupd06oYTZoNOGaUdTY1PqK+oS6ElF6vs4pBdk/aYxFVQQXzcrAqp9m7cl7lb2ubazX+g16k2Q==
+  dependencies:
+    debug "^3.2.7"
+    pkg-dir "^2.0.0"
+
+eslint-plugin-import@latest:
+  version "2.24.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.24.0.tgz#697ffd263e24da5e84e03b282f5fb62251777177"
+  integrity sha512-Kc6xqT9hiYi2cgybOc0I2vC9OgAYga5o/rAFinam/yF/t5uBqxQbauNPMC6fgb640T/89P0gFoO27FOilJ/Cqg==
+  dependencies:
+    array-includes "^3.1.3"
+    array.prototype.flat "^1.2.4"
+    debug "^2.6.9"
+    doctrine "^2.1.0"
+    eslint-import-resolver-node "^0.3.5"
+    eslint-module-utils "^2.6.2"
+    find-up "^2.0.0"
+    has "^1.0.3"
+    is-core-module "^2.4.0"
+    minimatch "^3.0.4"
+    object.values "^1.1.3"
+    pkg-up "^2.0.0"
+    read-pkg-up "^3.0.0"
+    resolve "^1.20.0"
+    tsconfig-paths "^3.9.0"
+
+eslint-plugin-jsdoc@latest:
+  version "36.0.7"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-36.0.7.tgz#6e6f9897fc2ff3b3934b09c2748b998bc03d2bf6"
+  integrity sha512-x73l/WCRQ1qCjLq46Ca7csuGd5o3y3vbJIa3cktg11tdf3UZleBdIXKN9Cf0xjs3tXYPEy2SoNXowT8ydnjNDQ==
+  dependencies:
+    "@es-joy/jsdoccomment" "0.10.7"
+    comment-parser "1.2.3"
+    debug "^4.3.2"
+    esquery "^1.4.0"
+    jsdoc-type-pratt-parser "^1.1.1"
+    lodash "^4.17.21"
+    regextras "^0.8.0"
+    semver "^7.3.5"
+    spdx-expression-parse "^3.0.1"
+
+eslint-plugin-prefer-arrow@latest:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.2.3.tgz#e7fbb3fa4cd84ff1015b9c51ad86550e55041041"
+  integrity sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==
+
+eslint-scope@5.1.1, eslint-scope@^5.1.0, eslint-scope@^5.1.1:
   version "5.1.1"
   resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
   integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
@@ -4193,11 +4491,97 @@ eslint-scope@5.1.1:
     esrecurse "^4.3.0"
     estraverse "^4.1.1"
 
+eslint-utils@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27"
+  integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==
+  dependencies:
+    eslint-visitor-keys "^1.1.0"
+
+eslint-utils@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672"
+  integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==
+  dependencies:
+    eslint-visitor-keys "^2.0.0"
+
+eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e"
+  integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==
+
+eslint-visitor-keys@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
+  integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
+
+eslint@^7.26.0:
+  version "7.32.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d"
+  integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==
+  dependencies:
+    "@babel/code-frame" "7.12.11"
+    "@eslint/eslintrc" "^0.4.3"
+    "@humanwhocodes/config-array" "^0.5.0"
+    ajv "^6.10.0"
+    chalk "^4.0.0"
+    cross-spawn "^7.0.2"
+    debug "^4.0.1"
+    doctrine "^3.0.0"
+    enquirer "^2.3.5"
+    escape-string-regexp "^4.0.0"
+    eslint-scope "^5.1.1"
+    eslint-utils "^2.1.0"
+    eslint-visitor-keys "^2.0.0"
+    espree "^7.3.1"
+    esquery "^1.4.0"
+    esutils "^2.0.2"
+    fast-deep-equal "^3.1.3"
+    file-entry-cache "^6.0.1"
+    functional-red-black-tree "^1.0.1"
+    glob-parent "^5.1.2"
+    globals "^13.6.0"
+    ignore "^4.0.6"
+    import-fresh "^3.0.0"
+    imurmurhash "^0.1.4"
+    is-glob "^4.0.0"
+    js-yaml "^3.13.1"
+    json-stable-stringify-without-jsonify "^1.0.1"
+    levn "^0.4.1"
+    lodash.merge "^4.6.2"
+    minimatch "^3.0.4"
+    natural-compare "^1.4.0"
+    optionator "^0.9.1"
+    progress "^2.0.0"
+    regexpp "^3.1.0"
+    semver "^7.2.1"
+    strip-ansi "^6.0.0"
+    strip-json-comments "^3.1.0"
+    table "^6.0.9"
+    text-table "^0.2.0"
+    v8-compile-cache "^2.0.3"
+
+espree@^7.3.0, espree@^7.3.1:
+  version "7.3.1"
+  resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6"
+  integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==
+  dependencies:
+    acorn "^7.4.0"
+    acorn-jsx "^5.3.1"
+    eslint-visitor-keys "^1.3.0"
+
 esprima@^4.0.0:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
   integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
 
+esquery@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5"
+  integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==
+  dependencies:
+    estraverse "^5.1.0"
+
 esrecurse@^4.3.0:
   version "4.3.0"
   resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
@@ -4210,16 +4594,11 @@ estraverse@^4.1.1:
   resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
   integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
 
-estraverse@^5.2.0:
+estraverse@^5.1.0, estraverse@^5.2.0:
   version "5.2.0"
   resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880"
   integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==
 
-esutils@^1.1.6:
-  version "1.1.6"
-  resolved "https://registry.yarnpkg.com/esutils/-/esutils-1.1.6.tgz#c01ccaa9ae4b897c6d0c3e210ae52f3c7a844375"
-  integrity sha1-wBzKqa5LiXxtDD4hCuUvPHqEQ3U=
-
 esutils@^2.0.2:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
@@ -4407,7 +4786,7 @@ extsprintf@^1.2.0:
   resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
   integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
 
-fast-deep-equal@^3.1.1:
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
   integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
@@ -4433,16 +4812,16 @@ fast-json-stable-stringify@2.1.0, fast-json-stable-stringify@^2.0.0:
   resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
   integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
 
+fast-levenshtein@^2.0.6:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+  integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
+
 fastest-levenshtein@^1.0.12:
   version "1.0.12"
   resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2"
   integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==
 
-fastparse@^1.1.2:
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9"
-  integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==
-
 fastq@^1.6.0:
   version "1.11.1"
   resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.1.tgz#5d8175aae17db61947f8b162cfc7f63264d22807"
@@ -4476,6 +4855,13 @@ file-uri-to-path@1.0.0:
   resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
   integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
 
+filelist@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.2.tgz#80202f21462d4d1c2e214119b1807c1bc0380e5b"
+  integrity sha512-z7O0IS8Plc39rTCq6i6iHxk43duYOn8uFJiWSewIq0Bww1RNybVHSCjahmcC87ZqAm4OTvFzlzeGu3XAzG1ctQ==
+  dependencies:
+    minimatch "^3.0.4"
+
 filestream@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/filestream/-/filestream-5.0.0.tgz#79015f3bae95ad0f47ef818694846f085087b92e"
@@ -4523,7 +4909,7 @@ find-cache-dir@3.3.1, find-cache-dir@^3.3.1:
     make-dir "^3.0.2"
     pkg-dir "^4.1.0"
 
-find-up@^2.0.0:
+find-up@^2.0.0, find-up@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
   integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c=
@@ -4640,6 +5026,16 @@ fs-extra@^8.1.0:
     jsonfile "^4.0.0"
     universalify "^0.1.0"
 
+fs-extra@^9.1.0:
+  version "9.1.0"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d"
+  integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==
+  dependencies:
+    at-least-node "^1.0.0"
+    graceful-fs "^4.2.0"
+    jsonfile "^6.0.1"
+    universalify "^2.0.0"
+
 fs-minipass@^2.0.0, fs-minipass@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
@@ -4675,6 +5071,11 @@ function-bind@^1.1.1:
   resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
   integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
 
+functional-red-black-tree@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
+  integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
+
 gauge@~2.7.3:
   version "2.7.4"
   resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
@@ -4709,7 +5110,7 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5:
   resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
   integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
 
-get-intrinsic@^1.0.2:
+get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6"
   integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==
@@ -4820,6 +5221,13 @@ globals@^11.1.0:
   resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
   integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
 
+globals@^13.6.0, globals@^13.9.0:
+  version "13.11.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-13.11.0.tgz#40ef678da117fe7bd2e28f1fab24951bd0255be7"
+  integrity sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==
+  dependencies:
+    type-fest "^0.20.2"
+
 globby@^11.0.3:
   version "11.0.4"
   resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5"
@@ -4909,6 +5317,11 @@ has-ansi@^2.0.0:
   dependencies:
     ansi-regex "^2.0.0"
 
+has-bigints@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113"
+  integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==
+
 has-cors@1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39"
@@ -5280,11 +5693,16 @@ ignore-walk@^3.0.3:
   dependencies:
     minimatch "^3.0.4"
 
-ignore@^5.1.4, ignore@^5.1.8:
+ignore@5.1.8, ignore@^5.0.4, ignore@^5.1.4, ignore@^5.1.8:
   version "5.1.8"
   resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57"
   integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
 
+ignore@^4.0.6:
+  version "4.0.6"
+  resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
+  integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
+
 image-size@~0.5.0:
   version "0.5.5"
   resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c"
@@ -5302,7 +5720,7 @@ immediate@~3.0.5:
   resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
   integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=
 
-import-fresh@^3.2.1:
+import-fresh@^3.0.0, import-fresh@^3.2.1:
   version "3.3.0"
   resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
   integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
@@ -5412,6 +5830,15 @@ internal-ip@^4.3.0:
     default-gateway "^4.2.0"
     ipaddr.js "^1.9.0"
 
+internal-slot@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c"
+  integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==
+  dependencies:
+    get-intrinsic "^1.1.0"
+    has "^1.0.3"
+    side-channel "^1.0.4"
+
 interpret@^2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
@@ -5499,6 +5926,13 @@ is-ascii@^1.0.0:
   resolved "https://registry.yarnpkg.com/is-ascii/-/is-ascii-1.0.0.tgz#f02ad0259a0921cd199ff21ce1b09e0f6b4e3929"
   integrity sha1-8CrQJZoJIc0Zn/Ic4bCeD2tOOSk=
 
+is-bigint@^1.0.1:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3"
+  integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==
+  dependencies:
+    has-bigints "^1.0.1"
+
 is-binary-path@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898"
@@ -5513,6 +5947,14 @@ is-binary-path@~2.1.0:
   dependencies:
     binary-extensions "^2.0.0"
 
+is-boolean-object@^1.1.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719"
+  integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==
+  dependencies:
+    call-bind "^1.0.2"
+    has-tostringtag "^1.0.0"
+
 is-buffer@^1.1.5:
   version "1.1.6"
   resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
@@ -5523,6 +5965,11 @@ is-buffer@^2.0.0:
   resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191"
   integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==
 
+is-callable@^1.1.4, is-callable@^1.2.3:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945"
+  integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==
+
 is-color-stop@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345"
@@ -5535,7 +5982,7 @@ is-color-stop@^1.1.0:
     rgb-regex "^1.0.1"
     rgba-regex "^1.0.0"
 
-is-core-module@^2.2.0:
+is-core-module@^2.2.0, is-core-module@^2.4.0:
   version "2.5.0"
   resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.5.0.tgz#f754843617c70bfd29b7bd87327400cda5c18491"
   integrity sha512-TXCMSDsEHMEEZ6eCA8rwRDbLu55MRGmrctljsBX/2v1d9/GzqHOxW5c5oPSgrUt2vBFXebu9rGqckXGPWOlYpg==
@@ -5664,6 +6111,18 @@ is-lambda@^1.0.1:
   resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5"
   integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=
 
+is-negative-zero@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24"
+  integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==
+
+is-number-object@^1.0.4:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0"
+  integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==
+  dependencies:
+    has-tostringtag "^1.0.0"
+
 is-number@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
@@ -5736,7 +6195,7 @@ is-plain-object@^5.0.0:
   resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
   integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==
 
-is-regex@^1.0.4:
+is-regex@^1.0.4, is-regex@^1.1.3:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
   integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==
@@ -5764,6 +6223,20 @@ is-stream@^2.0.0:
   resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
   integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
 
+is-string@^1.0.5, is-string@^1.0.6:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd"
+  integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==
+  dependencies:
+    has-tostringtag "^1.0.0"
+
+is-symbol@^1.0.2, is-symbol@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c"
+  integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==
+  dependencies:
+    has-symbols "^1.0.2"
+
 is-typedarray@^1.0.0, is-typedarray@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
@@ -5796,11 +6269,6 @@ is-wsl@^2.2.0:
   dependencies:
     is-docker "^2.0.0"
 
-isarray@0.0.1:
-  version "0.0.1"
-  resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
-  integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
-
 isarray@1.0.0, isarray@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
@@ -5886,6 +6354,16 @@ istanbul-reports@^3.0.2:
     html-escaper "^2.0.0"
     istanbul-lib-report "^3.0.0"
 
+jake@^10.6.1:
+  version "10.8.2"
+  resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.2.tgz#ebc9de8558160a66d82d0eadc6a2e58fbc500a7b"
+  integrity sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==
+  dependencies:
+    async "0.9.x"
+    chalk "^2.4.2"
+    filelist "^1.0.1"
+    minimatch "^3.0.4"
+
 jasmine-core@^3.6.0, jasmine-core@~3.8.0:
   version "3.8.0"
   resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.8.0.tgz#815399aae5aa5d9beeb1262805f981b99ffc9bf0"
@@ -5949,6 +6427,11 @@ jschannel@^1.0.2:
   resolved "https://registry.yarnpkg.com/jschannel/-/jschannel-1.0.2.tgz#8932010e9c6042a27bc93b918dac2e267976ae14"
   integrity sha1-iTIBDpxgQqJ7yTuRjawuJnl2rhQ=
 
+jsdoc-type-pratt-parser@1.1.1, jsdoc-type-pratt-parser@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-1.1.1.tgz#10fe5e409ba38de22a48b555598955a26ff0160f"
+  integrity sha512-uelRmpghNwPBuZScwgBG/OzodaFk5RbO5xaivBdsAY70icWfShwZ7PCMO0x1zSkOa8T1FzHThmrdoyg/0AwV5g==
+
 jsesc@^2.5.1:
   version "2.5.2"
   resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
@@ -5959,7 +6442,7 @@ jsesc@~0.5.0:
   resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
   integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=
 
-json-parse-better-errors@^1.0.2:
+json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
   integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
@@ -5984,6 +6467,11 @@ json-schema@0.2.3:
   resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
   integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
 
+json-stable-stringify-without-jsonify@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
+  integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
+
 json-stringify-safe@~5.0.1:
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
@@ -6001,7 +6489,7 @@ json5@^1.0.1:
   dependencies:
     minimist "^1.2.0"
 
-json5@^2.1.0, json5@^2.1.2:
+json5@^2.1.0, json5@^2.1.2, json5@^2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3"
   integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==
@@ -6020,6 +6508,15 @@ jsonfile@^4.0.0:
   optionalDependencies:
     graceful-fs "^4.1.6"
 
+jsonfile@^6.0.1:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
+  integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
+  dependencies:
+    universalify "^2.0.0"
+  optionalDependencies:
+    graceful-fs "^4.1.6"
+
 jsonparse@^1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
@@ -6222,6 +6719,14 @@ less@4.1.1:
     needle "^2.5.2"
     source-map "~0.6.0"
 
+levn@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
+  integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
+  dependencies:
+    prelude-ls "^1.2.1"
+    type-check "~0.4.0"
+
 license-webpack-plugin@2.3.20:
   version "2.3.20"
   resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-2.3.20.tgz#f51fb674ca31519dbedbe1c7aabc036e5a7f2858"
@@ -6285,6 +6790,16 @@ load-json-file@^2.0.0:
     pify "^2.0.0"
     strip-bom "^3.0.0"
 
+load-json-file@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b"
+  integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs=
+  dependencies:
+    graceful-fs "^4.1.2"
+    parse-json "^4.0.0"
+    pify "^3.0.0"
+    strip-bom "^3.0.0"
+
 loader-runner@^4.2.0:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384"
@@ -6351,6 +6866,11 @@ lodash.memoize@^4.1.2:
   resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
   integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
 
+lodash.merge@^4.6.2:
+  version "4.6.2"
+  resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
+  integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+
 lodash.truncate@^4.4.2:
   version "4.4.2"
   resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
@@ -6876,7 +7396,7 @@ mkdirp-classic@^0.5.2:
   resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
   integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
 
-mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5:
+mkdirp@^0.5.1, mkdirp@^0.5.5:
   version "0.5.5"
   resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
   integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
@@ -7010,6 +7530,11 @@ napi-macros@^2.0.0:
   resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.0.0.tgz#2b6bae421e7b96eb687aa6c77a7858640670001b"
   integrity sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==
 
+natural-compare@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+  integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
+
 needle@^2.5.2:
   version "2.8.0"
   resolved "https://registry.yarnpkg.com/needle/-/needle-2.8.0.tgz#1c8ef9c1a2c29dcc1e83d73809d7bc681c80a048"
@@ -7277,6 +7802,11 @@ object-copy@^0.1.0:
     define-property "^0.2.5"
     kind-of "^3.0.3"
 
+object-inspect@^1.11.0, object-inspect@^1.9.0:
+  version "1.11.0"
+  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1"
+  integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==
+
 object-is@^1.0.1:
   version "1.1.5"
   resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac"
@@ -7297,7 +7827,7 @@ object-visit@^1.0.0:
   dependencies:
     isobject "^3.0.0"
 
-object.assign@^4.1.0:
+object.assign@^4.1.0, object.assign@^4.1.2:
   version "4.1.2"
   resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940"
   integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==
@@ -7314,6 +7844,15 @@ object.pick@^1.3.0:
   dependencies:
     isobject "^3.0.1"
 
+object.values@^1.1.3:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.4.tgz#0d273762833e816b693a637d30073e7051535b30"
+  integrity sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.3"
+    es-abstract "^1.18.2"
+
 obuf@^1.0.0, obuf@^1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
@@ -7366,6 +7905,18 @@ opn@^5.5.0:
   dependencies:
     is-wsl "^1.1.0"
 
+optionator@^0.9.1:
+  version "0.9.1"
+  resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
+  integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==
+  dependencies:
+    deep-is "^0.1.3"
+    fast-levenshtein "^2.0.6"
+    levn "^0.4.1"
+    prelude-ls "^1.2.1"
+    type-check "^0.4.0"
+    word-wrap "^1.2.3"
+
 ora@5.4.1, ora@^5.3.0:
   version "5.4.1"
   resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18"
@@ -7554,6 +8105,14 @@ parse-json@^2.2.0:
   dependencies:
     error-ex "^1.2.0"
 
+parse-json@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
+  integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=
+  dependencies:
+    error-ex "^1.3.1"
+    json-parse-better-errors "^1.0.1"
+
 parse-json@^5.0.0:
   version "5.2.0"
   resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
@@ -7704,6 +8263,13 @@ path-type@^2.0.0:
   dependencies:
     pify "^2.0.0"
 
+path-type@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
+  integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==
+  dependencies:
+    pify "^3.0.0"
+
 path-type@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
@@ -7729,6 +8295,11 @@ pify@^2.0.0, pify@^2.3.0:
   resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
   integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw=
 
+pify@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
+  integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
+
 pify@^4.0.1:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
@@ -7764,6 +8335,13 @@ pkcs7@^1.0.4:
   dependencies:
     "@babel/runtime" "^7.5.5"
 
+pkg-dir@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b"
+  integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=
+  dependencies:
+    find-up "^2.1.0"
+
 pkg-dir@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3"
@@ -7778,6 +8356,13 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0:
   dependencies:
     find-up "^4.0.0"
 
+pkg-up@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f"
+  integrity sha1-yBmscoBZpGHKscOImivjxJoATX8=
+  dependencies:
+    find-up "^2.1.0"
+
 pngjs@^3.3.0:
   version "3.4.0"
   resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f"
@@ -8425,6 +9010,11 @@ postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.21, postcss@^7.0.
     source-map "^0.6.1"
     supports-color "^6.1.0"
 
+prelude-ls@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
+  integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
+
 pretty-bytes@^5.3.0:
   version "5.6.0"
   resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
@@ -8455,6 +9045,11 @@ process@^0.11.10:
   resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
   integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
 
+progress@^2.0.0:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
+  integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
+
 promise-inflight@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
@@ -8681,6 +9276,14 @@ read-pkg-up@^2.0.0:
     find-up "^2.0.0"
     read-pkg "^2.0.0"
 
+read-pkg-up@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07"
+  integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=
+  dependencies:
+    find-up "^2.0.0"
+    read-pkg "^3.0.0"
+
 read-pkg-up@^7.0.1:
   version "7.0.1"
   resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507"
@@ -8699,6 +9302,15 @@ read-pkg@^2.0.0:
     normalize-package-data "^2.3.2"
     path-type "^2.0.0"
 
+read-pkg@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389"
+  integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=
+  dependencies:
+    load-json-file "^4.0.0"
+    normalize-package-data "^2.3.2"
+    path-type "^3.0.0"
+
 read-pkg@^5.2.0:
   version "5.2.0"
   resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc"
@@ -8817,6 +9429,11 @@ regexp.prototype.flags@^1.2.0:
     call-bind "^1.0.2"
     define-properties "^1.1.3"
 
+regexpp@^3.1.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
+  integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
+
 regexpu-core@^4.7.1:
   version "4.7.1"
   resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.1.tgz#2dea5a9a07233298fbf0db91fa9abc4c6e0f8ad6"
@@ -8829,6 +9446,11 @@ regexpu-core@^4.7.1:
     unicode-match-property-ecmascript "^1.0.4"
     unicode-match-property-value-ecmascript "^1.2.0"
 
+regextras@^0.8.0:
+  version "0.8.0"
+  resolved "https://registry.yarnpkg.com/regextras/-/regextras-0.8.0.tgz#ec0f99853d4912839321172f608b544814b02217"
+  integrity sha512-k519uI04Z3SaY0fLX843MRXnDeG2+vHOFsyhiPZvNLe7r8rD2YNRjq4BQLZZ0oAr2NrtvZlICsXysGNFPGa3CQ==
+
 regjsgen@^0.5.1:
   version "0.5.2"
   resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733"
@@ -9106,7 +9728,12 @@ rust-result@^1.0.0:
   dependencies:
     individual "^2.0.0"
 
-rxjs@6.6.7, rxjs@^6.5.3:
+rxjs-for-await@0.0.2:
+  version "0.0.2"
+  resolved "https://registry.yarnpkg.com/rxjs-for-await/-/rxjs-for-await-0.0.2.tgz#26598a1d6167147cc192172970e7eed4e620384b"
+  integrity sha512-IJ8R/ZCFMHOcDIqoABs82jal00VrZx8Xkgfe7TOKoaRPAW5nH/VFlG23bXpeGdrmtqI9UobFPgUKgCuFc7Lncw==
+
+rxjs@6.6.7, rxjs@^6.5.4:
   version "6.6.7"
   resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9"
   integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==
@@ -9245,13 +9872,6 @@ selfsigned@^1.10.8:
   dependencies:
     node-forge "^0.10.0"
 
-semver-dsl@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/semver-dsl/-/semver-dsl-1.0.1.tgz#d3678de5555e8a61f629eed025366ae5f27340a0"
-  integrity sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=
-  dependencies:
-    semver "^5.3.0"
-
 "semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0:
   version "5.7.1"
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
@@ -9262,7 +9882,14 @@ semver@7.0.0:
   resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
   integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
 
-semver@7.3.5, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5:
+semver@7.3.4:
+  version "7.3.4"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97"
+  integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==
+  dependencies:
+    lru-cache "^6.0.0"
+
+semver@7.3.5, semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5:
   version "7.3.5"
   resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
   integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
@@ -9392,6 +10019,15 @@ shebang-regex@^3.0.0:
   resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
   integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
 
+side-channel@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
+  integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==
+  dependencies:
+    call-bind "^1.0.0"
+    get-intrinsic "^1.0.2"
+    object-inspect "^1.9.0"
+
 signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3:
   version "3.0.3"
   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
@@ -9649,7 +10285,7 @@ source-map@0.7.3, source-map@^0.7.3, source-map@~0.7.2:
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
   integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
 
-source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7:
+source-map@^0.5.0, source-map@^0.5.6:
   version "0.5.7"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
   integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
@@ -9672,7 +10308,7 @@ spdx-exceptions@^2.1.0:
   resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d"
   integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==
 
-spdx-expression-parse@^3.0.0:
+spdx-expression-parse@^3.0.0, spdx-expression-parse@^3.0.1:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679"
   integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==
@@ -9740,11 +10376,6 @@ split@^1.0.1:
   dependencies:
     through "2"
 
-sprintf-js@^1.1.2:
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673"
-  integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==
-
 sprintf-js@~1.0.2:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
@@ -9878,6 +10509,22 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2:
     is-fullwidth-code-point "^3.0.0"
     strip-ansi "^6.0.0"
 
+string.prototype.trimend@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80"
+  integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.3"
+
+string.prototype.trimstart@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed"
+  integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.3"
+
 string2compact@^1.3.0, string2compact@^1.3.2:
   version "1.3.2"
   resolved "https://registry.yarnpkg.com/string2compact/-/string2compact-1.3.2.tgz#c9d11a13f368404b8025425cc53f9916de1d0b8b"
@@ -9950,6 +10597,11 @@ strip-indent@^3.0.0:
   dependencies:
     min-indent "^1.0.0"
 
+strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
+  integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
+
 style-loader@3.2.1:
   version "3.2.1"
   resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.2.1.tgz#63cb920ec145c8669e9a50e92961452a1ef5dcde"
@@ -10136,7 +10788,7 @@ symbol-observable@4.0.0:
   resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205"
   integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==
 
-table@^6.6.0:
+table@^6.0.9, table@^6.6.0:
   version "6.7.1"
   resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2"
   integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==
@@ -10195,7 +10847,7 @@ terser@^4.6.3:
     source-map "~0.6.1"
     source-map-support "~0.5.12"
 
-text-table@0.2.0:
+text-table@0.2.0, text-table@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
   integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
@@ -10205,7 +10857,7 @@ thirty-two@^1.0.2:
   resolved "https://registry.yarnpkg.com/thirty-two/-/thirty-two-1.0.2.tgz#4ca2fffc02a51290d2744b9e3f557693ca6b627a"
   integrity sha1-TKL//AKlEpDSdEueP1V2k8prYno=
 
-through@2, through@X.X.X, through@^2.3.6:
+through@2, through@^2.3.6:
   version "2.3.8"
   resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
   integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
@@ -10232,6 +10884,13 @@ tmp@0.0.30:
   dependencies:
     os-tmpdir "~1.0.1"
 
+tmp@0.2.1, tmp@^0.2.1, tmp@~0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14"
+  integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==
+  dependencies:
+    rimraf "^3.0.0"
+
 tmp@^0.0.33:
   version "0.0.33"
   resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
@@ -10239,13 +10898,6 @@ tmp@^0.0.33:
   dependencies:
     os-tmpdir "~1.0.2"
 
-tmp@^0.2.1:
-  version "0.2.1"
-  resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14"
-  integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==
-  dependencies:
-    rimraf "^3.0.0"
-
 to-arraybuffer@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
@@ -10352,17 +11004,21 @@ ts-loader@^9.2.2:
     micromatch "^4.0.0"
     semver "^7.3.4"
 
-tslib@1.9.0:
-  version "1.9.0"
-  resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8"
-  integrity sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==
+tsconfig-paths@^3.9.0:
+  version "3.10.1"
+  resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.10.1.tgz#79ae67a68c15289fdf5c51cb74f397522d795ed7"
+  integrity sha512-rETidPDgCpltxF7MjBZlAFPUHv5aHH2MymyPvh+vEyWAED4Eb/WeMbsnD/JDr4OKPOA1TssDHgIcpTN5Kh0p6Q==
+  dependencies:
+    json5 "^2.2.0"
+    minimist "^1.2.0"
+    strip-bom "^3.0.0"
 
 tslib@2.3.0, tslib@>=1.7.1, tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
   integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
 
-tslib@^1.10.0, tslib@^1.13.0, tslib@^1.8.1, tslib@^1.9.0:
+tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0:
   version "1.14.1"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
   integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
@@ -10372,54 +11028,7 @@ tslib@~2.1.0:
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"
   integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==
 
-tslint-angular@^3.0.2:
-  version "3.0.3"
-  resolved "https://registry.yarnpkg.com/tslint-angular/-/tslint-angular-3.0.3.tgz#872d4fe36497d20582dbe4b8ed3338ff0c922c30"
-  integrity sha512-5xD1gLE89lBExfSbMslDw/ZfOZM0t0CJsoJa4svsgF7tlwVS3IpXjzNcNRN0RZqDBj+cdTlbeel6GpZ3PqpPiw==
-
-tslint-config-standard@^9.0.0:
-  version "9.0.0"
-  resolved "https://registry.yarnpkg.com/tslint-config-standard/-/tslint-config-standard-9.0.0.tgz#349a94819d93d5f8d803e3c71cb58ef38eff88e0"
-  integrity sha512-CAw9J743RnPMemQV/XQ4YyNreC+A1NItACfkm+cBedrOkz6CQfwlnbKn8anUXBfoa4Zo4tjAhblRbsMNcSLfSw==
-  dependencies:
-    tslint-eslint-rules "^5.3.1"
-
-tslint-eslint-rules@^5.3.1:
-  version "5.4.0"
-  resolved "https://registry.yarnpkg.com/tslint-eslint-rules/-/tslint-eslint-rules-5.4.0.tgz#e488cc9181bf193fe5cd7bfca213a7695f1737b5"
-  integrity sha512-WlSXE+J2vY/VPgIcqQuijMQiel+UtmXS+4nvK4ZzlDiqBfXse8FAvkNnTcYhnQyOTW5KFM+uRRGXxYhFpuBc6w==
-  dependencies:
-    doctrine "0.7.2"
-    tslib "1.9.0"
-    tsutils "^3.0.0"
-
-tslint@~6.1.0:
-  version "6.1.3"
-  resolved "https://registry.yarnpkg.com/tslint/-/tslint-6.1.3.tgz#5c23b2eccc32487d5523bd3a470e9aa31789d904"
-  integrity sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==
-  dependencies:
-    "@babel/code-frame" "^7.0.0"
-    builtin-modules "^1.1.1"
-    chalk "^2.3.0"
-    commander "^2.12.1"
-    diff "^4.0.1"
-    glob "^7.1.1"
-    js-yaml "^3.13.1"
-    minimatch "^3.0.4"
-    mkdirp "^0.5.3"
-    resolve "^1.3.2"
-    semver "^5.3.0"
-    tslib "^1.13.0"
-    tsutils "^2.29.0"
-
-tsutils@^2.29.0:
-  version "2.29.0"
-  resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99"
-  integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==
-  dependencies:
-    tslib "^1.8.1"
-
-tsutils@^3.0.0:
+tsutils@^3.21.0:
   version "3.21.0"
   resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
   integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==
@@ -10438,11 +11047,23 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
   resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
   integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
 
+type-check@^0.4.0, type-check@~0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
+  integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
+  dependencies:
+    prelude-ls "^1.2.1"
+
 type-fest@^0.18.0:
   version "0.18.1"
   resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f"
   integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==
 
+type-fest@^0.20.2:
+  version "0.20.2"
+  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
+  integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
+
 type-fest@^0.21.3:
   version "0.21.3"
   resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
@@ -10500,6 +11121,16 @@ uint64be@^2.0.2:
   dependencies:
     buffer-alloc "^1.1.0"
 
+unbox-primitive@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471"
+  integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==
+  dependencies:
+    function-bind "^1.1.1"
+    has-bigints "^1.0.1"
+    has-symbols "^1.0.2"
+    which-boxed-primitive "^1.0.2"
+
 unicode-canonical-property-names-ecmascript@^1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
@@ -10593,6 +11224,11 @@ universalify@^0.1.0:
   resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
   integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
 
+universalify@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
+  integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
+
 unordered-array-remove@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/unordered-array-remove/-/unordered-array-remove-1.0.2.tgz#c546e8f88e317a0cf2644c97ecb57dba66d250ef"
@@ -10721,7 +11357,7 @@ uuid@^3.3.2, uuid@^3.4.0:
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
   integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
 
-v8-compile-cache@^2.2.0, v8-compile-cache@^2.3.0:
+v8-compile-cache@^2.0.3, v8-compile-cache@^2.2.0, v8-compile-cache@^2.3.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
   integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
@@ -11135,6 +11771,17 @@ whatwg-fetch@^3.0.0:
   resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c"
   integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==
 
+which-boxed-primitive@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
+  integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==
+  dependencies:
+    is-bigint "^1.0.1"
+    is-boolean-object "^1.1.0"
+    is-number-object "^1.0.4"
+    is-string "^1.0.5"
+    is-symbol "^1.0.3"
+
 which-module@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
@@ -11166,6 +11813,11 @@ wildcard@^2.0.0:
   resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec"
   integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==
 
+word-wrap@^1.2.3:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
+  integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
+
 wrap-ansi@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
@@ -11291,6 +11943,11 @@ yaml@^1.10.0, yaml@^1.10.2:
   resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
   integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
 
+yargs-parser@20.0.0:
+  version "20.0.0"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.0.0.tgz#c65a1daaa977ad63cebdd52159147b789a4e19a9"
+  integrity sha512-8eblPHTL7ZWRkyjIZJjnGf+TijiKJSwA24svzLRVvtgoi/RZiKa9fFQTrlx0OKLnyHSdt/enrdadji6WFfESVA==
+
 yargs-parser@^13.1.2:
   version "13.1.2"
   resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38"
@@ -11407,11 +12064,6 @@ yocto-queue@^0.1.0:
   resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
   integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
 
-zone.js@~0.10.3:
-  version "0.10.3"
-  resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.10.3.tgz#3e5e4da03c607c9dcd92e37dd35687a14a140c16"
-  integrity sha512-LXVLVEq0NNOqK/fLJo3d0kfzd4sxwn2/h67/02pjCjfKDxgx1i9QqpvtHD8CrBnSSwMw5+dy11O7FRX5mkO7Cg==
-
 zone.js@~0.11.4:
   version "0.11.4"
   resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.11.4.tgz#0f70dcf6aba80f698af5735cbb257969396e8025"