Merge pull request #207 from unix/build

build: supprot esm bundle
This commit is contained in:
witt
2020-05-16 00:30:09 +08:00
committed by GitHub
60 changed files with 188 additions and 81 deletions

View File

@@ -2,6 +2,7 @@
.next/*
examples/*
dist/*
esm/*
public/*
scripts/*
tests/*

1
.gitignore vendored
View File

@@ -26,3 +26,4 @@ yarn-error.log*
.now
dist
esm

View File

@@ -3,4 +3,7 @@
.DS_Store
.env
examples
esm
dist
coverage
.circleci

View File

@@ -5,6 +5,7 @@
dist
coverage
public
esm
*.mdx
*.md
*.json

View File

@@ -0,0 +1,6 @@
import CssBaseline from '../css-baseline'
import useWarning from '../utils/use-warning'
useWarning('Module "CSSBaseline" is deprecated. Use `CssBaseline` instead of it.', 'CSSBaseline')
export default CssBaseline

View File

@@ -1,22 +1,22 @@
import React from 'react'
import { render } from 'enzyme'
import { CSSBaseline, ZEITUIProvider } from 'components'
import { CssBaseline, ZeitProvider } from 'components'
describe('CSSBaseline', () => {
it('should render correctly', () => {
const wrapper = render(
<ZEITUIProvider>
<CSSBaseline />
</ZEITUIProvider>,
<ZeitProvider>
<CssBaseline />
</ZeitProvider>,
)
expect(wrapper).toMatchSnapshot()
})
it('should render dark mode correctly', () => {
const wrapper = render(
<ZEITUIProvider theme={{ type: 'dark' }}>
<CSSBaseline />
</ZEITUIProvider>,
<ZeitProvider theme={{ type: 'dark' }}>
<CssBaseline />
</ZeitProvider>,
)
expect(wrapper).toMatchSnapshot()
})

View File

@@ -1,9 +1,9 @@
import React from 'react'
import useTheme from '../use-theme'
import useTheme from '../styles/use-theme'
import flush from 'styled-jsx/server'
import flushToReact from 'styled-jsx/server'
const CSSBaseline: React.FC<React.PropsWithChildren<{}>> = ({ children }) => {
const CssBaseline: React.FC<React.PropsWithChildren<{}>> = ({ children }) => {
const theme = useTheme()
return (
@@ -305,9 +305,9 @@ type MemoCssBaselineComponent<P = {}> = React.NamedExoticComponent<P> & {
flush: typeof flushToReact
}
const MemoCSSBaseline = React.memo(CSSBaseline) as MemoCssBaselineComponent<
const MemoCssBaseline = React.memo(CssBaseline) as MemoCssBaselineComponent<
React.PropsWithChildren<{}>
>
MemoCSSBaseline.flush = flush
MemoCssBaseline.flush = flush
export default MemoCSSBaseline
export default MemoCssBaseline

View File

@@ -1,14 +1,21 @@
/// <reference types="styled-jsx" />
export * from './styles/themes'
export { default as Utils } from './utils-shared/index'
export { default as Utils } from './utils-shared-deprecated/index'
export { default as useTheme } from './styles/use-theme'
export { default as ZEITUIProvider } from './providers/zeit-ui-provider'
export { default as useToasts } from './toast'
export { default as ZEITUIProvider } from './zeit-provider-deprecated'
export { default as ZeitProvider } from './zeit-provider'
export { default as CSSBaseline } from './css-baseline-deprecated'
export { default as CssBaseline } from './css-baseline'
export { default as useToasts } from './use-toasts'
export { default as useInput } from './input/use-input'
export { default as useModal } from './modal/use-modal'
export { default as useTabs } from './tabs/use-tabs'
export { default as CSSBaseline } from './styles/css-baseline'
export { default as useBodyScroll } from './use-body-scroll'
export { default as useClickAway } from './use-click-away'
export { default as useClipboard } from './use-clipboard'
export { default as useCurrentState } from './use-current-state'
export { default as useMediaQuery } from './use-media-query'
export { default as Avatar } from './avatar'
export { default as Text } from './text'
export { default as Note } from './note'
@@ -37,7 +44,7 @@ export { default as Radio } from './radio'
export { default as Select } from './select'
export { default as Tabs } from './tabs'
export { default as Progress } from './progress'
export { default as Tree } from './file-tree'
export { default as Tree } from './tree'
export { default as Badge } from './badge'
export { default as AutoComplete } from './auto-complete'
export { default as Collapse } from './collapse'

View File

@@ -1,7 +1,7 @@
import React from 'react'
import ReactDom from 'react-dom/server'
import { mount } from 'enzyme'
import { Page, ZEITUIProvider } from 'components'
import { Page, ZeitProvider } from 'components'
describe('Page', () => {
it('should render correctly', () => {
@@ -48,9 +48,9 @@ describe('Page', () => {
it('should disable dot style when in dark mode', () => {
const wrapper = mount(
<ZEITUIProvider theme={{ type: 'dark' }}>
<ZeitProvider theme={{ type: 'dark' }}>
<Page dotBackdrop />
</ZEITUIProvider>,
</ZeitProvider>,
)
expect(wrapper.html()).not.toContain('global(body)')
})

View File

@@ -5,7 +5,7 @@ import { SnippetTypes, CopyTypes } from '../utils/prop-types'
import { getStyles } from './styles'
import SnippetIcon from './snippet-icon'
import useClipboard from '../utils/use-clipboard'
import useToasts from '../toast/use-toast'
import useToasts from '../use-toasts'
interface Props {
text?: string | string[]

View File

@@ -2,7 +2,7 @@ import React from 'react'
import { render, mount } from 'enzyme'
import { deepMergeObject } from '../theme-provider/theme-provider'
import DefaultThemes from '../themes/default'
import { ZEITUIProvider, ZeitUIThemes, Text } from 'components'
import { ZeitProvider, ZeitUIThemes, Text } from 'components'
import { DeepPartial } from 'components/utils/types'
describe('ThemeProvider', () => {
@@ -51,9 +51,9 @@ describe('ThemeProvider', () => {
}
}
const wrapper = render(
<ZEITUIProvider theme={customFunc}>
<ZeitProvider theme={customFunc}>
<Text type="success">hello</Text>
</ZEITUIProvider>,
</ZeitProvider>,
)
expect(wrapper).toMatchSnapshot()
})
@@ -62,9 +62,9 @@ describe('ThemeProvider', () => {
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {})
const customFunc = () => 0 as DeepPartial<ZeitUIThemes>
const wrapper = mount(
<ZEITUIProvider theme={customFunc}>
<ZeitProvider theme={customFunc}>
<p>test</p>
</ZEITUIProvider>,
</ZeitProvider>,
)
expect(errorSpy).toHaveBeenCalled()

View File

@@ -1,6 +1,6 @@
import React from 'react'
import { mount, ReactWrapper } from 'enzyme'
import { Button, Tooltip, ZEITUIProvider } from 'components'
import { Button, Tooltip, ZeitProvider } from 'components'
import { nativeEvent, updateWrapper } from 'tests/utils'
import { act } from 'react-dom/test-utils'
@@ -15,9 +15,9 @@ const expectTooltipIsHidden = (wrapper: ReactWrapper) => {
describe('Tooltip', () => {
it('should render correctly', async () => {
const wrapper = mount(
<ZEITUIProvider theme={{ type: 'dark' }}>
<ZeitProvider theme={{ type: 'dark' }}>
<Tooltip text={<p id="test">custom-content</p>}>some tips</Tooltip>
</ZEITUIProvider>,
</ZeitProvider>,
)
expectTooltipIsHidden(wrapper)

View File

@@ -2,7 +2,7 @@ import React from 'react'
import { mount } from 'enzyme'
import { Tree } from 'components'
import { nativeEvent } from 'tests/utils'
import { FileTreeValue } from 'components/file-tree/tree'
import { FileTreeValue } from 'components/tree/tree'
const mockFiles: Array<FileTreeValue> = [
{

View File

@@ -3,7 +3,7 @@ import TreeFile from './tree-file'
import TreeFolder from './tree-folder'
import { TreeContext } from './tree-context'
import { tuple } from '../utils/prop-types'
import { sortChildren } from '../file-tree/tree-help'
import { sortChildren } from './/tree-help'
const FileTreeValueType = tuple('directory', 'file')

View File

@@ -1,5 +1,5 @@
import React, { RefObject } from 'react'
import useBodyScroll from '../use-body-scroll'
import { useBodyScroll } from 'components'
import { act, renderHook } from '@testing-library/react-hooks'
describe('UseBodyScroll', () => {

View File

@@ -0,0 +1,3 @@
import useBodyScroll from './use-body-scroll'
export default useBodyScroll

View File

@@ -1,6 +1,6 @@
import React from 'react'
import { renderHook } from '@testing-library/react-hooks'
import useClickAway from '../use-click-away'
import { useClickAway } from 'components'
const simulateNativeClick = (el: Element) => {
el.dispatchEvent(

View File

@@ -0,0 +1,3 @@
import useClickAway from './use-click-away'
export default useClickAway

View File

@@ -1,4 +1,4 @@
import useClipboard from '../use-clipboard'
import { useClipboard } from 'components'
import { renderHook } from '@testing-library/react-hooks'
describe('UseClipboard', () => {

View File

@@ -0,0 +1,3 @@
import useClipboard from './use-clipboard'
export default useClipboard

View File

@@ -1,6 +1,6 @@
import React, { useEffect } from 'react'
import { mount } from 'enzyme'
import useCurrentState from '../use-current-state'
import { useCurrentState } from 'components'
import { renderHook, act } from '@testing-library/react-hooks'
describe('UseCurrentState', () => {

View File

@@ -0,0 +1,3 @@
import useCurrentState from './use-current-state'
export default useCurrentState

View File

@@ -1,8 +1,7 @@
import { Utils } from 'components'
import { useMediaQuery } from 'components'
import { renderHook } from '@testing-library/react-hooks'
// @ts-ignore
import mediaQuery from 'css-mediaquery'
const { useMediaQuery } = Utils
const mediaListMock = (width: number) => {
;(window as any).listeners = [] as Array<Function>

View File

@@ -0,0 +1,3 @@
import useMediaQuery from './use-media-query'
export default useMediaQuery

View File

@@ -1,6 +1,6 @@
import React from 'react'
import { mount, ReactWrapper } from 'enzyme'
import { useToasts, ZEITUIProvider } from 'components'
import { useToasts, ZeitProvider } from 'components'
import { nativeEvent, updateWrapper } from 'tests/utils'
const MockToast: React.FC<{}> = () => {
@@ -41,9 +41,9 @@ const expectToastIsHidden = (wrapper: ReactWrapper) => {
describe('UseToast', () => {
it('should render correctly', async () => {
const wrapper = mount(
<ZEITUIProvider>
<ZeitProvider>
<MockToast />
</ZEITUIProvider>,
</ZeitProvider>,
)
expectToastIsHidden(wrapper)
@@ -54,9 +54,9 @@ describe('UseToast', () => {
it('should work with different types', async () => {
const wrapper = mount(
<ZEITUIProvider>
<ZeitProvider>
<MockToast />
</ZEITUIProvider>,
</ZeitProvider>,
)
expectToastIsHidden(wrapper)
@@ -68,9 +68,9 @@ describe('UseToast', () => {
it('should close toast', async () => {
const wrapper = mount(
<ZEITUIProvider>
<ZeitProvider>
<MockToast />
</ZEITUIProvider>,
</ZeitProvider>,
)
expectToastIsHidden(wrapper)
@@ -85,9 +85,9 @@ describe('UseToast', () => {
it('the removeal should be delayed when hover is triggerd', async () => {
const wrapper = mount(
<ZEITUIProvider>
<ZeitProvider>
<MockToast />
</ZEITUIProvider>,
</ZeitProvider>,
)
expectToastIsHidden(wrapper)
@@ -111,9 +111,9 @@ describe('UseToast', () => {
it('should render different actions', async () => {
const wrapper = mount(
<ZEITUIProvider>
<ZeitProvider>
<MockToast />
</ZEITUIProvider>,
</ZeitProvider>,
)
const actions = [
{
@@ -135,9 +135,9 @@ describe('UseToast', () => {
it('should close toast when action triggered', async () => {
const wrapper = mount(
<ZEITUIProvider>
<ZeitProvider>
<MockToast />
</ZEITUIProvider>,
</ZeitProvider>,
)
const actions = [
{
@@ -160,9 +160,9 @@ describe('UseToast', () => {
it('should work with multiple toasts', async () => {
const wrapper = mount(
<ZEITUIProvider>
<ZeitProvider>
<MockToast />
</ZEITUIProvider>,
</ZeitProvider>,
)
expectToastIsHidden(wrapper)

View File

@@ -0,0 +1,19 @@
import useBodyScroll from '../use-body-scroll'
import useClipboard from '../use-clipboard'
import useCurrentState from '../use-current-state'
import useClickAway from '../use-click-away'
import useMediaQuery from '../use-media-query'
import useWarning from '../utils/use-warning'
useWarning(
'Module "Utils" is deprecated. All hooks are now exported directly from the main module.',
'Utils',
)
export default {
useBodyScroll,
useClipboard,
useCurrentState,
useClickAway,
useMediaQuery,
}

View File

@@ -1,13 +0,0 @@
import { default as useBodyScroll } from './use-body-scroll'
import { default as useClipboard } from './use-clipboard'
import { default as useCurrentState } from './use-current-state'
import { default as useClickAway } from './use-click-away'
import { default as useMediaQuery } from './use-media-query'
export default {
useBodyScroll,
useClipboard,
useCurrentState,
useClickAway,
useMediaQuery,
}

View File

@@ -1,3 +1,3 @@
import useBodyScroll from '../utils-shared/use-body-scroll'
import useBodyScroll from '../use-body-scroll'
export default useBodyScroll

View File

@@ -1,3 +1,3 @@
import { default as useClickAway } from '../utils-shared/use-click-away'
import { default as useClickAway } from '../use-click-away'
export default useClickAway

View File

@@ -1,3 +1,3 @@
import useClipboard from '../utils-shared/use-clipboard'
import useClipboard from '../use-clipboard'
export default useClipboard

View File

@@ -1,3 +1,3 @@
import useCurrentState from '../utils-shared/use-current-state'
import useCurrentState from '../use-current-state'
export default useCurrentState

View File

@@ -1,5 +1,5 @@
import React from 'react'
import { ToastWithID } from '../toast/toast-container'
import { ToastWithID } from '../use-toasts/toast-container'
export type UpdateToastsFunction<T> = (fn: (toasts: Array<T>) => Array<T>) => any

View File

@@ -0,0 +1,9 @@
import ZeitProvider from '../zeit-provider'
import useWarning from '../utils/use-warning'
useWarning(
'Module "ZEITUIProvider" is deprecated. Use `ZeitProvider` instead of it.',
'ZEITUIProvider',
)
export default ZeitProvider

View File

@@ -0,0 +1,3 @@
import ZeitProvider from './zeit-provider'
export default ZeitProvider

View File

@@ -7,13 +7,13 @@ import {
import ThemeProvider from '../styles/theme-provider'
import { ThemeParam } from '../styles/theme-provider/theme-provider'
import useCurrentState from '../utils/use-current-state'
import ToastContainer, { ToastWithID } from '../toast/toast-container'
import ToastContainer, { ToastWithID } from '../use-toasts/toast-container'
export interface Props {
theme?: ThemeParam
}
const ZEITUIProvider: React.FC<PropsWithChildren<Props>> = ({ theme, children }) => {
const ZeitProvider: React.FC<PropsWithChildren<Props>> = ({ theme, children }) => {
const [toasts, setToasts, toastsRef] = useCurrentState<Array<ToastWithID>>([])
const [toastHovering, setToastHovering] = useState<boolean>(false)
const updateToasts: UpdateToastsFunction<ToastWithID> = (
@@ -48,4 +48,4 @@ const ZEITUIProvider: React.FC<PropsWithChildren<Props>> = ({ theme, children })
)
}
export default ZEITUIProvider
export default ZeitProvider

View File

@@ -2,7 +2,7 @@
"name": "@zeit-ui/react",
"version": "1.4.2",
"main": "dist/index.js",
"module": "dist/index.js",
"module": "esm/index.js",
"types": "dist/index.d.ts",
"unpkg": "dist/index.min.js",
"license": "MIT",
@@ -31,22 +31,28 @@
"docs-start": "next start",
"docs-collect": "node scripts/collect-meta.js",
"contributor-collect": "node scripts/collect-contributors.js",
"clear": "rm -rf dist",
"clear": "rm -rf ./dist ./esm",
"lint": "eslint \"{components,lib}/**/*.{js,ts,tsx}\"",
"prettier": "prettier --write .",
"test": "jest --config .jest.config.js --no-cache",
"test-update": "jest --config .jest.config.js --no-cache --update-snapshot",
"coverage": "yarn test --coverage",
"now-build": "yarn run docs-build",
"build": "yarn run clear && webpack --config scripts/webpack.config.js && tsc -p ./scripts",
"release": "yarn run build && yarn publish --access public --non-interactive"
"build:esm": "babel --config-file ./scripts/babel.config.js --extensions \".js,.ts,.tsx\" ./components --out-dir ./esm --ignore \"**/__tests__/**/*,**/*.d.ts\"",
"build:webpack": "webpack --config scripts/webpack.config.js",
"build:types": "tsc -p ./scripts",
"build": "yarn clear && yarn build:esm && yarn build:webpack && yarn build:types",
"release": "yarn build && yarn publish --access public --non-interactive"
},
"files": [
"/dist"
"/dist",
"/esm"
],
"devDependencies": {
"@babel/cli": "^7.8.4",
"@babel/plugin-transform-runtime": "^7.9.0",
"@babel/preset-typescript": "^7.8.3",
"@babel/runtime": "^7.9.6",
"@mapbox/rehype-prism": "^0.4.0",
"@mdx-js/loader": "^1.5.7",
"@next/mdx": "^9.4.0",

19
scripts/babel.config.js Normal file
View File

@@ -0,0 +1,19 @@
module.exports = {
presets: [
[
'@babel/preset-env',
{
bugfixes: true,
modules: false,
},
],
'@babel/preset-react',
'@babel/preset-typescript',
],
plugins: [
'styled-jsx/babel',
['@babel/plugin-proposal-object-rest-spread', { loose: true }],
['@babel/plugin-transform-runtime', { useESModules: true }],
],
ignore: [/@babel[\\|/]runtime/],
}

View File

@@ -45,6 +45,22 @@
dependencies:
cross-fetch "3.0.4"
"@babel/cli@^7.8.4":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.8.4.tgz#505fb053721a98777b2b175323ea4f090b7d3c1c"
integrity sha512-XXLgAm6LBbaNxaGhMAznXXaxtCWfuv6PIDJ9Alsy9JYTOh+j2jJz+L/162kkfU1j/pTSxK1xGmlwI4pdIMkoag==
dependencies:
commander "^4.0.1"
convert-source-map "^1.1.0"
fs-readdir-recursive "^1.1.0"
glob "^7.0.0"
lodash "^4.17.13"
make-dir "^2.1.0"
slash "^2.0.0"
source-map "^0.5.0"
optionalDependencies:
chokidar "^2.1.8"
"@babel/code-frame@7.8.3", "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e"
@@ -974,7 +990,7 @@
core-js-pure "^3.0.0"
regenerator-runtime "^0.13.4"
"@babel/runtime@7.9.6", "@babel/runtime@^7.5.4", "@babel/runtime@^7.8.4":
"@babel/runtime@7.9.6", "@babel/runtime@^7.5.4", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.6":
version "7.9.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.6.tgz#a9102eb5cadedf3f31d08a9ecf294af7827ea29f"
integrity sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ==
@@ -2881,6 +2897,11 @@ commander@^2.19.0, commander@^2.20.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
commander@^4.0.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
commondir@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
@@ -4335,6 +4356,11 @@ fs-minipass@^2.0.0:
dependencies:
minipass "^3.0.0"
fs-readdir-recursive@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27"
integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==
fs-write-stream-atomic@^1.0.8:
version "1.0.10"
resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9"
@@ -4448,7 +4474,7 @@ glob-to-regexp@^0.4.1:
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
@@ -6063,7 +6089,7 @@ magic-string@^0.25.1:
dependencies:
sourcemap-codec "^1.4.4"
make-dir@^2.0.0:
make-dir@^2.0.0, make-dir@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==
@@ -8484,6 +8510,11 @@ sisteransi@^1.0.4:
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
slash@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44"
integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==
slash@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"