diff --git a/README.md b/README.md index c9ef3e8f..84eba8fc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

fre logo

Fre

-

:ghost: Tiny React like framework with Concurrent.

+

:ghost: Tiny Coroutine framework with Fiber.

Build Status Code Coverage @@ -9,17 +9,12 @@ brotli

-### Feature -- :tada: Functional Component and hooks API -- :confetti_ball: Time slicing and Algebraic effects -- :telescope: keyed reconcilation algorithm +- **Coroutine with Fiber** — This is an amazing idea, which implements the coroutine scheduler in JavaScript, and the rendering is asynchronous, which supports Time slicing and suspense components. -### Real world +- **Highly-optimized algorithm** — Fre has a better reconciliation algorithm, which traverses from both ends with O (n) complexity, and supports keyed. -[clicli.me](https://www.clicli.me) - -Any other demos [click here](https://github.com/yisar/fre/tree/master/demo/src) +- **Do more with less** — After tree shaking, project of hello world is only 1KB, but it has most fetures, virtual DOM, hooks API, functional component and more. ### Use @@ -59,8 +54,6 @@ render(, document.getElementById('root')) - [useRef](https://github.com/yisar/fre#useref) -- [useContext](https://github.com/yisar/fre#usecontext) - #### useState `useState` is a base API, It will receive initial state and return a Array @@ -102,7 +95,7 @@ function App() {
{state.count} - +
) } @@ -138,7 +131,7 @@ If it return a function, the function can do cleanups: ```js useEffect(() => { document.title = 'count is ' + count - reutn () => { + return () => { store.unsubscribe() } }, []) @@ -146,7 +139,7 @@ useEffect(() => { #### useLayout -More like useEffect, but useEffect queue in `requestAnimationFrame`, but useLayout is sync and block commitWork. +More like useEffect, but useLayout is sync and blocking UI. ```js useLayout(() => { @@ -156,7 +149,7 @@ useLayout(() => { #### useMemo -`useMemo` has the same parameters as `useEffect`, but `useMemo` will return a cached value. +`useMemo` has the same rules as `useEffect`, but `useMemo` will return a cached value. ```js function App() { @@ -234,19 +227,5 @@ The above code needs babel plugin `@babel/plugin-transform-react-jsx` ] ``` -#### time slicing - -Time slicing is the scheduling of reconcilation, synchronous tasks, sacrifice CPU and reduce blocking time - -#### resumable exception - -resumable exception is a concept of algebraic effects. It can synchronously throw effects and then resume the execution of other logic of components. - -#### key-based reconcilation - -Fre implements a compact reconcilation algorithm support keyed, which also called diff. - -It uses hash to mark locations to reduce much size. - #### License _MIT @yisar diff --git a/demo/index.html b/demo/index.html index 85b1a17e..16b64b46 100644 --- a/demo/index.html +++ b/demo/index.html @@ -10,7 +10,7 @@
- + \ No newline at end of file diff --git a/demo/src/keys.tsx b/demo/src/keys.tsx new file mode 100644 index 00000000..18518608 --- /dev/null +++ b/demo/src/keys.tsx @@ -0,0 +1,56 @@ +import { h, render, useEffect, useState } from '../../src/index' + +// function App() { +// const [key, setKey] = useState(['a', 'b', 'c']) +// return [ +// , +// , +// ] +// } + +// function App() { +// const [key, setKey] = useState(['a', 'b', 'c']) +// return [ +// , +// , +// ] +// } + +// function App() { +// const [key, setKey] = useState(['a', 'b', 'c']) +// return [ +// , +// , +// ] +// } + +function App() { + const [key, setKey] = useState([1, 2]) + return [ + , + , + ] +} + +function Li(props) { + return
  • {props.i}
  • +} + +render(, document.getElementById('root')) diff --git a/demo/src/ref.tsx b/demo/src/ref.tsx new file mode 100644 index 00000000..a8fc1eca --- /dev/null +++ b/demo/src/ref.tsx @@ -0,0 +1,43 @@ +/** @jsx h */ + +// // preact: +// import { render, createElement as h } from "preact/compat"; +// import { useState, useEffect } from "preact/hooks"; + +// react: +// import { createElement as h, useState, useEffect } from "react"; +// import { render } from "react-dom"; + +// // fre: +import { render, h, useState, useEffect, useRef } from '../../src' + + +const Wrapper = () => { + const [showApp, setShowApp] = useState(true) + + useEffect(()=>{ + setTimeout(() => { + setShowApp(false) + }, 2000) + },[]) + + const p = dom => { + if (dom) { + } else { + console.log(111) + } + } + const c = dom => { + if (dom) { + } else { + console.log(222) + } + } + console.log(showApp) + + return showApp ?
    +

    before

    +
    :

    App removed...

    +} + +render(, document.getElementById('root')) diff --git a/demo/src/use-effect.tsx b/demo/src/use-effect.tsx index 99ea3ca6..5c83b5ef 100644 --- a/demo/src/use-effect.tsx +++ b/demo/src/use-effect.tsx @@ -7,13 +7,12 @@ import { h, render, useState, useEffect } from '../../src' function Counter({ id, remove }) { const [count, setCount] = useState(0) - useEffect(() => { - console.log(`111`) - - return () => { - console.log(`222`) - } - }) + // useEffect(() => { + // console.log(`111`) + // return () => { + // console.log(`222`) + // } + // }) return (
    diff --git a/demo/src/use-layout.tsx b/demo/src/use-layout.tsx new file mode 100644 index 00000000..88d4f689 --- /dev/null +++ b/demo/src/use-layout.tsx @@ -0,0 +1,24 @@ +import { h, render, useState, useEffect, useLayoutEffect } from '../../src' + +function App() { + const [count, setCount] = useState(0) + return ( +
    + {count < 5 && } +

    {count}

    + +
    + ) + } + + function A(props) { + useLayoutEffect(() => { + console.log(333) + return () => { + console.log(444) + } + }) + return
    {props.count}
    + } + + render(, document.body) \ No newline at end of file diff --git a/demo/src/use-state.tsx b/demo/src/use-state.tsx index 1b116c42..595c1ac5 100644 --- a/demo/src/use-state.tsx +++ b/demo/src/use-state.tsx @@ -5,7 +5,7 @@ function App() { // const [two, setTwo] = useState(0) return (
    - +
    ) } diff --git a/package.json b/package.json index 55035837..089c40ce 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ ], "scripts": { "test": "jest --coverage", + "ref": "jest ./test/ref.test.tsx", "build": "rollup -c && gzip-size dist/fre.js", "build:compat": "rollup --config compat/rollup.config.js", "dev": "rollup -c --watch", diff --git a/src/dom.ts b/src/dom.ts index 01669312..be6c0fe7 100644 --- a/src/dom.ts +++ b/src/dom.ts @@ -21,12 +21,10 @@ export const updateElement =

    ( if (oldValue) dom.removeEventListener(name, oldValue) dom.addEventListener(name, newValue) } else if (name in dom && !(dom instanceof SVGElement)) { - // for property, such as className ;(dom as any)[name] = newValue || '' } else if (newValue == null || newValue === false) { dom.removeAttribute(name) } else { - // for attributes dom.setAttribute(name, newValue) } } @@ -36,7 +34,7 @@ export const createElement =

    (fiber: IFiber) => { const dom = fiber.type === 'text' ? document.createTextNode('') - : fiber.op & (1 << 4) + : fiber.tag & (1 << 4) ? document.createElementNS( 'http://www.w3.org/2000/svg', fiber.type as string diff --git a/src/hooks.ts b/src/hooks.ts index 87f643f8..c7301835 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -15,9 +15,8 @@ export const useReducer = (reducer?: Reducer, initState?: S): [S, Di hook[0] = isFn(hook[1]) ? hook[1](hook[0]) : hook.length ? hook[1] : initState return [ hook[0] as S, - (action: A | Dispatch) => { - hook[1] = reducer ? reducer(hook[0], action as A) : action - hook[2] = reducer && (action as any).type[0] === '*' ? 0b1100 : 0b1000 + (value: A | Dispatch) => { + hook[1] = reducer || value dispatchUpdate(current) }, ] @@ -67,5 +66,5 @@ export const getHook = (cursor: numb } export const isChanged = (a: DependencyList, b: DependencyList) => { - return !a || a.length !== b.length || b.some((arg, index) => arg !== a[index]) + return !a || b.some((arg, index) => arg !== a[index]) } diff --git a/src/reconciler.ts b/src/reconciler.ts index 5676ca6f..4a9d28ad 100644 --- a/src/reconciler.ts +++ b/src/reconciler.ts @@ -1,16 +1,38 @@ -import { IFiber, FreElement, ITaskCallback, FC, Attributes, HTMLElementEx, FreNode, FiberMap, IRef, IEffect } from './type' -import { createElement, updateElement } from './dom' -import { resetCursor } from './hooks' -import { scheduleWork, shouldYield, schedule } from './scheduler' -import { isArr, createText } from './h' +import { + IFiber, + FreElement, + ITaskCallback, + FC, + Attributes, + HTMLElementEx, + FreNode, + FiberMap, + IRef, + IEffect, +} from "./type" +import { createElement, updateElement } from "./dom" +import { resetCursor } from "./hooks" +import { scheduleWork, shouldYield, schedule } from "./scheduler" +import { isArr, createText } from "./h" let preCommit: IFiber | undefined let currentFiber: IFiber let WIP: IFiber | undefined let commits: IFiber[] = [] -const microTask: IFiber[] = [] +let deletes = [] -export const render = (vnode: FreElement, node: Node, done?: () => void): void => { +const microTask: IFiber[] = [] +export const enum OP { + REMOVE = 1 << 4, + UPDATE = 1 << 1, + INSERT = 1 << 3, + MOUNT = UPDATE | INSERT, +} +export const render = ( + vnode: FreElement, + node: Node, + done?: () => void +): void => { const rootFiber = { node, props: { children: vnode }, @@ -20,8 +42,9 @@ export const render = (vnode: FreElement, node: Node, done?: () => void): void = } export const dispatchUpdate = (fiber?: IFiber) => { - if (fiber && !fiber.lane) { - fiber.lane = true + if (fiber && !fiber.dirty) { + fiber.dirty = true + fiber.tag = OP.UPDATE microTask.push(fiber) } scheduleWork(reconcileWork as ITaskCallback) @@ -38,12 +61,12 @@ const reconcileWork = (timeout: boolean): boolean => { const reconcile = (WIP: IFiber): IFiber | undefined => { WIP.parentNode = getParentNode(WIP) as HTMLElementEx isFn(WIP.type) ? updateHook(WIP) : updateHost(WIP) - WIP.lane = WIP.lane ? false : 0 + WIP.dirty = WIP.dirty ? false : 0 WIP.parent && commits.push(WIP) if (WIP.child) return WIP.child while (WIP) { - if (!preCommit && WIP.lane === false) { + if (!preCommit && WIP.dirty === false) { preCommit = WIP return null } @@ -55,21 +78,16 @@ const reconcile = (WIP: IFiber): IFiber | undefined => { const updateHook =

    (WIP: IFiber): void => { if (WIP.lastProps === WIP.props) return currentFiber = WIP - resetCursor() let children = (WIP.type as FC

    )(WIP.props) + resetCursor() if (isStr(children)) children = createText(children as string) reconcileChildren(WIP, children) } const updateHost = (WIP: IFiber): void => { if (!WIP.node) { - if (WIP.type === 'svg') WIP.op |= 1 << 4 WIP.node = createElement(WIP) as HTMLElementEx } - const p = WIP.parent || {} - WIP.insertPoint = (p as IFiber).last || null - ;(p as IFiber).last = WIP - WIP.last = null reconcileChildren(WIP, WIP.props.children) } @@ -79,115 +97,170 @@ const getParentNode = (WIP: IFiber): HTMLElement | undefined => { } } -const reconcileChildren = (WIP: IFiber, children: FreNode): void => { - delete WIP.child - const oldFibers = WIP.kids - const newFibers = (WIP.kids = hashfy(children as IFiber)) - - const reused = {} +const getChildNode = (WIP: IFiber): HTMLElement | undefined => { + while ((WIP = WIP.child)) { + if (!isFn(WIP.type)) return WIP.node + } +} - for (const k in oldFibers) { - const newFiber = newFibers[k] - const oldFiber = oldFibers[k] +const reconcileChildren = (WIP: any, children: FreNode): void => { + let oldKids = WIP.kids || [], + newKids = (WIP.kids = arrayfy(children) as any), + oldHead = 0, + newHead = 0, + oldTail = oldKids.length - 1, + newTail = newKids.length - 1 - if (newFiber && newFiber.type === oldFiber.type) { - reused[k] = oldFiber + while (oldHead <= oldTail && newHead <= newTail) { + let newFiber = null + if (oldKids[oldHead] == null) { + oldHead++ + } else if (oldKids[oldTail] == null) { + oldTail-- + } else if (same(oldKids[oldHead], newKids[newHead])) { + newFiber = newKids[newHead] + clone(newFiber, oldKids[oldHead]) + newFiber.tag = OP.UPDATE + oldHead++ + newHead++ + } else if (same(oldKids[oldTail], newKids[newTail])) { + newFiber = newKids[newTail] + clone(newFiber, oldKids[oldTail]) + newFiber.tag = OP.UPDATE + oldTail-- + newTail-- + } else if (same(oldKids[oldHead], newKids[newTail])) { + newFiber = newKids[newTail] + clone(newFiber, oldKids[oldHead]) + newFiber.tag = OP.MOUNT + newFiber.insertPoint = oldKids[oldTail].node.nextSibling + oldHead++ + newTail-- + } else if (same(oldKids[oldTail], newKids[newHead])) { + newFiber = newKids[newHead] + clone(newFiber, oldKids[oldTail]) + newFiber.tag = OP.MOUNT + newFiber.insertPoint = oldKids[oldHead].node + oldTail-- + newHead++ } else { - oldFiber.op |= 1 << 3 - commits.push(oldFiber) + const i = oldKids.findIndex((kid) => same(kid, newKids[newHead])) + if (i >= 0) { + const oldKid = oldKids[i] + newFiber = newKids[newHead] + clone(newFiber, oldKid) + newFiber.tag = OP.MOUNT + oldKids[i] = null + newFiber.insertPoint = oldKids[oldHead]?.node + } else { + newFiber = newKids[newHead] + newFiber.tag = OP.INSERT + newFiber.node = null + newFiber.insertPoint = oldKids[oldHead]?.node + } + newHead++ } } - - let prevFiber: IFiber | null - - for (const k in newFibers) { - let newFiber = newFibers[k] - const oldFiber = reused[k] - - if (oldFiber) { - oldFiber.op |= 1 << 2 - newFiber = { ...oldFiber, ...newFiber } - newFiber.lastProps = oldFiber.props - if (shouldPlace(newFiber)) { - newFiber.op &= 1 << 1 + if (oldTail < oldHead) { + for (let i = newHead; i <= newTail; i++) { + let newFiber = newKids[i] + newFiber.tag = OP.INSERT + newFiber.node = null + newFiber.insertPoint = oldKids[oldHead]?.node + } + } else if (newHead > newTail) { + for (let i = oldHead; i <= oldTail; i++) { + let oldFiber = oldKids[i] + if (oldFiber) { + oldFiber.tag = OP.REMOVE + deletes.push(oldFiber) } - } else { - newFiber.op |= 1 << 1 } + } - newFibers[k] = newFiber - newFiber.parent = WIP - - if (prevFiber) { - prevFiber.sibling = newFiber + for (var i = 0, prev = null; i < newKids.length; i++) { + const child = newKids[i] + child.parent = WIP + if (i > 0) { + prev.sibling = child } else { - if (WIP.op & (1 << 4)) { - newFiber.op |= 1 << 4 - } - WIP.child = newFiber + WIP.child = child } - prevFiber = newFiber + prev = child } - - delete prevFiber?.sibling } -const shouldPlace = (fiber: IFiber): string | boolean | undefined => { - const p = fiber.parent - if (isFn(p.type)) return p.key && !p.lane - return fiber.key +function clone(a, b) { + a.lastProps = b.props + a.node = b.node + a.kids = b.kids + a.hooks = b.hooks } +const getKey = (vdom) => (vdom == null ? vdom : vdom.key) + const commitWork = (fiber: IFiber): void => { commits.forEach(commit) + deletes.forEach(commit) fiber.done?.() commits = [] + deletes = [] preCommit = null WIP = null } const commit = (fiber: IFiber): void => { - const { op, parentNode, node, ref, hooks } = fiber - if (op & (1 << 3)) { - hooks?.list.forEach(cleanup) - cleanupRef(fiber.kids) - while (isFn(fiber.type)) fiber = fiber.child - parentNode.removeChild(fiber.node) - } else if (isFn(fiber.type)) { + let { tag, parentNode, node, ref, hooks } = fiber + if (isFn(fiber.type)) { + if (!fiber.node) fiber.node = getChildNode(fiber) as any + delete fiber.child.insertPoint if (hooks) { - side(hooks.layout) - schedule(() => side(hooks.effect)) + if (fiber.tag & OP.REMOVE) { + hooks.list.forEach(cleanup) + } else { + side(fiber.hooks.layout) + schedule(() => side(fiber.hooks.effect)) + } } - } else if (op & (1 << 2)) { + if (fiber.tag & OP.INSERT && fiber.insertPoint === undefined) return + } + if (tag & OP.UPDATE) { updateElement(node, fiber.lastProps, fiber.props) - } else { - const point = fiber.insertPoint ? fiber.insertPoint.node : null - const after = point ? point.nextSibling : parentNode.firstChild - if (after === node) return - if (after === null && node === parentNode.lastChild) return - parentNode.insertBefore(node, after) + } + if (tag & OP.REMOVE) { + node = null + cleanupRef(fiber.kids) + if (isChild(parentNode, fiber.node)) { + parentNode.removeChild(fiber.node) + } + } + if (tag & OP.INSERT) { + const after = fiber.insertPoint as any + if (after === fiber.node) return + if (after === null && fiber.node === parentNode.lastChild) return + parentNode.insertBefore(fiber.node, after) } refer(ref, node) } -const hashfy =

    (c: IFiber

    ): FiberMap

    => { - const out: FiberMap

    = {} - isArr(c) - ? c.forEach((v, i) => (isArr(v) ? v.forEach((vi, j) => (out[hs(i, j, vi.key)] = vi)) : some(v) && (out[hs(i, null, v.key)] = v))) - : some(c) && (out[hs(0, null, (c as any).key)] = c) - return out +function isChild(p, c) { + return Array.from(p.childNodes).some((i) => c == i) } +const same = (a, b) => a.type === b.type && getKey(a) === getKey(b) + +const arrayfy = arr => (!arr ? [] : isArr(arr) ? arr : [arr]) + const refer = (ref: IRef, dom?: HTMLElement): void => { - if (ref) isFn(ref) ? ref(dom) : ((ref as { current?: HTMLElement })!.current = dom) + if (ref) + isFn(ref) ? ref(dom) : ((ref as { current?: HTMLElement })!.current = dom) } -const cleanupRef =

    (kids: FiberMap

    ): void => { - for (const k in kids) { - const kid = kids[k] +const cleanupRef = (kids: any): void => { + kids.forEach((kid) => { refer(kid.ref, null) - if (kid.kids) cleanupRef(kid.kids) - } + kid.kids && cleanupRef(kid.kids) + }) } const side = (effects: IEffect[]): void => { @@ -198,12 +271,9 @@ const side = (effects: IEffect[]): void => { export const getCurrentFiber = () => currentFiber || null -const effect = (e: IEffect): void => (e[2] = e[0](currentFiber)) -const cleanup = (e: IEffect): void => e[2]?.(currentFiber) - -export const isFn = (x: any): x is Function => typeof x === 'function' -export const isStr = (s: any): s is number | string => typeof s === 'number' || typeof s === 'string' +const effect = (e: IEffect): void => e[2] = e[0]() +const cleanup = (e: IEffect): void => e[2] && e[2]() +export const isFn = (x: any): x is Function => typeof x === "function" +export const isStr = (s: any): s is number | string => + typeof s === "number" || typeof s === "string" export const some = (v: any) => v != null && v !== false && v !== true - -const hs = (i: number, j: string | number | null, k?: string): string => - k != null && j != null ? '.' + i + '.' + k : j != null ? '.' + i + '.' + j : k != null ? '.' + k : '.' + i diff --git a/src/type.ts b/src/type.ts index 7e3e56d6..1325dfc9 100644 --- a/src/type.ts +++ b/src/type.ts @@ -24,6 +24,7 @@ export interface FC

    { export interface FreElement

    { type: T props: P + key: string } export type HookTypes = 'list' | 'effect' | 'layout' @@ -34,46 +35,36 @@ export interface IHook { effect: IEffect[] } -export type IRef = ( - e: HTMLElement | undefined -) => void | { current?: HTMLElement } +export type IRef = (e: HTMLElement | undefined) => void | { current?: HTMLElement } export type FiberMap

    = Record> export interface IFiber

    { key?: string - lane?: any + dirty?: any type: string | FC

    - op: number parentNode: HTMLElementEx node: HTMLElementEx kids?: FiberMap

    parent?: IFiber

    sibling?: IFiber

    - last?: IFiber

    child?: IFiber

    done?: () => void ref: IRef hooks: IHook lastProps: P - insertPoint: IFiber | null + insertPoint: IFiber | null, props: P - oldProps?: P + tag: number } export type HTMLElementEx = HTMLElement & { last: IFiber | null } export type IEffect = [Function?, number?, Function?] export type FreText = string | number -export type FreNode = - | FreText - | FreElement - | FreNode[] - | boolean - | null - | undefined +export type FreNode = FreText | FreElement | FreNode[] | boolean | null | undefined export type SetStateAction = S | ((prevState: S) => S) -export type Dispatch = (value: A, resume?:boolean) => void +export type Dispatch = (value: A, resume?: boolean) => void export type Reducer = (prevState: S, action: A) => S export type IVoidCb = () => void export type EffectCallback = () => void | (IVoidCb | undefined) @@ -83,13 +74,11 @@ export interface PropsWithChildren { children?: FreNode } -export type ITaskCallback = -| ((time: boolean) => boolean) -| null +export type ITaskCallback = ((time: boolean) => boolean) | null export interface ITask { callback?: ITaskCallback time: number } -export type DOM = HTMLElement | SVGElement \ No newline at end of file +export type DOM = HTMLElement | SVGElement diff --git a/test/reconcilation.test.tsx b/test/reconcilation.test.tsx index 8eb664d1..84f9919f 100644 --- a/test/reconcilation.test.tsx +++ b/test/reconcilation.test.tsx @@ -28,13 +28,11 @@ test('reorder and reuse elements during key-based reconciliation of child-nodes' {state.map(value => (

  • {value}
  • ))} -
  • ), test: elements => { const children = [...elements[0].children] - children.pop() - expect(children.map(el => el.textContent)).toStrictEqual( + expect(children.map(el => el.textContent)).toEqual( state.map(value => '' + value) ) diff --git a/test/update.test.tsx b/test/update.test.tsx index f994fd9a..688bf6e0 100644 --- a/test/update.test.tsx +++ b/test/update.test.tsx @@ -9,7 +9,7 @@ test('batch updates', async () => { const [count, setState] = useState(0) updates++ const asyncUp = () => { - for (let i = 0; i < 10; i++) { + for (let i = 0; i <= 10; i++) { setState(i) } } @@ -29,7 +29,7 @@ test('batch updates', async () => { { content: , test: ([button]) => { - expect(+button.textContent).toBe(9) + expect(+button.textContent).toBe(10) expect(updates).toBe(2) } } diff --git a/yarn.lock b/yarn.lock index 3a70babe..8189d2df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1893,7 +1893,7 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: growly@^1.3.0: version "1.3.0" - resolved "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" + resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= gzip-size-cli@^3.0.0: @@ -2130,7 +2130,7 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-docker@^2.0.0: version "2.1.1" - resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz#4125a88e44e450d384e09047ede71adc2d144156" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.1.1.tgz#4125a88e44e450d384e09047ede71adc2d144156" integrity sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw== is-extendable@^0.1.0, is-extendable@^0.1.1: @@ -2206,7 +2206,7 @@ is-windows@^1.0.2: is-wsl@^2.2.0: version "2.2.0" - resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== dependencies: is-docker "^2.0.0" @@ -2907,6 +2907,13 @@ lru-cache@^4.0.1: pseudomap "^1.0.2" yallist "^2.1.2" +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + make-dir@^3.0.0, make-dir@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -3098,9 +3105,9 @@ node-modules-regexp@^1.0.0: integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= node-notifier@^8.0.0: - version "8.0.0" - resolved "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.0.tgz#a7eee2d51da6d0f7ff5094bc7108c911240c1620" - integrity sha512-46z7DUmcjoYdaWyXouuFNNfUo6eFa94t23c53c+lG/9Cvauk4a98rAUp9672X5dxGdQmLpPzTxzu8f/OeEPaFA== + version "8.0.1" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.1.tgz#f86e89bbc925f2b068784b31f382afdc6ca56be1" + integrity sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA== dependencies: growly "^1.3.0" is-wsl "^2.2.0" @@ -3695,9 +3702,11 @@ saxes@^5.0.0: integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== semver@7.x, semver@^7.3.2: - version "7.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" - integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== + version "7.3.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" + integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== + dependencies: + lru-cache "^6.0.0" semver@^6.0.0, semver@^6.3.0: version "6.3.0" @@ -3750,7 +3759,7 @@ shebang-regex@^3.0.0: shellwords@^0.1.1: version "0.1.1" - resolved "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" + resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== signal-exit@^3.0.0, signal-exit@^3.0.2: @@ -4242,9 +4251,9 @@ uuid@^3.3.2: integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== uuid@^8.3.0: - version "8.3.0" - resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea" - integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ== + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== v8-to-istanbul@^5.0.1: version "5.0.1" @@ -4338,7 +4347,7 @@ which@^1.2.9: which@^2.0.1, which@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" @@ -4397,6 +4406,11 @@ yallist@^2.1.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + yargs-parser@20.x: version "20.2.0" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.0.tgz#944791ca2be2e08ddadd3d87e9de4c6484338605"