From ad92272fa0e6ecdc254260e9e6499e114f4f1845 Mon Sep 17 00:00:00 2001 From: Kevin Barabash Date: Thu, 13 Apr 2023 15:00:53 -0400 Subject: [PATCH] Move eslint-plugin-khan repo into wonder-stuff (#638) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary: This code could use some TLC to update it to use jest and TS, but we can do that later. The important thing is that it's co-located with eslint-config-khan and will be using changeset for publishing instead of having to publish manually. Issue: None ## Test plan: - yarn --cwd packages/eslint-plugin-khan test Author: kevinbarabash Reviewers: github-code-scanning[bot], jeresig Required Reviewers: Approved By: jeresig Checks: ✅ codecov/project, ✅ Test (macos-latest, 16.x), ✅ CodeQL, ✅ Lint, typecheck, and coverage check (ubuntu-latest, 16.x), ✅ gerald, ✅ Prime node_modules cache for primary configuration (ubuntu-latest, 16.x), ✅ Analyze (javascript), ⏭ dependabot Pull Request URL: https://github.com/Khan/wonder-stuff/pull/638 --- .changeset/fresh-sloths-turn.md | 5 + .github/workflows/node-ci.yml | 3 + config/jest/test.config.js | 2 +- packages/eslint-plugin-khan/CHANGELOG.md | 1 + packages/eslint-plugin-khan/LICENSE | 21 + packages/eslint-plugin-khan/README.md | 18 + .../docs/flow-array-type-style.md | 29 + .../docs/flow-exact-props.md | 44 + .../docs/flow-exact-state.md | 38 + .../docs/flow-no-one-tuple.md | 33 + .../docs/imports-requiring-flow.md | 95 ++ .../docs/jest-async-use-real-timers.md | 56 ++ .../docs/jest-await-async-matchers.md | 120 +++ .../docs/jest-enzyme-matchers.md | 64 ++ .../docs/react-no-method-jsx-attribute.md | 80 ++ .../react-no-subscriptions-before-mount.md | 44 + .../docs/react-svg-path-precision.md | 24 + packages/eslint-plugin-khan/docs/sync-tag.md | 11 + packages/eslint-plugin-khan/lib/index.js | 16 + .../eslint-plugin-khan/lib/react-utils.js | 33 + .../lib/rules/flow-array-type-style.js | 44 + .../lib/rules/flow-exact-props.js | 76 ++ .../lib/rules/flow-exact-state.js | 66 ++ .../lib/rules/flow-no-one-tuple.js | 44 + .../lib/rules/imports-requiring-flow.js | 115 +++ .../lib/rules/jest-async-use-real-timers.js | 118 +++ .../lib/rules/jest-await-async-matchers.js | 107 +++ .../lib/rules/jest-enzyme-matchers.js | 232 +++++ .../rules/react-no-method-jsx-attribute.js | 73 ++ .../react-no-subscriptions-before-mount.js | 94 ++ .../lib/rules/react-svg-path-precision.js | 75 ++ .../eslint-plugin-khan/lib/rules/sync-tag.js | 125 +++ packages/eslint-plugin-khan/lib/util.js | 6 + packages/eslint-plugin-khan/package.json | 44 + .../test/flow-array-type-style_test.js | 39 + .../test/flow-exact-props_test.js | 78 ++ .../test/flow-exact-state_test.js | 74 ++ .../test/flow-no-one-tuple_test.js | 43 + .../test/imports-requiring-flow_test.js | 331 +++++++ .../test/jest-async-use-real-timers_test.js | 164 ++++ .../test/jest-await-async-matchers_test.js | 131 +++ .../test/jest-enzyme-matchers_test.js | 150 +++ .../react-no-method-jsx-attribute_test.js | 117 +++ ...eact-no-subscriptions-before-mount_test.js | 143 +++ .../test/react-svg-path-precision.js | 55 ++ .../eslint-plugin-khan/test/sync-tag_test.js | 80 ++ utils/pre-publish-check-ci.js | 2 +- yarn.lock | 883 +++++++++++++++++- 48 files changed, 4208 insertions(+), 38 deletions(-) create mode 100644 .changeset/fresh-sloths-turn.md create mode 100644 packages/eslint-plugin-khan/CHANGELOG.md create mode 100644 packages/eslint-plugin-khan/LICENSE create mode 100644 packages/eslint-plugin-khan/README.md create mode 100644 packages/eslint-plugin-khan/docs/flow-array-type-style.md create mode 100644 packages/eslint-plugin-khan/docs/flow-exact-props.md create mode 100644 packages/eslint-plugin-khan/docs/flow-exact-state.md create mode 100644 packages/eslint-plugin-khan/docs/flow-no-one-tuple.md create mode 100644 packages/eslint-plugin-khan/docs/imports-requiring-flow.md create mode 100644 packages/eslint-plugin-khan/docs/jest-async-use-real-timers.md create mode 100644 packages/eslint-plugin-khan/docs/jest-await-async-matchers.md create mode 100644 packages/eslint-plugin-khan/docs/jest-enzyme-matchers.md create mode 100644 packages/eslint-plugin-khan/docs/react-no-method-jsx-attribute.md create mode 100644 packages/eslint-plugin-khan/docs/react-no-subscriptions-before-mount.md create mode 100644 packages/eslint-plugin-khan/docs/react-svg-path-precision.md create mode 100644 packages/eslint-plugin-khan/docs/sync-tag.md create mode 100644 packages/eslint-plugin-khan/lib/index.js create mode 100644 packages/eslint-plugin-khan/lib/react-utils.js create mode 100644 packages/eslint-plugin-khan/lib/rules/flow-array-type-style.js create mode 100644 packages/eslint-plugin-khan/lib/rules/flow-exact-props.js create mode 100644 packages/eslint-plugin-khan/lib/rules/flow-exact-state.js create mode 100644 packages/eslint-plugin-khan/lib/rules/flow-no-one-tuple.js create mode 100644 packages/eslint-plugin-khan/lib/rules/imports-requiring-flow.js create mode 100644 packages/eslint-plugin-khan/lib/rules/jest-async-use-real-timers.js create mode 100644 packages/eslint-plugin-khan/lib/rules/jest-await-async-matchers.js create mode 100644 packages/eslint-plugin-khan/lib/rules/jest-enzyme-matchers.js create mode 100644 packages/eslint-plugin-khan/lib/rules/react-no-method-jsx-attribute.js create mode 100644 packages/eslint-plugin-khan/lib/rules/react-no-subscriptions-before-mount.js create mode 100644 packages/eslint-plugin-khan/lib/rules/react-svg-path-precision.js create mode 100644 packages/eslint-plugin-khan/lib/rules/sync-tag.js create mode 100644 packages/eslint-plugin-khan/lib/util.js create mode 100644 packages/eslint-plugin-khan/package.json create mode 100644 packages/eslint-plugin-khan/test/flow-array-type-style_test.js create mode 100644 packages/eslint-plugin-khan/test/flow-exact-props_test.js create mode 100644 packages/eslint-plugin-khan/test/flow-exact-state_test.js create mode 100644 packages/eslint-plugin-khan/test/flow-no-one-tuple_test.js create mode 100644 packages/eslint-plugin-khan/test/imports-requiring-flow_test.js create mode 100644 packages/eslint-plugin-khan/test/jest-async-use-real-timers_test.js create mode 100644 packages/eslint-plugin-khan/test/jest-await-async-matchers_test.js create mode 100644 packages/eslint-plugin-khan/test/jest-enzyme-matchers_test.js create mode 100644 packages/eslint-plugin-khan/test/react-no-method-jsx-attribute_test.js create mode 100644 packages/eslint-plugin-khan/test/react-no-subscriptions-before-mount_test.js create mode 100644 packages/eslint-plugin-khan/test/react-svg-path-precision.js create mode 100644 packages/eslint-plugin-khan/test/sync-tag_test.js diff --git a/.changeset/fresh-sloths-turn.md b/.changeset/fresh-sloths-turn.md new file mode 100644 index 00000000..3f3e8341 --- /dev/null +++ b/.changeset/fresh-sloths-turn.md @@ -0,0 +1,5 @@ +--- +"@khanacademy/eslint-plugin": patch +--- + +Update 'imports-require-flow' rule to support modern parsers diff --git a/.github/workflows/node-ci.yml b/.github/workflows/node-ci.yml index 9f0f1de1..9e1f143a 100644 --- a/.github/workflows/node-ci.yml +++ b/.github/workflows/node-ci.yml @@ -186,3 +186,6 @@ jobs: full: yarn test limited-trigger: ${{ steps.js-ts-files.outputs.filtered }} limited: yarn test --findRelatedTests --passWithNoTests {} + + - name: Mocha (eslint-khan-plugin) + run: yarn --cwd packages/eslint-plugin-khan test diff --git a/config/jest/test.config.js b/config/jest/test.config.js index 67324347..8be452f4 100644 --- a/config/jest/test.config.js +++ b/config/jest/test.config.js @@ -31,7 +31,7 @@ module.exports = { "!/node_modules/", "!packages/**/node_modules/", "!packages/**/.babelrc.js", - "!packages/eslint-config-khan/**", + "!packages/eslint-*-khan/**", "!packages/**/examples/**/*.ts", "!packages/**/types/**/*.ts", ], diff --git a/packages/eslint-plugin-khan/CHANGELOG.md b/packages/eslint-plugin-khan/CHANGELOG.md new file mode 100644 index 00000000..8b0bccda --- /dev/null +++ b/packages/eslint-plugin-khan/CHANGELOG.md @@ -0,0 +1 @@ +# @khanacademy/eslint-plugin diff --git a/packages/eslint-plugin-khan/LICENSE b/packages/eslint-plugin-khan/LICENSE new file mode 100644 index 00000000..fe9a400c --- /dev/null +++ b/packages/eslint-plugin-khan/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Khan Academy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/eslint-plugin-khan/README.md b/packages/eslint-plugin-khan/README.md new file mode 100644 index 00000000..b6f45f97 --- /dev/null +++ b/packages/eslint-plugin-khan/README.md @@ -0,0 +1,18 @@ +# eslint-plugin-khan + +[![Build Status](https://travis-ci.org/Khan/eslint-plugin-khan.svg?branch=master)](https://travis-ci.org/Khan/eslint-plugin-khan) + +eslint plugin with our set of custom rules for various things + +## Rules + +- [khan/flow-array-type-style](docs/flow-array-type-style.md) +- [khan/flow-exact-props](docs/flow-exact-props.md) +- [khan/flow-exact-state](docs/flow-exact-state.md) +- [khan/flow-no-one-tuple](docs/flow-no-one-tuple.md) +- [khan/imports-requiring-flow](docs/imports-requiring-flow.md) +- [khan/jest-async-use-real-timers](docs/jest-async-use-real-timers.md) +- [khan/jest-enzyme-matchers](docs/jest-enzyme-matchers.md) +- [khan/react-no-method-jsx-attribute](docs/react-no-method-jsx-attribute.md) +- [khan/react-no-subscriptions-before-mount](docs/react-no-subscriptions-before-mount.md) +- [khan/react-svg-path-precision](docs/react-svg-path-precision.md) diff --git a/packages/eslint-plugin-khan/docs/flow-array-type-style.md b/packages/eslint-plugin-khan/docs/flow-array-type-style.md new file mode 100644 index 00000000..573ba548 --- /dev/null +++ b/packages/eslint-plugin-khan/docs/flow-array-type-style.md @@ -0,0 +1,29 @@ +# Prefer generic style array types in flow (flow-array-type-style) + +In flow, array types can be written as `T[]` or `Array`. When the type is +nullable, the former can be consfusing, e.g. + +``` +?T[] = ?Array +(?T)[] = Array +``` + +This rule prefers the `Array` for array types to avoid the confusion. + +## Rule Details + +The following are considered warnings: + +```js +type foo = number[] +type foo = ?number[] +type foo = (?number)[] +``` + +The following are not considered warnings: + +```js +type foo = Array +type foo = ?Array +type foo = Array +``` diff --git a/packages/eslint-plugin-khan/docs/flow-exact-props.md b/packages/eslint-plugin-khan/docs/flow-exact-props.md new file mode 100644 index 00000000..a1fc3996 --- /dev/null +++ b/packages/eslint-plugin-khan/docs/flow-exact-props.md @@ -0,0 +1,44 @@ +# Require exact object types for Props (flow-exact-props) + +Using exact object types for Props can help identify extra props and in +the case of optional props, typos. + +This rule supports autofixing. Please note, this may result in new flow +errors. + +## Rule Details + +The following are considered warnings: + +```js +type Props = { x: number }; +class Foo extends React.Component {} +``` + +```js +type FooProps = { x: number }; +class Foo extends React.Component {} +``` + +```js +type Props = { x: number }; +const Foo = (props: Props) => {} +``` + +The following are not considered warnings: + +```js +type Props = {| x: number |}; +class Foo extends React.Component {} +``` + +```js +type BarProps = { x: number }; +type FooProps = {| x: number |}; +class Foo extends React.Component {} +``` + +```js +type Props = {| x: number |}; +const Foo = (props: Props) => {} +``` diff --git a/packages/eslint-plugin-khan/docs/flow-exact-state.md b/packages/eslint-plugin-khan/docs/flow-exact-state.md new file mode 100644 index 00000000..be12a589 --- /dev/null +++ b/packages/eslint-plugin-khan/docs/flow-exact-state.md @@ -0,0 +1,38 @@ +# Require exact object types for State (flow-exact-state) + +Using exact object types for State can help types when there are optional +state properties. + +This rule supports autofixing. Please note, this may result in new flow +errors. + +## Rule Details + +The following are considered warnings: + +```js +type Props = {| x: number |}; +type State = { x: number }; +class Foo extends React.Component {} +``` + +```js +type FooProps = {| x: number |}; +type FooState = { x: number }; +class Foo extends React.Component {} +``` + +The following are not considered warnings: + +```js +type Props = { x: number }; +type State = {| x: number |}; +class Foo extends React.Component {} +``` + +```js +type FooProps = { x: number }; +type BarState = { x: number }; +type FooState = {| x: number |}; +class Foo extends React.Component { +``` diff --git a/packages/eslint-plugin-khan/docs/flow-no-one-tuple.md b/packages/eslint-plugin-khan/docs/flow-no-one-tuple.md new file mode 100644 index 00000000..7ce2ee61 --- /dev/null +++ b/packages/eslint-plugin-khan/docs/flow-no-one-tuple.md @@ -0,0 +1,33 @@ +# Disallow one-tuples in flow (flow-no-one-tuple) + +A common mistake people make, when starting to use Flow, is to assume that +arrays of a type can be expressed by wrapping the type in brackets (eg. +`[number]`). + +This expression _actually_ refers to a tuple, which is an array that holds a +finite number of items, each with their type specified. The correct expression +would be `Array`. + +To help avoid this mistake, we warn against the uncommon pattern of a +single-value tuple. + +## Rule Details + +The following are considered warnings: + +```js +type foo = [number] +``` + +The following are not considered warnings: + +```js +type foo = Array +type foo = [number, number] +``` + +If you actually need a one-tuple, the rule can be disabled, e.g. + +```js +type foo = [number] // eslint-disable-line flow-no-one-tuple +``` diff --git a/packages/eslint-plugin-khan/docs/imports-requiring-flow.md b/packages/eslint-plugin-khan/docs/imports-requiring-flow.md new file mode 100644 index 00000000..18efe817 --- /dev/null +++ b/packages/eslint-plugin-khan/docs/imports-requiring-flow.md @@ -0,0 +1,95 @@ +# Specify imports that require flow (imports-requiring-flow) + +We'd like to require the use of flow with certain modules so that we can +rely on flow to catch issues when refactoring those modules. This rule +requires that any files importing one of the specified modules be using +flow as indicated by a `// @flow` comment in the file. + +This rule also works with directories, so that any modules under that directory +or its subdirectories are required to be using flow types. + +Notes: +- All paths in `modules` that aren't npm packages (i.e. are directories or files + within your codebase) are considered to be relative to `rootDir`. +- `rootDir` is required and should usually be set to `__dirname`. This + requires the the configuration of `@khanacademy/imports-requiring-flow` to be + done in a `.js` file. + +## Rule Details + +Give the following rule config: + +```js +"@khanacademy/imports-requiring-flow": [ + "error", { + rootDir: __dirname, + modules: ["foo", "src/bar.js"], + }, +] +``` + +The following are considered warnings: + +```js +import foo from "foo"; +``` + +```js +import foo from "foo/bar"; +``` + +```js +import bar from "./bar"; +``` + +```js +const foo = require("foo"); +``` + +```js +const foo = require("foo/bar"); +``` + +```js +const bar = require("./bar.js"); +``` + +The following are not considered warnings: + +```js +// @flow +import foo from "foo"; +``` + +```js +// @flow +import foo from "foo/bar"; +``` + +```js +// @flow +import bar from "./bar"; +``` + +```js +// @flow +const foo = require("foo"); +``` + +```js +// @flow +const foo = require("foo/bar"); +``` + +```js +// @flow +const bar = require("./bar.js"); +``` + +```js +import baz from "./baz.js"; +``` + +```js +const qux = require("qux"); +``` diff --git a/packages/eslint-plugin-khan/docs/jest-async-use-real-timers.md b/packages/eslint-plugin-khan/docs/jest-async-use-real-timers.md new file mode 100644 index 00000000..4cb19ca4 --- /dev/null +++ b/packages/eslint-plugin-khan/docs/jest-async-use-real-timers.md @@ -0,0 +1,56 @@ +# Require async tests to call jest.useRealTimers() (jest-async-use-real-timers) + +By default jest uses real timers. Some projects opt to use fake timers +as a default in order to speed up test execution. This requires any +async tests to explicitly call `jest.useRealTimers()`. This rule can +be used in this situation to help developers remember to make this call. + +## Rule Details + +The following are considered warnings: + +```js +describe("foo", () => { + it("requires real timers", async () => {}); +}) +``` + +```js +describe("foo", () => { + it("requires real timers", async function() {}); +}) +``` + +The following are not considered warnings: + +```js +describe("foo", () => { + it("doesn't require real timers", () => {}); +}) +``` + +```js +describe("foo", () => { + it("requires real timers", async () => { + jest.useRealTimers(); + }); +}) +``` + +```js +describe("foo", () => { + it("requires real timers", async function() { + jest.useRealTimers(); + }); +}) +``` + +```js +describe("foo", () => { + beforeEach(() => { + jest.useRealTimers(); + }); + + it("requires real timers", async () => {}); +}) +``` diff --git a/packages/eslint-plugin-khan/docs/jest-await-async-matchers.md b/packages/eslint-plugin-khan/docs/jest-await-async-matchers.md new file mode 100644 index 00000000..5b98f1e2 --- /dev/null +++ b/packages/eslint-plugin-khan/docs/jest-await-async-matchers.md @@ -0,0 +1,120 @@ +# Require async matchers to be awaited (jest-await-async-matchers) + +Matchers that contain asynchronous computations must be awaited to operate correctly. +If they aren't, a test could pass even if it should fail. This is because the +matcher hasn't finished running. This rule can help ensure that we're `await`-ing +those matchers that need it. By default the following matchers must be `await`-ed +with this rule: `.resolves.`, `.rejects.`, `.toResolve()`, and `.toReject()`. Custom +matchers can be added to this list using the `matchers` option. + +## Rule Details + +Give the following rule config: + +```js +"@khanacademy/jest": [ + "error", { + matchers: ["toHaveMarkedConversion"], + }, +] +``` + +The following are considered warnings: + +```js +async () => { + expect(callback).toHaveMarkedConversion(); +} +``` + +```js +async () => { + expect(callback).not.toHaveMarkedConversion(); +} +``` + +```js +async () => { + expect(promise).resolves.toBe(true); +} +``` + +```js +async () => { + expect(promise).resolves.not.toBe(true); +} +``` + +```js +async () => { + expect(promise).rejects.toThrow(new Error("bar")); +} +``` + +```js +async () => { + expect(promise).rejects.not.toThrow(new Error("bar")); +} +``` + +```js +async () => { + expect(promise).toResolve(); +} +``` + +```js +async () => { + expect(promise).toReject(); +} +``` + +The following are not considered warnings: + +```js +async () => { + await expect(callback).toHaveMarkedConversion(); +} +``` + +```js +async () => { + await expect(callback).not.toHaveMarkedConversion(); +} +``` + +```js +async () => { + await expect(promise).resolves.toBe(true); +} +``` + +```js +async () => { + await expect(promise).resolves.not.toBe(true); +} +``` + +```js +async () => { + await expect(promise).rejects.toThrow(new Error("bar")); +} +``` + +```js +async () => { + await expect(promise).rejects.not.toThrow(new Error("bar")); +} +``` + +```js +async () => { + await expect(promise).toResolve(); +} +``` + +```js +async () => { + await expect(promise).toReject(); +} +``` diff --git a/packages/eslint-plugin-khan/docs/jest-enzyme-matchers.md b/packages/eslint-plugin-khan/docs/jest-enzyme-matchers.md new file mode 100644 index 00000000..b035ffe8 --- /dev/null +++ b/packages/eslint-plugin-khan/docs/jest-enzyme-matchers.md @@ -0,0 +1,64 @@ +# Require the use of enzyme matchers when possible (jest-enzyme-matchers) + +[jest-enzyme](https://github.com/FormidableLabs/enzyme-matchers/tree/master/packages/jest-enzyme#readme) +provides a number of matchers that useful when writing enzyme tests. Using these +matchers providers better error messages when there are test failures. This rule +supporters auto-fixing. + +## Rule Details + +```js +// Invalid +expect(wrapper.first().prop("foo")).toEqual("bar"); + +// Valid +expect(wrapper).toHaveProp("foo", "bar"); +``` + +```js +// Invalid +expect(wrapper.first().state("foo")).toEqual("bar"); + +// Valid +expect(wrapper).toHaveState("foo", "bar"); +``` + +```js +// Invalid +expect(wrapper.find(".foo")).toHaveLength(2); + +// Valid +expect(wrapper).toContainMatchingElements(2, ".foo"); +``` + +```js +// Invalid +expect(wrapper.find(".foo").text()).toEqual("bar"); + +// Valid +expect(wrapper.find(".foo")).toHaveText("bar"); +``` + +```js +// Invalid +expect(wrapper.find(".foo").html()).toEqual("

bar

"); + +// Valid +expect(wrapper.find(".foo")).toHaveHTML("

bar

"); +``` + +```js +// Invalid +expect(wrapper.find(".foo").exists()).toBeTrue(); + +// Valid +expect(wrapper.find(".foo")).toExist(); +``` + +```js +// Invalid +expect(wrapper.find(".foo").exists()).toBeFalse(); + +// Valid +expect(wrapper.find(".foo")).not.toExist(); +``` diff --git a/packages/eslint-plugin-khan/docs/react-no-method-jsx-attribute.md b/packages/eslint-plugin-khan/docs/react-no-method-jsx-attribute.md new file mode 100644 index 00000000..b3214d28 --- /dev/null +++ b/packages/eslint-plugin-khan/docs/react-no-method-jsx-attribute.md @@ -0,0 +1,80 @@ +# Prevent passing methods as props to other components (react-no-method-jsx-attribute) + +Passing methods as props without pre-binding is a common mistake in React programming. +Components created using `createReactClass` avoid this issue because `createReactClass` +pre-binds all non-lifecycle methods. + +This eslint rule warns against passing methods as props to other components and suggests +using class properties instead. This ensure that `this` is correct when the component +receiving the props calls it. + +## Rule Details + +The following are considered warnings: + +```js +class Foo extends React.Component { + handleClick() {} + + render() { + return
+ } +} +``` + +```js +class Foo extends React.Component { + handleBaz() {} + + render() { + return + } +} +``` + +The following are not considered warnings: + +```js +class Foo extends React.Component { + handleClick = () => {} + + render() { + return
+ } +} +``` + +```js +class Foo extends React.Component { + handleClick() {} + + render() { + return
this.handleClick()}> + } +} +``` + +```js +class Foo extends React.Component { + constructor(props) { + super(props); + this.handleClick = () => {}; + } + + render() { + return
+ } +} +``` + +```js +class Foo extends React.Component { + get bar() { + return this._bar; + } + + render() { + return
+ } +} +``` diff --git a/packages/eslint-plugin-khan/docs/react-no-subscriptions-before-mount.md b/packages/eslint-plugin-khan/docs/react-no-subscriptions-before-mount.md new file mode 100644 index 00000000..f66dd8f4 --- /dev/null +++ b/packages/eslint-plugin-khan/docs/react-no-subscriptions-before-mount.md @@ -0,0 +1,44 @@ +# Disallow subscriptions before React components have mounted (react-no-subscriptions-before-mount) + +React components should avoid doing any sort of async work (data fetching, +event listeners, timeouts, etc) before the component has mounted. + +There are two main reasons for this: + +- We want to avoid these subscriptions on the server +- Starting in React 16, `componentWillMount` may be called multiple times +per mount. + +## Rule Details + +The two methods called before mount are `constructor` and `componentWillMount`. +This rule warns for signs of async work (eg. `addEventListener`, `setTimeout`, +`.then`) + +The following are considered warnings: + +```js +class Foo extends Component { + constructor() { + super(); + + fetchData().then(...) + } +} + +class Foo extends Component { + componentWillMount() { + window.addEventListener(...) + } +} +``` + +The following are not considered warnings: + +```js +class Foo extends Component { + componentDidMount() { + fetchData().then(...) + } +} +``` diff --git a/packages/eslint-plugin-khan/docs/react-svg-path-precision.md b/packages/eslint-plugin-khan/docs/react-svg-path-precision.md new file mode 100644 index 00000000..1e2b1a70 --- /dev/null +++ b/packages/eslint-plugin-khan/docs/react-svg-path-precision.md @@ -0,0 +1,24 @@ +# Disallow SVG paths with too many decimal places (react-svg-path-precision) + +SVG paths exported form asset creation tools often use too many decimal places +than are necessary especially for content where the viewbox is the same size +as the SVG element. + +This rule can detect and fix paths with too many decimal places. The rule +defaults to two decimal places of precision. This can be changed by passing +an object, e.g. `{precision: 3}`, as an option in your eslint configuration. + +## Rule Details + +The following are considered warnings: + +```js + + ^^^ ^^^ ^^^ +``` + +The following are not considered warnings: + +```js + +``` diff --git a/packages/eslint-plugin-khan/docs/sync-tag.md b/packages/eslint-plugin-khan/docs/sync-tag.md new file mode 100644 index 00000000..97a3f8c6 --- /dev/null +++ b/packages/eslint-plugin-khan/docs/sync-tag.md @@ -0,0 +1,11 @@ +# Ensure that sync tags are valid + +Sync tags provide a way keep code in separate files in sync. This lint rule +uses [checksync](https://github.com/somewhatabstract/checksync) to validate +sync tags and provide fixes when possible. + +Notes: +- All paths in `ignoreFiles` are considered to be relative to `rootDir`. +- `rootDir` is required and should usually be set to the root directory of the + repo. This requires the the configuration of `@khanacademy/sync-tags` to be done in + a `.js` file. diff --git a/packages/eslint-plugin-khan/lib/index.js b/packages/eslint-plugin-khan/lib/index.js new file mode 100644 index 00000000..cfd34dcb --- /dev/null +++ b/packages/eslint-plugin-khan/lib/index.js @@ -0,0 +1,16 @@ +module.exports = { + rules: { + "flow-array-type-style": require("./rules/flow-array-type-style.js"), + "flow-exact-props": require("./rules/flow-exact-props.js"), + "flow-exact-state": require("./rules/flow-exact-state.js"), + "flow-no-one-tuple": require("./rules/flow-no-one-tuple.js"), + "imports-requiring-flow": require("./rules/imports-requiring-flow.js"), + "jest-async-use-real-timers": require("./rules/jest-async-use-real-timers.js"), + "jest-await-async-matchers": require("./rules/jest-await-async-matchers.js"), + "jest-enzyme-matchers": require("./rules/jest-enzyme-matchers.js"), + "react-no-method-jsx-attribute": require("./rules/react-no-method-jsx-attribute.js"), + "react-no-subscriptions-before-mount": require("./rules/react-no-subscriptions-before-mount.js"), + "react-svg-path-precision": require("./rules/react-svg-path-precision.js"), + "sync-tag": require("./rules/sync-tag.js"), + }, +}; diff --git a/packages/eslint-plugin-khan/lib/react-utils.js b/packages/eslint-plugin-khan/lib/react-utils.js new file mode 100644 index 00000000..48f01b31 --- /dev/null +++ b/packages/eslint-plugin-khan/lib/react-utils.js @@ -0,0 +1,33 @@ +const t = require("@babel/types"); + +const isReactClassComponent = node => { + if (t.isClassDeclaration(node)) { + const {superClass} = node; + if (t.isMemberExpression(superClass)) { + const {object, property} = superClass; + if ( + t.isIdentifier(object, {name: "React"}) && + t.isIdentifier(property, {name: "Component"}) + ) { + return true; + } + } + } + return false; +}; + +const isReactFunctionalComponent = node => { + if (t.isArrowFunctionExpression(node)) { + if ( + node.params.length === 1 && + t.isIdentifier(node.params[0], {name: "props"}) + ) { + return true; + } + } +}; + +module.exports = { + isReactClassComponent: isReactClassComponent, + isReactFunctionalComponent: isReactFunctionalComponent, +}; diff --git a/packages/eslint-plugin-khan/lib/rules/flow-array-type-style.js b/packages/eslint-plugin-khan/lib/rules/flow-array-type-style.js new file mode 100644 index 00000000..6e1e12ee --- /dev/null +++ b/packages/eslint-plugin-khan/lib/rules/flow-array-type-style.js @@ -0,0 +1,44 @@ +const message = + "Shorthand syntax for array types can appear ambiguous. " + + "Please use the long-form: Array<>"; + +module.exports = { + meta: { + docs: { + description: "Prefer Array to T[]", + category: "flow", + recommended: false, + }, + fixable: "code", + schema: [ + { + enum: ["always", "never"], + }, + ], + }, + + create(context) { + const configuration = context.options[0] || "never"; + const sourceCode = context.getSource(); + + return { + ArrayTypeAnnotation(node) { + if (configuration === "always") { + context.report({ + fix(fixer) { + const type = node.elementType; + const typeText = sourceCode.slice(...type.range); + const replacementText = `Array<${typeText}>`; + + return fixer.replaceText(node, replacementText); + }, + node: node, + message: message, + }); + } + }, + }; + }, + + __message: message, +}; diff --git a/packages/eslint-plugin-khan/lib/rules/flow-exact-props.js b/packages/eslint-plugin-khan/lib/rules/flow-exact-props.js new file mode 100644 index 00000000..2b6a2bac --- /dev/null +++ b/packages/eslint-plugin-khan/lib/rules/flow-exact-props.js @@ -0,0 +1,76 @@ +const t = require("@babel/types"); + +const { + isReactClassComponent, + isReactFunctionalComponent, +} = require("../react-utils.js"); + +const maybeReport = (node, typeAnnotation, typeAliases, context) => { + if (t.isGenericTypeAnnotation(typeAnnotation)) { + const {name} = typeAnnotation.id; + const alias = typeAliases.get(name); + if (alias && !alias.right.exact) { + const sourceCode = context.getSource(); + context.report({ + fix(fixer) { + const right = alias.right; + const rightText = sourceCode.slice( + right.range[0] + 1, + right.range[1] - 1, + ); + const replacementText = `{|${rightText}|}`; + + return fixer.replaceText(alias.right, replacementText); + }, + node: alias, + message: `"${name}" type should be exact`, + }); + } + } +}; + +module.exports = { + meta: { + docs: { + description: "Prefer exact object type for react props", + category: "flow", + recommended: false, + }, + fixable: "code", + }, + + create(context) { + const configuration = context.options[0] || "never"; + const typeAliases = new Map(); + + return { + TypeAlias(node) { + const ancestors = context.getAncestors(); + if ( + t.isProgram(node.parent) && + t.isObjectTypeAnnotation(node.right) + ) { + typeAliases.set(node.id.name, node); + } + }, + ClassDeclaration(node) { + if (isReactClassComponent(node)) { + const {superTypeParameters} = node; + if (t.isTypeParameterInstantiation(superTypeParameters)) { + const props = superTypeParameters.params[0]; + maybeReport(node, props, typeAliases, context); + } + } + }, + ArrowFunctionExpression(node) { + if (isReactFunctionalComponent(node)) { + const {typeAnnotation} = node.params[0]; + if (t.isTypeAnnotation(typeAnnotation)) { + const props = typeAnnotation.typeAnnotation; + maybeReport(node, props, typeAliases, context); + } + } + }, + }; + }, +}; diff --git a/packages/eslint-plugin-khan/lib/rules/flow-exact-state.js b/packages/eslint-plugin-khan/lib/rules/flow-exact-state.js new file mode 100644 index 00000000..9d29651b --- /dev/null +++ b/packages/eslint-plugin-khan/lib/rules/flow-exact-state.js @@ -0,0 +1,66 @@ +const t = require("@babel/types"); + +const {isReactClassComponent} = require("../react-utils.js"); + +const maybeReport = (node, typeAnnotation, typeAliases, context) => { + if (t.isGenericTypeAnnotation(typeAnnotation)) { + const {name} = typeAnnotation.id; + const alias = typeAliases.get(name); + if (alias && !alias.right.exact) { + const sourceCode = context.getSource(); + context.report({ + fix(fixer) { + const right = alias.right; + const rightText = sourceCode.slice( + right.range[0] + 1, + right.range[1] - 1, + ); + const replacementText = `{|${rightText}|}`; + + return fixer.replaceText(alias.right, replacementText); + }, + node: alias, + message: `"${name}" type should be exact`, + }); + } + } +}; + +module.exports = { + meta: { + docs: { + description: "Prefer exact object type for react state", + category: "flow", + recommended: false, + }, + fixable: "code", + }, + + create(context) { + const configuration = context.options[0] || "never"; + const typeAliases = new Map(); + + return { + TypeAlias(node) { + const ancestors = context.getAncestors(); + if ( + t.isProgram(node.parent) && + t.isObjectTypeAnnotation(node.right) + ) { + typeAliases.set(node.id.name, node); + } + }, + ClassDeclaration(node) { + if (isReactClassComponent(node)) { + const {superTypeParameters} = node; + if (t.isTypeParameterInstantiation(superTypeParameters)) { + const [props, state] = superTypeParameters.params; + if (state) { + maybeReport(node, state, typeAliases, context); + } + } + } + }, + }; + }, +}; diff --git a/packages/eslint-plugin-khan/lib/rules/flow-no-one-tuple.js b/packages/eslint-plugin-khan/lib/rules/flow-no-one-tuple.js new file mode 100644 index 00000000..e52a33ad --- /dev/null +++ b/packages/eslint-plugin-khan/lib/rules/flow-no-one-tuple.js @@ -0,0 +1,44 @@ +const message = + "One-tuples can be confused with shorthand syntax for array types. " + + "Using Array<> avoids this confusion."; + +module.exports = { + meta: { + docs: { + description: "Disallow one-tuple", + category: "flow", + recommended: false, + }, + fixable: "code", + schema: [ + { + enum: ["always", "never"], + }, + ], + }, + + create(context) { + const configuration = context.options[0] || "never"; + const sourceCode = context.getSource(); + + return { + TupleTypeAnnotation(node) { + if (configuration === "always" && node.types.length === 1) { + context.report({ + fix(fixer) { + const type = node.types[0]; + const typeText = sourceCode.slice(...type.range); + const replacementText = `Array<${typeText}>`; + + return fixer.replaceText(node, replacementText); + }, + node: node, + message: message, + }); + } + }, + }; + }, + + __message: message, +}; diff --git a/packages/eslint-plugin-khan/lib/rules/imports-requiring-flow.js b/packages/eslint-plugin-khan/lib/rules/imports-requiring-flow.js new file mode 100644 index 00000000..9c02a5bb --- /dev/null +++ b/packages/eslint-plugin-khan/lib/rules/imports-requiring-flow.js @@ -0,0 +1,115 @@ +const path = require("path"); + +const checkImport = (context, rootDir, importPath, node) => { + const maybeReport = (importPath, testRulePath, testImportPath) => { + // TODO(somewhatabstract): This is not cross-platform. We need to do + // more work to make this rule work on Windows properly, regardless of + // the configuration format. + const subRulePath = testRulePath.endsWith(path.sep) + ? testRulePath + : `${testRulePath}${path.sep}`; + + console.log("testImportPath = ", testImportPath); + console.log("testRulePath = ", testRulePath); + + // If the path is the same, then it's a match for the error. + // If the path starts with the module listed, then it's also a match. + if ( + testImportPath === testRulePath || + testImportPath.startsWith(subRulePath) + ) { + context.report({ + node, + message: `Importing "${importPath}" requires using flow.`, + }); + return true; + } + return false; + }; + + const modules = context.options[0].modules || []; + console.log(modules); + + for (const mod of modules) { + if (importPath.startsWith(".")) { + const filename = context.getFilename(); + const absImportPath = path.join(path.dirname(filename), importPath); + const absModPath = path.join(rootDir, mod); + if (maybeReport(importPath, absModPath, absImportPath)) { + break; + } + } else { + if (maybeReport(importPath, mod, importPath)) { + break; + } + } + } +}; + +const isRequire = ({callee}) => + callee.type === "Identifier" && callee.name === "require"; +const isDynamicImport = ({callee}) => callee.type === "Import"; + +module.exports = { + meta: { + docs: { + description: "Require flow when using certain imports", + category: "flow", + recommended: false, + }, + schema: [ + { + type: "object", + properties: { + modules: { + type: "array", + items: { + type: "string", + }, + }, + rootDir: { + type: "string", + }, + }, + }, + ], + }, + + create(context) { + const rootDir = context.options[0].rootDir; + if (!rootDir) { + throw new Error("rootDir must be set"); + } + + let usingFlow = false; + return { + Program(node) { + usingFlow = node.comments.some( + (comment) => comment.value.trim() === "@flow", + ); + }, + ImportDeclaration(node) { + if (!usingFlow) { + const importPath = node.source.value; + checkImport(context, rootDir, importPath, node); + } + }, + ImportExpression(node) { + if (!usingFlow) { + const importPath = node.source.value; + checkImport(context, rootDir, importPath, node); + } + }, + // legacy support for dynamic imports + CallExpression(node) { + const {arguments: args} = node; + if (!usingFlow && (isRequire(node) || isDynamicImport(node))) { + if (args[0] && args[0].type === "Literal") { + const importPath = args[0].value; + checkImport(context, rootDir, importPath, node); + } + } + }, + }; + }, +}; diff --git a/packages/eslint-plugin-khan/lib/rules/jest-async-use-real-timers.js b/packages/eslint-plugin-khan/lib/rules/jest-async-use-real-timers.js new file mode 100644 index 00000000..c9a5afc7 --- /dev/null +++ b/packages/eslint-plugin-khan/lib/rules/jest-async-use-real-timers.js @@ -0,0 +1,118 @@ +const t = require("@babel/types"); + +/** + * Find and return the `beforeEach` call node inside a describe block if one exists. + * + * @param {CallExpression} describeCall the node for a call to `describe`. + */ +const findBeforeEach = describeCall => { + const funcExpr = describeCall.arguments[1]; + if ( + funcExpr && + (t.isArrowFunctionExpression(funcExpr) || + t.isFunctionExpression(funcExpr)) && + t.isBlockStatement(funcExpr.body) + ) { + for (const stmt of funcExpr.body.body) { + if (t.isExpressionStatement(stmt)) { + const expr = stmt.expression; + if ( + t.isCallExpression(expr) && + t.isIdentifier(expr.callee, {name: "beforeEach"}) + ) { + return expr; + } + } + } + } + return null; +}; + +/** + * Determine if a `beforeEach` or `it` block has called jest.useRealTimers(). + * + * NOTE: The call cannot be inside a control flow statement. + * + * @param {CallExpression} call the node for a call to `beforeEach` or `it`. + * @param {number} closureArgIndex the arg index of the closure passed to `call`. + */ +const usesRealTimers = (call, closureArgIndex = 0) => { + if (call == null) { + return false; + } + const funcExpr = call.arguments[closureArgIndex]; + + if ( + funcExpr && + (t.isArrowFunctionExpression(funcExpr) || + t.isFunctionExpression(funcExpr)) && + t.isBlockStatement(funcExpr.body) + ) { + for (const stmt of funcExpr.body.body) { + if (t.isExpressionStatement(stmt)) { + const expr = stmt.expression; + if ( + t.isCallExpression(expr) && + t.isMemberExpression(expr.callee) + ) { + const {object, property} = expr.callee; + if ( + t.isIdentifier(object, {name: "jest"}) && + t.isIdentifier(property, {name: "useRealTimers"}) + ); + return true; + } + } + } + } + return false; +}; + +const isAsync = itCall => { + return ( + t.isArrowFunctionExpression(itCall.arguments[1], {async: true}) || + t.isFunctionExpression(itCall.arguments[1], {async: true}) + ); +}; + +module.exports = { + meta: { + docs: { + description: + "Require a call to jest.useRealTimers() before or in all async tests.", + category: "jest", + recommended: false, + }, + }, + + create(context) { + const stack = []; + + return { + CallExpression(node) { + if (t.isIdentifier(node.callee, {name: "describe"})) { + stack.push(usesRealTimers(findBeforeEach(node), 0)); + } else if ( + t.isIdentifier(node.callee, {name: "it"}) && + isAsync(node) + ) { + // an `it` should always be inside a `describe` + if (stack.length > 0) { + if (!stack.some(Boolean) && !usesRealTimers(node, 1)) { + context.report({ + node, + message: + "Async tests require jest.useRealTimers().", + }); + } + } + } + }, + "CallExpression:exit"(node) { + if (t.isIdentifier(node.callee, {name: "describe"})) { + stack.pop(); + } + }, + }; + }, +}; diff --git a/packages/eslint-plugin-khan/lib/rules/jest-await-async-matchers.js b/packages/eslint-plugin-khan/lib/rules/jest-await-async-matchers.js new file mode 100644 index 00000000..85f8831a --- /dev/null +++ b/packages/eslint-plugin-khan/lib/rules/jest-await-async-matchers.js @@ -0,0 +1,107 @@ +const t = require("@babel/types"); + +const getPropertyNames = node => { + if (!t.isMemberExpression(node)) { + []; + } + if (t.isIdentifier(node.property)) { + return [node.property.name, ...getPropertyNames(node.parent)]; + } + return []; +}; + +const findParentCallExpression = node => { + if (t.isProgram(node)) { + return null; + } + if (t.isCallExpression(node.parent)) { + return node.parent; + } + return findParentCallExpression(node.parent); +}; + +const intersect = (array1, array2) => { + return array1.filter(value => array2.includes(value)); +}; + +const getCustomMatchers = options => { + if (options && options[0] && options[0].matchers) { + return options[0].matchers; + } + return []; +}; + +const DEFAULT_MATCHERS = [ + // built into jest + "resolves", + "rejects", + // from of jest-extended + "toResolve", + "toReject", +]; + +module.exports = { + meta: { + docs: { + description: "", + category: "jest", + recommended: false, + }, + fixable: true, + }, + + create(context) { + const customMatchers = getCustomMatchers(context.options); + const allowedMatchers = [...DEFAULT_MATCHERS, ...customMatchers]; + + return { + CallExpression(node) { + // We only run this check on expect() calls. + if (!t.isIdentifier(node.callee, {name: "expect"})) { + return; + } + if (!t.isMemberExpression(node.parent)) { + return; + } + // While someone could use an indexer to access matchers, + // that never happens in practice so we don't bother handling + // this edge case. + if (!t.isIdentifier(node.parent.property)) { + return; + } + + // Gets all the properties in a jest assertion, e.g. + // expect(foo).not.toEqual(bar) would result in the + // following property names: ["not", "toEqual"]. + const propertyNames = getPropertyNames(node.parent); + + // This should only contain a single entry when there's a match + const intersection = intersect(propertyNames, allowedMatchers); + if (intersection.length === 0) { + return; + } + + // This is the matcher call itself, e.g. .toBe(true) + const parentCallExpression = findParentCallExpression(node); + if (!parentCallExpression) { + return; + } + + if (!t.isAwaitExpression(parentCallExpression.parent)) { + context.report({ + node: parentCallExpression, + message: `Assertions using \`${intersection.join( + ", ", + )}\` should be awaited.`, + fix(fixer) { + return fixer.insertTextBefore( + parentCallExpression, + "await ", + ); + }, + }); + } + }, + }; + }, +}; diff --git a/packages/eslint-plugin-khan/lib/rules/jest-enzyme-matchers.js b/packages/eslint-plugin-khan/lib/rules/jest-enzyme-matchers.js new file mode 100644 index 00000000..dd5e585e --- /dev/null +++ b/packages/eslint-plugin-khan/lib/rules/jest-enzyme-matchers.js @@ -0,0 +1,232 @@ +const t = require("@babel/types"); + +module.exports = { + meta: { + docs: { + description: + "Requires the use more of enzyme matchers where appropriate.", + category: "jest", + recommended: false, + }, + fixable: true, + }, + + create(context) { + const stack = []; + + return { + CallExpression(node) { + if ( + t.isIdentifier(node.callee, {name: "expect"}) && + t.isCallExpression(node.arguments[0]) && + t.isMemberExpression(node.arguments[0].callee) && + t.isMemberExpression(node.parent) && + t.isIdentifier(node.parent.property) && + t.isCallExpression(node.parent.parent) + ) { + const {property} = node.arguments[0].callee; + const matcher = node.parent.property.name; + + if (matcher === "toEqual") { + if (t.isIdentifier(property, {name: "prop"})) { + const sourceCode = context.getSource(); + const propName = node.arguments[0].arguments[0]; + const expected = sourceCode.slice( + ...node.parent.parent.arguments[0].range, + ); + context.report({ + node, + message: `Use .toHaveProp(${propName.raw}, ${expected}) instead.`, + fix(fixer) { + const actual = sourceCode.slice( + ...node.arguments[0].callee.object + .range, + ); + const text = `expect(${actual}).toHaveProp(${propName.raw}, ${expected})`; + return fixer.replaceText( + node.parent.parent, + text, + ); + }, + }); + } else if (t.isIdentifier(property, {name: "state"})) { + const sourceCode = context.getSource(); + const stateKey = node.arguments[0].arguments[0]; + const expected = sourceCode.slice( + ...node.parent.parent.arguments[0].range, + ); + context.report({ + node, + message: `Use .toHaveState(${stateKey.raw}, ${expected}) instead.`, + fix(fixer) { + const actual = sourceCode.slice( + ...node.arguments[0].callee.object + .range, + ); + const text = `expect(${actual}).toHaveState(${stateKey.raw}, ${expected})`; + return fixer.replaceText( + node.parent.parent, + text, + ); + }, + }); + } else if (t.isIdentifier(property, {name: "text"})) { + const sourceCode = context.getSource(); + const expected = sourceCode.slice( + ...node.parent.parent.arguments[0].range, + ); + context.report({ + node, + message: `Use .toHaveText(${expected}) instead.`, + fix(fixer) { + const actual = sourceCode.slice( + ...node.arguments[0].callee.object + .range, + ); + const text = `expect(${actual}).toHaveText(${expected})`; + return fixer.replaceText( + node.parent.parent, + text, + ); + }, + }); + } else if (t.isIdentifier(property, {name: "html"})) { + const sourceCode = context.getSource(); + const expected = sourceCode.slice( + ...node.parent.parent.arguments[0].range, + ); + context.report({ + node, + message: `Use .toHaveHTML(${expected}) instead.`, + fix(fixer) { + const actual = sourceCode.slice( + ...node.arguments[0].callee.object + .range, + ); + const text = `expect(${actual}).toHaveHTML(${expected})`; + return fixer.replaceText( + node.parent.parent, + text, + ); + }, + }); + } + } else if ( + matcher === "toBeTrue" || + matcher === "toBeTruthy" + ) { + if (t.isIdentifier(property, {name: "exists"})) { + if (node.arguments[0].arguments.length === 0) { + const sourceCode = context.getSource(); + context.report({ + node, + message: `Use .toExist() instead.`, + fix(fixer) { + const actual = sourceCode.slice( + ...node.arguments[0].callee.object + .range, + ); + const text = `expect(${actual}).toExist()`; + return fixer.replaceText( + node.parent.parent, + text, + ); + }, + }); + } else { + const sourceCode = context.getSource(); + const selector = sourceCode.slice( + ...node.arguments[0].arguments[0].range, + ); + context.report({ + node, + message: `Use .toContainMatchingElement(${selector}) instead.`, + fix(fixer) { + const actual = sourceCode.slice( + ...node.arguments[0].callee.object + .range, + ); + const text = `expect(${actual}).toContainMatchingElement(${selector})`; + return fixer.replaceText( + node.parent.parent, + text, + ); + }, + }); + } + } + } else if ( + matcher === "toBeFalse" || + matcher === "toBeFalsy" + ) { + if (t.isIdentifier(property, {name: "exists"})) { + if (node.arguments[0].arguments.length === 0) { + const sourceCode = context.getSource(); + context.report({ + node, + message: `Use .not.toExist() instead.`, + fix(fixer) { + const actual = sourceCode.slice( + ...node.arguments[0].callee.object + .range, + ); + const text = `expect(${actual}).not.toExist()`; + return fixer.replaceText( + node.parent.parent, + text, + ); + }, + }); + } else { + const sourceCode = context.getSource(); + const selector = sourceCode.slice( + ...node.arguments[0].arguments[0].range, + ); + context.report({ + node, + message: `Use .not.toContainMatchingElement(${selector}) instead.`, + fix(fixer) { + const actual = sourceCode.slice( + ...node.arguments[0].callee.object + .range, + ); + const text = `expect(${actual}).not.toContainMatchingElement(${selector})`; + return fixer.replaceText( + node.parent.parent, + text, + ); + }, + }); + } + } + } else if (matcher === "toHaveLength") { + if (t.isIdentifier(property, {name: "find"})) { + const sourceCode = context.getSource(); + const selector = sourceCode.slice( + ...node.arguments[0].arguments[0].range, + ); + const count = sourceCode.slice( + ...node.parent.parent.arguments[0].range, + ); + context.report({ + node, + message: `Use .toContainMatchingElements(${count}, ${selector}) instead.`, + fix(fixer) { + const actual = sourceCode.slice( + ...node.arguments[0].callee.object + .range, + ); + const text = `expect(${actual}).toContainMatchingElements(${count}, ${selector})`; + return fixer.replaceText( + node.parent.parent, + text, + ); + }, + }); + } + } + } + }, + }; + }, +}; diff --git a/packages/eslint-plugin-khan/lib/rules/react-no-method-jsx-attribute.js b/packages/eslint-plugin-khan/lib/rules/react-no-method-jsx-attribute.js new file mode 100644 index 00000000..14984355 --- /dev/null +++ b/packages/eslint-plugin-khan/lib/rules/react-no-method-jsx-attribute.js @@ -0,0 +1,73 @@ +module.exports = { + meta: { + docs: { + description: "Ensure that methods aren't used as jsx attributes", + category: "react", + recommended: false, + }, + schema: [], + }, + + create(context) { + const methods = new Map(); + const classProperties = new Map(); + + return { + ClassDeclaration(node) { + for (const child of node.body.body) { + if ( + child.type === "ClassProperty" && + child.key.type === "Identifier" + ) { + classProperties.set(child.key.name, child); + } else if ( + child.type === "MethodDefinition" && + child.kind === "method" && + child.key.type === "Identifier" + ) { + methods.set(child.key.name, child); + } + } + }, + "ClassDeclaration:exit"(node) { + for (const child of node.body.body) { + if ( + child.type === "ClassProperty" && + child.key.type === "Identifier" + ) { + classProperties.delete(child.key.name); + } else if ( + child.type === "MethodDefinition" && + child.kind === "method" && + child.key.type === "Identifier" + ) { + methods.delete(child.key.name); + } + } + }, + JSXAttribute(node) { + const {value} = node; + // value doesn't exist for boolean shorthand attributes + if (value && value.type === "JSXExpressionContainer") { + const {expression} = node.value; + if (expression.type === "MemberExpression") { + const {object, property} = expression; + if ( + object.type === "ThisExpression" && + property.type === "Identifier" + ) { + const {name} = property; + if (methods.has(name)) { + context.report({ + node: methods.get(name), + message: + "Methods cannot be passed as props, use a class property instead.", + }); + } + } + } + } + }, + }; + }, +}; diff --git a/packages/eslint-plugin-khan/lib/rules/react-no-subscriptions-before-mount.js b/packages/eslint-plugin-khan/lib/rules/react-no-subscriptions-before-mount.js new file mode 100644 index 00000000..735cc9fb --- /dev/null +++ b/packages/eslint-plugin-khan/lib/rules/react-no-subscriptions-before-mount.js @@ -0,0 +1,94 @@ +const message = + "Subscriptions (eg. event listeners) should not be set before the " + + "component has mounted. This is to avoid firing them on the server, " + + "as well as to ensure compatibility with React 16.\n\n" + + "Please consider moving this subscription to `componentDidMount`."; + +const subscriptionNames = [ + "then", + "catch", + "addEventListener", + "setTimeout", + "setInterval", +]; + +const beforeMountMethods = ["constructor", "componentWillMount"]; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ +module.exports = { + meta: { + docs: { + // eslint-disable-next-line max-len + description: + "Avoid subscriptions in `constructor` and `componentWillMount`", + category: "react", + recommended: false, + }, + schema: [], + }, + create(context) { + return { + CallExpression(node) { + // Is this a function (eg. 'setTimeout()'), or a method + // (eg. 'window.setTimeout()')? + const isMethod = node.callee.type === "MemberExpression"; + + // Grab the function-call bit (setTimeout) + const identifier = isMethod + ? node.callee.property + : node.callee; + + // Bail early if this identifier isn't forbidden. + if (!subscriptionNames.includes(identifier.name)) { + return; + } + + // Are we in one of the before-mount methods? + // To answer this question, we need to find the ancestor + // MethodDefinition. + const ancestors = context.getAncestors(node.callee).reverse(); + const ancestorMethodDef = ancestors.find( + a => a.type === "MethodDefinition", + ); + + // If there _is_ no parent MethodDefinition, bail early. + // This subsciption is just fine. + if (!ancestorMethodDef) { + return; + } + + // Check if this is one of the pre-mount methods. + if (beforeMountMethods.includes(ancestorMethodDef.key.name)) { + return context.report({ + node, + message, + }); + } + + // TODO (josh): We aren't currently checking for 'deep' subs. + // For example, we aren't catching this: + /* + class Whatever extends Component { + componentWillMount() { + this.fetchData(); + } + + fetchData() { + someApi.fetch().then(...); + } + } + */ + // To do that, we need to find invocations of our ancestor + // MethodDefinition, and see if it's called from within one + // of the pre-mount methods. + // + // I haven't been able to figure out how to do this in a way + // that doesn't seem like total overkill, so I'm punting on + // this for now. + }, + }; + }, + __message: message, +}; diff --git a/packages/eslint-plugin-khan/lib/rules/react-svg-path-precision.js b/packages/eslint-plugin-khan/lib/rules/react-svg-path-precision.js new file mode 100644 index 00000000..3ed15bfb --- /dev/null +++ b/packages/eslint-plugin-khan/lib/rules/react-svg-path-precision.js @@ -0,0 +1,75 @@ +const t = require("@babel/types"); + +module.exports = { + meta: { + docs: { + description: + "Ensure that SVG paths don't use too many decimal places", + category: "react", + recommended: false, + }, + fixable: "code", + schema: [ + { + type: "object", + properties: { + precision: { + type: "number", + }, + }, + additionalProperties: false, + }, + ], + }, + + create(context) { + let precision = 2; + for (const option of context.options) { + if (typeof option === "object") { + if (option.hasOwnProperty("precision")) { + precision = Math.max(option.precision, 0); + } + } + } + const pattern = `\\d*\\.\\d{${precision},}\\d+`; + const regex = new RegExp(pattern, "g"); + + return { + JSXAttribute(node) { + if ( + t.isJSXAttribute(node) && + t.isJSXIdentifier(node.name, {name: "d"}) + ) { + if ( + t.isJSXOpeningElement(node.parent) && + t.isJSXIdentifier(node.parent.name, {name: "path"}) + ) { + const d = node.value.value; + + if (regex.test(d)) { + context.report({ + fix(fixer) { + const replacementText = d.replace( + regex, + match => + parseFloat(match).toFixed( + precision, + ), + ); + + return fixer.replaceText( + node.value, + `"${replacementText}"`, + ); + }, + node, + message: + "This path contains numbers with too many decimal places.", + }); + } + } + } + }, + }; + }, +}; diff --git a/packages/eslint-plugin-khan/lib/rules/sync-tag.js b/packages/eslint-plugin-khan/lib/rules/sync-tag.js new file mode 100644 index 00000000..c63d9606 --- /dev/null +++ b/packages/eslint-plugin-khan/lib/rules/sync-tag.js @@ -0,0 +1,125 @@ +const assert = require("assert"); +const path = require("path"); + +const util = require("../util.js"); + +const PKG_ROOT = path.join(__dirname, "..", ".."); + +const UPDATE_REMINDER = + "If necessary, check the sync-tag target and make relevant changes before updating the checksum."; + +const processItem = (item, node, context, comments) => { + if (item.sourceLine && item.message) { + const line = item.sourceLine; + const comment = comments.find( + (comment) => comment.loc.start.line === line, + ); + + if (item.fix) { + context.report({ + node: comment, + message: `${item.message} ${UPDATE_REMINDER}`, + fix(fixer) { + return fixer.replaceText(comment, item.fix.trim()); + }, + }); + } else { + context.report({ + node: comment, + message: item.message, + }); + } + } else if (item.message) { + context.report({ + node: node, + message: `${item.message} ${UPDATE_REMINDER}`, + }); + } else { + // eslint-disable-next-line no-console + console.error(`Unknown item type ${item.type}`); + } +}; + +module.exports = { + meta: { + docs: { + description: "Ensure sync tags are valid", + }, + schema: [ + { + type: "object", + properties: { + ignoreFiles: { + type: "array", + items: { + type: "string", + }, + }, + rootDir: { + type: "string", + }, + }, + }, + ], + fixable: "code", + }, + + create(context) { + const ignoreFiles = (context.options[0].ignoreFiles || []).join(","); + const rootDir = context.options[0].rootDir; + if (!rootDir) { + throw new Error("rootDir must be set"); + } + + return { + Program(node) { + const syncStartComments = node.comments.filter((comment) => + comment.value.trim().startsWith("sync-start:"), + ); + + const shouldChecksync = syncStartComments.length > 0; + + if (shouldChecksync) { + const filename = path.relative( + rootDir, + context.getFilename(), + ); + const checksyncPath = path.join( + PKG_ROOT, + "node_modules", + ".bin", + "checksync", + ); + + const command = `${checksyncPath} ${filename} -c "//,#,{/*,{{/*" -m .ka_root --ignore-files ${ignoreFiles} --json`; + const args = [ + filename, + "-c", + '"//,#,{/*,{{/*"', + "-m", + ".ka_root", + "--ignore-files", + ignoreFiles, + "--json", + ]; + assert.equal(command, [checksyncPath, ...args].join(" ")); + const stdout = util.execFile(checksyncPath, args, { + cwd: rootDir, + encoding: "utf-8", + }); + + try { + const data = JSON.parse(stdout); + for (const item of data.items) { + processItem(item, node, context, syncStartComments); + } + } catch (e) { + // eslint-disable-next-line no-console + console.error(e); + return; + } + } + }, + }; + }, +}; diff --git a/packages/eslint-plugin-khan/lib/util.js b/packages/eslint-plugin-khan/lib/util.js new file mode 100644 index 00000000..97462d59 --- /dev/null +++ b/packages/eslint-plugin-khan/lib/util.js @@ -0,0 +1,6 @@ +const {execFile} = require("child_process"); + +// This is done so that we can override execSync in the tests +module.exports = { + execFile, +}; diff --git a/packages/eslint-plugin-khan/package.json b/packages/eslint-plugin-khan/package.json new file mode 100644 index 00000000..336db6a4 --- /dev/null +++ b/packages/eslint-plugin-khan/package.json @@ -0,0 +1,44 @@ +{ + "name": "@khanacademy/eslint-plugin", + "version": "0.7.0", + "publishConfig": { + "access": "public" + }, + "files": [ + "/lib" + ], + "main": "lib/index.js", + "nobuild": true, + "repository": "https://github.com/Khan/eslint-plugin-khan", + "author": "Kevin Barabash ", + "license": "MIT", + "devDependencies": { + "babel-eslint": "^10.0.3", + "eslint": "^5.8.0", + "husky": "^4.2.3", + "lint-staged": "^10.1.2", + "mocha": "^3.3.0", + "prettier": "^1.18.2", + "pretty-quick": "^2.0.1" + }, + "scripts": { + "test": "mocha test/**/*.js", + "prettier": "prettier --write '{lib,test}/**/*.js'", + "prepublishOnly": "npm run test" + }, + "dependencies": { + "@babel/types": "^7.7.4", + "checksync": "^2.3.0" + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "lint-staged": { + "*.js": [ + "yarn test", + "pretty-quick" + ] + } +} diff --git a/packages/eslint-plugin-khan/test/flow-array-type-style_test.js b/packages/eslint-plugin-khan/test/flow-array-type-style_test.js new file mode 100644 index 00000000..312d99a1 --- /dev/null +++ b/packages/eslint-plugin-khan/test/flow-array-type-style_test.js @@ -0,0 +1,39 @@ +const {rules} = require("../lib/index.js"); +const RuleTester = require("eslint").RuleTester; + +const parserOptions = { + parser: "babel-eslint", +}; + +const ruleTester = new RuleTester(parserOptions); +const rule = rules["flow-array-type-style"]; + +const message = rule.__message; +const errors = [message]; + +ruleTester.run("flow-array-type-style", rule, { + valid: [ + { + code: "type foo = { bar: Array }", + options: ["always"], + }, + ], + invalid: [ + { + code: "type foo = { bar: number[] }", + options: ["always"], + errors: errors, + output: "type foo = { bar: Array }", + }, + { + code: "type foo = { bar: number[][] }", + options: ["always"], + // Two errors are reported because there are two array types, + // they just happen to be nested. + errors: [message, message], + // This is a partial fix. Multiple runs of eslint --fix are needed + // to fix nested (in the AST) array types completely. + output: "type foo = { bar: Array[] }", + }, + ], +}); diff --git a/packages/eslint-plugin-khan/test/flow-exact-props_test.js b/packages/eslint-plugin-khan/test/flow-exact-props_test.js new file mode 100644 index 00000000..d055cf7c --- /dev/null +++ b/packages/eslint-plugin-khan/test/flow-exact-props_test.js @@ -0,0 +1,78 @@ +const {rules} = require("../lib/index.js"); +const RuleTester = require("eslint").RuleTester; + +const parserOptions = { + parser: "babel-eslint", +}; + +const ruleTester = new RuleTester(parserOptions); +const rule = rules["flow-exact-props"]; + +const message = rule.__message; +const errors = [message]; + +ruleTester.run("flow-exact-props", rule, { + valid: [ + { + code: ` + type Props = {| x: number |}; + class Foo extends React.Component {}`, + }, + { + code: ` + type BarProps = { x: number }; + type FooProps = {| x: number |}; + class Foo extends React.Component {}`, + }, + { + code: ` + type Props = {| x: number |}; + const Foo = (props: Props) => {}`, + }, + ], + invalid: [ + { + code: ` + type Props = { x: number }; + class Foo extends React.Component {}`, + errors: ['"Props" type should be exact'], + output: ` + type Props = {| x: number |}; + class Foo extends React.Component {}`, + }, + { + code: ` + type FooProps = { x: number }; + class Foo extends React.Component {}`, + errors: ['"FooProps" type should be exact'], + output: ` + type FooProps = {| x: number |}; + class Foo extends React.Component {}`, + }, + { + code: ` + type FooProps = { x: number }; + class Foo extends React.Component {} + type BarProps = { x: number }; + class Bar extends React.Component {}`, + errors: [ + '"FooProps" type should be exact', + '"BarProps" type should be exact', + ], + output: ` + type FooProps = {| x: number |}; + class Foo extends React.Component {} + type BarProps = {| x: number |}; + class Bar extends React.Component {}`, + }, + { + code: ` +type Props = { x: number }; +const Foo = (props: Props) => {}`, + errors: ['"Props" type should be exact'], + output: ` +type Props = {| x: number |}; +const Foo = (props: Props) => {}`, + }, + ], +}); diff --git a/packages/eslint-plugin-khan/test/flow-exact-state_test.js b/packages/eslint-plugin-khan/test/flow-exact-state_test.js new file mode 100644 index 00000000..9ee0fdd9 --- /dev/null +++ b/packages/eslint-plugin-khan/test/flow-exact-state_test.js @@ -0,0 +1,74 @@ +const {rules} = require("../lib/index.js"); +const RuleTester = require("eslint").RuleTester; + +const parserOptions = { + parser: "babel-eslint", +}; + +const ruleTester = new RuleTester(parserOptions); +const rule = rules["flow-exact-state"]; + +const message = rule.__message; +const errors = [message]; + +ruleTester.run("flow-exact-state", rule, { + valid: [ + { + code: ` + type Props = { x: number }; + type State = {| x: number |}; + class Foo extends React.Component {}`, + }, + { + code: ` + type FooProps = { x: number }; + type BarState = { x: number }; + type FooState = {| x: number |}; + class Foo extends React.Component {}`, + }, + ], + invalid: [ + { + code: ` + type Props = {| x: number |}; + type State = { x: number }; + class Foo extends React.Component {}`, + errors: ['"State" type should be exact'], + output: ` + type Props = {| x: number |}; + type State = {| x: number |}; + class Foo extends React.Component {}`, + }, + { + code: ` + type FooProps = {| x: number |}; + type FooState = { x: number }; + class Foo extends React.Component {}`, + errors: ['"FooState" type should be exact'], + output: ` + type FooProps = {| x: number |}; + type FooState = {| x: number |}; + class Foo extends React.Component {}`, + }, + { + code: ` + type FooProps = { x: number }; + type FooState = { x: number }; + class Foo extends React.Component {} + type BarProps = { x: number }; + type BarState = { x: number }; + class Bar extends React.Component {}`, + errors: [ + '"FooState" type should be exact', + '"BarState" type should be exact', + ], + output: ` + type FooProps = { x: number }; + type FooState = {| x: number |}; + class Foo extends React.Component {} + type BarProps = { x: number }; + type BarState = {| x: number |}; + class Bar extends React.Component {}`, + }, + ], +}); diff --git a/packages/eslint-plugin-khan/test/flow-no-one-tuple_test.js b/packages/eslint-plugin-khan/test/flow-no-one-tuple_test.js new file mode 100644 index 00000000..37959532 --- /dev/null +++ b/packages/eslint-plugin-khan/test/flow-no-one-tuple_test.js @@ -0,0 +1,43 @@ +const {rules} = require("../lib/index.js"); +const RuleTester = require("eslint").RuleTester; + +const parserOptions = { + parser: "babel-eslint", +}; + +const ruleTester = new RuleTester(parserOptions); +const rule = rules["flow-no-one-tuple"]; + +const message = rule.__message; +const errors = [message]; + +ruleTester.run("flow-no-one-tuple", rule, { + valid: [ + { + code: "type foo = { bar: Array }", + options: ["always"], + }, + { + code: "type foo = { bar: [number, number] }", + options: ["always"], + }, + ], + invalid: [ + { + code: "type foo = { bar: [number] }", + options: ["always"], + errors: errors, + output: "type foo = { bar: Array }", + }, + { + code: "type foo = { bar: [[number]] }", + options: ["always"], + // Two errors are reported because there are two one-tuples, + // they just happen to be nested. + errors: [message, message], + // This is a partial fix. Multiple runs of eslint --fix are needed + // to fix nested 1-tuples completely. + output: "type foo = { bar: Array<[number]> }", + }, + ], +}); diff --git a/packages/eslint-plugin-khan/test/imports-requiring-flow_test.js b/packages/eslint-plugin-khan/test/imports-requiring-flow_test.js new file mode 100644 index 00000000..23cf016d --- /dev/null +++ b/packages/eslint-plugin-khan/test/imports-requiring-flow_test.js @@ -0,0 +1,331 @@ +const path = require("path"); + +const {rules} = require("../lib/index.js"); +const RuleTester = require("eslint").RuleTester; + +const parserOptions = { + parser: "babel-eslint", +}; + +const ruleTester = new RuleTester(parserOptions); +const rule = rules["imports-requiring-flow"]; + +const message = rule.__message; +const errors = [message]; +const rootDir = "/Users/nyancat/project"; + +const importFooPkgFlow = ` +// @flow +import foo from "foo"; +`; + +const importFooPkgNoflow = ` +// @noflow +import foo from "foo"; +`; + +const importBarModFlow = ` +// @flow +import bar from "../package-2/bar.js"; +`; + +const importBarModNoflow = ` +// @noflow +import bar from "../package-2/bar.js"; +`; + +const requireFooPkgFlow = ` +// @flow +const foo = require("foo"); +`; + +const requireFooPkgNoflow = ` +// @noflow +const foo = require("foo"); +`; + +const requireBarModFlow = ` +// @flow +const bar = require("../package-2/bar.js"); +`; + +const requireBarModNoflow = ` +// @noflow +const bar = require("../package-2/bar.js"); +`; + +const dynamicImportFooPkgFlow = ` +// @flow +const fooPromise = import("foo"); +`; + +const dynamicImportFooPkgNoflow = ` +// @noflow +const fooPromise = import("foo"); +`; + +const dynamicImportBarModFlow = ` +// @flow +const barPromise = import("../package-2/bar.js"); +`; + +const dynamicImportBarModNoflow = ` +// @noflow +const barPromise = import("../package-2/bar.js"); +`; + +ruleTester.run("imports-requiring-flow", rule, { + valid: [ + { + code: importFooPkgFlow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["foo"], + rootDir, + }, + ], + }, + { + code: importBarModFlow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["src/package-2/bar.js"], + rootDir, + }, + ], + }, + { + code: importBarModFlow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["src/package-2/"], + rootDir, + }, + ], + }, + { + code: requireFooPkgFlow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["foo"], + rootDir, + }, + ], + }, + { + code: dynamicImportBarModFlow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["src/package-2/bar.js"], + rootDir, + }, + ], + }, + { + code: dynamicImportBarModFlow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["src/package-2"], + rootDir, + }, + ], + }, + { + code: dynamicImportFooPkgFlow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["foo"], + rootDir, + }, + ], + }, + { + code: requireBarModFlow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["src/package-2/bar.js"], + rootDir, + }, + ], + }, + { + code: requireBarModFlow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["src/package-2"], + rootDir, + }, + ], + }, + { + code: importFooPkgNoflow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["baz"], // isn't imported so it's okay + rootDir, + }, + ], + }, + { + code: importBarModNoflow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["baz"], // isn't imported so it's okay + rootDir, + }, + ], + }, + { + code: requireFooPkgNoflow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["baz"], // isn't imported so it's okay + rootDir, + }, + ], + }, + { + code: requireBarModNoflow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["baz"], // isn't imported so it's okay + rootDir, + }, + ], + }, + { + code: dynamicImportBarModNoflow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["baz"], // isn't imported so it's okay + rootDir, + }, + ], + }, + { + code: dynamicImportFooPkgNoflow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["baz"], // isn't imported so it's okay + rootDir, + }, + ], + }, + ], + invalid: [ + { + code: importFooPkgNoflow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["foo"], + rootDir, + }, + ], + errors: ['Importing "foo" requires using flow.'], + }, + { + code: importBarModNoflow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["src/package-2/bar.js"], + rootDir, + }, + ], + errors: ['Importing "../package-2/bar.js" requires using flow.'], + }, + { + code: importBarModNoflow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["src/package-2"], + rootDir, + }, + ], + errors: ['Importing "../package-2/bar.js" requires using flow.'], + }, + { + code: requireFooPkgNoflow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["foo"], + rootDir, + }, + ], + errors: ['Importing "foo" requires using flow.'], + }, + { + code: requireBarModNoflow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["src/package-2/"], + rootDir, + }, + ], + errors: ['Importing "../package-2/bar.js" requires using flow.'], + }, + { + code: requireBarModNoflow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["src/package-2/bar.js"], + rootDir, + }, + ], + errors: ['Importing "../package-2/bar.js" requires using flow.'], + }, + { + code: dynamicImportFooPkgNoflow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["foo"], + rootDir, + }, + ], + errors: ['Importing "foo" requires using flow.'], + }, + { + code: dynamicImportBarModNoflow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["src/package-2/bar.js"], + rootDir, + }, + ], + errors: ['Importing "../package-2/bar.js" requires using flow.'], + }, + { + code: dynamicImportBarModNoflow, + filename: path.join(rootDir, "src/package-1/foobar.js"), + options: [ + { + modules: ["src/package-2"], + rootDir, + }, + ], + errors: ['Importing "../package-2/bar.js" requires using flow.'], + }, + ], +}); diff --git a/packages/eslint-plugin-khan/test/jest-async-use-real-timers_test.js b/packages/eslint-plugin-khan/test/jest-async-use-real-timers_test.js new file mode 100644 index 00000000..f8b7286b --- /dev/null +++ b/packages/eslint-plugin-khan/test/jest-async-use-real-timers_test.js @@ -0,0 +1,164 @@ +const path = require("path"); + +const {rules} = require("../lib/index.js"); +const RuleTester = require("eslint").RuleTester; + +const parserOptions = { + parser: "babel-eslint", +}; + +const ruleTester = new RuleTester(parserOptions); +const rule = rules["jest-async-use-real-timers"]; + +ruleTester.run("jest-real-timers", rule, { + valid: [ + { + code: ` +describe("foo", () => { + it("doesn't require real timers", () => {}); +})`, + options: [], + }, + { + code: ` +describe("foo", () => { + it("requires real timers", async () => { + jest.useRealTimers(); + }); +})`, + options: [], + }, + { + code: ` +describe("foo", () => { + it("requires real timers", async function() { + jest.useRealTimers(); + }); +})`, + options: [], + }, + { + code: ` +describe("foo", () => { + beforeEach(() => { + jest.useRealTimers(); + }); + + it("requires real timers", async () => {}); +})`, + options: [], + }, + { + code: ` +describe("foo", () => { + beforeEach(() => { + jest.useRealTimers(); + }); + + describe("bar", () => { + it("requires real timers", async () => {}); + }); +})`, + options: [], + }, + { + code: ` +describe("foo", () => { + describe("bar", () => { + beforeEach(() => { + jest.useRealTimers(); + }); + + it("requires real timers", async () => {}); + }); +})`, + options: [], + }, + { + code: `describe("foo", fn)`, + options: [], + }, + { + code: `describe("foo", () => fn())`, + options: [], + }, + { + code: ` +describe("foo", () => { + it("requires real timers", asyncFn); +})`, + options: [], + }, + { + code: ` +describe("foo", () => { + beforeEach(() => { + jest.useRealTimers(); + }); + + it("requires real timers", async () => asyncFn()); +})`, + options: [], + }, + ], + invalid: [ + { + code: ` +describe("foo", () => { + it("requires real timers", async () => {}); +})`, + options: [], + errors: ["Async tests require jest.useRealTimers()."], + }, + { + code: ` +describe("foo", () => { + it("requires real timers", async function() {}); +})`, + options: [], + errors: ["Async tests require jest.useRealTimers()."], + }, + { + code: ` +describe("foo", () => { + beforeEach(() => {}); + + it("requires real timers", async () => {}); +})`, + options: [], + errors: ["Async tests require jest.useRealTimers()."], + }, + { + code: ` +describe("foo", () => { + beforeEach(() => {}); + + describe("bar", () => { + it("requires real timers", async () => {}); + }); +})`, + options: [], + errors: ["Async tests require jest.useRealTimers()."], + }, + { + code: ` +describe("foo", () => { + describe("bar", () => { + beforeEach(() => {}); + + it("requires real timers", async () => {}); + }); +})`, + options: [], + errors: ["Async tests require jest.useRealTimers()."], + }, + { + code: ` +describe("foo", () => { + it("requires real timers", async () => asyncFn()); +})`, + options: [], + errors: ["Async tests require jest.useRealTimers()."], + }, + ], +}); diff --git a/packages/eslint-plugin-khan/test/jest-await-async-matchers_test.js b/packages/eslint-plugin-khan/test/jest-await-async-matchers_test.js new file mode 100644 index 00000000..d633396a --- /dev/null +++ b/packages/eslint-plugin-khan/test/jest-await-async-matchers_test.js @@ -0,0 +1,131 @@ +const path = require("path"); + +const {rules} = require("../lib/index.js"); +const RuleTester = require("eslint").RuleTester; + +const parserOptions = { + parser: "babel-eslint", +}; + +const ruleTester = new RuleTester(parserOptions); +const rule = rules["jest-await-async-matchers"]; + +ruleTester.run("jest-await-async-matchers", rule, { + valid: [ + { + code: `async () => await expect(promise).resolves.toBe(true);`, + options: [], + }, + { + code: `async () => await expect(promise).resolves.not.toBe(true);`, + options: [], + }, + { + code: `async () => await expect(promise).rejects.toThrow("foo");`, + options: [], + }, + { + code: `async () => await expect(promise).rejects.not.toThrow("foo");`, + options: [], + }, + { + code: `async () => await expect(promise).toResolve();`, + options: [], + }, + { + code: `async () => await expect(promise).not.toResolve();`, + options: [], + }, + { + code: `async () => await expect(promise).toReject();`, + options: [], + }, + { + code: `async () => await expect(promise).not.toReject();`, + options: [], + }, + { + code: `async () => await expect(callback).toHaveMarkedConversion();`, + options: [ + { + matchers: ["toHaveMarkedConversion"], + }, + ], + }, + // Don't report other matchers + { + code: `expect(callback).toBe(true);`, + options: [], + }, + ], + invalid: [ + { + code: `expect(promise).resolves.toBe(true);`, + options: [], + errors: ["Assertions using `resolves` should be awaited."], + output: "await expect(promise).resolves.toBe(true);", + }, + { + code: `expect(promise).resolves.not.toBe(true);`, + options: [], + errors: ["Assertions using `resolves` should be awaited."], + output: "await expect(promise).resolves.not.toBe(true);", + }, + { + code: `expect(promise).rejects.toThrow(new Error("foo"));`, + options: [], + errors: ["Assertions using `rejects` should be awaited."], + output: 'await expect(promise).rejects.toThrow(new Error("foo"));', + }, + { + code: `expect(promise).rejects.not.toThrow(new Error("foo"));`, + options: [], + errors: ["Assertions using `rejects` should be awaited."], + output: + 'await expect(promise).rejects.not.toThrow(new Error("foo"));', + }, + { + code: `expect(promise).resolves.not.toBe(true);`, + options: [], + errors: ["Assertions using `resolves` should be awaited."], + output: "await expect(promise).resolves.not.toBe(true);", + }, + { + code: `expect(promise).toResolve();`, + options: [], + errors: ["Assertions using `toResolve` should be awaited."], + output: "await expect(promise).toResolve();", + }, + { + code: `expect(promise).toReject();`, + options: [], + errors: ["Assertions using `toReject` should be awaited."], + output: "await expect(promise).toReject();", + }, + { + code: `expect(promise).not.toResolve();`, + options: [], + errors: ["Assertions using `toResolve` should be awaited."], + output: "await expect(promise).not.toResolve();", + }, + { + code: `expect(promise).not.toReject();`, + options: [], + errors: ["Assertions using `toReject` should be awaited."], + output: "await expect(promise).not.toReject();", + }, + // Report custom matchers if listed in the rule options + { + code: `expect(callback).toHaveMarkedConversion();`, + options: [ + { + matchers: ["toHaveMarkedConversion"], + }, + ], + errors: [ + "Assertions using `toHaveMarkedConversion` should be awaited.", + ], + output: "await expect(callback).toHaveMarkedConversion();", + }, + ], +}); diff --git a/packages/eslint-plugin-khan/test/jest-enzyme-matchers_test.js b/packages/eslint-plugin-khan/test/jest-enzyme-matchers_test.js new file mode 100644 index 00000000..e7690551 --- /dev/null +++ b/packages/eslint-plugin-khan/test/jest-enzyme-matchers_test.js @@ -0,0 +1,150 @@ +const path = require("path"); + +const {rules} = require("../lib/index.js"); +const RuleTester = require("eslint").RuleTester; + +const parserOptions = { + parser: "babel-eslint", +}; + +const ruleTester = new RuleTester(parserOptions); +const rule = rules["jest-enzyme-matchers"]; + +ruleTester.run("jest-real-timers", rule, { + valid: [ + { + code: 'expect(wrapper).toHaveProp("foo", "bar");', + options: [], + }, + { + code: 'expect(wrapper).toHaveState("foo", "bar");', + options: [], + }, + { + code: 'expect(wrapper).toContainMatchingElements(1, ".foo");', + options: [], + }, + { + code: 'expect(wrapper.find(".foo")).toHaveText("bar");', + options: [], + }, + { + code: 'expect(wrapper.find(".foo")).toHaveHTML("

bar

");', + options: [], + }, + { + code: 'expect(wrapper.find(".foo")).toExist();', + options: [], + }, + { + code: 'expect(wrapper).toContainMatchingElement(".foo");', + options: [], + }, + { + code: 'expect(wrapper.find(".foo")).not.toExist();', + options: [], + }, + { + code: 'expect(wrapper).not.toContainMatchingElement(".foo");', + options: [], + }, + ], + invalid: [ + { + code: `expect(wrapper.prop("foo")).toEqual("bar");`, + options: [], + errors: ['Use .toHaveProp("foo", "bar") instead.'], + output: 'expect(wrapper).toHaveProp("foo", "bar");', + }, + { + code: `expect(wrapper.first().prop("foo")).toEqual("bar");`, + options: [], + errors: ['Use .toHaveProp("foo", "bar") instead.'], + output: 'expect(wrapper.first()).toHaveProp("foo", "bar");', + }, + { + code: `expect(wrapper.state("foo")).toEqual("bar");`, + options: [], + errors: ['Use .toHaveState("foo", "bar") instead.'], + output: 'expect(wrapper).toHaveState("foo", "bar");', + }, + { + code: `expect(wrapper.first().state("foo")).toEqual("bar");`, + options: [], + errors: ['Use .toHaveState("foo", "bar") instead.'], + output: 'expect(wrapper.first()).toHaveState("foo", "bar");', + }, + { + code: `expect(wrapper.find(".foo")).toHaveLength(1);`, + options: [], + errors: ['Use .toContainMatchingElements(1, ".foo") instead.'], + output: 'expect(wrapper).toContainMatchingElements(1, ".foo");', + }, + { + code: `expect(wrapper.find(".foo")).toHaveLength(2);`, + options: [], + errors: ['Use .toContainMatchingElements(2, ".foo") instead.'], + output: 'expect(wrapper).toContainMatchingElements(2, ".foo");', + }, + { + code: `expect(wrapper.find(".foo").text()).toEqual("bar");`, + options: [], + errors: ['Use .toHaveText("bar") instead.'], + output: 'expect(wrapper.find(".foo")).toHaveText("bar");', + }, + { + code: `expect(wrapper.find(".foo").html()).toEqual("

bar

");`, + options: [], + errors: ['Use .toHaveHTML("

bar

") instead.'], + output: 'expect(wrapper.find(".foo")).toHaveHTML("

bar

");', + }, + { + code: `expect(wrapper.find(".foo").exists()).toBeTrue();`, + options: [], + errors: ["Use .toExist() instead."], + output: 'expect(wrapper.find(".foo")).toExist();', + }, + { + code: `expect(wrapper.find(".foo").exists()).toBeTruthy();`, + options: [], + errors: ["Use .toExist() instead."], + output: 'expect(wrapper.find(".foo")).toExist();', + }, + { + code: `expect(wrapper.exists(".foo")).toBeTrue();`, + options: [], + errors: ['Use .toContainMatchingElement(".foo") instead.'], + output: 'expect(wrapper).toContainMatchingElement(".foo");', + }, + { + code: `expect(wrapper.exists(".foo")).toBeTruthy();`, + options: [], + errors: ['Use .toContainMatchingElement(".foo") instead.'], + output: 'expect(wrapper).toContainMatchingElement(".foo");', + }, + { + code: `expect(wrapper.find(".foo").exists()).toBeFalse();`, + options: [], + errors: ["Use .not.toExist() instead."], + output: 'expect(wrapper.find(".foo")).not.toExist();', + }, + { + code: `expect(wrapper.find(".foo").exists()).toBeFalsy();`, + options: [], + errors: ["Use .not.toExist() instead."], + output: 'expect(wrapper.find(".foo")).not.toExist();', + }, + { + code: `expect(wrapper.exists(".foo")).toBeFalse();`, + options: [], + errors: ['Use .not.toContainMatchingElement(".foo") instead.'], + output: 'expect(wrapper).not.toContainMatchingElement(".foo");', + }, + { + code: `expect(wrapper.exists(".foo")).toBeFalsy();`, + options: [], + errors: ['Use .not.toContainMatchingElement(".foo") instead.'], + output: 'expect(wrapper).not.toContainMatchingElement(".foo");', + }, + ], +}); diff --git a/packages/eslint-plugin-khan/test/react-no-method-jsx-attribute_test.js b/packages/eslint-plugin-khan/test/react-no-method-jsx-attribute_test.js new file mode 100644 index 00000000..796c92a1 --- /dev/null +++ b/packages/eslint-plugin-khan/test/react-no-method-jsx-attribute_test.js @@ -0,0 +1,117 @@ +const path = require("path"); + +const {rules} = require("../lib/index.js"); +const RuleTester = require("eslint").RuleTester; + +const parserOptions = { + parser: "babel-eslint", +}; + +const ruleTester = new RuleTester(parserOptions); +const rule = rules["react-no-method-jsx-attribute"]; + +ruleTester.run("react-no-method-jsx-attribute", rule, { + valid: [ + // method arrow function in constructor + { + code: ` +class Foo { + constructor() { + this.handleClick = () => {}; + } + + render() { + return
+ } +}`, + options: [], + }, + // method arrow function class property + { + code: ` +class Foo { + handleClick = () => {} + + render() { + return
+ } +}`, + options: [], + }, + // different classes using the same event handler + { + code: ` +class Foo { + handleClick = () => {} + + render() { + return
+ } +} + +class Bar { + handleClick() {} + + render() { + return
this.handleClick()} /> + } +}`, + options: [], + }, + // getter method - called in Foo's scope so it's fine + { + code: ` +class Foo { + get bar() { + return this._bar; + } + + render() { + return
+ } +}`, + options: [], + }, + ], + invalid: [ + // regular method, not okay + { + code: ` +class Foo { + handleClick() {} + + render() { + return
+ } +}`, + options: [], + errors: [ + "Methods cannot be passed as props, use a class property instead.", + ], + }, + // two regular methods, both not okay, two errors + { + code: ` +class Foo { + handleClick() {} + + render() { + return
+ } +} + +class Bar { + handleClick() {} + + render() { + return
+ } +}`, + options: [], + errors: [ + "Methods cannot be passed as props, use a class property instead.", + "Methods cannot be passed as props, use a class property instead.", + ], + }, + ], +}); diff --git a/packages/eslint-plugin-khan/test/react-no-subscriptions-before-mount_test.js b/packages/eslint-plugin-khan/test/react-no-subscriptions-before-mount_test.js new file mode 100644 index 00000000..a7ec92dd --- /dev/null +++ b/packages/eslint-plugin-khan/test/react-no-subscriptions-before-mount_test.js @@ -0,0 +1,143 @@ +const {rules} = require("../lib/index.js"); +const RuleTester = require("eslint").RuleTester; + +const parserOptions = { + parser: "babel-eslint", +}; + +const ruleTester = new RuleTester(parserOptions); +const rule = rules["react-no-subscriptions-before-mount"]; + +const message = rule.__message; +const errors = [message]; + +const validBecauseNoSubs = ` +class MyComponent extends Component { + constructor(props) { + super(props); + + this.state = { + yadda: 5, + }; + } + + componentWillMount() { + console.log('Will mount!'); + } +}`; +const validBecauseSubAfterMount = ` +class MyComponent extends Component { + componentDidMount() { + window.addEventListener('scroll', () => {}) + } +}`; + +const invalidBecausePromiseInConstructor = ` +class MyComponent extends Component { + constructor(props) { + super(props); + + load().then(components => { + this.state = { + yadda: 5, + }; + }); + } +}`; + +const invalidBecausePromiseInCWM = ` +class MyComponent extends Component { + constructor(props) { + super(props); + + this.state = { + yadda: 5, + }; + } + + componentWillMount() { + const {load} = this.props; + + load().then(components => { + this.setState({components}); + }); + } +}`; + +const invalidBecauseEventListener = ` +class MyComponent extends Component { + componentWillMount() { + window.addEventListener('scroll', function() {}); + } +}`; + +const invalidBecauseSetTimeoutAsGlobal = ` +class MyComponent extends Component { + componentWillMount() { + setTimeout(function() {}, 1000); + } +}`; + +const invalidBecauseSetTimeoutAsProperty = ` +class MyComponent extends Component { + componentWillMount() { + window.setTimeout(function() {}, 1000); + } +}`; + +const invalidBecauseSubWithinBlock = ` +class MyComponent extends Component { + componentWillMount() { + if (this.whatever = 10) { + window.addEventListener('scroll', function() {}); + } + } +}`; + +const invalidWithNestedProperty = ` +class MyComponent extends Component { + componentWillMount() { + document.body.addEventListener('scroll', function() {}); + } +}`; + +const invalidWithinPromise = ` +class MyComponent extends Component { + componentWillMount() { + this.promise = new Promise((resolve, reject) => { + setTimeout(function() {}, 1000) + }) + } +}`; + +// TODO (josh): This example currently passes, but it should fail. +// Once the rule is less naive, add this example to the invalid tests. +// eslint-disable-next-line +const TODOInvalid = ` +class MyComponent extends Component { + componentWillMount() { + this.subscribeToInfo(); + } + + subscribeToInfo() { + api.fetch().then(() => { + + }); + } +}`; + +ruleTester.run("bind-react-methods", rule, { + valid: [validBecauseNoSubs, validBecauseSubAfterMount], + + invalid: [ + invalidBecausePromiseInConstructor, + invalidBecausePromiseInCWM, + invalidBecauseEventListener, + invalidBecauseSetTimeoutAsGlobal, + invalidBecauseSetTimeoutAsProperty, + invalidBecauseSubWithinBlock, + invalidWithNestedProperty, + invalidWithinPromise, + // TODOInvalid, + ].map(code => ({code, errors})), +}); diff --git a/packages/eslint-plugin-khan/test/react-svg-path-precision.js b/packages/eslint-plugin-khan/test/react-svg-path-precision.js new file mode 100644 index 00000000..27228b90 --- /dev/null +++ b/packages/eslint-plugin-khan/test/react-svg-path-precision.js @@ -0,0 +1,55 @@ +const path = require("path"); + +const {rules} = require("../lib/index.js"); +const RuleTester = require("eslint").RuleTester; + +const parserOptions = { + parser: "babel-eslint", +}; + +const ruleTester = new RuleTester(parserOptions); +const rule = rules["react-svg-path-precision"]; + +ruleTester.run("react-svg-path-precision", rule, { + valid: [ + { + code: '', + options: [], + }, + { + code: '
', + options: [], + }, + { + code: '', + options: [{precision: 3}], + }, + ], + invalid: [ + { + code: '', + options: [], + errors: [ + "This path contains numbers with too many decimal places.", + ], + output: '', + }, + { + code: '
', + options: [], + errors: [ + "This path contains numbers with too many decimal places.", + "This path contains numbers with too many decimal places.", + ], + output: '
', + }, + { + code: '', + options: [{precision: 1}], + errors: [ + "This path contains numbers with too many decimal places.", + ], + output: '', + }, + ], +}); diff --git a/packages/eslint-plugin-khan/test/sync-tag_test.js b/packages/eslint-plugin-khan/test/sync-tag_test.js new file mode 100644 index 00000000..07366849 --- /dev/null +++ b/packages/eslint-plugin-khan/test/sync-tag_test.js @@ -0,0 +1,80 @@ +const {rules} = require("../lib/index.js"); +const util = require("../lib/util.js"); +const RuleTester = require("eslint").RuleTester; + +const parserOptions = { + parser: "babel-eslint", +}; + +const ruleTester = new RuleTester(parserOptions); +const rule = rules["sync-tag"]; + +util.execFile = (file, ...args) => { + console.log("execFile mock --------"); + const command = [file, ...args].join(" "); + if (command.includes("filea")) { + const json = JSON.stringify({ + version: "2.2.3", + launchString: "", + items: [], + }); + return json; + } + if (command.includes("filex")) { + return JSON.stringify({ + version: "2.2.3", + launchString: "", + items: [ + { + type: "violation", + sourceFile: "filex", + sourceLine: 2, + targetFile: "filey", + targetLine: 23, + message: `filex:15 Updating checksum for sync-tag 'foo-bar' referencing 'filey:23' from No checksum to 1424803960.`, + fix: "// sync-start:foo-bar 1424803960 filey", + }, + ], + }); + } + return ""; +}; + +ruleTester.run("require-static-url", rule, { + valid: [ + { + code: + "// sync-start:foo-bar 1424803960 fileb\nconst FooBar = 'foobar';\n// sync-end:foobar", + filename: "filea", + options: [ + { + ignoreFiles: ["lint_blacklist.txt"], + rootDir: "/Users/nyancat/project", + }, + ], + }, + ], + invalid: [ + { + code: ` + // sync-start:foo-bar filey + const FooBar = 'foobar'; + // sync-end:foobar`, + filename: "filex", + errors: [ + "filex:15 Updating checksum for sync-tag 'foo-bar' referencing 'filey:23' from No checksum to 1424803960. " + + "If necessary, check the sync-tag target and make relevant changes before updating the checksum.", + ], + output: ` + // sync-start:foo-bar 1424803960 filey + const FooBar = 'foobar'; + // sync-end:foobar`, + options: [ + { + ignoreFiles: ["lint_blacklist.txt"], + rootDir: "/Users/nyancat/project", + }, + ], + }, + ], +}); diff --git a/utils/pre-publish-check-ci.js b/utils/pre-publish-check-ci.js index d41902f6..1f5d07c6 100644 --- a/utils/pre-publish-check-ci.js +++ b/utils/pre-publish-check-ci.js @@ -14,7 +14,7 @@ const { } = require("./pre-publish-utils"); // eslint-disable-next-line promise/catch-or-return -fg(path.join(__dirname, "..", "packages", "**", "package.json")).then( +fg(path.join(__dirname, "..", "packages", "*", "package.json")).then( (pkgPaths) => { // eslint-disable-next-line promise/always-return for (const pkgPath of pkgPaths) { diff --git a/yarn.lock b/yarn.lock index 60c1990e..cfdb9f7d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -282,7 +282,7 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.15", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4": +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.15", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4", "@babel/parser@^7.7.0": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== @@ -950,7 +950,7 @@ "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" -"@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4", "@babel/traverse@^7.7.2": +"@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4", "@babel/traverse@^7.7.0", "@babel/traverse@^7.7.2": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.4.tgz#a836aca7b116634e97a6ed99976236b3282c9d36" integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q== @@ -966,7 +966,7 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.7.4": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4" integrity sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA== @@ -2269,6 +2269,11 @@ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== +"@types/minimatch@^3.0.3": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" + integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== + "@types/minimatch@^5.1.2": version "5.1.2" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" @@ -2299,6 +2304,11 @@ resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + "@types/prettier@^2.1.5": version "2.7.2" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.2.tgz#6c2324641cc4ba050a8c710b2b251b377581fbf0" @@ -2504,7 +2514,7 @@ acorn-globals@^7.0.0: acorn "^8.1.0" acorn-walk "^8.0.2" -acorn-jsx@^5.3.2: +acorn-jsx@^5.0.0, 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== @@ -2514,6 +2524,11 @@ acorn-walk@^8.0.2: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== +acorn@^6.0.7: + 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.1.0, acorn@^8.5.0, acorn@^8.8.0, acorn@^8.8.2: version "8.8.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" @@ -2548,7 +2563,7 @@ ajv-errors@^1.0.1: resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== -ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.6: +ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.4, ajv@^6.12.6, ajv@^6.9.1: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -2575,19 +2590,34 @@ ansi-colors@^4.1.1, ansi-colors@^4.1.3: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== -ansi-escapes@^4.2.1: +ansi-escapes@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + +ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" +ansi-regex@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" + integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== + +ansi-regex@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" + integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== + 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== -ansi-styles@^3.2.1: +ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== @@ -2662,6 +2692,11 @@ array-buffer-byte-length@^1.0.0: call-bind "^1.0.2" is-array-buffer "^3.0.1" +array-differ@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" + integrity sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg== + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -2741,6 +2776,16 @@ ast-types-flow@^0.0.7: resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" integrity sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag== +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + async-listener@^0.6.0: version "0.6.10" resolved "https://registry.yarnpkg.com/async-listener/-/async-listener-0.6.10.tgz#a7c97abe570ba602d782273c0de60a51e3e17cbc" @@ -2776,6 +2821,18 @@ axobject-query@^3.1.1: dependencies: deep-equal "^2.0.5" +babel-eslint@^10.0.3: + version "10.1.0" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" + integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/parser" "^7.7.0" + "@babel/traverse" "^7.7.0" + "@babel/types" "^7.7.0" + eslint-visitor-keys "^1.0.0" + resolve "^1.12.0" + babel-jest@29.5.0, babel-jest@^29.5.0: version "29.5.0" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.5.0.tgz#3fe3ddb109198e78b1c88f9ebdecd5e4fc2f50a5" @@ -2983,6 +3040,11 @@ brotli-size@4.0.0: dependencies: duplexer "0.1.1" +browser-stdout@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" + integrity sha512-7Rfk377tpSM9TWBEeHs0FlDZGoAIei2V/4MdZJoFMBFAK6BqLpxAIUepGRHGdPFgGsLb02PXovC4qddyHvQqTg== + browserslist@^4.21.3, browserslist@^4.21.5: version "4.21.5" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" @@ -3148,6 +3210,11 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +checksync@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/checksync/-/checksync-2.3.0.tgz#d0801971aaa84a0adbc33734d1dab18f87ed54bd" + integrity sha512-QhR8MfvqAjo574gILPhGa1+j2NCyhhBC9biY1jigaPOnrtCO23QO+U+4cGfsw0IINXJd3yk6kcNtRKs5JYCdpg== + chokidar@^3.4.3: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" @@ -3168,6 +3235,11 @@ chownr@^2.0.0: resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + ci-info@^3.1.0, ci-info@^3.2.0: version "3.8.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" @@ -3188,6 +3260,33 @@ cli-boxes@^2.2.1: resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw== + dependencies: + restore-cursor "^2.0.0" + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-truncate@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" + integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== + dependencies: + slice-ansi "^3.0.0" + string-width "^4.2.0" + +cli-width@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" + integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== + cliui@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" @@ -3275,7 +3374,7 @@ color@^3.1.3: color-convert "^1.9.3" color-string "^1.6.0" -colorette@^2.0.19: +colorette@^2.0.16, colorette@^2.0.19: version "2.0.19" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== @@ -3300,6 +3399,13 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" +commander@2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" + integrity sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A== + dependencies: + graceful-readlink ">= 1.0.0" + commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -3310,6 +3416,11 @@ commander@^6.1.0, commander@^6.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== +compare-versions@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" + integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== + component-emitter@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" @@ -3382,6 +3493,17 @@ core-js-compat@^3.25.1: dependencies: browserslist "^4.21.5" +cosmiconfig@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + cosmiconfig@^8.0.0: version "8.1.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.1.0.tgz#947e174c796483ccf0a48476c24e4fefb7e1aea8" @@ -3401,7 +3523,18 @@ cross-spawn@^5.1.0: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -3456,6 +3589,13 @@ data-urls@^4.0.0: whatwg-mimetype "^3.0.0" whatwg-url "^12.0.0" +debug@2.6.8: + version "2.6.8" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" + integrity sha512-E22fsyWPt/lr4/UgQLt/pXqerGMDsanhbnmqIS3VAXuDi1v3IpiwXe2oncEIondHSBuPDWRoK/pMjlvi8FuOXQ== + dependencies: + ms "2.0.0" + debug@2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -3463,7 +3603,7 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: +debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, 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== @@ -3598,6 +3738,11 @@ diff-sequences@^29.4.3: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== +diff@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" + integrity sha512-597ykPFhtJYaXqPq6fF7Vl1fXTKgPdLOntyxpmdzUOKiYGqK7zcnbplj5088+8qJnWdzXhyeau5iVr8HVo9dgg== + dir-glob@^2.0.0: version "2.2.2" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" @@ -3689,6 +3834,11 @@ emittery@^0.13.1: resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -3731,7 +3881,7 @@ enhanced-resolve@^5.12.0: graceful-fs "^4.2.4" tapable "^2.2.0" -enquirer@^2.3.0: +enquirer@^2.3.0, enquirer@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== @@ -3860,7 +4010,7 @@ escape-html@~1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== -escape-string-regexp@^1.0.5: +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== @@ -4058,6 +4208,14 @@ eslint-scope@5.1.1, eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" +eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== + dependencies: + esrecurse "^4.1.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" @@ -4066,6 +4224,18 @@ eslint-scope@^7.1.1: esrecurse "^4.3.0" estraverse "^5.2.0" +eslint-utils@^1.3.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + eslint-visitor-keys@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" @@ -4076,6 +4246,48 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz#c7f0f956124ce677047ddbc192a68f999454dedc" integrity sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ== +eslint@^5.8.0: + version "5.16.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" + integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.9.1" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^4.0.3" + eslint-utils "^1.3.1" + eslint-visitor-keys "^1.0.0" + espree "^5.0.1" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.7.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^6.2.2" + js-yaml "^3.13.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.11" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^5.5.1" + strip-ansi "^4.0.0" + strip-json-comments "^2.0.1" + table "^5.2.3" + text-table "^0.2.0" + eslint@^8.38.0: version "8.38.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.38.0.tgz#a62c6f36e548a5574dd35728ac3c6209bd1e2f1a" @@ -4122,6 +4334,15 @@ eslint@^8.38.0: strip-json-comments "^3.1.0" text-table "^0.2.0" +espree@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" + integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A== + dependencies: + acorn "^6.0.7" + acorn-jsx "^5.0.0" + eslint-visitor-keys "^1.0.0" + espree@^9.0.0, espree@^9.5.1: version "9.5.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.1.tgz#4f26a4d5f18905bf4f2e0bd99002aab807e96dd4" @@ -4136,14 +4357,14 @@ esprima@^4.0.0, esprima@^4.0.1: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.4.2: +esquery@^1.0.1, esquery@^1.4.2: version "1.5.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== dependencies: estraverse "^5.1.0" -esrecurse@^4.3.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== @@ -4187,6 +4408,36 @@ eventid@^2.0.0: dependencies: uuid "^8.0.0" +execa@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-2.1.0.tgz#e5d3ecd837d2a60ec50f3da78fd39767747bbe99" + integrity sha512-Y/URAVapfbYy2Xp/gb6A0E7iR8xeqOCXsuuaoMn7A5PzrXUK84E1gyiEfq0wQd/GHA6GsoHWwhNq8anb0mleIw== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^3.0.0" + onetime "^5.1.0" + p-finally "^2.0.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + +execa@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" + integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -4278,7 +4529,7 @@ extendable-error@^0.1.5: resolved "https://registry.yarnpkg.com/extendable-error/-/extendable-error-0.1.7.tgz#60b9adf206264ac920058a7395685ae4670c2b96" integrity sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg== -external-editor@^3.1.0: +external-editor@^3.0.3, external-editor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== @@ -4347,6 +4598,20 @@ fecha@^4.2.0: resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + integrity sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA== + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + 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" @@ -4400,6 +4665,13 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" +find-versions@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-4.0.0.tgz#3c57e573bf97769b8cb8df16934b627915da4965" + integrity sha512-wgpWy002tA+wgmO27buH/9KzyEOQnKsG/R0yrcjPT9BOFm0zRBVQbZ95nRGXWMywS8YR5knRbpohio0bcJABxQ== + dependencies: + semver-regex "^3.1.2" + find-yarn-workspace-root2@1.2.16: version "1.2.16" resolved "https://registry.yarnpkg.com/find-yarn-workspace-root2/-/find-yarn-workspace-root2-1.2.16.tgz#60287009dd2f324f59646bdb4b7610a6b301c2a9" @@ -4413,6 +4685,15 @@ findit2@^2.2.3: resolved "https://registry.yarnpkg.com/findit2/-/findit2-2.2.3.tgz#58a466697df8a6205cdfdbf395536b8bd777a5f6" integrity sha512-lg/Moejf4qXovVutL0Lz4IsaPoNYMuxt4PA0nGqFxnJ1CTTGGlEO2wKgoDpwknhvZ8k4Q2F+eesgkLbG2Mxfog== +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" @@ -4421,6 +4702,11 @@ flat-cache@^3.0.4: flatted "^3.1.0" rimraf "^3.0.2" +flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + flatted@^3.1.0: version "3.2.7" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" @@ -4541,6 +4827,11 @@ function.prototype.name@^1.1.5: es-abstract "^1.19.0" functions-have-names "^1.2.2" +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== + functions-have-names@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" @@ -4639,11 +4930,23 @@ get-monorepo-packages@^1.1.0: globby "^7.1.1" load-json-file "^4.0.0" +get-own-enumerable-property-symbols@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" + integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== +get-stream@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + get-stream@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" @@ -4676,6 +4979,18 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" +glob@7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" + integrity sha512-mRyN/EsN2SyNhKWykF3eEGhDpeNplMWaW18Bmh76tnOqk5TbELAVwFAYOCmKVssOYFrYvvLMguiA+NXO3ZTuVA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.2" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@^7.0.0, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -4699,7 +5014,7 @@ glob@^8.0.0, glob@^8.0.1: minimatch "^5.0.1" once "^1.3.0" -globals@^11.1.0: +globals@^11.1.0, globals@^11.7.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== @@ -4859,11 +5174,21 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.5, graceful-fs@^4.1.6, graceful-fs@^4.1.9, resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== +"graceful-readlink@>= 1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" + integrity sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w== + 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== +growl@1.9.2: + version "1.9.2" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" + integrity sha512-RTBwDHhNuOx4F0hqzItc/siXCasGfC4DeWcBamclWd+6jWtBaeB/SGbMkGf0eiQoW7ib8JpvOgnUsmgMHI3Mfw== + gtoken@^5.0.4: version "5.3.2" resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-5.3.2.tgz#deb7dc876abe002178e0515e383382ea9446d58f" @@ -4899,6 +5224,11 @@ has-bigints@^1.0.1, has-bigints@^1.0.2: resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== +has-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + integrity sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA== + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -4945,6 +5275,11 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +he@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" + integrity sha512-z/GDPjlRMNOa2XJiB4em8wJpuuBfrFOlYKTZxtpkdr1uPdibHI8rYA3MY0KDObpVyaes0e/aunid/t88ZI2EKA== + heapdump@^0.3.15: version "0.3.15" resolved "https://registry.yarnpkg.com/heapdump/-/heapdump-0.3.15.tgz#631a8a2585588ea64778d8ec80a64c6c025f6a08" @@ -5026,6 +5361,11 @@ human-id@^1.0.2: resolved "https://registry.yarnpkg.com/human-id/-/human-id-1.0.2.tgz#e654d4b2b0d8b07e45da9f6020d8af17ec0a5df3" integrity sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw== +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" @@ -5038,6 +5378,22 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" +husky@^4.2.3: + version "4.3.8" + resolved "https://registry.yarnpkg.com/husky/-/husky-4.3.8.tgz#31144060be963fd6850e5cc8f019a1dfe194296d" + integrity sha512-LCqqsB0PzJQ/AlCgfrfzRe3e3+NvmefAdKQhRYpxS4u6clblBoDdzzvHi8fmxKRzvMxPY/1WZWzomPZww0Anow== + dependencies: + chalk "^4.0.0" + ci-info "^2.0.0" + compare-versions "^3.6.0" + cosmiconfig "^7.0.0" + find-versions "^4.0.0" + opencollective-postinstall "^2.0.2" + pkg-dir "^5.0.0" + please-upgrade-node "^3.2.0" + slash "^3.0.0" + which-pm-runs "^1.0.0" + iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -5064,7 +5420,12 @@ ignore@^3.3.5: resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== -ignore@^5.0.5, ignore@^5.2.0: +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +ignore@^5.0.5, ignore@^5.1.4, ignore@^5.2.0: version "5.2.4" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== @@ -5113,6 +5474,25 @@ inherits@2, inherits@2.0.4, inherits@^2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +inquirer@^6.2.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" + integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== + dependencies: + ansi-escapes "^3.2.0" + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^2.0.0" + lodash "^4.17.12" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^2.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + internal-slot@^1.0.3, internal-slot@^1.0.4, internal-slot@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" @@ -5234,6 +5614,11 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -5283,6 +5668,11 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + integrity sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg== + is-obj@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" @@ -5316,6 +5706,11 @@ is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" + integrity sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA== + is-set@^2.0.1, is-set@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" @@ -5965,6 +6360,11 @@ json-stable-stringify-without-jsonify@^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== +json3@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" + integrity sha512-I5YLeauH3rIaE99EE++UeH2M2gSYo8/2TqDac7oZEH6D/DSQ4Woa628Qrfj1X9/OY5Mk5VvIDQaKCDchXaKrmA== + json5@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" @@ -6063,6 +6463,14 @@ leven@^3.1.0: resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -6071,14 +6479,6 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" @@ -6091,6 +6491,41 @@ linkify-it@^3.0.1: dependencies: uc.micro "^1.0.1" +lint-staged@^10.1.2: + version "10.5.4" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.5.4.tgz#cd153b5f0987d2371fc1d2847a409a2fe705b665" + integrity sha512-EechC3DdFic/TdOPgj/RB3FicqE6932LTHCUm0Y2fsD9KGlLB+RwJl2q1IYBIvEsKzDOgn0D4gll+YxG5RsrKg== + dependencies: + chalk "^4.1.0" + cli-truncate "^2.1.0" + commander "^6.2.0" + cosmiconfig "^7.0.0" + debug "^4.2.0" + dedent "^0.7.0" + enquirer "^2.3.6" + execa "^4.1.0" + listr2 "^3.2.2" + log-symbols "^4.0.0" + micromatch "^4.0.2" + normalize-path "^3.0.0" + please-upgrade-node "^3.2.0" + string-argv "0.3.1" + stringify-object "^3.3.0" + +listr2@^3.2.2: + version "3.14.0" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.14.0.tgz#23101cc62e1375fd5836b248276d1d2b51fdbe9e" + integrity sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g== + dependencies: + cli-truncate "^2.1.0" + colorette "^2.0.16" + log-update "^4.0.0" + p-map "^4.0.0" + rfdc "^1.3.0" + rxjs "^7.5.1" + through "^2.3.8" + wrap-ansi "^7.0.0" + load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" @@ -6125,16 +6560,63 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash._baseassign@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" + integrity sha512-t3N26QR2IdSN+gqSy9Ds9pBu/J1EAFEshKlUHpJG3rvyJOYgcELIxcIeKKfZk7sjOz11cFfzJRsyFry/JyabJQ== + dependencies: + lodash._basecopy "^3.0.0" + lodash.keys "^3.0.0" + +lodash._basecopy@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" + integrity sha512-rFR6Vpm4HeCK1WPGvjZSJ+7yik8d8PVUdCJx5rT2pogG4Ve/2ZS7kfmO5l5T2o5V2mqlNIfSF5MZlr1+xOoYQQ== + +lodash._basecreate@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821" + integrity sha512-EDem6C9iQpn7fxnGdmhXmqYGjCkStmDXT4AeyB2Ph8WKbglg4aJZczNkQglj+zWXcOEEkViK8THuV2JvugW47g== + +lodash._getnative@^3.0.0: + version "3.9.1" + resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" + integrity sha512-RrL9VxMEPyDMHOd9uFbvMe8X55X16/cGM5IgOKgRElQZutpX89iS6vwl64duTV1/16w5JY7tuFNXqoekmh1EmA== + +lodash._isiterateecall@^3.0.0: + version "3.0.9" + resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" + integrity sha512-De+ZbrMu6eThFti/CSzhRvTKMgQToLxbij58LMfM8JnYDNSOjkjTCIaa8ixglOeGh2nyPlakbt5bJWJ7gvpYlQ== + lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== +lodash.create@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7" + integrity sha512-IUfOYwDEbI8JbhW6psW+Ig01BOVK67dTSCUAbS58M0HBkPcAv/jHuxD+oJVP2tUCo3H9L6f/8GM6rxwY+oc7/w== + dependencies: + lodash._baseassign "^3.0.0" + lodash._basecreate "^3.0.0" + lodash._isiterateecall "^3.0.0" + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== +lodash.isarguments@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg== + +lodash.isarray@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" + integrity sha512-JwObCrNJuT0Nnbuecmqr5DgtuBppuCvGD9lxjFpAzwnVtdGoDQ1zig+5W8k5/6Gcn0gZ3936HDAlGd28i7sOGQ== + lodash.isregexp@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.isregexp/-/lodash.isregexp-4.0.1.tgz#e13e647b30cd559752a04cd912086faf7da1c30b" @@ -6145,6 +6627,15 @@ lodash.isstring@^4.0.1: resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== +lodash.keys@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" + integrity sha512-CuBsapFjcubOGMn3VD+24HOAPxM79tH+V6ivJL3CHYjtrawauDJHUk//Yew9Hvc6e9rbCrURGk8z6PC+8WJBfQ== + dependencies: + lodash._getnative "^3.0.0" + lodash.isarguments "^3.0.0" + lodash.isarray "^3.0.0" + lodash.mapvalues@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz#1bafa5005de9dd6f4f26668c30ca37230cc9689c" @@ -6160,7 +6651,7 @@ lodash.startcase@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.startcase/-/lodash.startcase-4.4.0.tgz#9436e34ed26093ed7ffae1936144350915d9add8" integrity sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg== -lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: +lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -6170,7 +6661,7 @@ log-driver@^1.2.7: resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8" integrity sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg== -log-symbols@^4.1.0: +log-symbols@^4.0.0, log-symbols@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== @@ -6178,6 +6669,16 @@ log-symbols@^4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" +log-update@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" + integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== + dependencies: + ansi-escapes "^4.3.0" + cli-cursor "^3.1.0" + slice-ansi "^4.0.0" + wrap-ansi "^6.2.0" + logform@^2.3.2, logform@^2.4.0: version "2.5.1" resolved "https://registry.yarnpkg.com/logform/-/logform-2.5.1.tgz#44c77c34becd71b3a42a3970c77929e52c6ed48b" @@ -6431,6 +6932,11 @@ mime@^2.4.6: resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -6441,7 +6947,7 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, 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== @@ -6471,6 +6977,11 @@ minimist-options@4.1.0, minimist-options@^4.0.2: is-plain-obj "^1.1.0" kind-of "^6.0.3" +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q== + minimist@^1.2.0, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" @@ -6559,16 +7070,53 @@ mixme@^0.5.1: resolved "https://registry.yarnpkg.com/mixme/-/mixme-0.5.5.tgz#bf8f67d8caf10fdb49fd23198fd1fa6d8e406627" integrity sha512-/6IupbRx32s7jjEwHcycXikJwFD5UujbVNuJFkeKLYje+92OvtuPniF6JhnFm5JCTDUhS+kYK3W/4BWYQYXz7w== +mkdirp@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA== + dependencies: + minimist "0.0.8" + +mkdirp@^0.5.1: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +mocha@^3.3.0: + version "3.5.3" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.5.3.tgz#1e0480fe36d2da5858d1eb6acc38418b26eaa20d" + integrity sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg== + dependencies: + browser-stdout "1.3.0" + commander "2.9.0" + debug "2.6.8" + diff "3.2.0" + escape-string-regexp "1.0.5" + glob "7.1.1" + growl "1.9.2" + he "1.1.1" + json3 "3.3.2" + lodash.create "3.1.1" + mkdirp "0.5.1" + supports-color "3.1.2" + module-details-from-path@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== +mri@^1.1.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" + integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -6584,6 +7132,22 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +multimatch@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-4.0.0.tgz#8c3c0f6e3e8449ada0af3dd29efb491a375191b3" + integrity sha512-lDmx79y1z6i7RNx0ZGCPq1bzJ6ZoDDKbvh7jxr9SJcWLkShMzXrHbYVpTdnhNM5MXpDUxCQ4DgqVttVXlBgiBQ== + dependencies: + "@types/minimatch" "^3.0.3" + array-differ "^3.0.0" + array-union "^2.1.0" + arrify "^2.0.1" + minimatch "^3.0.4" + +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ== + nan@^2.13.2, nan@^2.14.0: version "2.17.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" @@ -6604,6 +7168,11 @@ negotiator@0.6.3, negotiator@^0.6.3: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + node-fetch@^2.6.1, node-fetch@^2.6.7: version "2.6.9" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" @@ -6773,7 +7342,14 @@ npm-registry-fetch@^14.0.0: npm-package-arg "^10.0.0" proc-log "^3.0.0" -npm-run-path@^4.0.1: +npm-run-path@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-3.1.0.tgz#7f91be317f6a466efed3c9f2980ad8a4ee8b0fa5" + integrity sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg== + dependencies: + path-key "^3.0.0" + +npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== @@ -6899,7 +7475,14 @@ one-time@^1.0.0: dependencies: fn.name "1.x.x" -onetime@^5.1.2: +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ== + dependencies: + mimic-fn "^1.0.0" + +onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== @@ -6915,7 +7498,12 @@ open@^8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" -optionator@^0.8.1: +opencollective-postinstall@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" + integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== + +optionator@^0.8.1, optionator@^0.8.2: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== @@ -6956,6 +7544,11 @@ p-filter@^2.1.0: dependencies: p-map "^2.0.0" +p-finally@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561" + integrity sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw== + p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -7092,6 +7685,11 @@ path-is-inside@^1.0.2: resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w== +path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== + path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" @@ -7156,6 +7754,20 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +pkg-dir@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-5.0.0.tgz#a02d6aebe6ba133a928f74aec20bafdfe6b8e760" + integrity sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA== + dependencies: + find-up "^5.0.0" + +please-upgrade-node@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942" + integrity sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg== + dependencies: + semver-compare "^1.0.0" + plur@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/plur/-/plur-4.0.0.tgz#729aedb08f452645fe8c58ef115bf16b0a73ef84" @@ -7211,6 +7823,11 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" +prettier@^1.18.2: + version "1.19.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" + integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== + prettier@^2.5.1, prettier@^2.7.1, prettier@^2.8.7: version "2.8.7" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450" @@ -7232,11 +7849,28 @@ pretty-ms@^7.0.0: dependencies: parse-ms "^2.1.0" +pretty-quick@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/pretty-quick/-/pretty-quick-2.0.2.tgz#4e44d6489ed513ef111bee501f63688d854584e6" + integrity sha512-aLb6vtOTEfJDwi1w+MBTeE20GwPVUYyn6IqNg6TtGpiOB1W3y6vKcsGFjqGeaaEtQgMLSPXTWONqh33UBuwG8A== + dependencies: + chalk "^2.4.2" + execa "^2.1.0" + find-up "^4.1.0" + ignore "^5.1.4" + mri "^1.1.4" + multimatch "^4.0.0" + proc-log@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-3.0.0.tgz#fb05ef83ccd64fd7b20bbe9c8c1070fc08338dd8" integrity sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A== +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" @@ -7560,6 +8194,11 @@ regexp.prototype.flags@^1.4.3: define-properties "^1.1.3" functions-have-names "^1.2.2" +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== + regexpu-core@^5.3.1: version "5.3.2" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" @@ -7641,6 +8280,15 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22 path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.12.0: + version "1.22.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== + dependencies: + is-core-module "^2.11.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^2.0.0-next.4: version "2.0.0-next.4" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" @@ -7650,6 +8298,22 @@ resolve@^2.0.0-next.4: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q== + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + retry-request@^4.0.0, retry-request@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/retry-request/-/retry-request-4.2.2.tgz#b7d82210b6d2651ed249ba3497f07ea602f1a903" @@ -7676,6 +8340,18 @@ reusify@^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" + integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== + +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -7731,6 +8407,11 @@ rrweb-cssom@^0.6.0: resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" integrity sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw== +run-async@^2.2.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -7738,6 +8419,20 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +rxjs@^6.4.0: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + +rxjs@^7.5.1: + version "7.8.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" + integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== + dependencies: + tslib "^2.1.0" + safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -7774,7 +8469,17 @@ saxes@^6.0.0: dependencies: xmlchars "^2.2.0" -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0: +semver-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" + integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow== + +semver-regex@^3.1.2: + version "3.1.4" + resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-3.1.4.tgz#13053c0d4aa11d070a2f2872b6b1e3ae1e1971b4" + integrity sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA== + +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -7925,6 +8630,33 @@ slash@^4.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +slice-ansi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + smart-buffer@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" @@ -8098,7 +8830,7 @@ stream-transform@^2.1.3: dependencies: mixme "^0.5.1" -string-argv@^0.3.1: +string-argv@0.3.1, string-argv@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== @@ -8120,6 +8852,23 @@ string-length@^4.0.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + string.prototype.matchall@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz#3bf85722021816dcd1bf38bb714915887ca79fd3" @@ -8168,6 +8917,29 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" +stringify-object@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" + integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== + dependencies: + get-own-enumerable-property-symbols "^3.0.0" + is-obj "^1.0.1" + is-regexp "^1.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow== + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + strip-ansi@^6.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" @@ -8197,6 +8969,11 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" +strip-json-comments@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + 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" @@ -8224,6 +9001,13 @@ superagent@^5.3.1: readable-stream "^3.6.0" semver "^7.3.2" +supports-color@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" + integrity sha512-F8dvPrZJtNzvDRX26eNXT4a7AecAvTGljmmnI39xEgSpbHKhQ7N0dO/NTxUExd0wuLHp4zbwYY7lvHq0aKpwrA== + dependencies: + has-flag "^1.0.0" + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -8263,6 +9047,16 @@ synckit@^0.8.5: "@pkgr/utils" "^2.3.1" tslib "^2.5.0" +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + tapable@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" @@ -8325,7 +9119,7 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== -through@2: +through@2, through@^2.3.6, through@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== @@ -8416,12 +9210,12 @@ tsconfig-paths@^3.14.1: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^1.8.1: +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== -tslib@^2.4.0, tslib@^2.5.0: +tslib@^2.1.0, tslib@^2.4.0, tslib@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== @@ -8783,6 +9577,11 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q== +which-pm-runs@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.1.0.tgz#35ccf7b1a0fce87bd8b92a478c9d045785d3bf35" + integrity sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA== + which-pm@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-pm/-/which-pm-2.0.0.tgz#8245609ecfe64bf751d0eef2f376d83bf1ddb7ae" @@ -8900,6 +9699,13 @@ write-file-atomic@^4.0.2: imurmurhash "^0.1.4" signal-exit "^3.0.7" +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + ws@^8.13.0: version "8.13.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" @@ -8945,6 +9751,11 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yaml@^1.10.0: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + yargs-parser@^18.1.2, yargs-parser@^18.1.3: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"