From f3e1c035419d5025d0b6cf35b44443479469b811 Mon Sep 17 00:00:00 2001 From: Ben Koshy Date: Tue, 30 Aug 2022 16:20:18 +1000 Subject: [PATCH 01/12] Add clarifying example for camelCase targets (#577) --- docs/reference/targets.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/reference/targets.md b/docs/reference/targets.md index 93151000..b2fe5bed 100644 --- a/docs/reference/targets.md +++ b/docs/reference/targets.md @@ -121,4 +121,15 @@ with a matching name, the corresponding callback _will not_ be invoked again. ## Naming Conventions -Always use camelCase to specify target names, since they map directly to properties on your controller. +Always use camelCase to specify target names, since they map directly to properties on your controller: + +```html + + +``` + +```js +export default class extends Controller { + static targets = [ "camelCase" ] +} +``` From a2a030f5547745839c02eaa639f16c5a7e50e835 Mon Sep 17 00:00:00 2001 From: Marco Roth Date: Tue, 30 Aug 2022 08:40:11 +0200 Subject: [PATCH 02/12] Make Action Parameters attributes case-insensitive (#571) Even though this is not the "recommended way" to specify the data attributes for the params it's to match the existing behaviour of Actions, Controllers, Targets and Values. Resolves #561 --- src/core/action.ts | 2 +- .../action_params_case_insensitive_tests.ts | 62 +++++++++++++++++++ src/tests/modules/core/action_params_tests.ts | 8 +-- 3 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 src/tests/modules/core/action_params_case_insensitive_tests.ts diff --git a/src/core/action.ts b/src/core/action.ts index 63b59dde..00832f72 100644 --- a/src/core/action.ts +++ b/src/core/action.ts @@ -31,7 +31,7 @@ export class Action { get params() { const params:{ [key: string]: any } = {} - const pattern = new RegExp(`^data-${this.identifier}-(.+)-param$`) + const pattern = new RegExp(`^data-${this.identifier}-(.+)-param$`, "i") for (const { name, value } of Array.from(this.element.attributes)) { const match = name.match(pattern) diff --git a/src/tests/modules/core/action_params_case_insensitive_tests.ts b/src/tests/modules/core/action_params_case_insensitive_tests.ts new file mode 100644 index 00000000..764c4c39 --- /dev/null +++ b/src/tests/modules/core/action_params_case_insensitive_tests.ts @@ -0,0 +1,62 @@ +import ActionParamsTests from "./action_params_tests" + +export default class ActionParamsCaseInsensitiveTests extends ActionParamsTests { + identifier = ["CamelCase", "AnotherOne"] + fixtureHTML = ` +
+ +
+
+ ` + expectedParamsForCamelCase = { + id: 123, + multiWordExample: "/path", + payload: { + value: 1 + }, + active: true, + empty: "", + inactive: false + } + + async "test clicking on the element does return its params"() { + this.actionValue = "click->CamelCase#log" + await this.nextFrame + await this.triggerEvent(this.buttonElement, "click") + + this.assertActions( + { identifier: "CamelCase", params: this.expectedParamsForCamelCase }, + ) + } + + async "test global event return element params where the action is defined"() { + this.actionValue = "keydown@window->CamelCase#log" + await this.nextFrame + await this.triggerEvent("#outside", "keydown") + + this.assertActions( + { identifier: "CamelCase", params: this.expectedParamsForCamelCase }, + ) + } + + async "test passing params to namespaced controller"() { + this.actionValue = "click->CamelCase#log click->AnotherOne#log2" + await this.nextFrame + await this.triggerEvent(this.buttonElement, "click") + + this.assertActions( + { identifier: "CamelCase", params: this.expectedParamsForCamelCase }, + { identifier: "AnotherOne", params: { id: 234 } }, + ) + } +} diff --git a/src/tests/modules/core/action_params_tests.ts b/src/tests/modules/core/action_params_tests.ts index 982c03cc..eadb0a28 100644 --- a/src/tests/modules/core/action_params_tests.ts +++ b/src/tests/modules/core/action_params_tests.ts @@ -69,10 +69,10 @@ export default class ActionParamsTests extends LogControllerTestCase { { identifier: "c", params: this.expectedParamsForC }, ) - await this.buttonElement.setAttribute("data-c-id-param", "234") - await this.buttonElement.setAttribute("data-c-new-param", "new") - await this.buttonElement.removeAttribute("data-c-payload-param") - await this.triggerEvent(this.buttonElement, "click") + this.buttonElement.setAttribute("data-c-id-param", "234") + this.buttonElement.setAttribute("data-c-new-param", "new") + this.buttonElement.removeAttribute("data-c-payload-param") + this.triggerEvent(this.buttonElement, "click") this.assertActions( { identifier: "c", params: this.expectedParamsForC }, From 11e4c169e4a02bc8cba9502b82456c8e3fbc528f Mon Sep 17 00:00:00 2001 From: Elliot Crosby-McCullough Date: Thu, 15 Sep 2022 21:29:24 +0100 Subject: [PATCH 03/12] Retain backtrace for TypeErrors in value change callback (#584) This code was originally added to provide better error messaging for TypeErrors when assigning values. Unfortunately throwing a new error object causes us to lose the context (including stacktrace) of the original error, making it harder to locate the source. Changing the message and rethrowing the original error retain that information. --- src/core/value_observer.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/value_observer.ts b/src/core/value_observer.ts index 46a1ded3..1aea032a 100644 --- a/src/core/value_observer.ts +++ b/src/core/value_observer.ts @@ -96,9 +96,11 @@ export class ValueObserver implements StringMapObserverDelegate { changedMethod.call(this.receiver, value, oldValue) } catch (error) { - if (!(error instanceof TypeError)) throw error + if (error instanceof TypeError) { + error.message = `Stimulus Value "${this.context.identifier}.${descriptor.name}" - ${error.message}` + } - throw new TypeError(`Stimulus Value "${this.context.identifier}.${descriptor.name}" - ${error.message}`) + throw error } } } From 286f284946c92552ac77184c8ef49e6f64501fa3 Mon Sep 17 00:00:00 2001 From: Garen Torikian Date: Thu, 29 Sep 2022 20:09:07 -0400 Subject: [PATCH 04/12] Fix typo in controllers reference (#585) --- docs/reference/controllers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/controllers.md b/docs/reference/controllers.md index 800d908b..ac6f8223 100644 --- a/docs/reference/controllers.md +++ b/docs/reference/controllers.md @@ -45,7 +45,7 @@ For example, this element has a controller which is an instance of the class def
``` -The following is an example of how Stimulus will generate identifiers for controllers in it's require context: +The following is an example of how Stimulus will generate identifiers for controllers in its require context: If your controller file is named… | its identifier will be… --------------------------------- | ----------------------- From a5acb81bd0d66e877b08c68435f8cbfcef8fc9ff Mon Sep 17 00:00:00 2001 From: Marco Roth Date: Fri, 30 Sep 2022 02:09:35 +0200 Subject: [PATCH 05/12] Upgrade TypeScript to 4.8 (#583) --- package.json | 6 +-- src/core/binding.ts | 2 +- src/core/blessing.ts | 6 +-- src/core/context.ts | 6 +-- src/core/inheritable_statics.ts | 2 +- src/mutation-observers/value_list_observer.ts | 2 +- yarn.lock | 48 ++++++++++++++----- 7 files changed, 49 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index 4940e45d..b080cc5b 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ }, "devDependencies": { "@rollup/plugin-node-resolve": "^13.0.0", - "@rollup/plugin-typescript": "^8.2.1", + "@rollup/plugin-typescript": "^8.5.0", "@types/qunit": "^2.9.0", "@types/webpack-env": "^1.14.0", "concurrently": "^6.2.1", @@ -57,8 +57,8 @@ "rollup": "^2.53", "rollup-plugin-terser": "^7.0.2", "ts-loader": "^6.0.4", - "tslib": "^2.3.0", - "typescript": "^4.3.5", + "tslib": "^2.4.0", + "typescript": "^4.8.2", "webpack": "^4.39.1" } } diff --git a/src/core/binding.ts b/src/core/binding.ts index b33b9755..c8561a46 100644 --- a/src/core/binding.ts +++ b/src/core/binding.ts @@ -72,7 +72,7 @@ export class Binding { const actionEvent: ActionEvent = Object.assign(event, { params }) this.method.call(this.controller, actionEvent) this.context.logDebugActivity(this.methodName, { event, target, currentTarget, action: this.methodName }) - } catch (error) { + } catch (error: any) { const { identifier, controller, element, index } = this const detail = { identifier, controller, element, index, event } this.context.handleError(error, `invoking action "${this.action}"`, detail) diff --git a/src/core/blessing.ts b/src/core/blessing.ts index 27592656..b546c95b 100644 --- a/src/core/blessing.ts +++ b/src/core/blessing.ts @@ -65,7 +65,7 @@ const getOwnKeys = (() => { })() const extend = (() => { - function extendWithReflect>(constructor: T): T { + function extendWithReflect>(constructor: T): T { function extended() { return Reflect.construct(constructor, arguments, new.target) } @@ -88,7 +88,7 @@ const extend = (() => { try { testReflectExtension() return extendWithReflect - } catch (error) { - return >(constructor: T) => class extended extends constructor {} + } catch (error: any) { + return >(constructor: T) => class extended extends constructor {} } })() diff --git a/src/core/context.ts b/src/core/context.ts index 501aaf69..0190ad9e 100644 --- a/src/core/context.ts +++ b/src/core/context.ts @@ -28,7 +28,7 @@ export class Context implements ErrorHandler, TargetObserverDelegate { try { this.controller.initialize() this.logDebugActivity("initialize") - } catch (error) { + } catch (error: any) { this.handleError(error, "initializing controller") } } @@ -41,7 +41,7 @@ export class Context implements ErrorHandler, TargetObserverDelegate { try { this.controller.connect() this.logDebugActivity("connect") - } catch (error) { + } catch (error: any) { this.handleError(error, "connecting controller") } } @@ -50,7 +50,7 @@ export class Context implements ErrorHandler, TargetObserverDelegate { try { this.controller.disconnect() this.logDebugActivity("disconnect") - } catch (error) { + } catch (error: any) { this.handleError(error, "disconnecting controller") } diff --git a/src/core/inheritable_statics.ts b/src/core/inheritable_statics.ts index 6cc2a8fb..33558320 100644 --- a/src/core/inheritable_statics.ts +++ b/src/core/inheritable_statics.ts @@ -17,7 +17,7 @@ export function readInheritableStaticObjectPairs(constructor: Constructor< } function getAncestorsForConstructor(constructor: Constructor) { - const ancestors: Constructor<{}>[] = [] + const ancestors: Constructor[] = [] while (constructor) { ancestors.push(constructor) constructor = Object.getPrototypeOf(constructor) diff --git a/src/mutation-observers/value_list_observer.ts b/src/mutation-observers/value_list_observer.ts index f1cf28be..69d365c6 100644 --- a/src/mutation-observers/value_list_observer.ts +++ b/src/mutation-observers/value_list_observer.ts @@ -88,7 +88,7 @@ export class ValueListObserver implements TokenListObserverDelegate { try { const value = this.delegate.parseValueForToken(token) return { value } - } catch (error) { + } catch (error: any) { return { error } } } diff --git a/yarn.lock b/yarn.lock index e4dcff4c..06758357 100644 --- a/yarn.lock +++ b/yarn.lock @@ -35,10 +35,10 @@ is-module "^1.0.0" resolve "^1.19.0" -"@rollup/plugin-typescript@^8.2.1": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-8.2.1.tgz#f1a32d4030cc83432ce36a80a922280f0f0b5d44" - integrity sha512-Qd2E1pleDR4bwyFxqbjt4eJf+wB0UKVMLc7/BAFDGVdAXQMCsD4DUv5/7/ww47BZCYxWtJqe1Lo0KVNswBJlRw== +"@rollup/plugin-typescript@^8.5.0": + version "8.5.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-8.5.0.tgz#7ea11599a15b0a30fa7ea69ce3b791d41b862515" + integrity sha512-wMv1/scv0m/rXx21wD2IsBbJFba8wGF3ErJIr6IKRfRj49S85Lszbxb4DCo8iILpluTjk2GAAu9CoZt4G3ppgQ== dependencies: "@rollup/pluginutils" "^3.1.0" resolve "^1.17.0" @@ -2678,6 +2678,13 @@ is-core-module@^2.2.0: dependencies: has "^1.0.3" +is-core-module@^2.9.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed" + integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg== + dependencies: + has "^1.0.3" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -3767,7 +3774,7 @@ path-key@^2.0.0, path-key@^2.0.1: resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= -path-parse@^1.0.6: +path-parse@^1.0.6, path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -4135,7 +4142,7 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.10.0, resolve@^1.17.0, resolve@^1.19.0: +resolve@^1.10.0, resolve@^1.19.0: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -4143,6 +4150,15 @@ resolve@^1.10.0, resolve@^1.17.0, resolve@^1.19.0: is-core-module "^2.2.0" path-parse "^1.0.6" +resolve@^1.17.0: + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + responselike@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" @@ -4750,6 +4766,11 @@ supports-color@^8.1.0: dependencies: has-flag "^4.0.0" +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + tapable@^1.0.0, tapable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" @@ -4947,11 +4968,16 @@ tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.3, tslib@^2.3.0: +tslib@^2.0.3: version "2.3.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== +tslib@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" @@ -4997,10 +5023,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^4.3.5: - version "4.3.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" - integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== +typescript@^4.8.2: + version "4.8.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.2.tgz#e3b33d5ccfb5914e4eeab6699cf208adee3fd790" + integrity sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw== ua-parser-js@0.7.22: version "0.7.22" From f690a6a2e856a35ebdb78246285b734da541338e Mon Sep 17 00:00:00 2001 From: Marco Roth Date: Fri, 30 Sep 2022 02:37:53 +0200 Subject: [PATCH 06/12] ESLint + Prettier config (#582) * Install and configure ESLint and Prettier * fix eslint errors and prettier warnings --- .eslintignore | 2 + .eslintrc | 29 + .github/workflows/ci.yml | 25 +- .prettierignore | 2 + .prettierrc.json | 5 + package.json | 10 +- src/core/action.ts | 30 +- src/core/action_descriptor.ts | 16 +- src/core/application.ts | 10 +- src/core/binding_observer.ts | 4 +- src/core/blessing.ts | 19 +- src/core/class_properties.ts | 8 +- src/core/constructor.ts | 2 +- src/core/controller.ts | 7 +- src/core/definition.ts | 2 +- src/core/dispatcher.ts | 36 +- src/core/event_listener.ts | 6 +- src/core/guide.ts | 4 +- src/core/inheritable_statics.ts | 14 +- src/core/module.ts | 4 +- src/core/router.ts | 10 +- src/core/schema.ts | 2 +- src/core/scope.ts | 8 +- src/core/scope_observer.ts | 6 +- src/core/target_observer.ts | 2 +- src/core/target_properties.ts | 8 +- src/core/target_set.ts | 31 +- src/core/value_observer.ts | 6 +- src/core/value_properties.ts | 107 ++- src/multimap/indexed_multimap.ts | 2 +- src/multimap/multimap.ts | 8 +- src/mutation-observers/element_observer.ts | 2 +- src/mutation-observers/string_map_observer.ts | 6 +- src/mutation-observers/token_list_observer.ts | 16 +- src/mutation-observers/value_list_observer.ts | 6 +- src/tests/cases/application_test_case.ts | 8 +- src/tests/cases/controller_test_case.ts | 8 +- src/tests/cases/dom_test_case.ts | 16 +- src/tests/cases/index.ts | 12 +- src/tests/cases/log_controller_test_case.ts | 10 +- src/tests/cases/test_case.ts | 13 +- src/tests/controllers/class_controller.ts | 6 +- .../controllers/default_value_controller.ts | 2 +- src/tests/controllers/log_controller.ts | 2 +- src/tests/controllers/target_controller.ts | 15 +- src/tests/controllers/value_controller.ts | 6 +- .../fixtures/application_start/helpers.ts | 2 +- src/tests/index.ts | 4 +- .../action_params_case_insensitive_tests.ts | 16 +- src/tests/modules/core/action_params_tests.ts | 33 +- src/tests/modules/core/action_tests.ts | 5 +- src/tests/modules/core/action_timing_tests.ts | 2 +- .../modules/core/application_start_tests.ts | 2 +- src/tests/modules/core/application_tests.ts | 2 +- src/tests/modules/core/class_tests.ts | 2 +- src/tests/modules/core/default_value_tests.ts | 5 +- src/tests/modules/core/error_handler_tests.ts | 18 +- src/tests/modules/core/es6_tests.ts | 4 +- src/tests/modules/core/event_options_tests.ts | 54 +- src/tests/modules/core/legacy_target_tests.ts | 16 +- src/tests/modules/core/loading_tests.ts | 4 +- src/tests/modules/core/target_tests.ts | 59 +- src/tests/modules/core/value_tests.ts | 23 +- .../attribute_observer_tests.ts | 24 +- .../token_list_observer_tests.ts | 16 +- .../value_list_observer_tests.ts | 20 +- yarn.lock | 655 +++++++++++++++++- 67 files changed, 1128 insertions(+), 391 deletions(-) create mode 100644 .eslintignore create mode 100644 .eslintrc create mode 100644 .prettierignore create mode 100644 .prettierrc.json diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..1eae0cf6 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +dist/ +node_modules/ diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..92ca465e --- /dev/null +++ b/.eslintrc @@ -0,0 +1,29 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "plugins": [ + "@typescript-eslint", + "prettier" + ], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "prettier" + ], + "rules": { + "prefer-rest-params": "off", + "prettier/prettier": ["error"], + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], + "@typescript-eslint/ban-types": ["error", { + "types": { + "Function": false, + "Object": false, + "{}": false + } + }] + } +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aa5df970..6fdc9dd0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,18 +8,17 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 - with: - node-version: '12.4.0' - - uses: actions/cache@v2 - with: - path: | - node_modules - */*/node_modules - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: 16 + cache: 'yarn' - - run: yarn install + - name: Install Dependencies + run: yarn install --frozen-lockfile - - name: Test - run: yarn test + - name: Test + run: yarn test + + - name: Lint + run: yarn lint diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..1eae0cf6 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +dist/ +node_modules/ diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 00000000..58a8557a --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,5 @@ + { + "singleQuote": false, + "printWidth": 120, + "semi": false + } diff --git a/package.json b/package.json index b080cc5b..c487d7c3 100644 --- a/package.json +++ b/package.json @@ -39,19 +39,27 @@ "start": "concurrently \"npm:watch\" \"npm:start:examples\"", "start:examples": "cd examples && yarn install && node server.js", "test": "yarn build:test && karma start karma.conf.cjs", - "test:watch": "yarn test --auto-watch --no-single-run" + "test:watch": "yarn test --auto-watch --no-single-run", + "lint": "eslint . --ext .ts", + "format": "yarn lint --fix" }, "devDependencies": { "@rollup/plugin-node-resolve": "^13.0.0", "@rollup/plugin-typescript": "^8.5.0", "@types/qunit": "^2.9.0", "@types/webpack-env": "^1.14.0", + "@typescript-eslint/eslint-plugin": "^5.36.2", + "@typescript-eslint/parser": "^5.36.2", "concurrently": "^6.2.1", + "eslint": "^8.23.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-prettier": "^4.2.1", "karma": "^5.2.3", "karma-chrome-launcher": "^3.1.0", "karma-qunit": "^4.0.0", "karma-sauce-launcher": "^4.3.6", "karma-webpack": "^4.0.2", + "prettier": "^2.7.1", "qunit": "^2.9.2", "rimraf": "^3.0.2", "rollup": "^2.53", diff --git a/src/core/action.ts b/src/core/action.ts index 00832f72..2111e8a6 100644 --- a/src/core/action.ts +++ b/src/core/action.ts @@ -15,13 +15,13 @@ export class Action { } constructor(element: Element, index: number, descriptor: Partial) { - this.element = element - this.index = index - this.eventTarget = descriptor.eventTarget || element - this.eventName = descriptor.eventName || getDefaultEventNameForElement(element) || error("missing event name") + this.element = element + this.index = index + this.eventTarget = descriptor.eventTarget || element + this.eventName = descriptor.eventName || getDefaultEventNameForElement(element) || error("missing event name") this.eventOptions = descriptor.eventOptions || {} - this.identifier = descriptor.identifier || error("missing identifier") - this.methodName = descriptor.methodName || error("missing method name") + this.identifier = descriptor.identifier || error("missing identifier") + this.methodName = descriptor.methodName || error("missing method name") } toString() { @@ -30,14 +30,14 @@ export class Action { } get params() { - const params:{ [key: string]: any } = {} + const params: { [key: string]: any } = {} const pattern = new RegExp(`^data-${this.identifier}-(.+)-param$`, "i") for (const { name, value } of Array.from(this.element.attributes)) { const match = name.match(pattern) const key = match && match[1] if (key) { - params[camelize(key)]= typecast(value) + params[camelize(key)] = typecast(value) } } return params @@ -49,13 +49,13 @@ export class Action { } const defaultEventNames: { [tagName: string]: (element: Element) => string } = { - "a": e => "click", - "button": e => "click", - "form": e => "submit", - "details": e => "toggle", - "input": e => e.getAttribute("type") == "submit" ? "click" : "input", - "select": e => "change", - "textarea": e => "input" + a: () => "click", + button: () => "click", + form: () => "submit", + details: () => "toggle", + input: (e) => (e.getAttribute("type") == "submit" ? "click" : "input"), + select: () => "change", + textarea: () => "input", } export function getDefaultEventNameForElement(element: Element): string | undefined { diff --git a/src/core/action_descriptor.ts b/src/core/action_descriptor.ts index 260ae31a..82bb3e3c 100644 --- a/src/core/action_descriptor.ts +++ b/src/core/action_descriptor.ts @@ -34,7 +34,7 @@ export const defaultActionDescriptorFilters: ActionDescriptorFilters = { } else { return true } - } + }, } // capture nos.: 12 23 4 43 1 5 56 7 768 9 98 @@ -44,11 +44,11 @@ export function parseActionDescriptorString(descriptorString: string): Partial - Object.assign(options, { [token.replace(/^!/, "")]: !/^!/.test(token) }) - , {}) + return eventOptions + .split(":") + .reduce((options, token) => Object.assign(options, { [token.replace(/^!/, "")]: !/^!/.test(token) }), {}) } export function stringifyEventTarget(eventTarget: EventTarget) { diff --git a/src/core/application.ts b/src/core/application.ts index 0efd01fc..74a451fe 100644 --- a/src/core/application.ts +++ b/src/core/application.ts @@ -14,7 +14,7 @@ export class Application implements ErrorHandler { readonly router: Router readonly actionDescriptorFilters: ActionDescriptorFilters logger: Logger = console - debug: boolean = false + debug = false static start(element?: Element, schema?: Schema): Application { const application = new Application(element, schema) @@ -57,7 +57,7 @@ export class Application implements ErrorHandler { load(definitions: Definition[]): void load(head: Definition | Definition[], ...rest: Definition[]) { const definitions = Array.isArray(head) ? head : [head, ...rest] - definitions.forEach(definition => { + definitions.forEach((definition) => { if ((definition.controllerConstructor as any).shouldLoad) { this.router.loadDefinition(definition) } @@ -68,13 +68,13 @@ export class Application implements ErrorHandler { unload(identifiers: string[]): void unload(head: string | string[], ...rest: string[]) { const identifiers = Array.isArray(head) ? head : [head, ...rest] - identifiers.forEach(identifier => this.router.unloadIdentifier(identifier)) + identifiers.forEach((identifier) => this.router.unloadIdentifier(identifier)) } // Controllers get controllers(): Controller[] { - return this.router.contexts.map(context => context.controller) + return this.router.contexts.map((context) => context.controller) } getControllerForElementAndIdentifier(element: Element, identifier: string): Controller | null { @@ -108,7 +108,7 @@ export class Application implements ErrorHandler { } function domReady() { - return new Promise(resolve => { + return new Promise((resolve) => { if (document.readyState == "loading") { document.addEventListener("DOMContentLoaded", () => resolve()) } else { diff --git a/src/core/binding_observer.ts b/src/core/binding_observer.ts index e1393704..99a5019f 100644 --- a/src/core/binding_observer.ts +++ b/src/core/binding_observer.ts @@ -19,7 +19,7 @@ export class BindingObserver implements ValueListObserverDelegate { constructor(context: Context, delegate: BindingObserverDelegate) { this.context = context this.delegate = delegate - this.bindingsByAction = new Map + this.bindingsByAction = new Map() } start() { @@ -72,7 +72,7 @@ export class BindingObserver implements ValueListObserverDelegate { } private disconnectAllActions() { - this.bindings.forEach(binding => this.delegate.bindingDisconnected(binding)) + this.bindings.forEach((binding) => this.delegate.bindingDisconnected(binding)) this.bindingsByAction.clear() } diff --git a/src/core/blessing.ts b/src/core/blessing.ts index b546c95b..699e005a 100644 --- a/src/core/blessing.ts +++ b/src/core/blessing.ts @@ -23,14 +23,14 @@ function getBlessedProperties(constructor: Constructor) { return blessings.reduce((blessedProperties, blessing) => { const properties = blessing(constructor) for (const key in properties) { - const descriptor = blessedProperties[key] || {} as PropertyDescriptor + const descriptor = blessedProperties[key] || ({} as PropertyDescriptor) blessedProperties[key] = Object.assign(descriptor, properties[key]) } return blessedProperties }, {} as PropertyDescriptorMap) } -function getShadowProperties(prototype: any, properties: PropertyDescriptorMap) { +function getShadowProperties(prototype: any, properties: PropertyDescriptorMap) { return getOwnKeys(properties).reduce((shadowProperties, key) => { const descriptor = getShadowedDescriptor(prototype, properties, key) if (descriptor) { @@ -55,10 +55,7 @@ function getShadowedDescriptor(prototype: any, properties: PropertyDescriptorMap const getOwnKeys = (() => { if (typeof Object.getOwnPropertySymbols == "function") { - return (object: any) => [ - ...Object.getOwnPropertyNames(object), - ...Object.getOwnPropertySymbols(object) - ] + return (object: any) => [...Object.getOwnPropertyNames(object), ...Object.getOwnPropertySymbols(object)] } else { return Object.getOwnPropertyNames } @@ -71,7 +68,7 @@ const extend = (() => { } extended.prototype = Object.create(constructor.prototype, { - constructor: { value: extended } + constructor: { value: extended }, }) Reflect.setPrototypeOf(extended, constructor) @@ -79,10 +76,12 @@ const extend = (() => { } function testReflectExtension() { - const a = function(this: any) { this.a.call(this) } as any + const a = function (this: any) { + this.a.call(this) + } as any const b = extendWithReflect(a) - b.prototype.a = function() {} - return new b + b.prototype.a = function () {} + return new b() } try { diff --git a/src/core/class_properties.ts b/src/core/class_properties.ts index 34389d11..0c034acf 100644 --- a/src/core/class_properties.ts +++ b/src/core/class_properties.ts @@ -21,19 +21,19 @@ function propertiesForClassDefinition(key: string) { const attribute = classes.getAttributeName(key) throw new Error(`Missing attribute "${attribute}"`) } - } + }, }, [`${key}Classes`]: { get(this: Controller) { return this.classes.getAll(key) - } + }, }, [`has${capitalize(key)}Class`]: { get(this: Controller) { return this.classes.has(key) - } - } + }, + }, } } diff --git a/src/core/constructor.ts b/src/core/constructor.ts index 9628e85d..73ebf339 100644 --- a/src/core/constructor.ts +++ b/src/core/constructor.ts @@ -1 +1 @@ -export type Constructor = new(...args: any[]) => T +export type Constructor = new (...args: any[]) => T diff --git a/src/core/controller.ts b/src/core/controller.ts index f46591e6..d31f1456 100644 --- a/src/core/controller.ts +++ b/src/core/controller.ts @@ -7,7 +7,7 @@ import { ValuePropertiesBlessing, ValueDefinitionMap } from "./value_properties" export type ControllerConstructor = Constructor export class Controller { - static blessings = [ ClassPropertiesBlessing, TargetPropertiesBlessing, ValuePropertiesBlessing ] + static blessings = [ClassPropertiesBlessing, TargetPropertiesBlessing, ValuePropertiesBlessing] static targets: string[] = [] static values: ValueDefinitionMap = {} @@ -61,7 +61,10 @@ export class Controller { // Override in your subclass to respond when the controller is disconnected from the DOM } - dispatch(eventName: string, { target = this.element, detail = {}, prefix = this.identifier, bubbles = true, cancelable = true } = {}) { + dispatch( + eventName: string, + { target = this.element, detail = {}, prefix = this.identifier, bubbles = true, cancelable = true } = {} + ) { const type = prefix ? `${prefix}:${eventName}` : eventName const event = new CustomEvent(type, { detail, bubbles, cancelable }) target.dispatchEvent(event) diff --git a/src/core/definition.ts b/src/core/definition.ts index 034b2a19..ad896c49 100644 --- a/src/core/definition.ts +++ b/src/core/definition.ts @@ -9,6 +9,6 @@ export interface Definition { export function blessDefinition(definition: Definition): Definition { return { identifier: definition.identifier, - controllerConstructor: bless(definition.controllerConstructor) + controllerConstructor: bless(definition.controllerConstructor), } } diff --git a/src/core/dispatcher.ts b/src/core/dispatcher.ts index 29a74af0..cfa9e365 100644 --- a/src/core/dispatcher.ts +++ b/src/core/dispatcher.ts @@ -10,27 +10,29 @@ export class Dispatcher implements BindingObserverDelegate { constructor(application: Application) { this.application = application - this.eventListenerMaps = new Map + this.eventListenerMaps = new Map() this.started = false } start() { if (!this.started) { this.started = true - this.eventListeners.forEach(eventListener => eventListener.connect()) + this.eventListeners.forEach((eventListener) => eventListener.connect()) } } stop() { if (this.started) { this.started = false - this.eventListeners.forEach(eventListener => eventListener.disconnect()) + this.eventListeners.forEach((eventListener) => eventListener.disconnect()) } } get eventListeners(): EventListener[] { - return Array.from(this.eventListenerMaps.values()) - .reduce((listeners, map) => listeners.concat(Array.from(map.values())), [] as EventListener[]) + return Array.from(this.eventListenerMaps.values()).reduce( + (listeners, map) => listeners.concat(Array.from(map.values())), + [] as EventListener[] + ) } // Binding observer delegate @@ -54,7 +56,11 @@ export class Dispatcher implements BindingObserverDelegate { return this.fetchEventListener(eventTarget, eventName, eventOptions) } - private fetchEventListener(eventTarget: EventTarget, eventName: string, eventOptions: AddEventListenerOptions): EventListener { + private fetchEventListener( + eventTarget: EventTarget, + eventName: string, + eventOptions: AddEventListenerOptions + ): EventListener { const eventListenerMap = this.fetchEventListenerMapForEventTarget(eventTarget) const cacheKey = this.cacheKey(eventName, eventOptions) let eventListener = eventListenerMap.get(cacheKey) @@ -65,7 +71,11 @@ export class Dispatcher implements BindingObserverDelegate { return eventListener } - private createEventListener(eventTarget: EventTarget, eventName: string, eventOptions: AddEventListenerOptions): EventListener { + private createEventListener( + eventTarget: EventTarget, + eventName: string, + eventOptions: AddEventListenerOptions + ): EventListener { const eventListener = new EventListener(eventTarget, eventName, eventOptions) if (this.started) { eventListener.connect() @@ -76,17 +86,19 @@ export class Dispatcher implements BindingObserverDelegate { private fetchEventListenerMapForEventTarget(eventTarget: EventTarget): Map { let eventListenerMap = this.eventListenerMaps.get(eventTarget) if (!eventListenerMap) { - eventListenerMap = new Map + eventListenerMap = new Map() this.eventListenerMaps.set(eventTarget, eventListenerMap) } return eventListenerMap } private cacheKey(eventName: string, eventOptions: any): string { - const parts = [ eventName ] - Object.keys(eventOptions).sort().forEach(key => { - parts.push(`${eventOptions[key] ? "" : "!"}${key}`) - }) + const parts = [eventName] + Object.keys(eventOptions) + .sort() + .forEach((key) => { + parts.push(`${eventOptions[key] ? "" : "!"}${key}`) + }) return parts.join(":") } } diff --git a/src/core/event_listener.ts b/src/core/event_listener.ts index fa923f4d..f78680d6 100644 --- a/src/core/event_listener.ts +++ b/src/core/event_listener.ts @@ -45,11 +45,11 @@ export class EventListener implements EventListenerObject { get bindings(): Binding[] { return Array.from(this.unorderedBindings).sort((left, right) => { - const leftIndex = left.index, rightIndex = right.index + const leftIndex = left.index, + rightIndex = right.index return leftIndex < rightIndex ? -1 : leftIndex > rightIndex ? 1 : 0 }) } - } function extendEvent(event: Event) { @@ -62,7 +62,7 @@ function extendEvent(event: Event) { stopImmediatePropagation() { this.immediatePropagationStopped = true stopImmediatePropagation.call(this) - } + }, }) } } diff --git a/src/core/guide.ts b/src/core/guide.ts index c3f19414..e96a2293 100644 --- a/src/core/guide.ts +++ b/src/core/guide.ts @@ -2,7 +2,7 @@ import { Logger } from "./logger" export class Guide { readonly logger: Logger - readonly warnedKeysByObject: WeakMap> = new WeakMap + readonly warnedKeysByObject: WeakMap> = new WeakMap() constructor(logger: Logger) { this.logger = logger @@ -12,7 +12,7 @@ export class Guide { let warnedKeys: Set | undefined = this.warnedKeysByObject.get(object) if (!warnedKeys) { - warnedKeys = new Set + warnedKeys = new Set() this.warnedKeysByObject.set(object, warnedKeys) } diff --git a/src/core/inheritable_statics.ts b/src/core/inheritable_statics.ts index 33558320..fa1239c2 100644 --- a/src/core/inheritable_statics.ts +++ b/src/core/inheritable_statics.ts @@ -2,16 +2,18 @@ import { Constructor } from "./constructor" export function readInheritableStaticArrayValues(constructor: Constructor, propertyName: string) { const ancestors = getAncestorsForConstructor(constructor) - return Array.from(ancestors.reduce((values, constructor) => { - getOwnStaticArrayValues(constructor, propertyName).forEach(name => values.add(name)) - return values - }, new Set as Set)) + return Array.from( + ancestors.reduce((values, constructor) => { + getOwnStaticArrayValues(constructor, propertyName).forEach((name) => values.add(name)) + return values + }, new Set() as Set) + ) } export function readInheritableStaticObjectPairs(constructor: Constructor, propertyName: string) { const ancestors = getAncestorsForConstructor(constructor) return ancestors.reduce((pairs, constructor) => { - pairs.push(...getOwnStaticObjectPairs(constructor, propertyName) as any) + pairs.push(...(getOwnStaticObjectPairs(constructor, propertyName) as any)) return pairs }, [] as [string, U][]) } @@ -32,5 +34,5 @@ function getOwnStaticArrayValues(constructor: Constructor, propertyName: s function getOwnStaticObjectPairs(constructor: Constructor, propertyName: string) { const definition = (constructor as any)[propertyName] - return definition ? Object.keys(definition).map(key => [key, definition[key]] as [string, U]) : [] + return definition ? Object.keys(definition).map((key) => [key, definition[key]] as [string, U]) : [] } diff --git a/src/core/module.ts b/src/core/module.ts index e5b404c3..5635abca 100644 --- a/src/core/module.ts +++ b/src/core/module.ts @@ -13,8 +13,8 @@ export class Module { constructor(application: Application, definition: Definition) { this.application = application this.definition = blessDefinition(definition) - this.contextsByScope = new WeakMap - this.connectedContexts = new Set + this.contextsByScope = new WeakMap() + this.connectedContexts = new Set() } get identifier(): string { diff --git a/src/core/router.ts b/src/core/router.ts index 5ea1a2a8..e0ce3af1 100644 --- a/src/core/router.ts +++ b/src/core/router.ts @@ -15,8 +15,8 @@ export class Router implements ScopeObserverDelegate { constructor(application: Application) { this.application = application this.scopeObserver = new ScopeObserver(this.element, this.schema, this) - this.scopesByIdentifier = new Multimap - this.modulesByIdentifier = new Map + this.scopesByIdentifier = new Multimap() + this.modulesByIdentifier = new Map() } get element() { @@ -67,7 +67,7 @@ export class Router implements ScopeObserverDelegate { getContextForElementAndIdentifier(element: Element, identifier: string) { const module = this.modulesByIdentifier.get(identifier) if (module) { - return module.contexts.find(context => context.element == element) + return module.contexts.find((context) => context.element == element) } } @@ -104,12 +104,12 @@ export class Router implements ScopeObserverDelegate { private connectModule(module: Module) { this.modulesByIdentifier.set(module.identifier, module) const scopes = this.scopesByIdentifier.getValuesForKey(module.identifier) - scopes.forEach(scope => module.connectContextForScope(scope)) + scopes.forEach((scope) => module.connectContextForScope(scope)) } private disconnectModule(module: Module) { this.modulesByIdentifier.delete(module.identifier) const scopes = this.scopesByIdentifier.getValuesForKey(module.identifier) - scopes.forEach(scope => module.disconnectContextForScope(scope)) + scopes.forEach((scope) => module.disconnectContextForScope(scope)) } } diff --git a/src/core/schema.ts b/src/core/schema.ts index 8ed55abc..6b04eb26 100644 --- a/src/core/schema.ts +++ b/src/core/schema.ts @@ -9,5 +9,5 @@ export const defaultSchema: Schema = { controllerAttribute: "data-controller", actionAttribute: "data-action", targetAttribute: "data-target", - targetAttributeForScope: identifier => `data-${identifier}-target` + targetAttributeForScope: (identifier) => `data-${identifier}-target`, } diff --git a/src/core/scope.ts b/src/core/scope.ts index bcd58ed0..9e70f8e0 100644 --- a/src/core/scope.ts +++ b/src/core/scope.ts @@ -23,15 +23,13 @@ export class Scope { } findElement(selector: string): Element | undefined { - return this.element.matches(selector) - ? this.element - : this.queryElements(selector).find(this.containsElement) + return this.element.matches(selector) ? this.element : this.queryElements(selector).find(this.containsElement) } findAllElements(selector: string): Element[] { return [ - ...this.element.matches(selector) ? [this.element] : [], - ...this.queryElements(selector).filter(this.containsElement) + ...(this.element.matches(selector) ? [this.element] : []), + ...this.queryElements(selector).filter(this.containsElement), ] } diff --git a/src/core/scope_observer.ts b/src/core/scope_observer.ts index 1b55811b..6ddcd0ef 100644 --- a/src/core/scope_observer.ts +++ b/src/core/scope_observer.ts @@ -22,8 +22,8 @@ export class ScopeObserver implements ValueListObserverDelegate { this.schema = schema this.delegate = delegate this.valueListObserver = new ValueListObserver(this.element, this.controllerAttribute, this) - this.scopesByIdentifierByElement = new WeakMap - this.scopeReferenceCounts = new WeakMap + this.scopesByIdentifierByElement = new WeakMap() + this.scopeReferenceCounts = new WeakMap() } start() { @@ -74,7 +74,7 @@ export class ScopeObserver implements ValueListObserverDelegate { private fetchScopesByIdentifierForElement(element: Element) { let scopesByIdentifier = this.scopesByIdentifierByElement.get(element) if (!scopesByIdentifier) { - scopesByIdentifier = new Map + scopesByIdentifier = new Map() this.scopesByIdentifierByElement.set(element, scopesByIdentifier) } return scopesByIdentifier diff --git a/src/core/target_observer.ts b/src/core/target_observer.ts index 2f144ef2..e59dbb43 100644 --- a/src/core/target_observer.ts +++ b/src/core/target_observer.ts @@ -16,7 +16,7 @@ export class TargetObserver implements TokenListObserverDelegate { constructor(context: Context, delegate: TargetObserverDelegate) { this.context = context this.delegate = delegate - this.targetsByName = new Multimap + this.targetsByName = new Multimap() } start() { diff --git a/src/core/target_properties.ts b/src/core/target_properties.ts index 8aad1af6..10f280d4 100644 --- a/src/core/target_properties.ts +++ b/src/core/target_properties.ts @@ -20,19 +20,19 @@ function propertiesForTargetDefinition(name: string) { } else { throw new Error(`Missing target element "${name}" for "${this.identifier}" controller`) } - } + }, }, [`${name}Targets`]: { get(this: Controller) { return this.targets.findAll(name) - } + }, }, [`has${capitalize(name)}Target`]: { get(this: Controller) { return this.targets.has(name) - } - } + }, + }, } } diff --git a/src/core/target_set.ts b/src/core/target_set.ts index ac79e6bf..bd83e691 100644 --- a/src/core/target_set.ts +++ b/src/core/target_set.ts @@ -25,19 +25,21 @@ export class TargetSet { } find(...targetNames: string[]) { - return targetNames.reduce((target, targetName) => - target - || this.findTarget(targetName) - || this.findLegacyTarget(targetName) - , undefined as Element | undefined) + return targetNames.reduce( + (target, targetName) => target || this.findTarget(targetName) || this.findLegacyTarget(targetName), + undefined as Element | undefined + ) } findAll(...targetNames: string[]) { - return targetNames.reduce((targets, targetName) => [ - ...targets, - ...this.findAllTargets(targetName), - ...this.findAllLegacyTargets(targetName) - ], [] as Element[]) + return targetNames.reduce( + (targets, targetName) => [ + ...targets, + ...this.findAllTargets(targetName), + ...this.findAllLegacyTargets(targetName), + ], + [] as Element[] + ) } private findTarget(targetName: string) { @@ -62,7 +64,7 @@ export class TargetSet { private findAllLegacyTargets(targetName: string) { const selector = this.getLegacySelectorForTargetName(targetName) - return this.scope.findAllElements(selector).map(element => this.deprecate(element, targetName)) + return this.scope.findAllElements(selector).map((element) => this.deprecate(element, targetName)) } private getLegacySelectorForTargetName(targetName: string) { @@ -75,9 +77,12 @@ export class TargetSet { const { identifier } = this const attributeName = this.schema.targetAttribute const revisedAttributeName = this.schema.targetAttributeForScope(identifier) - this.guide.warn(element, `target:${targetName}`, + this.guide.warn( + element, + `target:${targetName}`, `Please replace ${attributeName}="${identifier}.${targetName}" with ${revisedAttributeName}="${targetName}". ` + - `The ${attributeName} attribute is deprecated and will be removed in a future version of Stimulus.`) + `The ${attributeName} attribute is deprecated and will be removed in a future version of Stimulus.` + ) } return element } diff --git a/src/core/value_observer.ts b/src/core/value_observer.ts index 1aea032a..2d12c9cd 100644 --- a/src/core/value_observer.ts +++ b/src/core/value_observer.ts @@ -107,13 +107,13 @@ export class ValueObserver implements StringMapObserverDelegate { private get valueDescriptors() { const { valueDescriptorMap } = this - return Object.keys(valueDescriptorMap).map(key => valueDescriptorMap[key]) + return Object.keys(valueDescriptorMap).map((key) => valueDescriptorMap[key]) } private get valueDescriptorNameMap() { - const descriptors: { [type: string]: ValueDescriptor } = {} + const descriptors: { [type: string]: ValueDescriptor } = {} - Object.keys(this.valueDescriptorMap).forEach(key => { + Object.keys(this.valueDescriptorMap).forEach((key) => { const descriptor = this.valueDescriptorMap[key] descriptors[descriptor.name] = descriptor }) diff --git a/src/core/value_properties.ts b/src/core/value_properties.ts index b25c63ab..6a431314 100644 --- a/src/core/value_properties.ts +++ b/src/core/value_properties.ts @@ -13,8 +13,8 @@ export function ValuePropertiesBlessing(constructor: Constructor) { const attributeName = this.data.getAttributeNameForKey(valueDescriptor.key) return Object.assign(result, { [attributeName]: valueDescriptor }) }, {} as ValueDescriptorMap) - } - } + }, + }, } return valueDefinitionPairs.reduce((properties, valueDefinitionPair) => { @@ -22,7 +22,10 @@ export function ValuePropertiesBlessing(constructor: Constructor) { }, propertyDescriptorMap) } -export function propertiesForValueDefinitionPair(valueDefinitionPair: ValueDefinitionPair, controller?: string): PropertyDescriptorMap { +export function propertiesForValueDefinitionPair( + valueDefinitionPair: ValueDefinitionPair, + controller?: string +): PropertyDescriptorMap { const definition = parseValueDefinitionPair(valueDefinitionPair, controller) const { key, name, reader: read, writer: write } = definition @@ -43,24 +46,24 @@ export function propertiesForValueDefinitionPair(valueDefinitionPair: ValueDe } else { this.data.set(key, write(value)) } - } + }, }, [`has${capitalize(name)}`]: { get(this: Controller): boolean { return this.data.has(key) || definition.hasCustomDefaultValue - } - } + }, + }, } } export type ValueDescriptor = { - type: ValueType, - key: string, - name: string, - defaultValue: ValueTypeDefault, - hasCustomDefaultValue: boolean, - reader: Reader, + type: ValueType + key: string + name: string + defaultValue: ValueTypeDefault + hasCustomDefaultValue: boolean + reader: Reader writer: Writer } @@ -72,9 +75,9 @@ export type ValueDefinitionPair = [string, ValueTypeDefinition] export type ValueTypeConstant = typeof Array | typeof Boolean | typeof Number | typeof Object | typeof String -export type ValueTypeDefault = Array | Boolean | Number | Object | String +export type ValueTypeDefault = Array | boolean | number | Object | string -export type ValueTypeObject = { type: ValueTypeConstant, default: ValueTypeDefault } +export type ValueTypeObject = { type: ValueTypeConstant; default: ValueTypeDefault } export type ValueTypeDefinition = ValueTypeConstant | ValueTypeDefault | ValueTypeObject @@ -90,26 +93,34 @@ function parseValueDefinitionPair([token, typeDefinition]: ValueDefinitionPair, function parseValueTypeConstant(constant: ValueTypeConstant) { switch (constant) { - case Array: return "array" - case Boolean: return "boolean" - case Number: return "number" - case Object: return "object" - case String: return "string" + case Array: + return "array" + case Boolean: + return "boolean" + case Number: + return "number" + case Object: + return "object" + case String: + return "string" } } function parseValueTypeDefault(defaultValue: ValueTypeDefault) { switch (typeof defaultValue) { - case "boolean": return "boolean" - case "number": return "number" - case "string": return "string" + case "boolean": + return "boolean" + case "number": + return "number" + case "string": + return "string" } if (Array.isArray(defaultValue)) return "array" if (Object.prototype.toString.call(defaultValue) === "[object Object]") return "object" } -function parseValueTypeObject(payload: { controller?: string, token: string, typeObject: ValueTypeObject }) { +function parseValueTypeObject(payload: { controller?: string; token: string; typeObject: ValueTypeObject }) { const typeFromObject = parseValueTypeConstant(payload.typeObject.type) if (!typeFromObject) return @@ -119,17 +130,23 @@ function parseValueTypeObject(payload: { controller?: string, token: string, typ if (typeFromObject !== defaultValueType) { const propertyPath = payload.controller ? `${payload.controller}.${payload.token}` : payload.token - throw new Error(`The specified default value for the Stimulus Value "${propertyPath}" must match the defined type "${typeFromObject}". The provided default value of "${payload.typeObject.default}" is of type "${defaultValueType}".`) + throw new Error( + `The specified default value for the Stimulus Value "${propertyPath}" must match the defined type "${typeFromObject}". The provided default value of "${payload.typeObject.default}" is of type "${defaultValueType}".` + ) } return typeFromObject } -function parseValueTypeDefinition(payload: { controller?: string, token: string, typeDefinition: ValueTypeDefinition }): ValueType { +function parseValueTypeDefinition(payload: { + controller?: string + token: string + typeDefinition: ValueTypeDefinition +}): ValueType { const typeFromObject = parseValueTypeObject({ controller: payload.controller, token: payload.token, - typeObject: payload.typeDefinition as ValueTypeObject + typeObject: payload.typeDefinition as ValueTypeObject, }) const typeFromDefaultValue = parseValueTypeDefault(payload.typeDefinition as ValueTypeDefault) const typeFromConstant = parseValueTypeConstant(payload.typeDefinition as ValueTypeConstant) @@ -154,26 +171,38 @@ function defaultValueForDefinition(typeDefinition: ValueTypeDefinition): ValueTy return typeDefinition } -function valueDescriptorForTokenAndTypeDefinition(payload: { token: string, typeDefinition: ValueTypeDefinition, controller?: string }) { +function valueDescriptorForTokenAndTypeDefinition(payload: { + token: string + typeDefinition: ValueTypeDefinition + controller?: string +}) { const key = `${dasherize(payload.token)}-value` const type = parseValueTypeDefinition(payload) return { type, key, name: camelize(key), - get defaultValue() { return defaultValueForDefinition(payload.typeDefinition) }, - get hasCustomDefaultValue() { return parseValueTypeDefault(payload.typeDefinition) !== undefined }, + get defaultValue() { + return defaultValueForDefinition(payload.typeDefinition) + }, + get hasCustomDefaultValue() { + return parseValueTypeDefault(payload.typeDefinition) !== undefined + }, reader: readers[type], - writer: writers[type] || writers.default + writer: writers[type] || writers.default, } } const defaultValuesByType = { - get array() { return [] }, + get array() { + return [] + }, boolean: false, number: 0, - get object() { return {} }, - string: "" + get object() { + return {} + }, + string: "", } type Reader = (value: string) => any @@ -182,7 +211,9 @@ const readers: { [type: string]: Reader } = { array(value: string): any[] { const array = JSON.parse(value) if (!Array.isArray(array)) { - throw new TypeError(`expected value of type "array" but instead got value "${value}" of type "${parseValueTypeDefault(array)}"`) + throw new TypeError( + `expected value of type "array" but instead got value "${value}" of type "${parseValueTypeDefault(array)}"` + ) } return array }, @@ -198,14 +229,16 @@ const readers: { [type: string]: Reader } = { object(value: string): object { const object = JSON.parse(value) if (object === null || typeof object != "object" || Array.isArray(object)) { - throw new TypeError(`expected value of type "object" but instead got value "${value}" of type "${parseValueTypeDefault(object)}"`) + throw new TypeError( + `expected value of type "object" but instead got value "${value}" of type "${parseValueTypeDefault(object)}"` + ) } return object }, string(value: string): string { return value - } + }, } type Writer = (value: any) => string @@ -213,7 +246,7 @@ type Writer = (value: any) => string const writers: { [type: string]: Writer } = { default: writeString, array: writeJSON, - object: writeJSON + object: writeJSON, } function writeJSON(value: any) { diff --git a/src/multimap/indexed_multimap.ts b/src/multimap/indexed_multimap.ts index 8da60005..e81572da 100644 --- a/src/multimap/indexed_multimap.ts +++ b/src/multimap/indexed_multimap.ts @@ -6,7 +6,7 @@ export class IndexedMultimap extends Multimap { constructor() { super() - this.keysByValue = new Map + this.keysByValue = new Map() } get values(): V[] { diff --git a/src/multimap/multimap.ts b/src/multimap/multimap.ts index b9595d43..adc89f55 100644 --- a/src/multimap/multimap.ts +++ b/src/multimap/multimap.ts @@ -13,7 +13,7 @@ export class Multimap { get values(): V[] { const sets = Array.from(this.valuesByKey.values()) - return sets.reduce((values, set) => values.concat(Array.from(set)), []) + return sets.reduce((values, set) => values.concat(Array.from(set)), []) } get size(): number { @@ -40,7 +40,7 @@ export class Multimap { hasValue(value: V): boolean { const sets = Array.from(this.valuesByKey.values()) - return sets.some(set => set.has(value)) + return sets.some((set) => set.has(value)) } getValuesForKey(key: K): V[] { @@ -50,7 +50,7 @@ export class Multimap { getKeysForValue(value: V): K[] { return Array.from(this.valuesByKey) - .filter(([key, values]) => values.has(value)) - .map(([key, values]) => key) + .filter(([_key, values]) => values.has(value)) + .map(([key, _values]) => key) } } diff --git a/src/mutation-observers/element_observer.ts b/src/mutation-observers/element_observer.ts index f07e9450..2902cd00 100644 --- a/src/mutation-observers/element_observer.ts +++ b/src/mutation-observers/element_observer.ts @@ -21,7 +21,7 @@ export class ElementObserver { this.started = false this.delegate = delegate - this.elements = new Set + this.elements = new Set() this.mutationObserver = new MutationObserver((mutations) => this.processMutations(mutations)) } diff --git a/src/mutation-observers/string_map_observer.ts b/src/mutation-observers/string_map_observer.ts index cdf2b70b..245888d4 100644 --- a/src/mutation-observers/string_map_observer.ts +++ b/src/mutation-observers/string_map_observer.ts @@ -16,8 +16,8 @@ export class StringMapObserver { this.element = element this.delegate = delegate this.started = false - this.stringMap = new Map - this.mutationObserver = new MutationObserver(mutations => this.processMutations(mutations)) + this.stringMap = new Map() + this.mutationObserver = new MutationObserver((mutations) => this.processMutations(mutations)) } start() { @@ -108,7 +108,7 @@ export class StringMapObserver { } private get currentAttributeNames() { - return Array.from(this.element.attributes).map(attribute => attribute.name) + return Array.from(this.element.attributes).map((attribute) => attribute.name) } private get recordedAttributeNames() { diff --git a/src/mutation-observers/token_list_observer.ts b/src/mutation-observers/token_list_observer.ts index c0db034f..3c7f7a6e 100644 --- a/src/mutation-observers/token_list_observer.ts +++ b/src/mutation-observers/token_list_observer.ts @@ -21,7 +21,7 @@ export class TokenListObserver implements AttributeObserverDelegate { constructor(element: Element, attributeName: string, delegate: TokenListObserverDelegate) { this.attributeObserver = new AttributeObserver(element, attributeName, this) this.delegate = delegate - this.tokensByElement = new Multimap + this.tokensByElement = new Multimap() } get started(): boolean { @@ -69,11 +69,11 @@ export class TokenListObserver implements AttributeObserverDelegate { } private tokensMatched(tokens: Token[]) { - tokens.forEach(token => this.tokenMatched(token)) + tokens.forEach((token) => this.tokenMatched(token)) } private tokensUnmatched(tokens: Token[]) { - tokens.forEach(token => this.tokenUnmatched(token)) + tokens.forEach((token) => this.tokenUnmatched(token)) } private tokenMatched(token: Token) { @@ -89,8 +89,9 @@ export class TokenListObserver implements AttributeObserverDelegate { private refreshTokensForElement(element: Element): [Token[], Token[]] { const previousTokens = this.tokensByElement.getValuesForKey(element) const currentTokens = this.readTokensForElement(element) - const firstDifferingIndex = zip(previousTokens, currentTokens) - .findIndex(([previousToken, currentToken]) => !tokensAreEqual(previousToken, currentToken)) + const firstDifferingIndex = zip(previousTokens, currentTokens).findIndex( + ([previousToken, currentToken]) => !tokensAreEqual(previousToken, currentToken) + ) if (firstDifferingIndex == -1) { return [[], []] @@ -107,7 +108,10 @@ export class TokenListObserver implements AttributeObserverDelegate { } function parseTokenString(tokenString: string, element: Element, attributeName: string): Token[] { - return tokenString.trim().split(/\s+/).filter(content => content.length) + return tokenString + .trim() + .split(/\s+/) + .filter((content) => content.length) .map((content, index) => ({ element, attributeName, content, index })) } diff --git a/src/mutation-observers/value_list_observer.ts b/src/mutation-observers/value_list_observer.ts index 69d365c6..35cd214a 100644 --- a/src/mutation-observers/value_list_observer.ts +++ b/src/mutation-observers/value_list_observer.ts @@ -20,8 +20,8 @@ export class ValueListObserver implements TokenListObserverDelegate { constructor(element: Element, attributeName: string, delegate: ValueListObserverDelegate) { this.tokenListObserver = new TokenListObserver(element, attributeName, this) this.delegate = delegate - this.parseResultsByToken = new WeakMap - this.valuesByTokenByElement = new WeakMap + this.parseResultsByToken = new WeakMap() + this.valuesByTokenByElement = new WeakMap() } get started(): boolean { @@ -78,7 +78,7 @@ export class ValueListObserver implements TokenListObserverDelegate { private fetchValuesByTokenForElement(element: Element) { let valuesByToken = this.valuesByTokenByElement.get(element) if (!valuesByToken) { - valuesByToken = new Map + valuesByToken = new Map() this.valuesByTokenByElement.set(element, valuesByToken) } return valuesByToken diff --git a/src/tests/cases/application_test_case.ts b/src/tests/cases/application_test_case.ts index f8a6a30d..0c337afc 100644 --- a/src/tests/cases/application_test_case.ts +++ b/src/tests/cases/application_test_case.ts @@ -1,9 +1,9 @@ -import { Application } from "../../core/application"; -import { DOMTestCase } from "./dom_test_case"; -import { Schema, defaultSchema } from "../../core/schema"; +import { Application } from "../../core/application" +import { DOMTestCase } from "./dom_test_case" +import { Schema, defaultSchema } from "../../core/schema" class TestApplication extends Application { - handleError(error: Error, message: string, detail: object) { + handleError(error: Error, _message: string, _detail: object) { throw error } } diff --git a/src/tests/cases/controller_test_case.ts b/src/tests/cases/controller_test_case.ts index 97014cb6..bd4cb6bf 100644 --- a/src/tests/cases/controller_test_case.ts +++ b/src/tests/cases/controller_test_case.ts @@ -8,7 +8,7 @@ export class ControllerTests extends ApplicationTestCase { fixtureHTML = `
` setupApplication() { - this.identifiers.forEach(identifier => { + this.identifiers.forEach((identifier) => { this.application.register(identifier, this.controllerConstructor) }) } @@ -37,8 +37,10 @@ export class ControllerTests extends ApplicationTestCase { export function ControllerTestCase(): Constructor> export function ControllerTestCase(constructor: Constructor): Constructor> -export function ControllerTestCase(constructor?: Constructor): Constructor> { +export function ControllerTestCase( + constructor?: Constructor +): Constructor> { return class extends ControllerTests { - controllerConstructor = constructor || Controller as any + controllerConstructor = constructor || (Controller as any) } as any } diff --git a/src/tests/cases/dom_test_case.ts b/src/tests/cases/dom_test_case.ts index f132e6f4..6cfe2244 100644 --- a/src/tests/cases/dom_test_case.ts +++ b/src/tests/cases/dom_test_case.ts @@ -1,18 +1,18 @@ import { TestCase } from "./test_case" interface TriggerEventOptions { - bubbles?: boolean, + bubbles?: boolean setDefaultPrevented?: boolean } const defaultTriggerEventOptions: TriggerEventOptions = { - bubbles: true, - setDefaultPrevented: true + bubbles: true, + setDefaultPrevented: true, } export class DOMTestCase extends TestCase { - fixtureSelector: string = "#qunit-fixture" - fixtureHTML: string = "" + fixtureSelector = "#qunit-fixture" + fixtureHTML = "" async runTest(testName: string) { await this.renderFixture() @@ -41,7 +41,7 @@ export class DOMTestCase extends TestCase { // IE <= 11 does not set `defaultPrevented` when `preventDefault()` is called on synthetic events if (setDefaultPrevented) { - event.preventDefault = function() { + event.preventDefault = function () { Object.defineProperty(this, "defaultPrevented", { get: () => true, configurable: true }) } } @@ -61,10 +61,10 @@ export class DOMTestCase extends TestCase { } findElements(...selectors: string[]) { - return selectors.map(selector => this.findElement(selector)) + return selectors.map((selector) => this.findElement(selector)) } get nextFrame(): Promise { - return new Promise(resolve => requestAnimationFrame(resolve)) + return new Promise((resolve) => requestAnimationFrame(resolve)) } } diff --git a/src/tests/cases/index.ts b/src/tests/cases/index.ts index 92a7f26d..8ec465e9 100644 --- a/src/tests/cases/index.ts +++ b/src/tests/cases/index.ts @@ -1,6 +1,6 @@ -export * from './application_test_case' -export * from './controller_test_case' -export * from './dom_test_case' -export * from './log_controller_test_case' -export * from './observer_test_case' -export * from './test_case' +export * from "./application_test_case" +export * from "./controller_test_case" +export * from "./dom_test_case" +export * from "./log_controller_test_case" +export * from "./observer_test_case" +export * from "./test_case" diff --git a/src/tests/cases/log_controller_test_case.ts b/src/tests/cases/log_controller_test_case.ts index fceb4b4d..eefb83e3 100644 --- a/src/tests/cases/log_controller_test_case.ts +++ b/src/tests/cases/log_controller_test_case.ts @@ -16,7 +16,7 @@ export class LogControllerTestCase extends ControllerTestCase(LogController) { actions.forEach((expected, index) => { const keys = Object.keys(expected) const actual = slice(this.actionLog[index] || {}, keys) - const result = keys.every(key => deepEqual(expected[key], actual[key])) + const result = keys.every((key) => deepEqual(expected[key], actual[key])) this.assert.pushResult({ result, actual, expected, message: "" }) }) } @@ -31,15 +31,17 @@ export class LogControllerTestCase extends ControllerTestCase(LogController) { } function slice(object: any, keys: string[]): any { - return keys.reduce((result: any, key: string) => (result[key] = object[key], result), {}) + return keys.reduce((result: any, key: string) => ((result[key] = object[key]), result), {}) } function deepEqual(obj1: any, obj2: any): boolean { if (obj1 === obj2) { return true } else if (typeof obj1 === "object" && typeof obj2 === "object") { - if (Object.keys(obj1).length !== Object.keys(obj2).length) { return false } - for (var prop in obj1) { + if (Object.keys(obj1).length !== Object.keys(obj2).length) { + return false + } + for (const prop in obj1) { if (!deepEqual(obj1[prop], obj2[prop])) { return false } diff --git a/src/tests/cases/test_case.ts b/src/tests/cases/test_case.ts index 9bde0046..2f8d29b7 100644 --- a/src/tests/cases/test_case.ts +++ b/src/tests/cases/test_case.ts @@ -2,7 +2,7 @@ export class TestCase { readonly assert: Assert static defineModule(moduleName: string = this.name, qUnit: QUnit = QUnit) { - qUnit.module(moduleName, hooks => { + qUnit.module(moduleName, (_hooks) => { this.manifest.forEach(([type, name]) => { type = this.shouldSkipTest(name) ? "skip" : type const method = (qUnit as any)[type] as Function @@ -13,8 +13,7 @@ export class TestCase { } static getTest(testName: string) { - return async (assert: Assert) => - this.runTest(testName, assert) + return async (assert: Assert) => this.runTest(testName, assert) } static runTest(testName: string, assert: Assert) { @@ -22,20 +21,20 @@ export class TestCase { return testCase.runTest(testName) } - static shouldSkipTest(testName: string): boolean { + static shouldSkipTest(_testName: string): boolean { return false } static get manifest() { - return this.testPropertyNames.map(name => [name.slice(0, 4), name.slice(5)]) + return this.testPropertyNames.map((name) => [name.slice(0, 4), name.slice(5)]) } static get testNames(): string[] { - return this.manifest.map(([type, name]) => name) + return this.manifest.map(([_type, name]) => name) } static get testPropertyNames(): string[] { - return Object.keys(this.prototype).filter(name => name.match(/^(skip|test|todo) /)) + return Object.keys(this.prototype).filter((name) => name.match(/^(skip|test|todo) /)) } constructor(assert: Assert) { diff --git a/src/tests/controllers/class_controller.ts b/src/tests/controllers/class_controller.ts index af0cdcdc..971484a7 100644 --- a/src/tests/controllers/class_controller.ts +++ b/src/tests/controllers/class_controller.ts @@ -1,7 +1,7 @@ -import { Controller } from "../../core/controller"; +import { Controller } from "../../core/controller" class BaseClassController extends Controller { - static classes = [ "active" ] + static classes = ["active"] readonly activeClass!: string readonly activeClasses!: string[] @@ -9,7 +9,7 @@ class BaseClassController extends Controller { } export class ClassController extends BaseClassController { - static classes = [ "enabled", "loading", "success" ] + static classes = ["enabled", "loading", "success"] readonly hasEnabledClass!: boolean readonly enabledClass!: string diff --git a/src/tests/controllers/default_value_controller.ts b/src/tests/controllers/default_value_controller.ts index 9e866481..603842c5 100644 --- a/src/tests/controllers/default_value_controller.ts +++ b/src/tests/controllers/default_value_controller.ts @@ -23,7 +23,7 @@ export class DefaultValueController extends Controller { defaultObject: {}, defaultObjectPerson: { type: Object, default: { name: "David" } }, - defaultObjectOverride: { override: "me" } + defaultObjectOverride: { override: "me" }, } valueDescriptorMap!: ValueDescriptorMap diff --git a/src/tests/controllers/log_controller.ts b/src/tests/controllers/log_controller.ts index 397d7112..2468b22c 100644 --- a/src/tests/controllers/log_controller.ts +++ b/src/tests/controllers/log_controller.ts @@ -69,7 +69,7 @@ export class LogController extends Controller { currentTarget: event.currentTarget, params: event.params, defaultPrevented: event.defaultPrevented, - passive: passive || false + passive: passive || false, }) } } diff --git a/src/tests/controllers/target_controller.ts b/src/tests/controllers/target_controller.ts index 8ccadd3d..dd94248c 100644 --- a/src/tests/controllers/target_controller.ts +++ b/src/tests/controllers/target_controller.ts @@ -1,7 +1,7 @@ import { Controller } from "../../core/controller" class BaseTargetController extends Controller { - static targets = [ "alpha" ] + static targets = ["alpha"] alphaTarget!: Element | null alphaTargets!: Element[] @@ -9,9 +9,14 @@ class BaseTargetController extends Controller { } export class TargetController extends BaseTargetController { - static classes = [ "connected", "disconnected" ] - static targets = [ "beta", "input", "recursive" ] - static values = { inputTargetConnectedCallCount: Number, inputTargetDisconnectedCallCount: Number, recursiveTargetConnectedCallCount: Number, recursiveTargetDisconnectedCallCount: Number } + static classes = ["connected", "disconnected"] + static targets = ["beta", "input", "recursive"] + static values = { + inputTargetConnectedCallCount: Number, + inputTargetDisconnectedCallCount: Number, + recursiveTargetConnectedCallCount: Number, + recursiveTargetDisconnectedCallCount: Number, + } betaTarget!: Element | null betaTargets!: Element[] @@ -48,7 +53,7 @@ export class TargetController extends BaseTargetController { this.element.append(element) } - recursiveTargetDisconnected(element: Element) { + recursiveTargetDisconnected(_element: Element) { this.recursiveTargetDisconnectedCallCountValue++ } } diff --git a/src/tests/controllers/value_controller.ts b/src/tests/controllers/value_controller.ts index 88f11613..50bde761 100644 --- a/src/tests/controllers/value_controller.ts +++ b/src/tests/controllers/value_controller.ts @@ -5,7 +5,7 @@ class BaseValueController extends Controller { static values: ValueDefinitionMap = { shadowedBoolean: String, string: String, - numeric: Number + numeric: Number, } valueDescriptorMap!: ValueDescriptorMap @@ -19,14 +19,14 @@ export class ValueController extends BaseValueController { missingString: String, ids: Array, options: Object, - "time-24hr": Boolean + "time-24hr": Boolean, } shadowedBooleanValue!: boolean missingStringValue!: string idsValue!: any[] optionsValue!: { [key: string]: any } - time24hrValue!: Boolean + time24hrValue!: boolean loggedNumericValues: number[] = [] oldLoggedNumericValues: any[] = [] diff --git a/src/tests/fixtures/application_start/helpers.ts b/src/tests/fixtures/application_start/helpers.ts index e9fe402a..0dd64bc3 100644 --- a/src/tests/fixtures/application_start/helpers.ts +++ b/src/tests/fixtures/application_start/helpers.ts @@ -6,7 +6,7 @@ export function startApplication() { class PostMessageController extends Controller { itemTargets!: Element[] - static targets = [ "item" ] + static targets = ["item"] connect() { const connectState = document.readyState diff --git a/src/tests/index.ts b/src/tests/index.ts index 4296ece3..e5eb1877 100644 --- a/src/tests/index.ts +++ b/src/tests/index.ts @@ -1,3 +1,3 @@ const context = require.context("./modules", true, /\.js$/) -const modules = context.keys().map(key => context(key).default) -modules.forEach(constructor => constructor.defineModule()) +const modules = context.keys().map((key) => context(key).default) +modules.forEach((constructor) => constructor.defineModule()) diff --git a/src/tests/modules/core/action_params_case_insensitive_tests.ts b/src/tests/modules/core/action_params_case_insensitive_tests.ts index 764c4c39..4f2cfe7b 100644 --- a/src/tests/modules/core/action_params_case_insensitive_tests.ts +++ b/src/tests/modules/core/action_params_case_insensitive_tests.ts @@ -9,7 +9,7 @@ export default class ActionParamsCaseInsensitiveTests extends ActionParamsTests data-CamelCase-active-param="true" data-CamelCase-inactive-param="false" data-CamelCase-empty-param="" - data-CamelCase-payload-param='${JSON.stringify({value: 1})}' + data-CamelCase-payload-param='${JSON.stringify({ value: 1 })}' data-CamelCase-param-something="not-reported" data-Something-param="not-reported" data-AnotherOne-id-param="234"> @@ -22,11 +22,11 @@ export default class ActionParamsCaseInsensitiveTests extends ActionParamsTests id: 123, multiWordExample: "/path", payload: { - value: 1 + value: 1, }, active: true, empty: "", - inactive: false + inactive: false, } async "test clicking on the element does return its params"() { @@ -34,9 +34,7 @@ export default class ActionParamsCaseInsensitiveTests extends ActionParamsTests await this.nextFrame await this.triggerEvent(this.buttonElement, "click") - this.assertActions( - { identifier: "CamelCase", params: this.expectedParamsForCamelCase }, - ) + this.assertActions({ identifier: "CamelCase", params: this.expectedParamsForCamelCase }) } async "test global event return element params where the action is defined"() { @@ -44,9 +42,7 @@ export default class ActionParamsCaseInsensitiveTests extends ActionParamsTests await this.nextFrame await this.triggerEvent("#outside", "keydown") - this.assertActions( - { identifier: "CamelCase", params: this.expectedParamsForCamelCase }, - ) + this.assertActions({ identifier: "CamelCase", params: this.expectedParamsForCamelCase }) } async "test passing params to namespaced controller"() { @@ -56,7 +52,7 @@ export default class ActionParamsCaseInsensitiveTests extends ActionParamsTests this.assertActions( { identifier: "CamelCase", params: this.expectedParamsForCamelCase }, - { identifier: "AnotherOne", params: { id: 234 } }, + { identifier: "AnotherOne", params: { id: 234 } } ) } } diff --git a/src/tests/modules/core/action_params_tests.ts b/src/tests/modules/core/action_params_tests.ts index eadb0a28..d53fb0f4 100644 --- a/src/tests/modules/core/action_params_tests.ts +++ b/src/tests/modules/core/action_params_tests.ts @@ -9,7 +9,7 @@ export default class ActionParamsTests extends LogControllerTestCase { data-c-active-param="true" data-c-inactive-param="false" data-c-empty-param="" - data-c-payload-param='${JSON.stringify({value: 1})}' + data-c-payload-param='${JSON.stringify({ value: 1 })}' data-c-param-something="not-reported" data-something-param="not-reported" data-d-id-param="234"> @@ -22,11 +22,11 @@ export default class ActionParamsTests extends LogControllerTestCase { id: 123, multiWordExample: "/path", payload: { - value: 1 + value: 1, }, active: true, empty: "", - inactive: false + inactive: false, } async "test clicking on the element does return its params"() { @@ -34,9 +34,7 @@ export default class ActionParamsTests extends LogControllerTestCase { await this.nextFrame await this.triggerEvent(this.buttonElement, "click") - this.assertActions( - { identifier: "c", params: this.expectedParamsForC }, - ) + this.assertActions({ identifier: "c", params: this.expectedParamsForC }) } async "test global event return element params where the action is defined"() { @@ -44,9 +42,7 @@ export default class ActionParamsTests extends LogControllerTestCase { await this.nextFrame await this.triggerEvent("#outside", "keydown") - this.assertActions( - { identifier: "c", params: this.expectedParamsForC }, - ) + this.assertActions({ identifier: "c", params: this.expectedParamsForC }) } async "test passing params to namespaced controller"() { @@ -54,10 +50,7 @@ export default class ActionParamsTests extends LogControllerTestCase { await this.nextFrame await this.triggerEvent(this.buttonElement, "click") - this.assertActions( - { identifier: "c", params: this.expectedParamsForC }, - { identifier: "d", params: { id: 234 } }, - ) + this.assertActions({ identifier: "c", params: this.expectedParamsForC }, { identifier: "d", params: { id: 234 } }) } async "test updating manually the params values"() { @@ -65,9 +58,7 @@ export default class ActionParamsTests extends LogControllerTestCase { await this.nextFrame await this.triggerEvent(this.buttonElement, "click") - this.assertActions( - { identifier: "c", params: this.expectedParamsForC }, - ) + this.assertActions({ identifier: "c", params: this.expectedParamsForC }) this.buttonElement.setAttribute("data-c-id-param", "234") this.buttonElement.setAttribute("data-c-new-param", "new") @@ -77,12 +68,16 @@ export default class ActionParamsTests extends LogControllerTestCase { this.assertActions( { identifier: "c", params: this.expectedParamsForC }, { - identifier: "c", params: { - id: 234, new: "new", + identifier: "c", + params: { + id: 234, + new: "new", multiWordExample: "/path", active: true, empty: "", - inactive: false} }, + inactive: false, + }, + } ) } diff --git a/src/tests/modules/core/action_tests.ts b/src/tests/modules/core/action_tests.ts index d8bf3678..4e3c7718 100644 --- a/src/tests/modules/core/action_tests.ts +++ b/src/tests/modules/core/action_tests.ts @@ -64,9 +64,6 @@ export default class ActionTests extends LogControllerTestCase { async "test actions on svg elements"() { await this.triggerEvent("#svgRoot", "click") await this.triggerEvent("#svgChild", "mousedown") - this.assertActions( - { name: "log", eventType: "click" }, - { name: "log", eventType: "mousedown" } - ) + this.assertActions({ name: "log", eventType: "click" }, { name: "log", eventType: "mousedown" }) } } diff --git a/src/tests/modules/core/action_timing_tests.ts b/src/tests/modules/core/action_timing_tests.ts index 7361576e..60177ce7 100644 --- a/src/tests/modules/core/action_timing_tests.ts +++ b/src/tests/modules/core/action_timing_tests.ts @@ -2,7 +2,7 @@ import { Controller } from "../../../core/controller" import { ControllerTestCase } from "../../cases/controller_test_case" class ActionTimingController extends Controller { - static targets = [ "button" ] + static targets = ["button"] buttonTarget!: HTMLButtonElement event?: Event diff --git a/src/tests/modules/core/application_start_tests.ts b/src/tests/modules/core/application_start_tests.ts index c1dfbd17..7bad49a4 100644 --- a/src/tests/modules/core/application_start_tests.ts +++ b/src/tests/modules/core/application_start_tests.ts @@ -28,7 +28,7 @@ export default class ApplicationStartTests extends DOMTestCase { } private messageFromStartState(startState: string): Promise { - return new Promise(resolve => { + return new Promise((resolve) => { const receiveMessage = (event: MessageEvent) => { if (event.source == this.iframe.contentWindow) { const message = JSON.parse(event.data) diff --git a/src/tests/modules/core/application_tests.ts b/src/tests/modules/core/application_tests.ts index df9b8566..8cee672f 100644 --- a/src/tests/modules/core/application_tests.ts +++ b/src/tests/modules/core/application_tests.ts @@ -8,7 +8,7 @@ export default class ApplicationTests extends ApplicationTestCase { fixtureHTML = `
` private definitions = [ { controllerConstructor: AController, identifier: "a" }, - { controllerConstructor: BController, identifier: "b" } + { controllerConstructor: BController, identifier: "b" }, ] async "test Application#register"() { diff --git a/src/tests/modules/core/class_tests.ts b/src/tests/modules/core/class_tests.ts index 27508e1c..be5bc8e6 100644 --- a/src/tests/modules/core/class_tests.ts +++ b/src/tests/modules/core/class_tests.ts @@ -32,6 +32,6 @@ export default class ClassTests extends ControllerTestCase(ClassController) { } "test accessing a class property returns first class if multiple classes are used"() { - this.assert.equal(this.controller.successClass, "bg-green-400"); + this.assert.equal(this.controller.successClass, "bg-green-400") } } diff --git a/src/tests/modules/core/default_value_tests.ts b/src/tests/modules/core/default_value_tests.ts index 3d800a19..924d85f3 100644 --- a/src/tests/modules/core/default_value_tests.ts +++ b/src/tests/modules/core/default_value_tests.ts @@ -56,7 +56,6 @@ export default class DefaultValueTests extends ControllerTestCase(DefaultValueCo this.assert.deepEqual(this.controller.defaultStringHelloValue, "Hello") this.assert.ok(this.controller.hasDefaultStringHelloValue) this.assert.deepEqual(this.get("default-string-hello-value"), null) - } "test should be able to set a new value for custom default string values"() { @@ -160,13 +159,13 @@ export default class DefaultValueTests extends ControllerTestCase(DefaultValueCo this.controller.defaultObjectValue = { new: "value" } - this.assert.deepEqual(this.get("default-object-value"), "{\"new\":\"value\"}") + this.assert.deepEqual(this.get("default-object-value"), '{"new":"value"}') this.assert.deepEqual(this.controller.defaultObjectValue, { new: "value" }) this.assert.ok(this.controller.hasDefaultObjectValue) } "test should override custom default object value with given data-attribute"() { - this.assert.deepEqual(this.get("default-object-override-value"), "{\"expected\":\"value\"}") + this.assert.deepEqual(this.get("default-object-override-value"), '{"expected":"value"}') this.assert.deepEqual(this.controller.defaultObjectOverrideValue, { expected: "value" }) this.assert.ok(this.controller.hasDefaultObjectOverrideValue) } diff --git a/src/tests/modules/core/error_handler_tests.ts b/src/tests/modules/core/error_handler_tests.ts index b3878e50..d94482ea 100644 --- a/src/tests/modules/core/error_handler_tests.ts +++ b/src/tests/modules/core/error_handler_tests.ts @@ -25,12 +25,11 @@ class MockLogger { class ErrorWhileConnectingController extends Controller { connect() { - throw new Error('bad!'); + throw new Error("bad!") } } -class TestApplicationWithDefaultErrorBehavior extends Application { -} +class TestApplicationWithDefaultErrorBehavior extends Application {} export default class ErrorHandlerTests extends ControllerTestCase(ErrorWhileConnectingController) { controllerConstructor = ErrorWhileConnectingController @@ -41,11 +40,13 @@ export default class ErrorHandlerTests extends ControllerTestCase(ErrorWhileConn this.application = new TestApplicationWithDefaultErrorBehavior(this.fixtureElement, this.schema) this.application.logger = logger - window.onerror = function(message, source, lineno, colno, error) { - logger.log(`error from window.onerror. message = ${message}, source = ${source}, lineno = ${lineno}, colno = ${colno}`) + window.onerror = function (message, source, lineno, colno, _error) { + logger.log( + `error from window.onerror. message = ${message}, source = ${source}, lineno = ${lineno}, colno = ${colno}` + ) } - await super.setupApplication() + super.setupApplication() } async "test errors in connect are thrown and handled by built in logger"() { @@ -60,6 +61,9 @@ export default class ErrorHandlerTests extends ControllerTestCase(ErrorWhileConn const mockLogger: any = this.application.logger this.assert.equal(1, mockLogger.logs.length) - this.assert.equal('error from window.onerror. message = Error connecting controller, source = , lineno = 0, colno = 0', mockLogger.logs[0]) + this.assert.equal( + "error from window.onerror. message = Error connecting controller, source = , lineno = 0, colno = 0", + mockLogger.logs[0] + ) } } diff --git a/src/tests/modules/core/es6_tests.ts b/src/tests/modules/core/es6_tests.ts index 8b58975f..156ecd33 100644 --- a/src/tests/modules/core/es6_tests.ts +++ b/src/tests/modules/core/es6_tests.ts @@ -2,7 +2,7 @@ import { LogController } from "../../controllers/log_controller" import { LogControllerTestCase } from "../../cases/log_controller_test_case" export default class ES6Tests extends LogControllerTestCase { - static shouldSkipTest(testName: string) { + static shouldSkipTest(_testName: string) { return !(supportsES6Classes() && supportsReflectConstruct()) } @@ -17,7 +17,7 @@ export default class ES6Tests extends LogControllerTestCase { ` async renderFixture() { - (window as any)["_stimulus"] = { LogController, application: this.application } + ;(window as any)["_stimulus"] = { LogController, application: this.application } await super.renderFixture() const scriptElement = document.createElement("script") diff --git a/src/tests/modules/core/event_options_tests.ts b/src/tests/modules/core/event_options_tests.ts index 5aa48541..403cb10a 100644 --- a/src/tests/modules/core/event_options_tests.ts +++ b/src/tests/modules/core/event_options_tests.ts @@ -18,7 +18,7 @@ export default class EventOptionsTests extends LogControllerTestCase { this.assertActions( { name: "log", identifier: "c", eventType: "click", currentTarget: this.buttonElement }, { name: "log2", identifier: "d", eventType: "click", currentTarget: this.buttonElement }, - { name: "log3", identifier: "c", eventType: "click", currentTarget: this.buttonElement }, + { name: "log3", identifier: "c", eventType: "click", currentTarget: this.buttonElement } ) } @@ -33,7 +33,7 @@ export default class EventOptionsTests extends LogControllerTestCase { { name: "log2", identifier: "d", eventType: "click", currentTarget: this.buttonElement }, { name: "log3", identifier: "c", eventType: "click", currentTarget: this.buttonElement }, { name: "log2", identifier: "d", eventType: "click", currentTarget: this.buttonElement }, - { name: "log3", identifier: "c", eventType: "click", currentTarget: this.buttonElement }, + { name: "log3", identifier: "c", eventType: "click", currentTarget: this.buttonElement } ) } @@ -42,15 +42,13 @@ export default class EventOptionsTests extends LogControllerTestCase { await this.triggerEvent(this.buttonElement, "click") - this.assertActions( - { name: "stop", identifier: "c", eventType: "click", currentTarget: this.buttonElement }, - ) + this.assertActions({ name: "stop", identifier: "c", eventType: "click", currentTarget: this.buttonElement }) await this.nextFrame await this.triggerEvent(this.buttonElement, "click") this.assertActions( { name: "stop", identifier: "c", eventType: "click", currentTarget: this.buttonElement }, - { name: "log", identifier: "c", eventType: "click", currentTarget: this.buttonElement }, + { name: "log", identifier: "c", eventType: "click", currentTarget: this.buttonElement } ) } @@ -76,7 +74,7 @@ export default class EventOptionsTests extends LogControllerTestCase { this.assertActions( { name: "log", identifier: "c" }, { name: "log2", identifier: "c" }, - { name: "log", identifier: "d" }, + { name: "log", identifier: "d" } ) } @@ -117,10 +115,7 @@ export default class EventOptionsTests extends LogControllerTestCase { await this.triggerEvent(this.buttonElement, "click") await this.triggerEvent(this.buttonElement, "click") - this.assertActions( - { name: "log", identifier: "c" }, - { name: "log", identifier: "c" } - ) + this.assertActions({ name: "log", identifier: "c" }, { name: "log", identifier: "c" }) } async "test stop option with implicit event"() { @@ -129,9 +124,7 @@ export default class EventOptionsTests extends LogControllerTestCase { await this.triggerEvent(this.buttonElement, "click") - this.assertActions( - { name: "log2", eventType: "click" } - ) + this.assertActions({ name: "log2", eventType: "click" }) } async "test stop option with explicit event"() { @@ -140,9 +133,7 @@ export default class EventOptionsTests extends LogControllerTestCase { await this.triggerEvent(this.buttonElement, "keydown") - this.assertActions( - { name: "log2", eventType: "keydown" } - ) + this.assertActions({ name: "log2", eventType: "keydown" }) } async "test event propagation without stop option"() { @@ -151,10 +142,7 @@ export default class EventOptionsTests extends LogControllerTestCase { await this.triggerEvent(this.buttonElement, "click") - this.assertActions( - { name: "log2", eventType: "click" }, - { name: "log", eventType: "click" } - ) + this.assertActions({ name: "log2", eventType: "click" }, { name: "log", eventType: "click" }) } async "test prevent option with implicit event"() { @@ -162,9 +150,7 @@ export default class EventOptionsTests extends LogControllerTestCase { await this.triggerEvent(this.buttonElement, "click") - this.assertActions( - { name: "log", eventType: "click", defaultPrevented: true } - ) + this.assertActions({ name: "log", eventType: "click", defaultPrevented: true }) } async "test prevent option with explicit event"() { @@ -172,9 +158,7 @@ export default class EventOptionsTests extends LogControllerTestCase { await this.triggerEvent(this.buttonElement, "keyup") - this.assertActions( - { name: "log", eventType: "keyup", defaultPrevented: true } - ) + this.assertActions({ name: "log", eventType: "keyup", defaultPrevented: true }) } async "test self option"() { @@ -182,9 +166,7 @@ export default class EventOptionsTests extends LogControllerTestCase { await this.triggerEvent(this.buttonElement, "click") - this.assertActions( - { name: "log", eventType: "click" } - ) + this.assertActions({ name: "log", eventType: "click" }) } async "test self option on parent"() { @@ -198,8 +180,10 @@ export default class EventOptionsTests extends LogControllerTestCase { async "test custom option"() { this.application.registerActionOption("open", ({ value, event: { type, target } }) => { switch (type) { - case "toggle": return target instanceof HTMLDetailsElement && target.open == value - default: return true + case "toggle": + return target instanceof HTMLDetailsElement && target.open == value + default: + return true } }) await this.setAction(this.detailsElement, "toggle->c#log:open") @@ -214,8 +198,10 @@ export default class EventOptionsTests extends LogControllerTestCase { async "test inverted custom option"() { this.application.registerActionOption("open", ({ value, event: { type, target } }) => { switch (type) { - case "toggle": return target instanceof HTMLDetailsElement && target.open == value - default: return true + case "toggle": + return target instanceof HTMLDetailsElement && target.open == value + default: + return true } }) await this.setAction(this.detailsElement, "toggle->c#log:!open") diff --git a/src/tests/modules/core/legacy_target_tests.ts b/src/tests/modules/core/legacy_target_tests.ts index 92030638..331035cc 100644 --- a/src/tests/modules/core/legacy_target_tests.ts +++ b/src/tests/modules/core/legacy_target_tests.ts @@ -19,11 +19,11 @@ export default class LegacyTargetTests extends ControllerTestCase(TargetControll warningCount = 0 async setupApplication() { - await super.setupApplication() + super.setupApplication() this.application.logger = Object.create(console, { warn: { - value: (message: string, ...args: any[]) => this.warningCount++ - } + value: () => this.warningCount++, + }, }) } @@ -38,18 +38,12 @@ export default class LegacyTargetTests extends ControllerTestCase(TargetControll } "test TargetSet#findAll"() { - this.assert.deepEqual( - this.controller.targets.findAll("alpha"), - this.findElements("#alpha1", "#alpha2") - ) + this.assert.deepEqual(this.controller.targets.findAll("alpha"), this.findElements("#alpha1", "#alpha2")) this.assert.equal(this.warningCount, 2) } "test TargetSet#findAll prioritizes scoped target attributes"() { - this.assert.deepEqual( - this.controller.targets.findAll("gamma"), - this.findElements("#beta1", "#gamma1") - ) + this.assert.deepEqual(this.controller.targets.findAll("gamma"), this.findElements("#beta1", "#gamma1")) this.assert.equal(this.warningCount, 1) } diff --git a/src/tests/modules/core/loading_tests.ts b/src/tests/modules/core/loading_tests.ts index e716f3e4..efe496d9 100644 --- a/src/tests/modules/core/loading_tests.ts +++ b/src/tests/modules/core/loading_tests.ts @@ -2,12 +2,12 @@ import { ApplicationTestCase } from "../../cases/application_test_case" import { LogController } from "../../controllers/log_controller" class UnloadableController extends LogController { - static get shouldLoad(){ + static get shouldLoad() { return false } } class LoadableController extends LogController { - static get shouldLoad(){ + static get shouldLoad() { return true } } diff --git a/src/tests/modules/core/target_tests.ts b/src/tests/modules/core/target_tests.ts index 40aecec5..3d26c94e 100644 --- a/src/tests/modules/core/target_tests.ts +++ b/src/tests/modules/core/target_tests.ts @@ -21,10 +21,7 @@ export default class TargetTests extends ControllerTestCase(TargetController) { } "test TargetSet#findAll"() { - this.assert.deepEqual( - this.controller.targets.findAll("alpha"), - this.findElements("#alpha1", "#alpha2") - ) + this.assert.deepEqual(this.controller.targets.findAll("alpha"), this.findElements("#alpha1", "#alpha2")) } "test TargetSet#findAll with multiple arguments"() { @@ -64,7 +61,7 @@ export default class TargetTests extends ControllerTestCase(TargetController) { } "test target connected callback fires after initialize() and when calling connect()"() { - const connectedInputs = this.controller.inputTargets.filter(target => target.classList.contains("connected")) + const connectedInputs = this.controller.inputTargets.filter((target) => target.classList.contains("connected")) this.assert.equal(connectedInputs.length, 1) this.assert.equal(this.controller.inputTargetConnectedCallCountValue, 1) @@ -80,7 +77,10 @@ export default class TargetTests extends ControllerTestCase(TargetController) { await this.nextFrame this.assert.equal(this.controller.inputTargetConnectedCallCountValue, 2) - this.assert.ok(connectedInput.classList.contains("connected"), `expected "${connectedInput.className}" to contain "connected"`) + this.assert.ok( + connectedInput.classList.contains("connected"), + `expected "${connectedInput.className}" to contain "connected"` + ) this.assert.ok(connectedInput.isConnected, "element is present in document") } @@ -111,13 +111,19 @@ export default class TargetTests extends ControllerTestCase(TargetController) { } async "test target disconnected callback fires when calling disconnect() on the controller"() { - this.assert.equal(this.controller.inputTargets.filter(target => target.classList.contains("disconnected")).length, 0) + this.assert.equal( + this.controller.inputTargets.filter((target) => target.classList.contains("disconnected")).length, + 0 + ) this.assert.equal(this.controller.inputTargetDisconnectedCallCountValue, 0) this.controller.context.disconnect() await this.nextFrame - this.assert.equal(this.controller.inputTargets.filter(target => target.classList.contains("disconnected")).length, 1) + this.assert.equal( + this.controller.inputTargets.filter((target) => target.classList.contains("disconnected")).length, + 1 + ) this.assert.equal(this.controller.inputTargetDisconnectedCallCountValue, 1) } @@ -125,13 +131,19 @@ export default class TargetTests extends ControllerTestCase(TargetController) { const disconnectedInput = this.findElement("#input1") this.assert.equal(this.controller.inputTargetDisconnectedCallCountValue, 0) - this.assert.notOk(disconnectedInput.classList.contains("disconnected"), `expected "${disconnectedInput.className}" not to contain "disconnected"`) + this.assert.notOk( + disconnectedInput.classList.contains("disconnected"), + `expected "${disconnectedInput.className}" not to contain "disconnected"` + ) disconnectedInput.parentElement?.removeChild(disconnectedInput) await this.nextFrame this.assert.equal(this.controller.inputTargetDisconnectedCallCountValue, 1) - this.assert.ok(disconnectedInput.classList.contains("disconnected"), `expected "${disconnectedInput.className}" to contain "disconnected"`) + this.assert.ok( + disconnectedInput.classList.contains("disconnected"), + `expected "${disconnectedInput.className}" to contain "disconnected"` + ) this.assert.notOk(disconnectedInput.isConnected, "element is not present in document") } @@ -139,13 +151,19 @@ export default class TargetTests extends ControllerTestCase(TargetController) { const element = this.findElement("#input1") this.assert.equal(this.controller.inputTargetDisconnectedCallCountValue, 0) - this.assert.notOk(element.classList.contains("disconnected"), `expected "${element.className}" not to contain "disconnected"`) + this.assert.notOk( + element.classList.contains("disconnected"), + `expected "${element.className}" not to contain "disconnected"` + ) element.removeAttribute(`data-${this.controller.identifier}-target`) await this.nextFrame this.assert.equal(this.controller.inputTargetDisconnectedCallCountValue, 1) - this.assert.ok(element.classList.contains("disconnected"), `expected "${element.className}" to contain "disconnected"`) + this.assert.ok( + element.classList.contains("disconnected"), + `expected "${element.className}" to contain "disconnected"` + ) this.assert.ok(element.isConnected, "element is still present in document") } @@ -154,21 +172,30 @@ export default class TargetTests extends ControllerTestCase(TargetController) { this.assert.equal(this.controller.inputTargetConnectedCallCountValue, 1) this.assert.equal(this.controller.inputTargetDisconnectedCallCountValue, 0) - this.assert.notOk(element.classList.contains("disconnected"), `expected "${element.className}" not to contain "disconnected"`) + this.assert.notOk( + element.classList.contains("disconnected"), + `expected "${element.className}" not to contain "disconnected"` + ) element.setAttribute(`data-${this.controller.identifier}-target`, "input") await this.nextFrame this.assert.equal(this.controller.inputTargetConnectedCallCountValue, 2) this.assert.equal(this.controller.inputTargetDisconnectedCallCountValue, 1) - this.assert.ok(element.classList.contains("disconnected"), `expected "${element.className}" to contain "disconnected"`) + this.assert.ok( + element.classList.contains("disconnected"), + `expected "${element.className}" to contain "disconnected"` + ) this.assert.ok(element.isConnected, "element is still present in document") } async "test [target]Connected() and [target]Disconnected() do not loop infinitely"() { - this.controller.element.insertAdjacentHTML("beforeend", ` + this.controller.element.insertAdjacentHTML( + "beforeend", + `
- `) + ` + ) await this.nextFrame this.assert.ok(!!this.fixtureElement.querySelector("#recursive2")) diff --git a/src/tests/modules/core/value_tests.ts b/src/tests/modules/core/value_tests.ts index 99824648..1bcb0377 100644 --- a/src/tests/modules/core/value_tests.ts +++ b/src/tests/modules/core/value_tests.ts @@ -90,10 +90,10 @@ export default class ValueTests extends ControllerTestCase(ValueController) { } "test object values"() { - this.assert.deepEqual(this.controller.optionsValue, { "one": [2, 3] }) + this.assert.deepEqual(this.controller.optionsValue, { one: [2, 3] }) this.controller.optionsValue["one"] = 0 - this.assert.deepEqual(this.controller.optionsValue, { "one": [2, 3] }) + this.assert.deepEqual(this.controller.optionsValue, { one: [2, 3] }) this.controller.optionsValue = {} this.assert.deepEqual(this.controller.optionsValue, {}) @@ -161,15 +161,26 @@ export default class ValueTests extends ControllerTestCase(ValueController) { this.assert.deepEqual(this.controller.optionsValues, [{ one: [2, 3] }]) this.assert.deepEqual(this.controller.oldOptionsValues, [{}]) - this.controller.optionsValue = { person: { name: 'John', age: 42, active: true } } + this.controller.optionsValue = { person: { name: "John", age: 42, active: true } } await this.nextFrame - this.assert.deepEqual(this.controller.optionsValues, [{ one: [2, 3] }, { person: { name: 'John', age: 42, active: true } }]) + this.assert.deepEqual(this.controller.optionsValues, [ + { one: [2, 3] }, + { person: { name: "John", age: 42, active: true } }, + ]) this.assert.deepEqual(this.controller.oldOptionsValues, [{}, { one: [2, 3] }]) this.set("options-value", "{}") await this.nextFrame - this.assert.deepEqual(this.controller.optionsValues, [{ one: [2, 3] }, { person: { name: 'John', age: 42, active: true } }, {}]) - this.assert.deepEqual(this.controller.oldOptionsValues, [{}, { one: [2, 3] }, { person: { name: 'John', age: 42, active: true } }]) + this.assert.deepEqual(this.controller.optionsValues, [ + { one: [2, 3] }, + { person: { name: "John", age: 42, active: true } }, + {}, + ]) + this.assert.deepEqual(this.controller.oldOptionsValues, [ + {}, + { one: [2, 3] }, + { person: { name: "John", age: 42, active: true } }, + ]) } async "test default values trigger changed callbacks"() { diff --git a/src/tests/modules/mutation-observers/attribute_observer_tests.ts b/src/tests/modules/mutation-observers/attribute_observer_tests.ts index 265b3985..8e58d21b 100644 --- a/src/tests/modules/mutation-observers/attribute_observer_tests.ts +++ b/src/tests/modules/mutation-observers/attribute_observer_tests.ts @@ -7,9 +7,7 @@ export default class AttributeObserverTests extends ObserverTestCase implements observer = new AttributeObserver(this.fixtureElement, this.attributeName, this) async "test elementMatchedAttribute"() { - this.assert.deepEqual(this.calls, [ - ["elementMatchedAttribute", this.outerElement, this.attributeName] - ]) + this.assert.deepEqual(this.calls, [["elementMatchedAttribute", this.outerElement, this.attributeName]]) } async "test elementAttributeValueChanged"() { @@ -18,7 +16,7 @@ export default class AttributeObserverTests extends ObserverTestCase implements this.assert.deepEqual(this.calls, [ ["elementMatchedAttribute", this.outerElement, this.attributeName], - ["elementAttributeValueChanged", this.outerElement, this.attributeName] + ["elementAttributeValueChanged", this.outerElement, this.attributeName], ]) } @@ -28,7 +26,7 @@ export default class AttributeObserverTests extends ObserverTestCase implements this.assert.deepEqual(this.calls, [ ["elementMatchedAttribute", this.outerElement, this.attributeName], - ["elementUnmatchedAttribute", this.outerElement, this.attributeName] + ["elementUnmatchedAttribute", this.outerElement, this.attributeName], ]) } @@ -38,7 +36,7 @@ export default class AttributeObserverTests extends ObserverTestCase implements this.assert.deepEqual(this.calls, [ ["elementMatchedAttribute", this.outerElement, this.attributeName], - ["elementMatchedAttribute", this.innerElement, this.attributeName] + ["elementMatchedAttribute", this.innerElement, this.attributeName], ]) } @@ -46,9 +44,7 @@ export default class AttributeObserverTests extends ObserverTestCase implements this.outerElement.setAttribute(this.attributeName + "-x", "hello") await this.nextFrame - this.assert.deepEqual(this.calls, [ - ["elementMatchedAttribute", this.outerElement, this.attributeName] - ]) + this.assert.deepEqual(this.calls, [["elementMatchedAttribute", this.outerElement, this.attributeName]]) } async "test observes removal of nested matched element HTML"() { @@ -64,7 +60,7 @@ export default class AttributeObserverTests extends ObserverTestCase implements ["elementMatchedAttribute", outerElement, this.attributeName], ["elementMatchedAttribute", innerElement, this.attributeName], ["elementUnmatchedAttribute", outerElement, this.attributeName], - ["elementUnmatchedAttribute", innerElement, this.attributeName] + ["elementUnmatchedAttribute", innerElement, this.attributeName], ]) } @@ -75,9 +71,7 @@ export default class AttributeObserverTests extends ObserverTestCase implements innerElement.setAttribute(this.attributeName, "") await this.nextFrame - this.assert.deepEqual(this.calls, [ - ["elementMatchedAttribute", outerElement, this.attributeName] - ]) + this.assert.deepEqual(this.calls, [["elementMatchedAttribute", outerElement, this.attributeName]]) } async "test ignores synchronously moved elements"() { @@ -87,9 +81,7 @@ export default class AttributeObserverTests extends ObserverTestCase implements innerElement.setAttribute(this.attributeName, "") await this.nextFrame - this.assert.deepEqual(this.calls, [ - ["elementMatchedAttribute", outerElement, this.attributeName] - ]) + this.assert.deepEqual(this.calls, [["elementMatchedAttribute", outerElement, this.attributeName]]) document.body.removeChild(innerElement) } diff --git a/src/tests/modules/mutation-observers/token_list_observer_tests.ts b/src/tests/modules/mutation-observers/token_list_observer_tests.ts index a23f4780..4b9b5a88 100644 --- a/src/tests/modules/mutation-observers/token_list_observer_tests.ts +++ b/src/tests/modules/mutation-observers/token_list_observer_tests.ts @@ -9,7 +9,7 @@ export default class TokenListObserverTests extends ObserverTestCase implements async "test tokenMatched"() { this.assert.deepEqual(this.calls, [ ["tokenMatched", this.element, this.attributeName, "one", 0], - ["tokenMatched", this.element, this.attributeName, "two", 1] + ["tokenMatched", this.element, this.attributeName, "two", 1], ]) } @@ -17,9 +17,7 @@ export default class TokenListObserverTests extends ObserverTestCase implements this.tokenString = "one two three" await this.nextFrame - this.assert.deepEqual(this.testCalls, [ - ["tokenMatched", this.element, this.attributeName, "three", 2] - ]) + this.assert.deepEqual(this.testCalls, [["tokenMatched", this.element, this.attributeName, "three", 2]]) } async "test inserting a token in the middle"() { @@ -29,7 +27,7 @@ export default class TokenListObserverTests extends ObserverTestCase implements this.assert.deepEqual(this.testCalls, [ ["tokenUnmatched", this.element, this.attributeName, "two", 1], ["tokenMatched", this.element, this.attributeName, "three", 1], - ["tokenMatched", this.element, this.attributeName, "two", 2] + ["tokenMatched", this.element, this.attributeName, "two", 2], ]) } @@ -40,7 +38,7 @@ export default class TokenListObserverTests extends ObserverTestCase implements this.assert.deepEqual(this.testCalls, [ ["tokenUnmatched", this.element, this.attributeName, "one", 0], ["tokenUnmatched", this.element, this.attributeName, "two", 1], - ["tokenMatched", this.element, this.attributeName, "two", 0] + ["tokenMatched", this.element, this.attributeName, "two", 0], ]) } @@ -48,9 +46,7 @@ export default class TokenListObserverTests extends ObserverTestCase implements this.tokenString = "one" await this.nextFrame - this.assert.deepEqual(this.testCalls, [ - ["tokenUnmatched", this.element, this.attributeName, "two", 1] - ]) + this.assert.deepEqual(this.testCalls, [["tokenUnmatched", this.element, this.attributeName, "two", 1]]) } async "test removing the only token"() { @@ -61,7 +57,7 @@ export default class TokenListObserverTests extends ObserverTestCase implements this.assert.deepEqual(this.testCalls, [ ["tokenUnmatched", this.element, this.attributeName, "two", 1], - ["tokenUnmatched", this.element, this.attributeName, "one", 0] + ["tokenUnmatched", this.element, this.attributeName, "one", 0], ]) } diff --git a/src/tests/modules/mutation-observers/value_list_observer_tests.ts b/src/tests/modules/mutation-observers/value_list_observer_tests.ts index 2e5ec0a7..1a4b1517 100644 --- a/src/tests/modules/mutation-observers/value_list_observer_tests.ts +++ b/src/tests/modules/mutation-observers/value_list_observer_tests.ts @@ -13,18 +13,14 @@ export default class ValueListObserverTests extends ObserverTestCase implements lastValueId = 0 async "test elementMatchedValue"() { - this.assert.deepEqual(this.calls, [ - ["elementMatchedValue", this.element, 1, "one"] - ]) + this.assert.deepEqual(this.calls, [["elementMatchedValue", this.element, 1, "one"]]) } async "test adding a token to the right"() { this.valueString = "one two" await this.nextFrame - this.assert.deepEqual(this.testCalls, [ - ["elementMatchedValue", this.element, 2, "two"] - ]) + this.assert.deepEqual(this.testCalls, [["elementMatchedValue", this.element, 2, "two"]]) } async "test adding a token to the left"() { @@ -34,7 +30,7 @@ export default class ValueListObserverTests extends ObserverTestCase implements this.assert.deepEqual(this.testCalls, [ ["elementUnmatchedValue", this.element, 1, "one"], ["elementMatchedValue", this.element, 2, "two"], - ["elementMatchedValue", this.element, 3, "one"] + ["elementMatchedValue", this.element, 3, "one"], ]) } @@ -46,7 +42,7 @@ export default class ValueListObserverTests extends ObserverTestCase implements this.assert.deepEqual(this.testCalls, [ ["elementMatchedValue", this.element, 2, "two"], - ["elementUnmatchedValue", this.element, 2, "two"] + ["elementUnmatchedValue", this.element, 2, "two"], ]) } @@ -60,7 +56,7 @@ export default class ValueListObserverTests extends ObserverTestCase implements ["elementMatchedValue", this.element, 2, "two"], ["elementUnmatchedValue", this.element, 1, "one"], ["elementUnmatchedValue", this.element, 2, "two"], - ["elementMatchedValue", this.element, 3, "two"] + ["elementMatchedValue", this.element, 3, "two"], ]) } @@ -68,9 +64,7 @@ export default class ValueListObserverTests extends ObserverTestCase implements this.valueString = "" await this.nextFrame - this.assert.deepEqual(this.testCalls, [ - ["elementUnmatchedValue", this.element, 1, "one"] - ]) + this.assert.deepEqual(this.testCalls, [["elementUnmatchedValue", this.element, 1, "one"]]) } async "test removing and re-adding a token produces a new value"() { @@ -81,7 +75,7 @@ export default class ValueListObserverTests extends ObserverTestCase implements this.assert.deepEqual(this.testCalls, [ ["elementUnmatchedValue", this.element, 1, "one"], - ["elementMatchedValue", this.element, 2, "one"] + ["elementMatchedValue", this.element, 2, "one"], ]) } diff --git a/yarn.lock b/yarn.lock index 06758357..a4d03d18 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23,6 +23,66 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@eslint/eslintrc@^1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.2.tgz#58b69582f3b7271d8fa67fe5251767a5b38ea356" + integrity sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.4.0" + globals "^13.15.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@humanwhocodes/config-array@^0.10.5": + version "0.10.7" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.10.7.tgz#6d53769fd0c222767e6452e8ebda825c22e9f0dc" + integrity sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w== + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.4" + +"@humanwhocodes/gitignore-to-minimatch@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz#316b0a63b91c10e53f242efb4ace5c3b34e8728d" + integrity sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA== + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + "@rollup/plugin-node-resolve@^13.0.0": version "13.0.0" resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.0.0.tgz#352f07e430ff377809ec8ec8a6fd636547162dc4" @@ -89,6 +149,11 @@ resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== +"@types/json-schema@^7.0.9": + version "7.0.11" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" + integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== + "@types/keyv@*": version "3.1.2" resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.2.tgz#5d97bb65526c20b6e0845f6b0d2ade4f28604ee5" @@ -156,6 +221,86 @@ dependencies: "@types/node" "*" +"@typescript-eslint/eslint-plugin@^5.36.2": + version "5.38.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.38.1.tgz#9f05d42fa8fb9f62304cc2f5c2805e03c01c2620" + integrity sha512-ky7EFzPhqz3XlhS7vPOoMDaQnQMn+9o5ICR9CPr/6bw8HrFkzhMSxuA3gRfiJVvs7geYrSeawGJjZoZQKCOglQ== + dependencies: + "@typescript-eslint/scope-manager" "5.38.1" + "@typescript-eslint/type-utils" "5.38.1" + "@typescript-eslint/utils" "5.38.1" + debug "^4.3.4" + ignore "^5.2.0" + regexpp "^3.2.0" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/parser@^5.36.2": + version "5.38.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.38.1.tgz#c577f429f2c32071b92dff4af4f5fbbbd2414bd0" + integrity sha512-LDqxZBVFFQnQRz9rUZJhLmox+Ep5kdUmLatLQnCRR6523YV+XhRjfYzStQ4MheFA8kMAfUlclHSbu+RKdRwQKw== + dependencies: + "@typescript-eslint/scope-manager" "5.38.1" + "@typescript-eslint/types" "5.38.1" + "@typescript-eslint/typescript-estree" "5.38.1" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.38.1": + version "5.38.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.38.1.tgz#f87b289ef8819b47189351814ad183e8801d5764" + integrity sha512-BfRDq5RidVU3RbqApKmS7RFMtkyWMM50qWnDAkKgQiezRtLKsoyRKIvz1Ok5ilRWeD9IuHvaidaLxvGx/2eqTQ== + dependencies: + "@typescript-eslint/types" "5.38.1" + "@typescript-eslint/visitor-keys" "5.38.1" + +"@typescript-eslint/type-utils@5.38.1": + version "5.38.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.38.1.tgz#7f038fcfcc4ade4ea76c7c69b2aa25e6b261f4c1" + integrity sha512-UU3j43TM66gYtzo15ivK2ZFoDFKKP0k03MItzLdq0zV92CeGCXRfXlfQX5ILdd4/DSpHkSjIgLLLh1NtkOJOAw== + dependencies: + "@typescript-eslint/typescript-estree" "5.38.1" + "@typescript-eslint/utils" "5.38.1" + debug "^4.3.4" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.38.1": + version "5.38.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.38.1.tgz#74f9d6dcb8dc7c58c51e9fbc6653ded39e2e225c" + integrity sha512-QTW1iHq1Tffp9lNfbfPm4WJabbvpyaehQ0SrvVK2yfV79SytD9XDVxqiPvdrv2LK7DGSFo91TB2FgWanbJAZXg== + +"@typescript-eslint/typescript-estree@5.38.1": + version "5.38.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.38.1.tgz#657d858d5d6087f96b638ee383ee1cff52605a1e" + integrity sha512-99b5e/Enoe8fKMLdSuwrfH/C0EIbpUWmeEKHmQlGZb8msY33qn1KlkFww0z26o5Omx7EVjzVDCWEfrfCDHfE7g== + dependencies: + "@typescript-eslint/types" "5.38.1" + "@typescript-eslint/visitor-keys" "5.38.1" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.38.1": + version "5.38.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.38.1.tgz#e3ac37d7b33d1362bb5adf4acdbe00372fb813ef" + integrity sha512-oIuUiVxPBsndrN81oP8tXnFa/+EcZ03qLqPDfSZ5xIJVm7A9V0rlkQwwBOAGtrdN70ZKDlKv+l1BeT4eSFxwXA== + dependencies: + "@types/json-schema" "^7.0.9" + "@typescript-eslint/scope-manager" "5.38.1" + "@typescript-eslint/types" "5.38.1" + "@typescript-eslint/typescript-estree" "5.38.1" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + +"@typescript-eslint/visitor-keys@5.38.1": + version "5.38.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.38.1.tgz#508071bfc6b96d194c0afe6a65ad47029059edbc" + integrity sha512-bSHr1rRxXt54+j2n4k54p4fj8AHJ49VDWtjpImOpzQj4qjAiOpPni+V1Tyajh19Api1i844F757cur8wH3YvOA== + dependencies: + "@typescript-eslint/types" "5.38.1" + eslint-visitor-keys "^3.3.0" + "@wdio/config@6.12.1": version "6.12.1" resolved "https://registry.yarnpkg.com/@wdio/config/-/config-6.12.1.tgz#86d987b505d8ca85ec11471830d2ba296dab3bcf" @@ -357,11 +502,21 @@ accepts@~1.3.4: mime-types "~2.1.24" negotiator "0.6.2" +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + acorn@^6.4.1: version "6.4.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== +acorn@^8.8.0: + version "8.8.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" + integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== + after@0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" @@ -382,7 +537,7 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@^6.1.0, ajv@^6.10.2: +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -397,7 +552,7 @@ ansi-colors@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== -ansi-regex@^5.0.0: +ansi-regex@^5.0.0, ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== @@ -478,6 +633,11 @@ archiver@^5.0.0: tar-stream "^2.2.0" zip-stream "^4.1.0" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -493,6 +653,11 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" @@ -935,6 +1100,11 @@ cacheable-request@^7.0.1: normalize-url "^6.0.1" responselike "^2.0.0" +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + camel-case@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" @@ -1385,6 +1555,15 @@ cross-spawn@^6.0.0: shebang-command "^1.2.0" which "^1.2.9" +cross-spawn@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" @@ -1446,6 +1625,13 @@ debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.3: dependencies: ms "2.1.2" +debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + debug@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -1537,6 +1723,11 @@ decompress@^4.2.0: pify "^2.3.0" strip-dirs "^2.0.0" +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + deepmerge@^4.0.0, deepmerge@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" @@ -1633,6 +1824,20 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +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 "^2.0.2" + dom-serialize@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" @@ -1830,6 +2035,18 @@ 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-config-prettier@^8.5.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" + integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== + +eslint-plugin-prettier@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" + integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== + dependencies: + prettier-linter-helpers "^1.0.0" + eslint-scope@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" @@ -1838,7 +2055,101 @@ eslint-scope@^4.0.3: esrecurse "^4.1.0" estraverse "^4.1.1" -esrecurse@^4.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== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.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@^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-visitor-keys@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + +eslint@^8.23.0: + version "8.24.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.24.0.tgz#489516c927a5da11b3979dbfb2679394523383c8" + integrity sha512-dWFaPhGhTAiPcCgm3f6LI2MBWbogMnTJzFBbhXVRQDJPkr9pGZvVjlVfXd+vyDcWPA2Ic9L2AXPIQM0+vk/cSQ== + dependencies: + "@eslint/eslintrc" "^1.3.2" + "@humanwhocodes/config-array" "^0.10.5" + "@humanwhocodes/gitignore-to-minimatch" "^1.0.2" + "@humanwhocodes/module-importer" "^1.0.1" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.1.1" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.3.0" + espree "^9.4.0" + esquery "^1.4.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.1" + globals "^13.15.0" + globby "^11.1.0" + grapheme-splitter "^1.0.4" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-sdsl "^4.1.4" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.1" + regexpp "^3.2.0" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + +espree@^9.4.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.0.tgz#cd4bc3d6e9336c433265fc0aa016fc1aaf182f8a" + integrity sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw== + dependencies: + acorn "^8.8.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.3.0" + +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.1.0, esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== @@ -1850,6 +2161,11 @@ estraverse@^4.1.1: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== +estraverse@^5.1.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + estraverse@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" @@ -1860,6 +2176,11 @@ estree-walker@^1.0.1: resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + eventemitter3@^4.0.0: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -1994,16 +2315,44 @@ fast-deep-equal@^2.0.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= -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== +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + +fast-glob@^3.2.9: + version "3.2.12" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" + integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + fast-json-stable-stringify@^2.0.0: version "2.1.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 sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastq@^1.6.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" + integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + dependencies: + reusify "^1.0.4" + fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" @@ -2016,6 +2365,13 @@ figgy-pudding@^3.5.1: resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + file-type@^3.8.0: version "3.9.0" resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" @@ -2114,6 +2470,14 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + find-versions@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-3.2.0.tgz#10297f98030a786829681690545ef659ed1d254e" @@ -2121,6 +2485,19 @@ find-versions@^3.0.0: dependencies: semver-regex "^2.0.0" +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + dependencies: + flatted "^3.1.0" + rimraf "^3.0.2" + +flatted@^3.1.0: + version "3.2.7" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" + integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== + flatted@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.4.tgz#28d9969ea90661b5134259f312ab6aa7929ac5e2" @@ -2282,13 +2659,20 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@~5.1.2: +glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" +glob-parent@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + glob@^7.1.2, glob@^7.1.4, glob@^7.1.6: version "7.1.7" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" @@ -2326,6 +2710,13 @@ global-agent@^2.1.12: semver "^7.3.2" serialize-error "^7.0.1" +globals@^13.15.0: + version "13.17.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.17.0.tgz#902eb1e680a41da93945adbdcb5a9f361ba69bd4" + integrity sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw== + dependencies: + type-fest "^0.20.2" + globalthis@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.2.tgz#2a235d34f4d8036219f7e34929b5de9e18166b8b" @@ -2338,6 +2729,18 @@ globalyzer@0.1.0: resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.0.tgz#cb76da79555669a1519d5a8edf093afaa0bf1465" integrity sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q== +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + globrex@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" @@ -2393,7 +2796,7 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== -grapheme-splitter@^1.0.2: +grapheme-splitter@^1.0.2, grapheme-splitter@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== @@ -2577,6 +2980,19 @@ iferr@^0.1.5: resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= +ignore@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" + integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== + +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== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + import-lazy@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-3.1.0.tgz#891279202c8a2280fdbd6674dbd8da1a1dfc67cc" @@ -2758,6 +3174,13 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + is-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" @@ -2873,11 +3296,23 @@ jest-worker@^26.2.1: merge-stream "^2.0.0" supports-color "^7.0.0" +js-sdsl@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.1.4.tgz#78793c90f80e8430b7d8dc94515b6c77d98a26a6" + integrity sha512-Y2/yD55y5jteOAmY50JbUZYwk3CP3wnLPEZnlR1w9oKhITrBEtAxwuWKebFf8hMrPMgbYwFoWK/lH2sBkErELw== + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + json-buffer@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" @@ -2903,6 +3338,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +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 sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + 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" @@ -3031,6 +3471,14 @@ lazystream@^1.0.0: dependencies: readable-stream "^2.0.5" +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" + lighthouse-logger@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/lighthouse-logger/-/lighthouse-logger-1.2.0.tgz#b76d56935e9c137e86a04741f6bb9b2776e886ca" @@ -3073,6 +3521,13 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + lodash.clonedeep@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" @@ -3103,7 +3558,7 @@ lodash.isplainobject@^4.0.6: resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= -lodash.merge@^4.6.1: +lodash.merge@^4.6.1, 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== @@ -3262,6 +3717,11 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" @@ -3289,6 +3749,14 @@ micromatch@^4.0.0: braces "^3.0.1" picomatch "^2.2.3" +micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -3341,6 +3809,13 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" +minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" @@ -3431,6 +3906,11 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -3593,6 +4073,18 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" +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" + os-browserify@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" @@ -3639,6 +4131,13 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + p-locate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" @@ -3653,6 +4152,13 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-timeout@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038" @@ -3687,6 +4193,13 @@ param-case@^3.0.4: dot-case "^3.0.4" tslib "^2.0.3" +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + parse-asn1@^5.0.0, parse-asn1@^5.1.5: version "5.1.6" resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" @@ -3774,11 +4287,21 @@ path-key@^2.0.0, path-key@^2.0.1: resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + path-parse@^1.0.6, path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + pbkdf2@^3.0.3: version "3.1.2" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" @@ -3800,6 +4323,11 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + pify@^2.2.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -3846,11 +4374,28 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= +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== + prepend-http@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" + integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== + printj@~1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" @@ -3995,6 +4540,11 @@ querystring@0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + quick-lru@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" @@ -4102,6 +4652,11 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexpp@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== + remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -4137,6 +4692,11 @@ resolve-alpn@^1.0.0: resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.1.2.tgz#30b60cfbb0c0b8dc897940fe13fe255afcdd4d28" integrity sha512-8OyfzhAtA32LVUsJSke3auIyINcwdh5l3cvYKdKO0nvsYSKuiLfTM5i78PJswFPT8y6cPW+L1v6/hE95chcpDA== +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -4185,6 +4745,11 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + rfdc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" @@ -4246,6 +4811,13 @@ rollup@^2.53: optionalDependencies: fsevents "~2.3.2" +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + run-queue@^1.0.0, run-queue@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" @@ -4345,6 +4917,13 @@ semver@^7.3.2: dependencies: lru-cache "^6.0.0" +semver@^7.3.7: + version "7.3.7" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + dependencies: + lru-cache "^6.0.0" + sentence-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-3.0.4.tgz#3645a7b8c117c787fde8702056225bb62a45131f" @@ -4422,16 +5001,33 @@ shebang-command@^1.2.0: dependencies: shebang-regex "^1.0.0" +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + signal-exit@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + snake-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" @@ -4726,6 +5322,13 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-dirs@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5" @@ -4738,6 +5341,11 @@ strip-eof@^1.0.0: resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= +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== + strip-outer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631" @@ -4843,6 +5451,11 @@ terser@^5.0.0: source-map "~0.7.2" source-map-support "~0.5.19" +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + through2@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -4963,7 +5576,7 @@ ts-loader@^6.0.4: micromatch "^4.0.0" semver "^6.0.0" -tslib@^1.9.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== @@ -4978,6 +5591,13 @@ tslib@^2.4.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== +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== + dependencies: + tslib "^1.8.1" + tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" @@ -4995,6 +5615,13 @@ tunnel@0.0.6: resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== +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.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" @@ -5336,13 +5963,18 @@ which@^1.2.1, which@^1.2.9: dependencies: isexe "^2.0.0" -which@^2.0.2: +which@^2.0.1, which@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" +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== + worker-farm@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" @@ -5474,6 +6106,11 @@ yeast@0.1.2: resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + zip-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.1.0.tgz#51dd326571544e36aa3f756430b313576dc8fc79" From 96869438506e23ab9ccda5df4b2a96fa4ce0f55c Mon Sep 17 00:00:00 2001 From: "LB (Ben Johnston)" Date: Tue, 4 Oct 2022 19:04:26 +1000 Subject: [PATCH 07/12] Split out getting help / contributing back & add dev docs (#587) * Readme - split out getting help / contributing back & add dev docs - add basic development docs * Update README.md Co-authored-by: Marco Roth --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f2d3863..7016e7e9 100644 --- a/README.md +++ b/README.md @@ -44,16 +44,27 @@ You can use Stimulus with any asset packaging systems. And if you prefer no buil See the [Installation Guide](https://stimulus.hotwired.dev/handbook/installing) for detailed instructions. -## Getting Help & Contributing Back +## Getting Help Looking for the docs? Once you've read through the Handbook, consult the [Stimulus Reference](https://stimulus.hotwired.dev/reference/controllers) for API details. Have a question about Stimulus? Connect with other Stimulus developers on the [Hotwire Discourse](https://discuss.hotwired.dev/) community forum. +## Contributing Back + Find a bug? Head over to our [issue tracker](https://github.com/hotwired/stimulus/issues) and we'll do our best to help. We love pull requests, too! We expect all Stimulus contributors to abide by the terms of our [Code of Conduct](CODE_OF_CONDUCT.md). +### Development + +- Fork the project locally +- `yarn install` +- `yarn start` - to run the local dev server with examples +- `yarn test` - to run the unit tests +- `yarn lint` - to run the linter with ESLint +- `yarn format` - to format changes with Prettier + ## Acknowledgments Stimulus is [MIT-licensed](LICENSE.md) open-source software from [Basecamp](https://basecamp.com/), the creators of [Ruby on Rails](http://rubyonrails.org). From 8b8b79b45036843fbc3eeee9334c84c9ae67bd2f Mon Sep 17 00:00:00 2001 From: Jacopo Beschi Date: Mon, 31 Oct 2022 12:58:31 +0100 Subject: [PATCH 08/12] Clear dangling EventListeners and Detached Nodes when a controller is removed from the DOM (#592) `eventListenerMaps` maps HTML elements handled by Stimulus to `eventListenerMap`, a map of Stimulus EventListeners in this way: `{ HTMLelement: { eventName: EventListener } }`. When a controller HTML is removed from the DOM the dangling EventListeners aren't removed, moreover, the removed HTML elements are still referenced via the `eventListenerMaps` keys. This leads to having multiple detached HTMLelements and eventListeners which can't be GCed hence a memory leak. The leak is fixed by removing the dangling eventListeners and clearing unused `eventListenerMaps` keys. When the HTMLelement is attached again to the DOM, the `eventListenerMaps` and the related `eventListeners` are automatically re-created by Stimulus via the MutationObservers, no data is lost by doing this. Below is an example to reproduce the issue: ``` EventListenerMaps memory leak
To reproduce:
  • Check heap snapshot
  • Click "trigger leak" button
  • Check heap snapshot again
``` --- src/core/binding_observer.ts | 4 ++-- src/core/dispatcher.ts | 20 +++++++++++++++++++- src/core/event_listener.ts | 4 ++++ src/tests/modules/core/memory_tests.ts | 22 ++++++++++++++++++++++ 4 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 src/tests/modules/core/memory_tests.ts diff --git a/src/core/binding_observer.ts b/src/core/binding_observer.ts index 99a5019f..fd56295e 100644 --- a/src/core/binding_observer.ts +++ b/src/core/binding_observer.ts @@ -7,7 +7,7 @@ import { Token, ValueListObserver, ValueListObserverDelegate } from "../mutation export interface BindingObserverDelegate extends ErrorHandler { bindingConnected(binding: Binding): void - bindingDisconnected(binding: Binding): void + bindingDisconnected(binding: Binding, clearEventListeners?: boolean): void } export class BindingObserver implements ValueListObserverDelegate { @@ -72,7 +72,7 @@ export class BindingObserver implements ValueListObserverDelegate { } private disconnectAllActions() { - this.bindings.forEach((binding) => this.delegate.bindingDisconnected(binding)) + this.bindings.forEach((binding) => this.delegate.bindingDisconnected(binding, true)) this.bindingsByAction.clear() } diff --git a/src/core/dispatcher.ts b/src/core/dispatcher.ts index cfa9e365..8494253c 100644 --- a/src/core/dispatcher.ts +++ b/src/core/dispatcher.ts @@ -41,8 +41,9 @@ export class Dispatcher implements BindingObserverDelegate { this.fetchEventListenerForBinding(binding).bindingConnected(binding) } - bindingDisconnected(binding: Binding) { + bindingDisconnected(binding: Binding, clearEventListeners: boolean = false) { this.fetchEventListenerForBinding(binding).bindingDisconnected(binding) + if (clearEventListeners) this.clearEventListenersForBinding(binding) } // Error handling @@ -51,6 +52,23 @@ export class Dispatcher implements BindingObserverDelegate { this.application.handleError(error, `Error ${message}`, detail) } + private clearEventListenersForBinding(binding: Binding) { + const eventListener = this.fetchEventListenerForBinding(binding) + if (!eventListener.hasBindings()) { + eventListener.disconnect() + this.removeMappedEventListenerFor(binding) + } + } + + private removeMappedEventListenerFor(binding: Binding) { + const { eventTarget, eventName, eventOptions } = binding + const eventListenerMap = this.fetchEventListenerMapForEventTarget(eventTarget) + const cacheKey = this.cacheKey(eventName, eventOptions) + + eventListenerMap.delete(cacheKey) + if (eventListenerMap.size == 0) this.eventListenerMaps.delete(eventTarget) + } + private fetchEventListenerForBinding(binding: Binding): EventListener { const { eventTarget, eventName, eventOptions } = binding return this.fetchEventListener(eventTarget, eventName, eventOptions) diff --git a/src/core/event_listener.ts b/src/core/event_listener.ts index f78680d6..1b849e47 100644 --- a/src/core/event_listener.ts +++ b/src/core/event_listener.ts @@ -43,6 +43,10 @@ export class EventListener implements EventListenerObject { } } + hasBindings() { + return this.unorderedBindings.size > 0 + } + get bindings(): Binding[] { return Array.from(this.unorderedBindings).sort((left, right) => { const leftIndex = left.index, diff --git a/src/tests/modules/core/memory_tests.ts b/src/tests/modules/core/memory_tests.ts new file mode 100644 index 00000000..ae73a28c --- /dev/null +++ b/src/tests/modules/core/memory_tests.ts @@ -0,0 +1,22 @@ +import { ControllerTestCase } from "../../cases/controller_test_case" + +export default class MemoryTests extends ControllerTestCase() { + controllerElement!: Element + + async setup() { + this.controllerElement = this.controller.element + } + + fixtureHTML = ` +
+ + +
+ ` + + async "test removing a controller clears dangling eventListeners"() { + this.assert.equal(this.application.dispatcher.eventListeners.length, 2) + await this.fixtureElement.removeChild(this.controllerElement) + this.assert.equal(this.application.dispatcher.eventListeners.length, 0) + } +} From 710342f98ae2dc7cc9b82b8f73d55d01dbc2ae07 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 31 Oct 2022 14:33:41 +0100 Subject: [PATCH 09/12] Later Node --- .node-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.node-version b/.node-version index 2e4239c3..99cdd800 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -16.7.0 +16.15.0 From d2db2f669ea17826af13b5f89951db77d2172b0e Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 31 Oct 2022 14:35:13 +0100 Subject: [PATCH 10/12] Bump for 3.1.1 --- package.json | 2 +- packages/stimulus/package.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index c487d7c3..6047221c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@hotwired/stimulus", - "version": "3.1.0", + "version": "3.1.1", "license": "MIT", "description": "A modest JavaScript framework for the HTML you already have.", "author": "Basecamp, LLC", diff --git a/packages/stimulus/package.json b/packages/stimulus/package.json index 9a76c624..8d0d36ed 100644 --- a/packages/stimulus/package.json +++ b/packages/stimulus/package.json @@ -1,6 +1,6 @@ { "name": "stimulus", - "version": "3.1.0", + "version": "3.1.1", "description": "Stimulus JavaScript framework", "repository": "https://stimulus.hotwired.dev", "author": "Basecamp, LLC", @@ -42,7 +42,7 @@ ], "license": "MIT", "dependencies": { - "@hotwired/stimulus": "^3.1.0", + "@hotwired/stimulus": "^3.1.1", "@hotwired/stimulus-webpack-helpers": "^1.0.0" }, "devDependencies": { From f9bf546a807f0c9be092d59cc6a1b6b69fafb3c6 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 31 Oct 2022 14:38:41 +0100 Subject: [PATCH 11/12] Update lock --- packages/stimulus/yarn.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/stimulus/yarn.lock b/packages/stimulus/yarn.lock index 5c5c297c..261b28b4 100644 --- a/packages/stimulus/yarn.lock +++ b/packages/stimulus/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@hotwired/stimulus-webpack-helpers/-/stimulus-webpack-helpers-1.0.0.tgz#6bd7906a4a2b6e1cd8732203b60264f987bd1084" integrity sha512-6oKDmJDSsV+zdlHnF485nneuekY/Zbl669wei4HIiwxUWHhVSU1XIVji4aj+Ws9AXghjTYBS8H5ralB97BVMDw== -"@hotwired/stimulus@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@hotwired/stimulus/-/stimulus-3.0.0.tgz#45171e61417af60f0e546665c52fae5b67295cee" - integrity sha512-UFIuuf7GjKJoIYromuTmqfzT8gZ8eu5zIB5m2QoEsopymGeN7rfDSTRPyRfwHIqP0x+0vWo4O1LFozw+/sWXxg== +"@hotwired/stimulus@^3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@hotwired/stimulus/-/stimulus-3.1.1.tgz#652f08a8e1d5edcb407340e58818fcff463b5848" + integrity sha512-e0JpzIaYLsRRXevRDVs0yevabiCvieIWWCwh7VqVXjXM5AOHdjb7AjaKIj34zYFmY1N6HIRRfk915WVMYlHnDA== "@rollup/plugin-node-resolve@^13.0.0": version "13.0.5" From 2fdac1a33ceeb2c9786af3f115e2de618339f7f0 Mon Sep 17 00:00:00 2001 From: "LB (Ben Johnston)" Date: Thu, 17 Nov 2022 20:31:47 +1000 Subject: [PATCH 12/12] add ability to set `afterLoad` static methods on Controllers (#579) - when a controller is registered, the `afterLoad` static method, if present, will be called - it gets passed the application instance and the identifier that was used to register it - resolves #574 --- docs/reference/controllers.md | 32 +++++++++++++++++++++++++ src/core/controller.ts | 5 ++++ src/core/router.ts | 4 ++++ src/tests/modules/core/loading_tests.ts | 30 +++++++++++++++++++++++ 4 files changed, 71 insertions(+) diff --git a/docs/reference/controllers.md b/docs/reference/controllers.md index ac6f8223..71b86814 100644 --- a/docs/reference/controllers.md +++ b/docs/reference/controllers.md @@ -163,6 +163,38 @@ class UnloadableController extends ApplicationController { application.register("unloadable", UnloadableController) ``` +### Trigger Behaviour When A Controller Is Registered + +If you want to trigger some behaviour once a controller has been registered you can add a static `afterLoad` method: + +```js +class SpinnerButton extends Controller { + static afterLoad(identifier, application) { + // use the application instance to read the configured 'data-controller' attribute + const { controllerAttribute } = application.schema + + // update any legacy buttons with the controller's registered identifier + const updateLegacySpinners = () => { + document.querySelector(".legacy-spinner-button").forEach((element) => { + element.setAttribute(controllerAttribute, identifier) + }) + } + + // called as soon as registered so DOM many not have loaded yet + if (document.readyState == "loading") { + document.addEventListener("DOMContentLoaded", updateLegacySpinners) + } else { + updateLegacySpinners() + } + } +} + +// This controller will update any legacy spinner buttons to use the controller +application.register("spinner-button", SpinnerButton) +``` + +The `afterLoad` method will get called as soon as the controller has been registered, even if no controlled elements exist in the DOM. It gets called with the `identifier` that was used when registering the controller and the Stimulus application instance. + ## Cross-Controller Coordination With Events If you need controllers to communicate with each other, you should use events. The `Controller` class has a convenience method called `dispatch` that makes this easier. It takes an `eventName` as the first argument, which is then automatically prefixed with the name of the controller separated by a colon. The payload is held in `detail`. It works like this: diff --git a/src/core/controller.ts b/src/core/controller.ts index d31f1456..f3db251a 100644 --- a/src/core/controller.ts +++ b/src/core/controller.ts @@ -1,3 +1,4 @@ +import { Application } from "./application" import { ClassPropertiesBlessing } from "./class_properties" import { Constructor } from "./constructor" import { Context } from "./context" @@ -15,6 +16,10 @@ export class Controller { return true } + static afterLoad(_identifier: string, _application: Application) { + return + } + readonly context: Context constructor(context: Context) { diff --git a/src/core/router.ts b/src/core/router.ts index e0ce3af1..c06e5e57 100644 --- a/src/core/router.ts +++ b/src/core/router.ts @@ -55,6 +55,10 @@ export class Router implements ScopeObserverDelegate { this.unloadIdentifier(definition.identifier) const module = new Module(this.application, definition) this.connectModule(module) + const afterLoad = (definition.controllerConstructor as any).afterLoad + if (afterLoad) { + afterLoad(definition.identifier, this.application) + } } unloadIdentifier(identifier: string) { diff --git a/src/tests/modules/core/loading_tests.ts b/src/tests/modules/core/loading_tests.ts index efe496d9..1190dcdb 100644 --- a/src/tests/modules/core/loading_tests.ts +++ b/src/tests/modules/core/loading_tests.ts @@ -12,6 +12,16 @@ class LoadableController extends LogController { } } +class AfterLoadController extends LogController { + static afterLoad(identifier: string, application: any) { + const newElement = document.createElement("div") + newElement.classList.add("after-load-test") + newElement.setAttribute(application.schema.controllerAttribute, identifier) + application.element.append(newElement) + document.dispatchEvent(new CustomEvent("test", { detail: { identifier, application } })) + } +} + export default class ApplicationTests extends ApplicationTestCase { fixtureHTML = `
` @@ -25,6 +35,26 @@ export default class ApplicationTests extends ApplicationTestCase { this.assert.equal(this.controllers.length, 1) } + "test module with afterLoad method should be triggered when registered"() { + // set up an event listener to track the params passed into the AfterLoadController + let data: { application?: any; identifier?: string } = {} + document.addEventListener("test", (({ detail }: CustomEvent) => { + data = detail + }) as EventListener) + + this.assert.equal(data.identifier, undefined) + this.assert.equal(data.application, undefined) + + this.application.register("after-load", AfterLoadController) + + // check the DOM element has been added based on params provided + this.assert.equal(this.findElements('[data-controller="after-load"]').length, 1) + + // check that static method was correctly called with the params + this.assert.equal(data.identifier, "after-load") + this.assert.equal(data.application, this.application) + } + get controllers() { return this.application.controllers as LogController[] }