diff --git a/jest.config.js b/jest.config.js index 18331a7..45b21bc 100644 --- a/jest.config.js +++ b/jest.config.js @@ -59,7 +59,9 @@ module.exports = { // globalTeardown: undefined, // A set of global variables that need to be available in all test environments - // globals: {}, + globals: { + window: true + }, // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. // maxWorkers: "50%", @@ -138,7 +140,7 @@ module.exports = { // snapshotSerializers: [], // The test environment that will be used for testing - testEnvironment: 'node', + testEnvironment: 'jsdom', // Options that will be passed to the testEnvironment // testEnvironmentOptions: {}, diff --git a/packages/utils/package.json b/packages/utils/package.json index 6e84f4d..0914d0e 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -23,6 +23,9 @@ "peerDependencies": { "@alfalab/data": "^0.2.0" }, + "dependencies": { + "bezier-easing": "2.1.0" + }, "publishConfig": { "access": "public" } diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 85f3b8d..df89924 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -7,3 +7,4 @@ export * from './crop-account-number'; export * from './pluralize'; export * from './get-countries'; export * from './format-phone'; +export * from './scroll-to'; diff --git a/packages/utils/src/scroll-to/easings.ts b/packages/utils/src/scroll-to/easings.ts new file mode 100644 index 0000000..abf7a39 --- /dev/null +++ b/packages/utils/src/scroll-to/easings.ts @@ -0,0 +1,35 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + import bezierEasing from 'bezier-easing'; + + const easings = { + easeInSine: bezierEasing(0.47, 0, 0.745, 0.715), + easeOutSine: bezierEasing(0.39, 0.575, 0.565, 1), + easeInOutSine: bezierEasing(0.445, 0.05, 0.55, 0.95), + easeInQuad: bezierEasing(0.55, 0.085, 0.68, 0.53), + easeOutQuad: bezierEasing(0.25, 0.46, 0.45, 0.94), + easeInOutQuad: bezierEasing(0.455, 0.03, 0.515, 0.955), + easeInCubic: bezierEasing(0.55, 0.055, 0.675, 0.19), + easeOutCubic: bezierEasing(0.215, 0.61, 0.355, 1), + easeInOutCubic: bezierEasing(0.645, 0.045, 0.355, 1), + easeInQuart: bezierEasing(0.895, 0.03, 0.685, 0.22), + easeOutQuart: bezierEasing(0.165, 0.84, 0.44, 1), + easeInOutQuart: bezierEasing(0.77, 0, 0.175, 1), + easeInQuint: bezierEasing(0.755, 0.05, 0.855, 0.06), + easeOutQuint: bezierEasing(0.23, 1, 0.32, 1), + easeInOutQuint: bezierEasing(0.86, 0, 0.07, 1), + easeInExpo: bezierEasing(0.95, 0.05, 0.795, 0.035), + easeOutExpo: bezierEasing(0.19, 1, 0.22, 1), + easeInOutExpo: bezierEasing(1, 0, 0, 1), + easeInCirc: bezierEasing(0.6, 0.04, 0.98, 0.335), + easeOutCirc: bezierEasing(0.075, 0.82, 0.165, 1), + easeInOutCirc: bezierEasing(0.785, 0.135, 0.15, 0.86), + easeInBack: bezierEasing(0.6, -0.28, 0.735, 0.045), + easeOutBack: bezierEasing(0.175, 0.885, 0.32, 1.275), + easeInOutBack: bezierEasing(0.68, -0.55, 0.265, 1.55), + }; + + export type EasingType = keyof typeof easings; + export default easings; \ No newline at end of file diff --git a/packages/utils/src/scroll-to/index.ts b/packages/utils/src/scroll-to/index.ts new file mode 100644 index 0000000..133b1de --- /dev/null +++ b/packages/utils/src/scroll-to/index.ts @@ -0,0 +1 @@ +export * from './util'; \ No newline at end of file diff --git a/packages/utils/src/scroll-to/util.test.ts b/packages/utils/src/scroll-to/util.test.ts new file mode 100644 index 0000000..2dc7c13 --- /dev/null +++ b/packages/utils/src/scroll-to/util.test.ts @@ -0,0 +1,114 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + import scrollTo from './util'; + + function getContainer() { + return document.getElementById('container') as HTMLElement; + } + + function getScrollContainer() { + return document.getElementById('scroll-container') as HTMLElement; + } + + function getScrollContainerChild(n: number) { + return getScrollContainer().children[n] as HTMLElement; + } + + describe('scroll-to', () => { + const originalWindowScrollTo = window.scrollTo; + + beforeEach(() => { + const domContainerNode = document.createElement('div'); + + domContainerNode.setAttribute('id', 'container'); + domContainerNode.setAttribute('style', 'min-height: 9999px; margin: 0; padding: 0;'); + domContainerNode.innerHTML = ` +
+
+
+
+
+
+
+ `; + document.body.appendChild(domContainerNode); + + window.scrollTo = jest.fn(); + }); + + afterEach(() => { + const domContainerNode = getContainer(); + + document.body.removeChild(domContainerNode); + window.scrollTo = originalWindowScrollTo; + }); + + it('should scroll to Y in window', async () => { + await scrollTo({ + targetY: 100, + duration: 200, + }); + expect(window.scrollTo).toHaveBeenCalledWith(0, 100); + }); + + it('should scroll to Y in container', async () => { + const container = getScrollContainer(); + + await scrollTo({ + targetY: 100, + container, + }); + + expect(container.scrollTop).toBe(100); + }); + + it('should catch error with incorrect easing', () => { + const fn = function () { + scrollTo({ + targetY: 100, + duration: 200, + easing: 'incorrectEase', + } as any); + }; + + expect(fn).toThrow('Incorrect easing in options'); + }); + + it('should catch error with incorrect duration', () => { + const fn = function () { + scrollTo({ + targetY: 100, + duration: -200, + }); + }; + + expect(fn).toThrow('Incorrect duration in options'); + }); + + it('should scroll down to element in container', async () => { + const element = getScrollContainerChild(3); + const container = getScrollContainer(); + const correction = element.offsetHeight; + + if (element.offsetTop + correction > container.scrollTop + container.offsetHeight) { + await scrollTo({ container, targetY: element.offsetTop }); + expect(container.scrollTop).toBe(500); + } + }); + + it('should scroll up to element in container', async () => { + const element = getScrollContainerChild(0); + const container = getScrollContainer(); + const correction = element.offsetHeight; + + container.scrollTop = 500; + + if (element.offsetTop < container.scrollTop) { + await scrollTo({ container, targetY: (element.offsetTop - container.offsetHeight) + correction }); + + expect(container.scrollTop).toBe(0); + } + }); + }); \ No newline at end of file diff --git a/packages/utils/src/scroll-to/util.ts b/packages/utils/src/scroll-to/util.ts new file mode 100644 index 0000000..b4d1a69 --- /dev/null +++ b/packages/utils/src/scroll-to/util.ts @@ -0,0 +1,86 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + import easings, { EasingType } from './easings'; + + export const SCROLL_TO_CORRECTION = 16; + export const SCROLL_TO_DEFAULT_DURATION = 0; + export const SCROLL_TO_NORMAL_DURATION = 250; + export const SCROLL_TO_EASING = 'easeInOutSine'; + + type ScrollToOptions = { + /** + * Цель по оси Y + */ + targetY: number; + /** + * Элемент в котором скроллим + */ + container?: HTMLElement; + /** + * Продолжительность анимации в миллесекундах + */ + duration?: number; + /** + * Название функции плавности для анимации + */ + easing?: EasingType; + } + /** + * Скроллит по элементу или странице. + * В настоящее время доступно перемещение только по оси Y. + * TODO: Make a move on the x axis + */ + export default function scrollTo({ + targetY, + container, + duration = 0, + easing = SCROLL_TO_EASING, + }: ScrollToOptions): Promise { + const scrollY = container ? container.scrollTop : window.pageYOffset; + const startTime = window.performance.now(); + + if (duration < 0) { + throw new Error('Incorrect duration in options'); + } + + if (!easings[easing]) { + throw new Error('Incorrect easing in options'); + } + + const easingFunc = easings[easing]; + + return new Promise((resolve) => { + function scrollToTarget(y: number): void { + if (container) { + // eslint-disable-next-line no-param-reassign + container.scrollTop = y; + } else { + window.scrollTo(0, y); + } + } + + function loop(timestamp: number): void { + const currentTime = Math.abs(timestamp - startTime); + const t = currentTime / duration; + const val = easingFunc(t); + const currentTargetY = scrollY + ((targetY - scrollY) * val); + + if (t < 1) { + window.requestAnimationFrame(loop); + scrollToTarget(currentTargetY); + } else { + scrollToTarget(targetY); + resolve(); + } + } + + if (duration === 0) { + scrollToTarget(targetY); + resolve(); + } else { + loop(window.performance.now()); + } + }); + } \ No newline at end of file diff --git a/packages/utils/yarn.lock b/packages/utils/yarn.lock new file mode 100644 index 0000000..62978e5 --- /dev/null +++ b/packages/utils/yarn.lock @@ -0,0 +1,210 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@alfalab/data@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@alfalab/data/-/data-0.2.0.tgz#4956618252d0eb754652ec20554ff71292578add" + integrity sha512-so5/YttRoIvaulQincuwpensE4zmC9nSRdD2ikCgo6nhyZs6vPB4o7VNhvBcMddrvPOiXRYLerCU+J98GPe22Q== + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +bezier-easing@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/bezier-easing/-/bezier-easing-2.1.0.tgz#c04dfe8b926d6ecaca1813d69ff179b7c2025d86" + integrity sha1-wE3+i5JtbsrKGBPWn/F5t8ICXYY= + +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +dts-bundle-generator@^5.6.0: + version "5.7.0" + resolved "https://registry.yarnpkg.com/dts-bundle-generator/-/dts-bundle-generator-5.7.0.tgz#914b90b96dfb19dd7b9696567c02b6fe71595b9a" + integrity sha512-sSFtdHL/G8y90wWRB8HCWPwdXXbToOuPLgON1q8f1+vXuiDdRez12M4RAf770dLWb05ekaFdYYGaT68jUU52zw== + dependencies: + typescript ">=3.0.1" + yargs "^15.3.1" + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +fsevents@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +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" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +rollup@^2.38.0: + version "2.41.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.41.0.tgz#b2a398bbabbf227738dedaef099e494aed468982" + integrity sha512-Gk76XHTggulWPH95q8V62bw6uqDH6UGvbD6LOa3QUyhuMF3eOuaeDHR7SLm1T9faitkpNrqzUAVYx47klcMnlA== + optionalDependencies: + fsevents "~2.3.1" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" + integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +typescript@>=3.0.1: + version "4.2.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3" + integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw== + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +y18n@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" + integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== + +yargs-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@^15.3.1: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2"