diff --git a/langs/tl/api.md b/langs/tl/api.md new file mode 100644 index 00000000..953848e9 --- /dev/null +++ b/langs/tl/api.md @@ -0,0 +1,1390 @@ +--- +title: API +description: Outline ng lahat ng Solid API. +sort: 0 +--- + +# Basic Reactivity + +## `createSignal` + +```ts +export function createSignal( + value: T, + options?: { equals?: false | ((prev: T, next: T) => boolean) } +): [get: () => T, set:(v: T) => T]; +``` + +Ito ang pinakapayak na reactive primitive na ginagamit upang i-track ang isang value na pabagu-bago. Nagbibigay ang `createSignal` ng dalawang function sa isang array: isang getter (o _accessor_) at isang setter. + +Nagbibigay ng kasalukuyang value ng signal ang getter. Kapag tinawag ito sa loob ng isang tracking scope (tulad ng mga function na pinasa sa `createEffect` o ginamit sa loob ng JSX expression), tatakbo muli ang calling context kapag nag-update ang signal. + +Ang setter ang siyang maga-update ng signal. Tumatanggap ang setter ng iisang argument--maaaring ang bagong value ng signal, o kaya naman ay isang function na magbibigay ng panibagong value sa signal mula sa luma nitong value. Ibabalik ng setter ang bagong value. + +```js +const [getValue, setValue] = createSignal(initialValue); + +// kunin ang value +getValue(); + +// magpalit ng value +setValue(nextValue); + +// baguhin ang value gamit ang function setter +setValue((prev) => prev + next); +``` + +> Kung gusto mong maglagay ng function bilang value ng Signal, kailangan mong gamitin ang function form ng setter: +> +> ```js +> setValue(() => myFunction); +> ``` + +### Mga Option + +Marami sa mga primitive ng Solid ang tumatanggap ng "options" object sa huli nitong mga argument. Maaari kang makapagbigay ng `equals` na option sa options object ng `createSignal`. + +```js +const [getValue, setValue] = createSignal(initialValue, { equals: false }); +``` + +Sa simula, kapag tinawag ang setter ng signal, tatakbo muli ang mga dependency ng signal kung talagang magkaiba ang bago at luma nitong value. Maaari mong i-set ang `equals` na option sa `false` para palaging tumakbo ang mga dependency ng signal tuwing pagkatapos tawagin ang setter, o kaya naman ay puwede kang magpasa na sarili mong equality function. + +```js +const [myString, setMyString] = createSignal("string", { + equals: (newVal, oldVal) => newVal.length === oldVal.length +}); + +setMyString("strung") //itinuturing na equal sa huling value at hindi makapagsisimula ng mga update +setMyString("stranger") //itinuturing na magkaiba at makapagsisimula ng mga update +``` + + +## `createEffect` + +```ts +export function createEffect( + fn: (v: T) => T, + value?: T, +): void; +``` + +Gumagawa ng panibagong computation na automatic na magta-track sa mga dependency at tatakbo pagkatapos ng bawat render sa oras na magbago ang isa sa mga dependency. Mabisa katuwang ng paggamit ng mga `ref` at pagma-manage ng iba pang mga side effect. + +```js +const [a, setA] = createSignal(initialValue); + +// effect na nakadepende sa signal `a` +createEffect(() => doSideEffect(a())); +``` + +Tatawagin ang effect function na may isang argument mula sa ibinalik nito noong huli itong tinawag. Maaaring ibigay ang value ng argument na ito bilang optional na pangalawang argument. Magagamit ito sa diffing nang walang ginagawang panibagong closure. + +```js +createEffect((prev) => { + const sum = a() + b(); + if (sum !== prev) console.log(sum); + return sum; +}, 0); +``` + +Nagbabago ang mga signal sa loob ng mga effect nang _naka-batch_: walang magpo-propagate na pag-update sa signal habang hindi pa tapos tumakbo ang effect na naglalaman ng pagtawag sa pag-update nitong signal. + +## `createMemo` + +```ts +export function createMemo( + fn: (v: T) => T, + value?: T, + options?: { equals?: false | ((prev: T, next: T) => boolean) } +): () => T; +``` + +Gumagawa ng derived signal na readonly at may value na magre-recalculate sa tuwing magbabago ang kahit isa sa mga dependency nito. + +```js +const getValue = createMemo(() => computeExpensiveValue(a(), b())); + +// kunin ang value +getValue(); +``` + +Tatawagin ang memo function na may isang argument mula sa ibinalik nito noong huli itong tinawag. Maaaring ibigay ang value ng argument na ito bilang optional na pangalawang argument. Magagamit ito upang bawasan ang karagdagang mga computation. + +```js +const sum = createMemo((prev) => input() + prev, 0); +``` + +## `createResource` + +```ts +type ResourceReturn = [ + { + (): T | undefined; + loading: boolean; + error: any; + }, + { + mutate: (v: T | undefined) => T | undefined; + refetch: () => void; + } +]; + +export function createResource( + fetcher: (k: U, getPrev: () => T | undefined) => T | Promise, + options?: { initialValue?: T; } +): ResourceReturn; + +export function createResource( + source: U | false | null | (() => U | false | null), + fetcher: (k: U, getPrev: () => T | undefined) => T | Promise, + options?: { initialValue?: T; } +): ResourceReturn; +``` + +Gumagawa ng signal na makapagma-manage ng mga async request. Isang async function ang `fetcher` na tumatanggap ng value na ibinabalik ng `source` kung ibinigay, at magbabalik ng Promise na magre-resolve sa isang value. Itong resolved na value ay siyang ilalagay bilang panibagong value ng resource. Hindi reactive ang fetcher kaya magbigay ng `source` kung gusto mong tumakbo ito nang higit sa isang beses. Kung magre-resolve ang source sa false, null, o kaya ay undefined, hindi muling tatakbo ang fetcher upang baguhin ang value ng resource. + +```js +const [data, { mutate, refetch }] = createResource(getQuery, fetchData); + +// kunin ang value +data(); + +// tingnan kung naglo-load +data.loading; + +// tingnan kung nag-error +data.error; + +// baguhin ang value kaagad +mutate(optimisticValue); + +// mag-fetch uli sa anumang dahilan +refetch(); +``` + +Ang `loading` at `error` ay mga reactive na getter at maaaring i-track. + +# Mga Lifecycle + +## `onMount` + +```ts +export function onMount(fn: () => void): void; +``` + +Magre-register ng method na tatakbo pagkatapos ng unang render at kapag naikarga na ang mga element. Mainam katuwang ng mga `ref` at pagma-manage ng iba pang mga side effect na tatakbo lamang nang isang beses. Katumbas ito ng `createEffect` na walang mga dependency. + +## `onCleanup` + +```ts +export function onCleanup(fn: () => void): void; +``` + +Magre-register ng cleanup method na tatakbo tuwing disposal o kaya ng recalculation ng reactive scope kung saan ito kabilang. Maaaring gamitin sa anumang Component o Effect. + +## `onError` + +```ts +export function onError(fn: (err: any) => void): void; +``` + +Magre-register ng error handler method na tatakbo kapag nakatanggap ng error kahit saan sa child scope. Tatakbo lamang ang error handler sa pinakamalapit na scope. Ibato muli ang error para maipasa paitaas sa mga scope. + +# Mga Reactive Utility + +Nagbibigay ang mga helper na ito ng kakayahan upang makapag-schedule ng mga update nang mas maayos, at kontroling kung papaano tinatrack ang reactivity. + +## `untrack` + +```ts +export function untrack(fn: () => T): T; +``` + +Binabalewala ang tracking ng anumang dependency sa code block na tumatakbo at nagbibigay ng value. + +## `batch` + +```ts +export function batch(fn: () => T): T; +``` + +Iniipon ang mga update sa loob ng block hanggang sa huli upang maiwasan ang sobrang recalculation. Ibig sabihin, hindi pa maga-update ang pagkuha ng value sa susunod na linya. Automatic na nakapaloob ang set method ng [Solid Store](#createstore) at mga Effect sa isang batch. + +## `on` + +```ts +export function on any> | (() => any), U>( + deps: T, + fn: (input: T, prevInput: T, prevValue?: U) => U, + options: { defer?: boolean } = {} +): (prevValue?: U) => U | undefined; +``` + +Dinisenyo ang `on` para ipasa sa loob ng computation upang gawing hayagan ang mga dependency nito. Kung array ng mga dependency ang ipinasa, magiging array rin ang `input` at `prevInput`. + +```js +createEffect(on(a, (v) => console.log(v, b()))); + +// ay katumbas ng: +createEffect(() => { + const v = a(); + untrack(() => console.log(v, b())); +}); +``` + +Maaari ring huwag munang gawin ang computation kaagad at sa halip ay gawin ito kapag nagbago ang mga dependency sa pamamagitan ng paglalagay ng defer option sa true. + +```js +// hindi kaagad tatakbo +createEffect(on(a, (v) => console.log(v), { defer: true })); + +setA("new"); // ngayon tatakbo na ito +``` + +## `createRoot` + +```ts +export function createRoot(fn: (dispose: () => void) => T): T; +``` + +Gumagawa ng bagong non-tracked context na hindi kaagad mago-auto-dispose. Magagamit ito sa mga nested na reactive context kung saan hindi dapat ire-release kapag nag-re-evaluate ang parent. Mabisa ito para sa caching. + +Dapat naka-wrap ang lahat ng Solid code sa ganitong top level wrapper dahil sinisiguro nito na lahat ng memory/mga computation ay freed up na. Madalas, hindi na kailangang gawin ang ganito dahil ang lahat ng `render` entry function ay may kasama nang `createRoot` sa loob. + +## `mergeProps` + +```ts +export function mergeProps(...sources: any): any; +``` + +Isang method para sa reactive object `merge`. Magagamito ito sa pagse-set ng mga default prop ng mga component sa tuwing hindi ibinigay ng caller ang mga ito. O kaya naman ay pagko-clone ng props object kasama ng mga reactive property. + +Gumagana ang method na ito gamit ang proxy at nire-resolve nito ang mga property nang pabaligtad. Ito ang makapagbibigay ng kakayahan upang maisagawa ang dynamic tracking ng mga property na hindi kasama noong unang minerge ang prop object. + +```js +// defaul na mga prop +props = mergeProps({ name: "Smith" }, props); + +// i-clone ang mga prop +newProps = mergeProps(props); + +// i-merge ang mga prop +props = mergeProps(props, otherProps); +``` + +## `splitProps` + +```ts +export function splitProps( + props: T, + ...keys: Array<(keyof T)[]> +): [...parts: Partial]; +``` + +Hinahati ang reactive object gamit ang mga key. + +Tumatanggap ito ng reactive object at kahit ilang array ng key; sa bawat array ng key, magbabalik ito ng reactive object na meron lamang property nung orihinal na object. Itong reactive object ay maglalaman lamang ng mga property na kasama ang key sa mga ibinigay sa mga array. Ang huling reactive object sa ibinalik na array ay maglalaman ng mga natirang property. + +Magagamit ito kung ninanais na kumuha lamang ng iilang mga prop at ipasa ang natitira sa isang child. + +```js +const [local, others] = splitProps(props, ["children"]); + +<> + +
{local.children}
+ +``` + +## `useTransition` + +```ts +export function useTransition(): [ + () => boolean, + (fn: () => void, cb?: () => void) => void +]; +``` + +Ginagamit ito upang i-batch ang mga async update sa isang transaction, at ihihinto ang mga commit pansamantala hanggang matapos ang lahat ng async process. Katuwang nito ang Suspense at tinatrack lamang ang mga resource na ginagamit sa loob ng mga Suspense boundary. + +```js +const [isPending, start] = useTransition(); + +// i-check kung nagta-transitioning +isPending(); + +// i-wrap sa loob ng transition +start(() => setSignal(newValue), () => /* transition is done */) +``` + +## `observable` + +```ts +export function observable(input: () => T): Observable; +``` + +Tumatanggap ang method na ito ng isang signal at nagbabalik ng simpleng Observable. Gamitin ito sa napipiling Observable library sa pamamagitan ng `from` operator. + +```js +import { from } from "rxjs"; + +const [s, set] = createSignal(0); + +const obsv$ = from(observable(s)); + +obsv$.subscribe((v) => console.log(v)); +``` + +## `mapArray` + +```ts +export function mapArray( + list: () => readonly T[], + mapFn: (v: T, i: () => number) => U +): () => U[]; +``` + +Reactive map helper na nagka-cache ng bawat item by reference upang bawasan ang karagdagang mapping sa mga update. Tumatakbo lamang ang mapping function isang beses sa bawat value na nililipat o tinatanggal kung kinakailangan. Isang signal ang index argument. Hindi nagta-track mismo ang map function. + +Ginagamit ito ng `` control flow. + +```js +const mapped = mapArray(source, (model) => { + const [name, setName] = createSignal(model.name); + const [description, setDescription] = createSignal(model.description); + + return { + id: model.id, + get name() { + return name(); + }, + get description() { + return description(); + } + setName, + setDescription + } +}); +``` + +## `indexArray` + +```ts +export function indexArray( + list: () => readonly T[], + mapFn: (v: () => T, i: number) => U +): () => U[]; +``` + +Katulad ng `mapArray`, ngunit nagma-map ito gamit ang index. Isang signal ang item, at constant na ngayon ang index. + +Ginagamit ito ng `` control flow. + +```js +const mapped = indexArray(source, (model) => { + return { + get id() { + return model().id + } + get firstInitial() { + return model().firstName[0]; + }, + get fullName() { + return `${model().firstName} ${model().lastName}`; + }, + } +}); +``` + +# Stores + +These APIs are available at `solid-js/store`. They allow the creation of stores: [proxy objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) that allow a tree of signals to be independently tracked and modified. + +## Using Stores + +### `createStore` + +```ts +export function createStore( + state: T | Store, +): [get: Store, set: SetStoreFunction]; +``` + +The create function takes an initial state, wraps it in a store, and returns a readonly proxy object and a setter function. + +```js +import { createStore } from "solid-js/store"; +const [state, setState] = createStore(initialValue); + +// read value +state.someValue; + +// set value +setState({ merge: "thisValue" }); + +setState("path", "to", "value", newValue); +``` + +As proxies, store objects only track when a property is accessed. + +When nested objects are accessed, stores will produce nested store objects, and this applies all the way down the tree. However, this only applies to arrays and plain objects. Classes are not wrapped, so objects like `Date`, `HTMLElement`, `RegExp`, `Map`, `Set` won't be granularly reactive as properties on a store. + +The top level state object cannot be tracked, so put any lists on a key of state rather than using the state object itself. +```js +// put the list as a key on the state object +const [state, setState] = createStore({ list: [] }); + +// access the `list` property on the state object +{item => /*...*/} +``` + +### Getters + +Store objects support the use of getters to store calculated values. + +```js +import { createStore } from "solid-js/store"; +const [state, setState] = createStore({ + user: { + firstName: "John", + lastName: "Smith", + get fullName() { + return `${this.firstName} ${this.lastName}`; + }, + }, +}); +``` + +These are simple getters, so you still need to use a memo if you want to cache a value: + +```js +let fullName; +const [state, setState] = createStore({ + user: { + firstName: "John", + lastName: "Smith", + get fullName() { + return fullName(); + }, + }, +}); +fullName = createMemo(() => `${state.user.firstName} ${state.user.lastName}`); +``` + +### Updating Stores + +Changes can take the form of function that passes previous state and returns new state or a value. Objects are always shallowly merged. Set values to `undefined` to delete them from the Store. + +```js +import { createStore } from "solid-js/store"; +const [state, setState] = createStore({ + firstName: "John", + lastName: "Miller", +}); + +setState({ firstName: "Johnny", middleName: "Lee" }); +// ({ firstName: 'Johnny', middleName: 'Lee', lastName: 'Miller' }) + +setState((state) => ({ preferredName: state.firstName, lastName: "Milner" })); +// ({ firstName: 'Johnny', preferredName: 'Johnny', middleName: 'Lee', lastName: 'Milner' }) +``` + +It supports paths including key arrays, object ranges, and filter functions. + +setState also supports nested setting where you can indicate the path to the change. When nested the state you are updating may be other non Object values. Objects are still merged but other values (including Arrays) are replaced. + +```js +const [state, setState] = createStore({ + counter: 2, + list: [ + { id: 23, title: 'Birds' } + { id: 27, title: 'Fish' } + ] +}); + +setState('counter', c => c + 1); +setState('list', l => [...l, {id: 43, title: 'Marsupials'}]); +setState('list', 2, 'read', true); +// { +// counter: 3, +// list: [ +// { id: 23, title: 'Birds' } +// { id: 27, title: 'Fish' } +// { id: 43, title: 'Marsupials', read: true } +// ] +// } +``` + +Path can be string keys, array of keys, iterating objects ({from, to, by}), or filter functions. This gives incredible expressive power to describe state changes. + +```js +const [state, setState] = createStore({ + todos: [ + { task: 'Finish work', completed: false } + { task: 'Go grocery shopping', completed: false } + { task: 'Make dinner', completed: false } + ] +}); + +setState('todos', [0, 2], 'completed', true); +// { +// todos: [ +// { task: 'Finish work', completed: true } +// { task: 'Go grocery shopping', completed: false } +// { task: 'Make dinner', completed: true } +// ] +// } + +setState('todos', { from: 0, to: 1 }, 'completed', c => !c); +// { +// todos: [ +// { task: 'Finish work', completed: false } +// { task: 'Go grocery shopping', completed: true } +// { task: 'Make dinner', completed: true } +// ] +// } + +setState('todos', todo => todo.completed, 'task', t => t + '!') +// { +// todos: [ +// { task: 'Finish work', completed: false } +// { task: 'Go grocery shopping!', completed: true } +// { task: 'Make dinner!', completed: true } +// ] +// } + +setState('todos', {}, todo => ({ marked: true, completed: !todo.completed })) +// { +// todos: [ +// { task: 'Finish work', completed: true, marked: true } +// { task: 'Go grocery shopping!', completed: false, marked: true } +// { task: 'Make dinner!', completed: false, marked: true } +// ] +// } +``` + +## Store Utilities +### `produce` + +```ts +export function produce( + fn: (state: T) => void +): ( + state: T extends NotWrappable ? T : Store +) => T extends NotWrappable ? T : Store; +``` + +Immer inspired API for Solid's Store objects that allows for localized mutation. + +```js +setState( + produce((s) => { + s.user.name = "Frank"; + s.list.push("Pencil Crayon"); + }) +); +``` + +### `reconcile` + +```ts +export function reconcile( + value: T | Store, + options?: { + key?: string | null; + merge?: boolean; + } = { key: "id" } +): ( + state: T extends NotWrappable ? T : Store +) => T extends NotWrappable ? T : Store; +``` + +Diffs data changes when we can't apply granular updates. Useful for when dealing with immutable data from stores or large API responses. + +The key is used when available to match items. By default `merge` false does referential checks where possible to determine equality and replaces where items are not referentially equal. `merge` true pushes all diffing to the leaves and effectively morphs the previous data to the new value. + +```js +// subscribing to an observable +const unsubscribe = store.subscribe(({ todos }) => ( + setState('todos', reconcile(todos))); +); +onCleanup(() => unsubscribe()); +``` + +### `createMutable` + +```ts +export function createMutable( + state: T | Store, +): Store { +``` + +Creates a new mutable Store proxy object. Stores only trigger updates on values changing. Tracking is done by intercepting property access and automatically tracks deep nesting via proxy. + +Useful for integrating external systems or as a compatibility layer with MobX/Vue. + +> **Note:** A mutable state can be passed around and mutated anywhere, which can make it harder to follow and easier to break unidirectional flow. It is generally recommended to use `createStore` instead. The `produce` modifier can give many of the same benefits without any of the downsides. + +```js +const state = createMutable(initialValue); + +// read value +state.someValue; + +// set value +state.someValue = 5; + +state.list.push(anotherValue); +``` + +Mutables support setters along with getters. + +```js +const user = createMutable({ + firstName: "John", + lastName: "Smith", + get fullName() { + return `${this.firstName} ${this.lastName}`; + }, + set fullName(value) { + [this.firstName, this.lastName] = value.split(" "); + }, +}); +``` + +# Component APIs + +## `createContext` + +```ts +interface Context { + id: symbol; + Provider: (props: { value: T; children: any }) => any; + defaultValue: T; +} +export function createContext(defaultValue?: T): Context; +``` + +Context provides a form of dependency injection in Solid. It is used to save from needing to pass data as props through intermediate components. + +This function creates a new context object that can be used with `useContext` and provides the `Provider` control flow. Default Context is used when no `Provider` is found above in the hierarchy. + +```js +export const CounterContext = createContext([{ count: 0 }, {}]); + +export function CounterProvider(props) { + const [state, setState] = createStore({ count: props.count || 0 }); + const store = [ + state, + { + increment() { + setState("count", (c) => c + 1); + }, + decrement() { + setState("count", (c) => c - 1); + }, + }, + ]; + + return ( + + {props.children} + + ); +} +``` + +The value passed to provider is passed to `useContext` as is. That means wrapping as a reactive expression will not work. You should pass in Signals and Stores directly instead of accessing them in the JSX. + +## `useContext` + +```ts +export function useContext(context: Context): T; +``` + +Used to grab context to allow for deep passing of props without having to pass them through each Component function. + +```js +const [state, { increment, decrement }] = useContext(CounterContext); +``` + +## `children` + +```ts +export function children(fn: () => any): () => any; +``` + +Used to make it easier to interact with `props.children`. This helper resolves any nested reactivity and returns a memo. Recommended approach to using `props.children` in anything other than passing directly through to JSX. + +```js +const list = children(() => props.children); + +// do something with them +createEffect(() => list()); +``` + +## `lazy` + +```ts +export function lazy>( + fn: () => Promise<{ default: T }> +): T & { preload: () => Promise }; +``` + +Used to lazy load components to allow for code splitting. Components are not loaded until rendered. Lazy loaded components can be used the same as its statically imported counterpart, receiving props etc... Lazy components trigger `` + +```js +// wrap import +const ComponentA = lazy(() => import("./ComponentA")); + +// use in JSX +; +``` + +# Secondary Primitives + +You probably won't need them for your first app, but these are useful tools to have. + +## `createDeferred` + +```ts +export function createDeferred( + source: () => T, + options?: { + timeoutMs?: number; + equals?: false | ((prev: T, next: T) => boolean); + } +): () => T; +``` + +Creates a readonly that only notifies downstream changes when the browser is idle. `timeoutMs` is the maximum time to wait before forcing the update. + +## `createComputed` + +```ts +export function createComputed( + fn: (v: T) => T, + value?: T, +): void; +``` + +Creates a new computation that automatically tracks dependencies and runs immediately before render. Use this to write to other reactive primitives. When possible use `createMemo` instead as writing to a signal mid update can cause other computations to need to re-calculate. + +## `createRenderEffect` + +```ts +export function createRenderEffect( + fn: (v: T) => T, + value?: T, +): void; +``` + +Creates a new computation that automatically tracks dependencies and runs during the render phase as DOM elements are created and updated but not necessarily connected. All internal DOM updates happen at this time. + +## `createSelector` + +```ts +export function createSelector( + source: () => T, + fn?: (a: U, b: T) => boolean, +): (k: U) => boolean; +``` + +Creates a conditional signal that only notifies subscribers when entering or exiting their key matching the value. Useful for delegated selection state. As it makes the operation O(2) instead of O(n). + +```js +const isSelected = createSelector(selectedId); + + + {(item) =>
  • {item.name}
  • } +
    ; +``` + +# Rendering + +These imports are exposed from `solid-js/web`. + +## `render` + +```ts +export function render( + code: () => JSX.Element, + element: MountableElement +): () => void; +``` + +This is the browser app entry point. Provide a top level component definition or function and an element to mount to. It is recommended this element be empty as the returned dispose function will wipe all children. + +```js +const dispose = render(App, document.getElementById("app")); +``` + +## `hydrate` + +```ts +export function hydrate( + fn: () => JSX.Element, + node: MountableElement +): () => void; +``` + +This method is similar to `render` except it attempts to rehydrate what is already rendered to the DOM. When initializing in the browser a page has already been server rendered. + +```js +const dispose = hydrate(App, document.getElementById("app")); +``` + +## `renderToString` + +```ts +export function renderToString( + fn: () => T, + options?: { + eventNames?: string[]; + nonce?: string; + } +): string; +``` + +Renders to a string synchronously. The function also generates a script tag for progressive hydration. Options include eventNames to listen to before the page loads and play back on hydration, and nonce to put on the script tag. + +```js +const html = renderToString(App); +``` + +## `renderToStringAsync` + +```ts +export function renderToStringAsync( + fn: () => T, + options?: { + eventNames?: string[]; + timeoutMs?: number; + nonce?: string; + } +): Promise; +``` + +Same as `renderToString` except it will wait for all `` boundaries to resolve before returning the results. Resource data is automatically serialized into the script tag and will be hydrated on client load. + +```js +const html = await renderToStringAsync(App); +``` + +## `pipeToNodeWritable` + +```ts +export type PipeToWritableResults = { + startWriting: () => void; + write: (v: string) => void; + abort: () => void; +}; +export function pipeToNodeWritable( + fn: () => T, + writable: { write: (v: string) => void }, + options?: { + eventNames?: string[]; + nonce?: string; + noScript?: boolean; + onReady?: (r: PipeToWritableResults) => void; + onComplete?: (r: PipeToWritableResults) => void | Promise; + } +): void; +``` + +This method renders to a Node stream. It renders the content synchronously including any Suspense fallback placeholders, and then continues to stream the data from any async resource as it completes. + +```js +pipeToNodeWritable(App, res); +``` + +The `onReady` option is useful for writing into the stream around the the core app rendering. Remember if you use `onReady` to manually call `startWriting`. + +## `pipeToWritable` + +```ts +export type PipeToWritableResults = { + write: (v: string) => void; + abort: () => void; + script: string; +}; +export function pipeToWritable( + fn: () => T, + writable: WritableStream, + options?: { + eventNames?: string[]; + nonce?: string; + noScript?: boolean; + onReady?: ( + writable: { write: (v: string) => void }, + r: PipeToWritableResults + ) => void; + onComplete?: ( + writable: { write: (v: string) => void }, + r: PipeToWritableResults + ) => void; + } +): void; +``` + +This method renders to a web stream. It renders the content synchronously including any Suspense fallback placeholders, and then continues to stream the data from any async resource as it completes. + +```js +const { readable, writable } = new TransformStream(); +pipeToWritable(App, writable); +``` + +The `onReady` option is useful for writing into the stream around the the core app rendering. Remember if you use `onReady` to manually call `startWriting`. + +## `isServer` + +```ts +export const isServer: boolean; +``` + +This indicates that the code is being run as the server or browser bundle. As the underlying runtimes export this as a constant boolean it allows bundlers to eliminate the code and their used imports from the respective bundles. + +```js +if (isServer) { + // I will never make it to the browser bundle +} else { + // won't be run on the server; +} +``` + +# Control Flow + + For reactive contriol flow to be performant, we have to control how elements are created. For example, with lists, a simple `map` is inefficient as it always maps the entire array. + +This means helper functions. Wrapping these in components is convenient way for terse templating and allows users to compose and build their own control flow components. + +These built-in control flow components will be automatically imported. All except `Portal` and `Dynamic` are exported from `solid-js`. Those two, which are DOM-specific, are exported by `solid-js/web`. + +> Note: All callback/render function children of control flow are non-tracking. This allows for nesting state creation, and better isolates reactions. + +## `` + +```ts +export function For(props: { + each: readonly T[]; + fallback?: JSX.Element; + children: (item: T, index: () => number) => U; +}): () => U[]; +``` + +Simple referentially keyed loop. The callback takes the current item as the first argument: + +```jsx +Loading...
    }> + {(item) =>
    {item}
    } + +``` + +The optional second argument is an index signal: + +```jsx +Loading...
    }> + {(item, index) => ( +
    + #{index()} {item} +
    + )} + +``` + +## `` + +```ts +function Show(props: { + when: T | undefined | null | false; + fallback?: JSX.Element; + children: JSX.Element | ((item: T) => JSX.Element); +}): () => JSX.Element; +``` + +The Show control flow is used to conditional render part of the view: it renders `children` when the `when` is truthy, an `fallback` otherwise. It is similar to the ternary operator (`when ? children : fallback`) but is ideal for templating JSX. + +```jsx + 0} fallback={
    Loading...
    }> +
    My Content
    +
    +``` + +Show can also be used as a way of keying blocks to a specific data model. Ex the function is re-executed whenever the user model is replaced. + +```jsx +Loading...}> + {(user) =>
    {user.firstName}
    } +
    +``` + +## ``/`` + +```ts +export function Switch(props: { + fallback?: JSX.Element; + children: JSX.Element; +}): () => JSX.Element; + +type MatchProps = { + when: T | undefined | null | false; + children: JSX.Element | ((item: T) => JSX.Element); +}; +export function Match(props: MatchProps); +``` + +Useful for when there are more than 2 mutual exclusive conditions. Can be used to do things like simple routing. + +```jsx +Not Found}> + + + + + + + +``` + +Match also supports function children to serve as keyed flow. + +## `` + +```ts +export function Index(props: { + each: readonly T[]; + fallback?: JSX.Element; + children: (item: () => T, index: number) => U; +}): () => U[]; +``` + +Non-keyed list iteration (rendered nodes are keyed to an array index). This is useful when there is no conceptual key, like if the data consists of primitives and it is the index that is fixed rather than the value. + +The item is a signal: + +```jsx +Loading...}> + {(item) =>
    {item()}
    } +
    +``` + +Optional second argument is an index number: + +```jsx +Loading...}> + {(item, index) => ( +
    + #{index} {item()} +
    + )} +
    +``` + +## `` + +```ts +function ErrorBoundary(props: { + fallback: JSX.Element | ((err: any, reset: () => void) => JSX.Element); + children: JSX.Element; +}): () => JSX.Element; +``` + +Catches uncaught errors and renders fallback content. + +```jsx +Something went terribly wrong}> + + +``` + +Also supports callback form which passes in error and a reset function. + +```jsx +
    Error: {err.toString()}
    } +> + +
    +``` + +## `` + +```ts +export function Suspense(props: { + fallback?: JSX.Element; + children: JSX.Element; +}): JSX.Element; +``` + +A component that tracks all resources read under it and shows a fallback placeholder state until they are resolved. What makes `Suspense` different than `Show` is it is non-blocking in that both branches exist at the same time even if not currently in the DOM. + +```jsx +Loading...}> + + +``` + +## `` (Experimental) + +```ts +function SuspenseList(props: { + children: JSX.Element; + revealOrder: "forwards" | "backwards" | "together"; + tail?: "collapsed" | "hidden"; +}): JSX.Element; +``` + +`SuspenseList` allows for coordinating multiple parallel `Suspense` and `SuspenseList` components. It controls the order in which content is revealed to reduce layout thrashing and has an option to collapse or hide fallback states. + +```jsx + + + Loading posts...}> + + + Loading fun facts...}> + + + +``` + +SuspenseList is still experimental and does not have full SSR support. + +## `` + +```ts +function Dynamic( + props: T & { + children?: any; + component?: Component | string | keyof JSX.IntrinsicElements; + } +): () => JSX.Element; +``` + +This component lets you insert an arbitrary Component or tag and passes the props through to it. + +```jsx + +``` + +## `` + +```ts +export function Portal(props: { + mount?: Node; + useShadow?: boolean; + isSVG?: boolean; + children: JSX.Element; +}): Text; +``` + +This inserts the element in the mount node. Useful for inserting Modals outside of the page layout. Events still propagate through the Component Hierarchy. + +The portal is mounted in a `
    ` unless the target is the document head. `useShadow` places the element in a Shadow Root for style isolation, and `isSVG` is required if inserting into an SVG element so that the `
    ` is not inserted. + +```jsx + +
    My Content
    +
    +``` + +# Special JSX Attributes + +In general Solid attempts to stick to DOM conventions. Most props are treated as attributes on native elements and properties on Web Components, but a few of them have special behavior. + +For custom namespaced attributes with TypeScript you need to extend Solid's JSX namespace: + +```ts +declare module "solid-js" { + namespace JSX { + interface Directives { + // use:____ + } + interface ExplicitProperties { + // prop:____ + } + interface ExplicitAttributes { + // attr:____ + } + interface CustomEvents { + // on:____ + } + interface CustomCaptureEvents { + // oncapture:____ + } + } +} +``` + +## `ref` + +Refs are a way of getting access to underlying DOM elements in our JSX. While it is true one could just assign an element to a variable, it is more optimal to leave components in the flow of JSX. Refs are assigned at render time but before the elements are connected to the DOM. They come in 2 flavors. + +```js +// simple assignment +let myDiv; + +// use onMount or createEffect to read after connected to DOM +onMount(() => console.log(myDiv)); +
    + +// Or, callback function (called before connected to DOM) +
    console.log(el)} /> +``` + +Refs can also be used on Components. They still need to be attached on the otherside. + +```jsx +function MyComp(props) { + return
    ; +} + +function App() { + let myDiv; + onMount(() => console.log(myDiv.clientWidth)); + return ; +} +``` + +## `classList` + +A helper that leverages `element.classList.toggle`. It takes an object whose keys are class names and assigns them when the resolved value is true. + +```jsx +
    +``` + +## `style` + +Solid's style helper works with either a string or with an object. Unlike React's version Solid uses `element.style.setProperty` under the hood. This means support for CSS vars, but it also means we use the lower, dash-case version of properties. This actually leads to better performance and consistency with SSR output. + +```jsx +// string +
    + +// object +
    + +// css variable +
    +``` + +## `innerHTML`/`textContent` + +These work the same as their property equivalent. Set a string and they will be set. **Be careful!!** Setting `innerHTML` with any data that could be exposed to an end user as it could be a vector for malicious attack. `textContent` while generally not needed is actually a performance optimization when you know the children will only be text as it bypasses the generic diffing routine. + +```jsx +
    +``` + +## `on___` + +Event handlers in Solid typically take the form of `onclick` or `onClick` depending on style. + +Solid uses semi-synthetic event delegation for common UI events that are composed and bubble. This improves performance for these common events. + +```jsx +
    console.log(e.currentTarget)} /> +``` + +Solid also supports passing an array to the event handler to bind a value to the first argument of the event handler. This doesn't use `bind` or create an additional closure, so it is a highly optimized way of delegating events. + +```jsx +function handler(itemId, e) { + /*...*/ +} + +
      + {(item) =>
    • } +
    ; +``` + +Events are never rebound and the bindings are not reactive, as it is expensive to attach and detach listeners. +Since event handlers are called like any other function each time an event fires, there is no need for reactivity; simply shortcut your handler if desired. + +```jsx +// if defined call it, otherwised don't. +
    props.handleClick?.()} /> +``` + +Note that `onChange` and `onInput` work according to their native behavior. `onInput` will fire immediately after the value has changed; for `` fields, `onChange` will only fire after the field loses focus. +## `on:___`/`oncapture:___` + +For any other events, perhaps ones with unusual names, or ones you wish not to be delegated there are the `on` namespace events. This simply adds an event listener verbatim. + +```jsx +
    alert(e.detail)} /> +``` + +## `use:___` + +These are custom directives. In a sense this is just syntax sugar over ref but allows us to easily attach multiple directives to a single element. A directive is simply a function with the following signature: + +```ts +function directive(element: Element, accessor: () => any): void; +``` + +Directive functions are called at render time but before being added to the DOM. You can do whatever you'd like in them including create signals, effects, register clean-up etc. + +```js +const [name, setName] = createSignal(""); + +function model(el, value) { + const [field, setField] = value(); + createRenderEffect(() => (el.value = field())); + el.addEventListener("input", (e) => setField(e.target.value)); +} + +; +``` + +To register with TypeScript extend the JSX namespace. + +```ts +declare module "solid-js" { + namespace JSX { + interface Directives { + model: [() => any, (v: any) => any]; + } + } +} +``` + +## `prop:___` + +Forces the prop to be treated as a property instead of an attribute. + +```jsx +
    +``` + +## `attr:___` + +Forces the prop to be treated as a attribute instead of an property. Useful for Web Components where you want to set attributes. + +```jsx + +``` + +## `/* @once */` + +Solid's compiler uses a simple heuristic for reactive wrapping and lazy evaluation of JSX expressions. Does it contain a function call, a property access, or JSX? If yes we wrap it in a getter when passed to components or in an effect if passed to native elements. + +Knowing this we can reduce overhead of things we know will never change simply by accessing them outside of the JSX. A simple variable will never be wrapped. We can also tell the compiler not to wrap them by starting the expression with a comment decorator `/_ @once _/`. + +```jsx + +``` + +This also works on children. + +```jsx +{/*@once*/ state.wontUpdate} +``` diff --git a/langs/tl/guides/comparison.md b/langs/tl/guides/comparison.md new file mode 100644 index 00000000..56099cd4 --- /dev/null +++ b/langs/tl/guides/comparison.md @@ -0,0 +1,83 @@ +--- +title: Comparison +description: Comparisons of Solid against other frameworks. +sort: 4 +--- + +# Comparison with other Libraries + +This section cannot escape some bias but I think it is important to understand where Solid's solution sits compared to other libraries. This is not about performance. For a definitive look at performance feel free to look at the [JS Framework Benchmark](https://github.com/krausest/js-framework-benchmark). + +## React + +React has had a big influence on Solid. Its unidirectional flow and explicit segregation of read and write in its Hooks API informed Solid's API. More so than the objective of being just a "Render Library" rather than a framework. Solid has strong opinions on how to approach managing data in application development but doesn't seek to constrain its execution. + +However, as much as Solid aligns with React's design philosophy, it works fundamentally differently. React uses a Virtual DOM and Solid does not. React's abstraction is top down component partition where render methods are called repeatedly and diffed. Solid, instead, renders each Template once in its entirety, constructing its reactive graph and only then executes instructions related to fine-grained changes. + +#### Advice for migrating: + +Solid's update model is nothing like React, or even React + MobX. Instead of thinking of function components as the `render` function, think of them as a `constructor`. + +In Solid, props and stores are [proxy objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) that rely on property access for tracking and reactive updates. Watch out for destructuring or early property access, which can cause these properties to lose reactivity or trigger at the wrong time. + +Solid's primitives have no restrictions like the Hook Rules so you are free to nest them as you see fit. + +You don't need explicit keys on list rows to have "keyed" behavior. + +In React, `onChange` fires whenever an input field is modified, but this isn't how `onChange` [works natively](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onchange). In Solid, use `onInput` to subscribe to each value change. + +Finally, there is no VDOM so imperative VDOM APIs like `React.Children` and `React.cloneElement` have no equivalent in Solid. Instead of creating or modifying DOM elements directly, express your intentions declaratively. + +## Vue + +Solid is not particularly influenced by Vue design-wise, but they are comparable in approach. They both use Proxies in their Reactive system with read based auto-tracking. But that is where the similarities end. Vue's fine grained dependency detection just feeds into a less fine-grained Virtual DOM and Component system whereas Solid keeps its granularity right down to its direct DOM updates. + +Vue values easiness where Solid values transparency. Although Vue's new direction with Vue 3 aligns more with the approach Solid takes. These libraries might align more over time depending on how they continue to evolve. + +#### Advice for migrating: + +As another modern reactive library migration from Vue 3 should feel familiar. Solid's components are very much like tagging the template on the end of Vue's `setup` function. Be wary of overwrapping state derivations with computations, try a function. Reactivity is pervasive. Solid's proxies are intentionally read-only. Don't knock it before you try it. + +## Svelte + +Svelte pioneered the precompiled disappearing framework that Solid also employs to a certain degree. Both libraries are truly reactive and can produce really small execution code bundles although Svelte is the winner here for small demos. Solid requires a bit more explicitness in its declarations, relying less on implicit analysis from the compiler, but that is part of what gives Solid superior performance. Solid also keeps more in the runtime which scales better in larger apps. Solid's RealWorld demo implementation is 25% smaller than Svelte's. + +Both libraries aim to help their developers write less code but approach it completely differently. Svelte 3 focuses on the optimization of the ease of dealing with localized change focusing on plain object interaction and two-way binding. In constrast Solid focuses on the data flow by deliberately embracing CQRS and immutable interface. With functional template composition, in many cases, Solid allows developers to write even less code than Svelte although Svelte's template syntax is definitely terser. + +#### Advice for migrating: + +Developer experience is different enough that while some things are analogous it is a very different experience. Components in Solid are cheap, so don't shy away from having more of them. + +## Knockout.js + +This library owes its existence to Knockout. Modernizing its model for fine grained dependency detection was the motivation for this project. Knockout was released in 2010 and supports Microsoft Explorer back to IE6 while much of Solid doesn't support IE at all. + +Knockout's bindings are just strings in HTML which are walked over at runtime. They depend on cloning context ($parent etc...). Whereas Solid uses JSX or Tagged Template Literals for templating opting for an in JavaScript API. + +The biggest difference might be that Solid's approach to batching changes which ensures synchronicity whereas Knockout has deferUpdates which uses a deferred microtask queue. + +#### Advice for migrating: + +If you are used to Knockout, Solid's primitives might look strange to you. The read/write separation is intentional and not just to make life harder. Look to adopting a state/action (Flux) mental model. While the libraries look similar they promote different best practices. + +## Lit & LighterHTML + +These libraries are incredibly similar and have had some influence on Solid. Mostly that Solid's compiled code uses a very similar method to performantly initially render the DOM. Cloning Template elements and using comment placeholders are something that Solid and these libraries share in common. + +The biggest difference is that while these libraries do not use the Virtual DOM they treat rendering the same way, top down, requiring component partitioning to keep things sane. By contrast, Solid uses its fine grained Reactive Graph to only update what has changed and in doing so only shares this technique for its initial render. This approach takes advantage from the initial speed only available to native DOM and also have the most performant approach to updates. + +#### Advice for migrating: + +These libraries are pretty minimal and easy to build on top. However, keep in mind that `` isn't just HTMLElement (array or function). Try to keep your things in the JSX template. Hoisting works for the most part but it is best to mentally think of this still as a render library and not a HTMLElement factory. + +## S.js + +This library had the greatest influence on Solid's reactive design. Solid used S.js internally for a couple of years until the feature set placed them on diffenent paths. S.js is one of the most efficient reactive libraries to date. It models everything off synchronous time steps like a digital circuit and ensures consistency without having to do many of the more complicated mechanisms found in libraries like MobX. Solid's reactivity in the end is a sort of hybrid between S and MobX. This gives it greater performance than most reactive libraries (Knockout, MobX, Vue) while retaining the ease of mental model for the developer. S.js ultimately is still the more performant reactive library although the difference is hardly noticeable in all but the most grueling synthetic benchmarks. + +## RxJS + +RxJS is a Reactive library. While Solid has a similar idea of Observable data it uses a much different application of the observer pattern. While Signals are like a simple version of an Observable (only the next), the pattern of auto dependency detection supplants RxJS' hundred or so operators. Solid could have taken this approach, and indeed earlier, versions of the library included similar operators, but in most cases it is more straightforward to write your own transformation logic in a computation. Where Observables are cold starting, unicast and push-based, many problems on the client lend themselves to hot startup and being multicast which is Solid's default behavior. + +## Others + +Angular and a few other popular libraries are notably missing from this comparison. Lack of experience with them prevents making any adequate comparisons. Generally, Solid has little in common with larger Frameworks and it is much harder to compare them head on. diff --git a/langs/tl/guides/faq.md b/langs/tl/guides/faq.md new file mode 100644 index 00000000..d3c58c70 --- /dev/null +++ b/langs/tl/guides/faq.md @@ -0,0 +1,84 @@ +--- +title: FAQ +description: Frequently asked questions from the community. +sort: 5 +--- + +# FAQ + +### JSX without a virtual DOM? Is this vaporware? I've heard prominent voices say that this isn't possible. + +It is possible when you don't have React's update model. JSX is a template language those in Svelte or Vue—just one that is more flexible in certain ways. Inserting arbitrary JavaScript can be challenging at times, but no different than supporting spread operators. So, no: this isn't vaporware, but an approach proven to be one of the most performant. + +The real benefit comes in how extensible it is. We have a compiler working for you to give you optimal native DOM updates, but you have all the freedom of a library like React. You can write components using standard techniques like [render props](https://reactjs.org/docs/render-props.html) and higher order components along side your reactive "hooks". Don't like how Solid's control flow works? Write your own. + +### How is Solid so performant? + +We wish we could point to a single thing, but it really is the combination of several important design decisions: + +1. Explicit reactivity, so only the things that should be reactive are tracked. +2. Compilation with initial creation in mind. Solid uses heuristics and combines the right expressions to reduce the number of computations, but keep key updates granular and performant. +3. Reactive expressions are just functions. This enables "vanishing components" with lazy prop evaluation removing unnecessary wrappers and synchronization overhead. + +These are currently unique techniques in a combination that gives Solid an edge over the competition. + +### Is there React-Compat, or some way to use my React libraries in Solid? + +No. And there likely never will be. While the APIs are similar and components often can be moved across with minor edits, the update model is fundamentally different. React Ccmponents render over and over so code outside of Hooks works very differently. The closures and hook rules are not only unnecessary in Solid: they can prescribe code that does not work here. + +Vue-compat on the other hand, that'd be doable; although there are no plans to implement it currently. + +### Why shouldn't I use `map` in my template, and what's the difference between `` and ``? + +If your array is static, there's nothing wrong with using map. But if you're looping over a signal or reactive property, `map` is inefficient: if the array changes for any reason, _the entire map_ will rerun and all of the nodes will be recreated. + +`` and `` both provide a loop solution that is smarter than this. They couple each rendered node with an element in the array, so when an array element changes, only the corresponding node will rerender. + +`` will do this _by index_: each node corresponds to an index in the array; `` will do this _by value_: each node corresponds to a piece of data in the array. This is why, in the callback, `` gives you a signal for the item: the index for each item is considered fixed, but the data at that index can change. On the other hand, `` gives you a signal for the index: the content of the item is considered fixed, but it can move around if elements get moved in the array. + +For example, if two elements in the array are swapped, `` will reposition the two corresponding DOM nodes and update their `index()` signals along the way. `` won't reposition any DOM nodes, but will update the `item()` signals for the two DOM nodes and cause them to rerender. + +For an in-depth demonstration of the difference, see [this segment](https://www.youtube.com/watch?v=YxroH_MXuhw&t=2164s) of Ryan's stream. +### Why doesn't destructuring work with props or stores? + +With props and store objects, reactivity is tracked on property access: when you call `props.whatever` within a reactive context, it tells Solid to keep track of that context and update it when the prop changes. By destructuring, you separate the value from the object, giving you the value at that point in time and losing reactivity. + +### Why isn't my `onChange` event handler firing on time? + +In some frameworks, the `onChange` event for inputs is modified so that it fires on every key press. But this isn't how the `onChange` event [works natively](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onchange): it is meant to reflect a _commited_ change to the input and will usually fire when the input loses focus. To handle all changes to the value of an input, use `onInput`. + +### Can you add support for class components? I find the lifecycles are easier to reason about. + +We don't intend to support class components. The lifecycles of components in Solid are tied to the scheduling of the reactive system and are artificial. You could make a class out of it, but effectively all of the non-event handler code would be run in the constructor, including the render function. It's just more syntax for an excuse to make your data less granular. + +Group data and its behaviors together, rather than lifecycles. This is a reactive best practice that has worked for decades. + +### I really dislike JSX, any chance of a different template language? Oh, I see you have Tagged Template Literals/HyperScript. Maybe I will use those... + +Don't. Stop you right there. We use JSX the way Svelte uses their templates, to create optimized DOM instructions. The Tagged Template Literal and HyperScript solutions may be really impressive in their own right, but unless you have a real reason like a no-build requirement they are inferior in every way. Larger bundles, slower performance, and the need for manual workaround wrapping values. + +It's good to have options, but Solid's JSX is really the best solution here. A Template DSL would be great as well, albeit more restrictive, but JSX gives us so much for free. TypeScript, Existing Parsers, Syntax Highlighting, TypeScript, Prettier, Code Completion, and last and not least TypeScript. + +Other libraries have been adding support for these features but that has been an enormous effort and is still imperfect and a constant maintenance headache. This is really taking a pragmatic stance. + +### When do I use a Signal vs Store? Why are these different? + +Stores automatically wrap nested values making it ideal for deep data structures, and for things like models. For most other things Signals are lightweight and do the job wonderfully. + +As much we would love to wrap these together as a single thing, you can't proxy primitives. Functions are the simplest interface and any reactive expression (including state access) can be wrapped in one on transport so this provides a universal API. You can name your signals and state as you choose and it stays minimal. Last thing we'd want to do is force typing `.get()` `.set()` on the end-user or even worse `.value`. At least the former can be aliased for brevity, whereas the latter is just the least terse way to call a function. + +### Why can I not just assign a value to Solid's Store as I can in Vue. Svelte, or MobX? Where is the 2-way binding? + +Reactivity is a powerful tool but also a dangerous one. MobX knows this and introduced Strict mode and Actions to limit where/when updates occur. In Solid, which deals with whole Component trees of data, it became apparent that we can learn something from React here. You don't need to be actually immutable as long as you provide the means to have the same contract. + +Being able to pass the ability to update state is arguably even more important than deciding to pass the state. So being able to separate it is important, and only possible if reading is immutable. We also don't need to pay the cost of immutability if we can still granularly update. Luckily there are tons of prior art here between ImmutableJS and Immer. Ironically Solid acts mostly as a reverse Immer with its mutable internals and immutable interface. + +### Can I use Solid's reactivity on its own? + +Of course. While we haven't exported a standalone package it is easy to install Solid without the compiler and just use the reactive primitives. One of the benefits of granular reactivity is it is library agnostic. For that matter, almost every reactive library works this way. That is what inspired [Solid](https://github.com/solidjs/solid) and its underlying [DOM Expressions library](https://github.com/ryansolid/dom-expressions) in the first place to make a renderer purely from the reactive system. + +To list a few to try: [Solid](https://github.com/solidjs/solid), [MobX](https://github.com/mobxjs/mobx), [Knockout](https://github.com/knockout/knockout), [Svelte](https://github.com/sveltejs/svelte), [S.js](https://github.com/adamhaile/S), [CellX](https://github.com/Riim/cellx), [Derivable](https://github.com/ds300/derivablejs), [Sinuous](https://github.com/luwes/sinuous), and even recently [Vue](https://github.com/vuejs/vue). Much more goes into making a reactive library than tagging it onto a renderer like, [lit-html](https://github.com/Polymer/lit-html) for example, but it's a good way to get a feel. + +### Does Solid have a Next.js or Material Components like library I can use? + + They're in the works! If you are interested in building one, we are readily available on our [Discord](https://discord.com/invite/solidjs), where you can join existing ecosystem efforts or start your own. diff --git a/langs/tl/guides/getting-started.md b/langs/tl/guides/getting-started.md new file mode 100644 index 00000000..7c1f6be8 --- /dev/null +++ b/langs/tl/guides/getting-started.md @@ -0,0 +1,119 @@ +--- +title: Getting Started +description: A guide for how to get started with Solid. +sort: 0 +--- +# Getting Started +## Try Solid + +By far the easiest way to get started with Solid is to try it online. Our REPL at https://playground.solidjs.com is the perfect way to try out ideas. As is https://codesandbox.io/ where you can modify any of [our Examples](https://github.com/solidjs/solid/blob/main/documentation/resources/examples.md). + +Alternatively, you can use our simple [Vite](https://vitejs.dev/) templates by runnings these commands in your terminal: + +```sh +> npx degit solidjs/templates/js my-app +> cd my-app +> npm i # or yarn or pnpm +> npm run dev # or yarn or pnpm +``` + +Or for TypeScript: + +```sh +> npx degit solidjs/templates/ts my-app +> cd my-app +> npm i # or yarn or pnpm +> npm run dev # or yarn or pnpm +``` + +## Learn Solid + +Solid is all about small composable pieces that serve as building blocks for applications. These pieces are mostly functions which make up many shallow top-level APIs. Fortunately, you won't need to know about most of them to get started. + +The two main types of building blocks you have at your disposal are Components and Reactive Primitives. + +Components are functions that accept a props object and return JSX elements including native DOM elements and other components. They can be expressed as JSX Elements in PascalCase: + +```jsx +function MyComponent(props) { + return
    Hello {props.name}
    ; +} + +; +``` + +Components are lightweight in that they are not stateful themselves and have no instances. Instead, they serve as factory functions for DOM elements and reactive primitives. + +Solid's fine-grained reactivity is built on 3 simple primitives: Signals, Memos, and Effects. Together, they form an auto-tracking synchronization engine that ensures your view stays up to date. Reactive computations take the form of simple function-wrapped expressions that execute synchronously. + +```js +const [first, setFirst] = createSignal("JSON"); +const [last, setLast] = createSignal("Bourne"); + +createEffect(() => console.log(`${first()} ${last()}`)); +``` + +You can learn more about [Solid's Reactivity](#reactivity) and [Solid's Rendering](#rendering). + +## Think Solid + +Solid's design carries several opinions on what principles and values help us best build websites and applications. It is easier to learn and use Solid when you are aware of the philosophy behind it. + +### 1. Declarative Data + +Declarative data is the practice of tying the description of data’s behavior to its declaration. This allows for easy composition by packaging all aspects of data’s behavior in a single place. + +### 2. Vanishing Components + +It's hard enough to structure your components without taking updates into consideration. Solid updates are completely independent of the components. Component functions are called once and then cease to exist. Components exist to organize your code and not much else. + +### 3. Read/Write segregation + +Precise control and predictability make for better systems. We don't need true immutability to enforce unidirectional flow, just the ability to make the conscious decision which consumers may write and which may not. + +### 4. Simple is better than easy + +A lesson that comes hard for fine-grained reactivity. Explicit and consistent conventions even if they require more effort are worth it. The aim is to provide minimal tools to serve as the basis to build upon. + +## Web Components + +Solid was born with the desire to have Web Components as first class citizens. Over time its design has evolved and goals have changed. However, Solid is still a great way to author Web Components. [Solid Element](https://github.com/solidjs/solid/tree/main/packages/solid-element) allows you to write and wrap Solid's function components to produce small and performant Web Components. Inside Solid apps Solid Element is able to still leverage Solid's Context API, and Solid's Portals support Shadow DOM isolated styling. + +## Server Rendering + +Solid has a dynamic server side rendering solution that enables a truly isomorphic development experience. Through the use of our Resource primitive, async data requests are easily made and, more importantly, automatically serialized and synchronized between client and browser. + +Since Solid supports asynchronous and stream rendering on the server, you get to write your code one way and have it execute on the server. This means that features like [render-as-you-fetch](https://reactjs.org/docs/concurrent-mode-suspense.html#approach-3-render-as-you-fetch-using-suspense) and code splitting just work in Solid. + +For more information, read the [Server guide](#server-side-rendering). + +## No Compilation? + +Dislike JSX? Don't mind doing manual work to wrap expressions, worse performance, and having larger bundle sizes? Alternatively, you can create a Solid app using Tagged Template Literals or HyperScript in non-compiled environments. + +You can run them straight from the browser using [Skypack](https://www.skypack.dev/): + +```html + + + + + +``` + +Remember you still need the corresponding DOM Expressions library for these to work with TypeScript. You can use Tagged Template Literals with [Lit DOM Expressions](https://github.com/ryansolid/dom-expressions/tree/main/packages/lit-dom-expressions) or HyperScript with [Hyper DOM Expressions](https://github.com/ryansolid/dom-expressions/tree/main/packages/hyper-dom-expressions). diff --git a/langs/tl/guides/reactivity.md b/langs/tl/guides/reactivity.md new file mode 100644 index 00000000..5d526da3 --- /dev/null +++ b/langs/tl/guides/reactivity.md @@ -0,0 +1,101 @@ +--- +title: Reactivity +description: Full rundown of Solid's reactivity. +sort: 1 +--- + +# Reactivity + +Solid's data management is built off a set of flexible reactive primitives which are responsible for all the updates. It takes a very similar approach to MobX or Vue except it never trades its granularity for a VDOM. Dependencies are automatically tracked when you access your reactive values in your Effects and JSX View code. + +Solid's primitives come in the form of `create` calls that often return tuples, where generally the first element is a readable primitive and the second is a setter. It is common to refer to only the readable part by the primitive name. + +Here is a basic auto incrementing counter that is updating based on setting the `count` signal. + +```jsx +import { createSignal, onCleanup } from "solid-js"; +import { render } from "solid-js/web"; + +const App = () => { + const [count, setCount] = createSignal(0), + timer = setInterval(() => setCount(count() + 1), 1000); + onCleanup(() => clearInterval(timer)); + + return
    {count()}
    ; +}; + +render(() => , document.getElementById("app")); +``` + +## Introducing Primitives + +Solid is made up of 3 primary primitives, Signal, Memo, and Effect. At their core is the Observer pattern where Signals (and Memos) are tracked by wrapping Memos and Effects. + +Signals are the simplest primitive. They contain value, and get and set functions so we can intercept when they are read and written to. + +```js +const [count, setCount] = createSignal(0); +``` + +Effects are functions that wrap reads of our signal and re-execute whenever a dependent Signal's value changes. This is useful for creating side effects, like rendering. + +```js +createEffect(() => console.log("The latest count is", count())); +``` + +Finally, Memos are cached derived values. They share the properties of both Signals and Effects. They track their own dependent Signals, re-executing only when those change, and are trackable Signals themselves. + +```js +const fullName = createMemo(() => `${firstName()} ${lastName()}`); +``` + +## How it Works + +Signals are event emitters that hold a list of subscriptions. They notify their subscribers whenever their value changes. + +Where things get more interesting is how these subscriptions happen. Solid uses automatic dependency tracking. Updates happen automatically as the data changes. + +The trick is a global stack at runtime. Before an Effect or Memo executes (or re-executes) its developer-provided function, it pushes itself on to that stack. Then any Signal that is read checks if there is a current listener on the stack and if so adds the listener to its subscriptions. + +You can think of it like this: + +```js +function createSignal(value) { + const subscribers = new Set(); + + const read = () => { + const listener = getCurrentListener(); + if (listener) subscribers.add(listener); + return value; + }; + + const write = (nextValue) => { + value = nextValue; + for (const sub of subscribers) sub.run(); + }; + + return [read, write]; +} +``` + +Now whenever we update the Signal we know which Effects to re-run. Simple yet effective. The actual implementation is much more complicated but that is the guts of what is going on. + +For more detailed understanding of how Reactivity works these are useful articles: + +[A Hands-on Introduction to Fine-Grained Reactivity](https://dev.to/ryansolid/a-hands-on-introduction-to-fine-grained-reactivity-3ndf) + +[Building a Reactive Library from Scratch](https://dev.to/ryansolid/building-a-reactive-library-from-scratch-1i0p) + +[SolidJS: Reactivity to Rendering](https://indepth.dev/posts/1289/solidjs-reactivity-to-rendering) + +## Considerations + +This approach to reactivity is very powerful and dynamic. It can handle dependencies changing on the fly through executing different branches of conditional code. It also works through many levels of indirection. Any function executed inside a tracking scope is also being tracked. + +However, there are some key behaviors and tradeoffs we must be aware of. + +1. All reactivity is tracked from function calls whether directly or hidden beneath getter/proxy and triggered by property access. This means where you access properties on reactive objects is important. + +2. Components and callbacks from control flows are not tracking scopes and only execute once. This means destructuring or doing logic top-level in your components will not re-execute. You must access these Signals, Stores, and props from within other reactive primitives or the JSX for that part of the code to re-evaluate. + +3. This approach only tracks synchronously. If you have a setTimeout or use an async function in your Effect the code that executes async after the fact won't be tracked. diff --git a/langs/tl/guides/rendering.md b/langs/tl/guides/rendering.md new file mode 100644 index 00000000..b35921c5 --- /dev/null +++ b/langs/tl/guides/rendering.md @@ -0,0 +1,288 @@ +--- +title: Rendering +description: Discusses the different templating and rendering options in Solid. +sort: 2 +--- + +# Rendering + +Solid supports templating in 3 forms JSX, Tagged Template Literals and Solid's HyperScript variant, although JSX is the predominate form. Why? JSX is a great DSL made for compilation. It has clear syntax, supports TypeScript, works with Babel and supports other tooling like Code Syntax Highlighting and Prettier. It was only pragmatic to use a tool that basically gives you that all for free. As a compiled solution it provides great DX. Why struggle with custom Syntax DSLs when you can use one so widely supported? + +## JSX Compilation + +Rendering involves precompilation of JSX templates into optimized native js code. The JSX code constructs: + +- Template DOM elements which are cloned on each instantiation +- A series of reference declarations using only firstChild and nextSibling +- Fine grained computations to update the created elements. + +This approach is both more performant and produces less code than creating each element, one by one, with document.createElement. + +## Attributes and Props + +Solid attempts to reflect HTML conventions as much as possible including case insensitivity of attributes. + +The majority of all attributes on native element JSX are set as DOM attributes. Static values are built right into the template that is cloned. There are a number of exceptions like `class`, `style`, `value`, `innerHTML` which provide extra functionality. + +However, custom elements (with exception of native built-ins) default to properties when dynamic. This is to handle more complex data types. It does this conversion by camel casing standard snake case attribute names `some-attr` to `someAttr`. + +However, it is possible to control this behavior directly with namespace directives. You can force an attribute with `attr:` or force prop `prop:` + +```jsx + +``` + +> **Note:** Static attributes are created as part of the html template that is cloned. Expressions fixed and dynamic are applied afterwards in JSX binding order. While this is fine for most DOM elements there are some, like input elements with `type='range'`, where order matters. Keep this in mind when binding elements. + +## Entry + +The easiest way to mount Solid is to import render from 'solid-js/web'. `render` takes a function as the first argument and the mounting container for the second and returns a disposal method. This `render` automatically creates the reactive root and handles rendering into the mount container. For best performance use an element with no children. + +```jsx +import { render } from "solid-js/web"; + +render(() => , document.getElementById("main")); +``` + +> **Important** The first argument needs to be a function. Otherwise we can't properly track and schedule the reactive system. This simple ommission will cause your Effects not to run. + +## Components + +Components in Solid are just Pascal (Capital) cased functions. Their first argument is a props object and they return real DOM nodes. + +```jsx +const Parent = () => ( +
    + +
    +); + +const Label = (props) => ( + <> +
    {props.greeting}
    + {props.children} + +); +``` + +Since all JSX nodes are actual DOM nodes, the only responsibility of top level Components is to append them to the DOM. + +## Props + +Much like React, Vue, Angular and other frameworks, Solid allows you to define properties on your components to pass data to child components. Here a parent is passing the string "Hello" to the `Label` component via a `greeting` property. + +```jsx +const Parent = () => ( +
    + +
    +); +``` + +In the above example, the value set on `greeting` is static, but we can also set dynamic values. For example: + +```jsx +const Parent = () => { + const [greeting, setGreeting] = createSignal("Hello"); + + return ( +
    + +
    + ); +}; +``` + +Components can access properties passed to them via a `props` argument. + +```jsx +const Label = (props) => ( + <> +
    {props.greeting}
    + {props.children} + +); +``` + +Unlike in some other frameworks, you cannot use object destructuring on the `props` of a component. This is because the `props` object, behind the scenes, relies on Object getters to lazily retrieve values. Using object destructuring breaks the reactivity of `props`. + +This example shows the "correct" way of accessing props in Solid: + +```jsx +// Here, `props.name` will update like you'd expect +const MyComponent = (props) =>
    {props.name}
    ; +``` + +This example shows the wrong way of accessing props in Solid: + +```jsx +// This is bad +// Here, `props.name` will not update (i.e. is not reactive) as it is destructured into `name` +const MyComponent = ({ name }) =>
    {name}
    ; +``` + +While the props object looks like a normal object when you use it (and Typescript users will note that it is typed like a normal object), in reality it is reactive – somewhat similar to a Signal. This has a few implications. + +Because unlike most JSX frameworks, Solid's function components are only executed once (rather than every render cycle), the following example will not work as expected. + +```jsx +import { createSignal } from "solid-js"; + +const BasicComponent = (props) => { + const value = props.value || "default"; + + return
    {value}
    ; +}; + +export default function Form() { + const [value, setValue] = createSignal(""); + + return ( +
    + + setValue(e.currentTarget.value)} /> +
    + ); +} +``` + +In this example, what we probably want to happen is for the `BasicComponent` to display the current value typed into the `input`. But, as a reminder, the `BasicComponent` function will only be executed once when the component is initially created. At this time (at creation), `props.value` will equal `''`. This means that `const value` in `BasicComponent` will resolve to `'default'` and never update. While the `props` object is reactive, accessing the props in `const value = props.value || 'default';` is outside the observable scope of Solid, so it isn't automatically re-evaluated when props change. + +So how can we fix our problem? + +Well, in general, we need to access `props` somewhere that Solid can observe it. Generally this means inside JSX or inside a `createMemo`, `createEffect`, or thunk(`() => ...`). Here is one solution that works as expected: + +```jsx +const BasicComponent = (props) => { + return
    {props.value || "default"}
    ; +}; +``` + +This, equivalently, can be hoisted into a function: + +```jsx +const BasicComponent = (props) => { + const value = () => props.value || "default"; + + return
    {value()}
    ; +}; +``` + +Another option, if it is an expensive computation, is to use `createMemo`. For example: + +```jsx +const BasicComponent = (props) => { + const value = createMemo(() => props.value || "default"); + + return
    {value()}
    ; +}; +``` + +Or using a helper + +```jsx +const BasicComponent = (props) => { + props = mergeProps({ value: "default" }, props); + + return
    {props.value}
    ; +}; +``` + +As a reminder, the following examples will _not_ work: + +```jsx +// bad +const BasicComponent = (props) => { + const { value: valueProp } = props; + const value = createMemo(() => valueProp || "default"); + return
    {value()}
    ; +}; + +// bad +const BasicComponent = (props) => { + const valueProp = prop.value; + const value = createMemo(() => valueProp || "default"); + return
    {value()}
    ; +}; +``` + +Solid's Components are the key part of its performance. Solid's approach of "Vanishing" Components is made possible by lazy prop evaluation. Instead of evaluating prop expressions immediately and passing in values, execution is deferred until the prop is accessed in the child. Doing so we postpone execution until the last moment, typically right in the DOM bindings, maximizing performance. This flattens the hierarchy and removes the need to maintain a tree of Components. + +```jsx +; + +// compiles roughly to: + +// we untrack the component body to isolate it and prevent costly updates +untrack(() => + Component({ + prop1: "static", + // dynamic expression so we wrap in a getter + get prop2() { + return state.dynamic; + }, + }) +); +``` + +To help maintain reactivity Solid has a couple of prop helpers: + +```jsx +// default props +props = mergeProps({ name: "Smith" }, props); + +// clone props +const newProps = mergeProps(props); + +// merge props +props = mergeProps(props, otherProps); + +// split props into multiple props objects +const [local, others] = splitProps(props, ["className"]) +
    +``` + +## Children + +Solid handles JSX Children similar to React. A single child is a single value on `props.children` and multiple children is handled via an array of values. Normally, you pass them through to the JSX view. However, if you want to interact with them the suggested method is the `children` helper which resolves any downstream control flows and returns a memo. + +```jsx +// single child +const Label = (props) =>
    Hi, { props.children }
    + + + +// multi child +const List = (props) =>
    {props.children}
    ; + + +
    First
    + {state.expression} + +
    + +// map children +const List = (props) =>
      + {item =>
    • {item}
    • }
      +
    ; + +// modify and map children using helper +const List = (props) => { + // children helper memoizes value and resolves all intermediate reactivity + const memo = children(() => props.children); + createEffect(() => { + const children = memo(); + children.forEach((c) => c.classList.add("list-child")) + }) + return
      + {item =>
    • {item}
    • }
      +
    ; +``` + +**Important:** Solid treats child tags as expensive expressions and wraps them the same way as dynamic reactive expressions. This means they evaluate lazily on `prop` access. Be careful accessing them multiple times or destructuring before the place you would use them in the view. This is because Solid doesn't have the luxury of creating Virtual DOM nodes ahead of time and then diffing them, so resolution of these `props` must be lazy and deliberate. Use `children` helper if you wish to do this as it memoizes them. diff --git a/langs/tl/guides/server.md b/langs/tl/guides/server.md new file mode 100644 index 00000000..5ac87c1c --- /dev/null +++ b/langs/tl/guides/server.md @@ -0,0 +1,141 @@ +--- +title: Server +description: An explanation of Solid's server-side capabilities. +sort: 3 +--- + +# Server Side Rendering + +Solid handles Server rendering by compiling JSX templates to ultra efficient string appending code. This can be achieved through the babel plugin or preset by passing in `generate: "ssr"`. For both client and server you need to pass in `hydratable: true` to generate the hydration compatible code. + +The `solid-js` and `solid-js/web` runtimes are swapped for non-reactive counterparts when running in a node environment. For other environments you will need to bundle the server code with conditional exports set to `node`. Most bundlers have a way of doing this. In general we also recommend using the `solid` export conditions as well as it is recommend that libraries ship their source under the `solid` export. + +Building for SSR definitely takes a bit more configuration because we will be generating 2 separate bundles. The client entry should use `hydrate`: + +```jsx +import { hydrate } from "solid-js/web"; + +hydrate(() => , document); +``` + +_Note: It is possible to render and hydrate from the Document root. This allows us to describe our full view in JSX._ + +The server entry can use one of the four rendering options offered by Solid. Each produces the output and a script tag to be inserted in the head of the document. + +```jsx +import { + renderToString, + renderToStringAsync, + renderToNodeStream, + renderToWebStream, +} from "solid-js/web"; + +// Synchronous string rendering +const html = renderToString(() => ); + +// Asynchronous string rendering +const html = await renderToStringAsync(() => ); + +// Node Stream API +pipeToNodeWritable(App, res); + +// Web Stream API (for like Cloudflare Workers) +const { readable, writable } = new TransformStream(); +pipeToWritable(() => , writable); +``` + +For your convenience `solid-js/web` exports an `isServer` flag. This is useful as most bundlers will be able to treeshake anything under this flag or imports only used by code under this flag out of your client bundle. + +```jsx +import { isServer } from "solid-js/web"; + +if (isServer) { + // only do this on the server +} else { + // only do this in the browser +} +``` + +## Hydration Script + +In order to progressively hydrate even before Solid's runtime loads, a special script needs to be inserted on the page. It can either be generated and inserted via `generateHydrationScript` or included as part of the JSX using the `` tag. + +```js +import { generateHydrationScript } from "solid-js/web"; + +const app = renderToString(() => ); + +const html = ` + + + 🔥 Solid SSR 🔥 + + + + ${generateHydrationScript()} + + ${app} + +`; +``` + +```jsx +import { HydrationScript } from "solid-js/web"; + +const App = () => { + return ( + + + 🔥 Solid SSR 🔥 + + + + + + {/*... rest of App*/} + + ); +}; +``` + +When hydrating from the document inserting assets that aren't available in the client run also can mess things up. Solid provides a `` component whose children will work as normal on the server, but not hydrate in the browser. + +```jsx + + {manifest.map((m) => ( + + ))} + +``` + +## Async and Streaming SSR + +These mechanisms are built on Solid's knowledge of how your application works. It does so by using Suspense and the Resource API on the server, instead of fetching ahead and then rendering. Solid fetches as it renders on the server just like it does on the client. Your code and execution patterns is written exactly the same way. + +Async rendering waits until all Suspense boundaries resolve and then sends the results (or writes them to a file in the case of Static Site Generation). + +Streaming starts flushing synchronous content to the browser immediately rendering your Suspense Fallbacks on the server. Then as the async data finishes on the server it sends the data over the same stream to the client to resolve Suspense where the browser finishes the job and replaces the fallback with real content. + +The advantage of this approach: + +- Server doesn't have to wait for Async data to respond. Assets can start loading sooner in the browser, and the user can start seeing content sooner. +- Compared to client fetching like JAMStack, data loading starts on the server immediately and doesn't have to wait for client JavaScript to load. +- All data is serialized and transported from server to client automatically. + +## SSR Caveats + +Solid's Isomorphic SSR solution is very powerful in that you can write your code mostly as single code base that runs similarly in both environments. However there are expectations that this puts on hydration. Mostly that the rendered view in the client is the same as it would be rendered on the server. It doesn't need to be exact in terms of text, but structurally the markup should be the same. + +We use markers rendered in the server to match elements and resource locations on server. For this reason the Client and Server should have the same components. This is not typically a problem given that Solid renders the same way on client and server. But currently there is no means to render something on the server that does not get hydrated on the client. Currently, there is no way to partially hydrate a whole page, and not generate hydration markers for it. It is all or nothing. Partial Hydration is something we want to explore in the future. + +Finally, all resources need to be defined under the `render` tree. They are automatically serialized and picked up in the browser, but that works because the `render` or `pipeTo` methods keep track of the progress of the render. Something we cannot do if they are created in isolated context. Similarly there is no reactivity on the server so do not update signals on initial render and expect them to reflect higher up the tree. While we have Suspense boundaries Solid's SSR is basically top down. + +## Getting Started with SSR + +SSR configurations are tricky. We have a few examples in the [solid-ssr](https://github.com/solidjs/solid/blob/main/packages/solid-ssr) package. + +However, a new starter is in the works [SolidStart](https://github.com/solidjs/solid-start) that aims to make this experience much smoother. + +## Getting Started with Static Site Generation + +[solid-ssr](https://github.com/solidjs/solid/blob/main/packages/solid-ssr) also ships with a simple utility for generating static or prerendered sites. Read the README for more information. diff --git a/langs/tl/tutorials/async_lazy/lesson.json b/langs/tl/tutorials/async_lazy/lesson.json new file mode 100644 index 00000000..de56f031 --- /dev/null +++ b/langs/tl/tutorials/async_lazy/lesson.json @@ -0,0 +1,12 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { lazy } from \"solid-js\";\n\nimport Greeting from \"./greeting\"\n\nfunction App() {\n return (\n <>\n

    Welcome

    \n \n \n );\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + }, + { + "name": "greeting", + "content": "export default function Greeting(props) {\n return

    Hi, {props.name}

    \n}" + } + ] +} diff --git a/langs/tl/tutorials/async_lazy/lesson.md b/langs/tl/tutorials/async_lazy/lesson.md new file mode 100644 index 00000000..af9fdf45 --- /dev/null +++ b/langs/tl/tutorials/async_lazy/lesson.md @@ -0,0 +1,20 @@ +Most bundlers (like Webpack, Rollup, Parcel, Vite) automatically handle code splitting when you use a dynamic import. Solid's `lazy` method allows us to wrap the component's dynamic import for deferred lazy loading. The output is a Component that can be used as normal in our JSX template with the exception that internally it dynamically loads the underlying imported code when it is rendered the first time, halting that branch of rendering until the code is available. + +To use `lazy`, replace the import statement: +```js +import Greeting from "./greeting"; +``` +with: +```js +const Greeting = lazy(() => import("./greeting")); +``` + +This will likely still load too quickly to see. But you add a fake delay if you wish to make the loading more visible. + +```js +const Greeting = lazy(async () => { + // simulate delay + await new Promise(r => setTimeout(r, 1000)) + return import("./greeting") +}); +``` diff --git a/langs/tl/tutorials/async_lazy/solved.json b/langs/tl/tutorials/async_lazy/solved.json new file mode 100644 index 00000000..c61a2066 --- /dev/null +++ b/langs/tl/tutorials/async_lazy/solved.json @@ -0,0 +1,12 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { lazy } from \"solid-js\";\n\nconst Greeting = lazy(() => import(\"./greeting\"));\n\nfunction App() {\n return (\n <>\n

    Welcome

    \n \n \n );\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + }, + { + "name": "greeting", + "content": "export default function Greeting(props) {\n return

    Hi, {props.name}

    \n}" + } + ] +} diff --git a/langs/tl/tutorials/async_resources/lesson.json b/langs/tl/tutorials/async_resources/lesson.json new file mode 100644 index 00000000..445c1b28 --- /dev/null +++ b/langs/tl/tutorials/async_resources/lesson.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { createSignal, createResource } from \"solid-js\";\nimport { render } from \"solid-js/web\";\n\nconst fetchUser = async (id) =>\n (await fetch(`https://swapi.dev/api/people/${id}/`)).json();\n\nconst App = () => {\n const [userId, setUserId] = createSignal();\n const [user] = createSignal();\n\n return (\n <>\n setUserId(e.currentTarget.value)}\n />\n {user.loading && \"Loading...\"}\n
    \n
    {JSON.stringify(user(), null, 2)}
    \n
    \n \n );\n};\n\nrender(App, document.getElementById(\"app\"));\n" + } + ] +} diff --git a/langs/tl/tutorials/async_resources/lesson.md b/langs/tl/tutorials/async_resources/lesson.md new file mode 100644 index 00000000..fa6079ed --- /dev/null +++ b/langs/tl/tutorials/async_resources/lesson.md @@ -0,0 +1,19 @@ +Resources are special Signals designed specifically to handle Async loading. Their purpose is wrap async values in a way that makes them easy to interact with in Solid's distributed execution model. This is the opposite to `async`/`await` or generators which provide sequential execution models. The goal is for async to not block execution and not color our code. + +Resources can be driven by a source signal that provides the query to an async data fetcher function that returns a promise. The contents of the fetcher function can be anything. You can hit typical REST endpoints or GraphQL or anything that generates a promise. Resources are not opinionated on the means of loading the data, only that they are driven by promises. + +The resulting Resource Signal also contains reactive `loading` and `error` properties that make it easy to control our view based on the current status. + +So let's replace our user signal with a resource: +```js +const [user] = createResource(userId, fetchUser); +``` +It is driven by the `userId` Signal and calls our fetch method on change. Not much else to it. + +The second value that comes back from `createResource` contains a `mutate` method for directly updating the internal Signal and a `refetch` method to reload the current query even if the source hasn't changed. + +```js +const [user, { mutate, refetch }] = createResource(userId, fetchUser); +``` + +`lazy` uses `createResource` internally to manage its dynamic imports. diff --git a/langs/tl/tutorials/async_resources/solved.json b/langs/tl/tutorials/async_resources/solved.json new file mode 100644 index 00000000..e37ab753 --- /dev/null +++ b/langs/tl/tutorials/async_resources/solved.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { createSignal, createResource } from \"solid-js\";\nimport { render } from \"solid-js/web\";\n\nconst fetchUser = async (id) =>\n (await fetch(`https://swapi.dev/api/people/${id}/`)).json();\n\nconst App = () => {\n const [userId, setUserId] = createSignal();\n const [user] = createResource(userId, fetchUser);\n\n return (\n <>\n setUserId(e.currentTarget.value)}\n />\n {user.loading && \"Loading...\"}\n
    \n
    {JSON.stringify(user(), null, 2)}
    \n
    \n \n );\n};\n\nrender(App, document.getElementById(\"app\"));\n" + } + ] +} diff --git a/langs/tl/tutorials/async_suspense/lesson.json b/langs/tl/tutorials/async_suspense/lesson.json new file mode 100644 index 00000000..6779e6fb --- /dev/null +++ b/langs/tl/tutorials/async_suspense/lesson.json @@ -0,0 +1,12 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { lazy, Suspense } from \"solid-js\";\n\nconst Greeting = lazy(async () => {\n // simulate delay\n await new Promise(r => setTimeout(r, 1000))\n return import(\"./greeting\")\n});\n\nfunction App() {\n return (\n <>\n

    Welcome

    \n \n \n );\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + }, + { + "name": "greeting", + "content": "export default function Greeting(props) {\n return

    Hi, {props.name}

    \n}" + } + ] +} diff --git a/langs/tl/tutorials/async_suspense/lesson.md b/langs/tl/tutorials/async_suspense/lesson.md new file mode 100644 index 00000000..87159b1b --- /dev/null +++ b/langs/tl/tutorials/async_suspense/lesson.md @@ -0,0 +1,31 @@ +While `lazy` and `createResource` can be used on their own, Solid also provides a mechanism for coordinating the display of multiple async events. `Suspense` serves as a boundary that can show a fallback placeholder instead of the partially loaded content as these async events resolve. + +This can improve user experience by removing visual jank caused by too many intermediate and partial loading states. `Suspense` automatically detects any descendant async reads and acts accordingly. You can nest as many `Suspense` components as needed and only the nearest ancestor will transform to `fallback` when the `loading` state is detected. + +Let's add a `Suspense` component to our lazy loading example: + +```jsx +<> +

    Welcome

    + Loading...

    }> + +
    + +``` + +Now we have a loading placeholder. + +It's important to note that it's the read of an async derived value that triggers `Suspense`, not the async fetching itself. If a resource signal (including `lazy` components) is not read under the `Suspense` boundary, it will not suspend. + +`Suspense` in many ways is just a `Show` component that renders both branches. While `Suspense` is vital for asynchronous Server rendering, do not feel the need to jump immediately to using it for client-rendered code. Solid's fine-grained rendering has no additional cost for splitting things manually. + +```jsx +function Deferred(props) { + const [resume, setResume] = createSignal(false); + setTimeout(() => setResume(true), 0); + + return {props.children}; +} +``` + +All work in Solid is queued independently already. No need for things like Time Slicing. diff --git a/langs/tl/tutorials/async_suspense/solved.json b/langs/tl/tutorials/async_suspense/solved.json new file mode 100644 index 00000000..c43b56f0 --- /dev/null +++ b/langs/tl/tutorials/async_suspense/solved.json @@ -0,0 +1,12 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { lazy, Suspense } from \"solid-js\";\n\nconst Greeting = lazy(async () => {\n // simulate delay\n await new Promise(r => setTimeout(r, 1000))\n return import(\"./greeting\")\n});\n\nfunction App() {\n return (\n <>\n

    Welcome

    \n Loading...

    }>\n \n
    \n \n );\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + }, + { + "name": "greeting", + "content": "export default function Greeting(props) {\n return

    Hi, {props.name}

    \n}" + } + ] +} diff --git a/langs/tl/tutorials/async_suspense_list/lesson.json b/langs/tl/tutorials/async_suspense_list/lesson.json new file mode 100644 index 00000000..a627b09a --- /dev/null +++ b/langs/tl/tutorials/async_suspense_list/lesson.json @@ -0,0 +1,16 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { Suspense } from \"solid-js\";\n\nimport fetchProfileData from \"./mock-api\";\nimport ProfilePage from \"./profile\";\n\nconst App = () => {\n const { user, posts, trivia } = fetchProfileData();\n return (\n Loading...}>\n \n \n );\n};\n\nrender(App, document.getElementById(\"app\"));\n" + }, + { + "name": "profile", + "content": "import { For, Suspense, SuspenseList } from \"solid-js\";\n\nconst ProfileDetails = (props) =>

    {props.user?.name}

    ;\n\nconst ProfileTimeline = (props) => (\n
      \n {(post) =>
    • {post.text}
    • }
      \n
    \n);\n\nconst ProfileTrivia = (props) => (\n <>\n

    Fun Facts

    \n
      \n {(fact) =>
    • {fact.text}
    • }
      \n
    \n \n);\n\nconst ProfilePage = (props) => (\n <>\n \n Loading posts...}>\n \n \n Loading fun facts...}>\n \n \n \n);\n\nexport default ProfilePage;" + }, + { + "name": "mock-api", + "content": "import { createResource } from \"solid-js\";\n\nexport default function fetchProfileData() {\n const [user] = createResource(fetchUser);\n const [posts] = createResource(fetchPosts);\n const [trivia] = createResource(fetchTrivia);\n return { user, posts, trivia };\n}\n\nfunction fetchUser() {\n console.log(\"fetch user...\");\n return new Promise((resolve) => {\n setTimeout(() => {\n console.log(\"fetched user\");\n resolve({\n name: \"Ringo Starr\"\n });\n }, 500);\n });\n}\n\nlet ringoPosts = [\n {\n id: 0,\n text: \"I get by with a little help from my friends\"\n },\n {\n id: 1,\n text: \"I'd like to be under the sea in an octupus's garden\"\n },\n {\n id: 2,\n text: \"You got that sand all over your feet\"\n }\n];\n\nfunction fetchPosts() {\n let ringoPostsAtTheTime = ringoPosts;\n console.log(\"fetch posts...\");\n return new Promise((resolve) => {\n setTimeout(() => {\n console.log(\"fetched posts\");\n resolve(ringoPostsAtTheTime);\n }, 3000 * Math.random());\n });\n}\n\nfunction fetchTrivia() {\n console.log(\"fetch trivia...\");\n return new Promise((resolve) => {\n setTimeout(() => {\n console.log(\"fetched trivia\");\n resolve([\n {\n id: 1,\n text:\n 'The nickname \"Ringo\" came from his habit of wearing numerous rings.'\n },\n {\n id: 2,\n text: \"Plays the drums left-handed with a right-handed drum set.\"\n },\n {\n id: 3,\n text: \"Nominated for one Daytime Emmy Award, but did not win\"\n }\n ]);\n }, 3000 * Math.random());\n });\n}\n" + } + ] +} diff --git a/langs/tl/tutorials/async_suspense_list/lesson.md b/langs/tl/tutorials/async_suspense_list/lesson.md new file mode 100644 index 00000000..ef7a656b --- /dev/null +++ b/langs/tl/tutorials/async_suspense_list/lesson.md @@ -0,0 +1,17 @@ +Sometimes you have multiple `Suspense` Components that you want to coordinate. One possible approach is to put everything under a single `Suspense`, but that limits us to a single loading behavior. A single fallback state means that everything always needs to wait until the last thing is loaded. Instead, Solid introduces the `SuspenseList` Component to coordinate that. + +Consider having multiple `Suspense` components like our example. If we wrap them with a `SuspenseList` configured with `revealOrder` of `forwards`, they will render in the order they appear in the tree regardless of the order they load. This reduces the page jumping around. You can set `revealOrder` to `backwards` or `together`, which, respectively, reverses the order or waits for all Suspense Components to load. In addition there is a `tail` option that can be set to `hidden` or `collapsed`. This overrides the default behavior of showing all fallbacks with either showing none or showing the next one in the direction set by `revealOrder`. + +Our example currently is a bit of a mess in terms of loading placeholders. While it loads all the data independently we are often showing multiple placeholders depending on the order data loads in. Let's wrap our `ProfilePage` component's JSX in a ``: + +```jsx + + + Loading posts...}> + + + Loading fun facts...}> + + + +``` diff --git a/langs/tl/tutorials/async_suspense_list/solved.json b/langs/tl/tutorials/async_suspense_list/solved.json new file mode 100644 index 00000000..4df03067 --- /dev/null +++ b/langs/tl/tutorials/async_suspense_list/solved.json @@ -0,0 +1,16 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { Suspense } from \"solid-js\";\n\nimport fetchProfileData from \"./mock-api\";\nimport ProfilePage from \"./profile\";\n\nconst App = () => {\n const { user, posts, trivia } = fetchProfileData();\n return (\n Loading...}>\n \n \n );\n};\n\nrender(App, document.getElementById(\"app\"));\n" + }, + { + "name": "profile", + "content": "import { For, Suspense, SuspenseList } from \"solid-js\";\n\nconst ProfileDetails = (props) =>

    {props.user?.name}

    ;\n\nconst ProfileTimeline = (props) => (\n
      \n {(post) =>
    • {post.text}
    • }
      \n
    \n);\n\nconst ProfileTrivia = (props) => (\n <>\n

    Fun Facts

    \n
      \n {(fact) =>
    • {fact.text}
    • }
      \n
    \n \n);\n\nconst ProfilePage = (props) => (\n \n \n Loading posts...}>\n \n \n Loading fun facts...}>\n \n \n \n);\n\nexport default ProfilePage;" + }, + { + "name": "mock-api", + "content": "import { createResource } from \"solid-js\";\n\nexport default function fetchProfileData() {\n const [user] = createResource(fetchUser);\n const [posts] = createResource(fetchPosts);\n const [trivia] = createResource(fetchTrivia);\n return { user, posts, trivia };\n}\n\nfunction fetchUser() {\n console.log(\"fetch user...\");\n return new Promise((resolve) => {\n setTimeout(() => {\n console.log(\"fetched user\");\n resolve({\n name: \"Ringo Starr\"\n });\n }, 500);\n });\n}\n\nlet ringoPosts = [\n {\n id: 0,\n text: \"I get by with a little help from my friends\"\n },\n {\n id: 1,\n text: \"I'd like to be under the sea in an octupus's garden\"\n },\n {\n id: 2,\n text: \"You got that sand all over your feet\"\n }\n];\n\nfunction fetchPosts() {\n let ringoPostsAtTheTime = ringoPosts;\n console.log(\"fetch posts...\");\n return new Promise((resolve) => {\n setTimeout(() => {\n console.log(\"fetched posts\");\n resolve(ringoPostsAtTheTime);\n }, 3000 * Math.random());\n });\n}\n\nfunction fetchTrivia() {\n console.log(\"fetch trivia...\");\n return new Promise((resolve) => {\n setTimeout(() => {\n console.log(\"fetched trivia\");\n resolve([\n {\n id: 1,\n text:\n 'The nickname \"Ringo\" came from his habit of wearing numerous rings.'\n },\n {\n id: 2,\n text: \"Plays the drums left-handed with a right-handed drum set.\"\n },\n {\n id: 3,\n text: \"Nominated for one Daytime Emmy Award, but did not win\"\n }\n ]);\n }, 3000 * Math.random());\n });\n}\n" + } + ] +} diff --git a/langs/tl/tutorials/async_transitions/lesson.json b/langs/tl/tutorials/async_transitions/lesson.json new file mode 100644 index 00000000..b39a168a --- /dev/null +++ b/langs/tl/tutorials/async_transitions/lesson.json @@ -0,0 +1,17 @@ +{ + "files": [ + { + "name": "main", + "content": "import { createSignal, Suspense, Switch, Match, useTransition } from \"solid-js\";\nimport { render } from \"solid-js/web\";\nimport Child from \"./child\";\n\nimport \"./styles.css\";\n\nconst App = () => {\n const [tab, setTab] = createSignal(0);\n const updateTab = (index) => () => setTab(index);\n\n return (\n <>\n
      \n
    • \n Uno\n
    • \n
    • \n Dos\n
    • \n
    • \n Tres\n
    • \n
    \n
    \n Loading...
    }>\n \n \n \n \n \n \n \n \n \n \n \n \n
    \n \n );\n};\n\nrender(App, document.getElementById(\"app\"));\n" + }, + { + "name": "child", + "content": "import { createResource } from \"solid-js\";\n\nconst CONTENT = {\n Uno: `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`,\n Dos: `Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?`,\n Tres: `On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains.`\n};\n\nfunction createDelay() {\n return new Promise((resolve) => {\n const delay = Math.random() * 420 + 160;\n setTimeout(() => resolve(delay), delay);\n });\n}\n\nconst Child = (props) => {\n const [time] = createResource(createDelay);\n\n return (\n
    \n This content is for page \"{props.page}\" after {time()?.toFixed()}ms.\n

    {CONTENT[props.page]}

    \n
    \n );\n};\n\nexport default Child;\n" + }, + { + "name": "styles", + "type": "css", + "content": "body{\n background-color:#eee;\n}\n\n#app{\n border-radius:3px;\n border: 1px solid #e5e5e5;\n margin:15px;\n background-color:white;\n}\n\n.tab{\n padding:25px;\n font-family:sans-serif;\n color:#444;\n min-height: 400px;\n}\n\n.pending {\n transition: color .3s;\n transition-delay: .1s;\n transition-timing-function: ease-in;\n color: #aaa;\n}\n\nul.inline{\n list-style:none;\n padding: 0;\n margin-bottom:0;\n -webkit-margin-before: 0;\n -webkit-margin-after: 0;\n -webkit-margin-start: 0px;\n -webkit-margin-end: 0px;\n -webkit-padding-start: 0px;\n}\n\nul.inline li {\n display:inline-block;\n margin-left:0;\n padding:10px;\n border-bottom:2px solid #eee;\n transition: all .5s;\n font-family:sans-serif;\n font-weight: 300;\n cursor:pointer;\n color: #aaa;\n}\n\nul.inline li.selected{\n border-bottom:2px solid #337ab7;\n color:#444;\n}\n\n.loader{\n color:#aaa;\n font-size: 16px;\n font-weight: 600;\n width: 80px;\n}" + } + ] +} diff --git a/langs/tl/tutorials/async_transitions/lesson.md b/langs/tl/tutorials/async_transitions/lesson.md new file mode 100644 index 00000000..19e386f8 --- /dev/null +++ b/langs/tl/tutorials/async_transitions/lesson.md @@ -0,0 +1,22 @@ +`Suspense` allows us to show fallback content when data is loading. This is great for initial loading, but on subsequent navigation it is often worse UX to fallback to the skeleton state. + +We can avoid going back to the fallback state by leveraging `useTransition`. It provides a wrapper and a pending indicator. The wrapper puts all downstream updates in a transaction that doesn't commit until all async events complete. + +This means that when control flow is suspended, it continues to show the current branch while rendering the next off-screen. Resource reads under existing boundaries add it to the transition. However, any new nested `Suspense` components will show "fallback" if they have not completed loading before coming into view. + +Notice when you navigate in the example, we keep seeing the content disappear back to a loading placeholder. Let's add a transition in our `App` component. First, let's replace the `updateTab` function: + +```js +const [pending, start] = useTransition(); +const updateTab = (index) => () => start(() => setTab(index)); +``` + +`useTransition` returns a pending signal indicator and a method to start the transition, which we will wrap around our update. + +We should use that pending signal to give an indicator in our UI. We can add a pending class to our tab container div: + +```js +
    +``` + +And with that our tab switching should be much smoother. diff --git a/langs/tl/tutorials/async_transitions/solved.json b/langs/tl/tutorials/async_transitions/solved.json new file mode 100644 index 00000000..04b7b6bb --- /dev/null +++ b/langs/tl/tutorials/async_transitions/solved.json @@ -0,0 +1,17 @@ +{ + "files": [ + { + "name": "main", + "content": "import { createSignal, Suspense, Switch, Match, useTransition } from \"solid-js\";\nimport { render } from \"solid-js/web\";\nimport Child from \"./child\";\n\nimport \"./styles.css\";\n\nconst App = () => {\n const [tab, setTab] = createSignal(0);\n const [pending, start] = useTransition();\n const updateTab = (index) => () => start(() => setTab(index));\n\n return (\n <>\n
      \n
    • \n Uno\n
    • \n
    • \n Dos\n
    • \n
    • \n Tres\n
    • \n
    \n
    \n Loading...
    }>\n \n \n \n \n \n \n \n \n \n \n \n \n
    \n \n );\n};\n\nrender(App, document.getElementById(\"app\"));\n" + }, + { + "name": "child", + "content": "import { createResource } from \"solid-js\";\n\nconst CONTENT = {\n Uno: `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`,\n Dos: `Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?`,\n Tres: `On the other hand, we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment, so blinded by desire, that they cannot foresee the pain and trouble that are bound to ensue; and equal blame belongs to those who fail in their duty through weakness of will, which is the same as saying through shrinking from toil and pain. These cases are perfectly simple and easy to distinguish. In a free hour, when our power of choice is untrammelled and when nothing prevents our being able to do what we like best, every pleasure is to be welcomed and every pain avoided. But in certain circumstances and owing to the claims of duty or the obligations of business it will frequently occur that pleasures have to be repudiated and annoyances accepted. The wise man therefore always holds in these matters to this principle of selection: he rejects pleasures to secure other greater pleasures, or else he endures pains to avoid worse pains.`\n};\n\nfunction createDelay() {\n return new Promise((resolve) => {\n const delay = Math.random() * 420 + 160;\n setTimeout(() => resolve(delay), delay);\n });\n}\n\nconst Child = (props) => {\n const [time] = createResource(createDelay);\n\n return (\n
    \n This content is for page \"{props.page}\" after {time()?.toFixed()}ms.\n

    {CONTENT[props.page]}

    \n
    \n );\n};\n\nexport default Child;\n" + }, + { + "name": "styles", + "type": "css", + "content": "body{\n background-color:#eee;\n}\n\n#app{\n border-radius:3px;\n border: 1px solid #e5e5e5;\n margin:15px;\n background-color:white;\n}\n\n.tab{\n padding:25px;\n font-family:sans-serif;\n color:#444;\n min-height: 400px;\n}\n\n.pending {\n transition: color .3s;\n transition-delay: .1s;\n transition-timing-function: ease-in;\n color: #aaa;\n}\n\nul.inline{\n list-style:none;\n padding: 0;\n margin-bottom:0;\n -webkit-margin-before: 0;\n -webkit-margin-after: 0;\n -webkit-margin-start: 0px;\n -webkit-margin-end: 0px;\n -webkit-padding-start: 0px;\n}\n\nul.inline li {\n display:inline-block;\n margin-left:0;\n padding:10px;\n border-bottom:2px solid #eee;\n transition: all .5s;\n font-family:sans-serif;\n font-weight: 300;\n cursor:pointer;\n color: #aaa;\n}\n\nul.inline li.selected{\n border-bottom:2px solid #337ab7;\n color:#444;\n}\n\n.loader{\n color:#aaa;\n font-size: 16px;\n font-weight: 600;\n width: 80px;\n}" + } + ] +} diff --git a/langs/tl/tutorials/bindings_classlist/lesson.json b/langs/tl/tutorials/bindings_classlist/lesson.json new file mode 100644 index 00000000..8114b5fe --- /dev/null +++ b/langs/tl/tutorials/bindings_classlist/lesson.json @@ -0,0 +1,13 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal } from \"solid-js\";\n\nimport \"./style.css\";\n\nfunction App() {\n const [current, setCurrent] = createSignal(\"foo\");\n\n return <>\n setCurrent('foo')}\n >foo\n setCurrent('bar')}\n >bar\n setCurrent('baz')}\n >baz\n ;\n}\n\nrender(() => , document.getElementById('app'));" + }, + { + "name": "style", + "type": "css", + "content": "button {\n display: block;\n}\n\n.selected {\n background-color: #ff3e00;\n color: white;\n}" + } + ] +} diff --git a/langs/tl/tutorials/bindings_classlist/lesson.md b/langs/tl/tutorials/bindings_classlist/lesson.md new file mode 100644 index 00000000..b4246d9c --- /dev/null +++ b/langs/tl/tutorials/bindings_classlist/lesson.md @@ -0,0 +1,27 @@ +Solid supports using both `class` and `className` to set the `className` property on an element. However it is often convenient to conditionally set classes. For that reason, Solid has a built-in `classList` JSX attribute that takes an object where the key is the class name(s) and the value is a boolean expression. When true, the class is applied, and when false, it is removed. + +In the example, we can replace: + +```jsx + +``` + +with: + +```jsx + +``` + +Remember that you can apply names dynamically like what you'd receive in CSS modules as well: + +```jsx +import { active } from "./style.module.css" + +
    +``` diff --git a/langs/tl/tutorials/bindings_classlist/solved.json b/langs/tl/tutorials/bindings_classlist/solved.json new file mode 100644 index 00000000..666cf63c --- /dev/null +++ b/langs/tl/tutorials/bindings_classlist/solved.json @@ -0,0 +1,13 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal } from \"solid-js\";\n\nimport \"./style.css\";\n\nfunction App() {\n const [current, setCurrent] = createSignal(\"foo\");\n\n return <>\n setCurrent('foo')}\n >foo\n setCurrent('bar')}\n >bar\n setCurrent('baz')}\n >baz\n ;\n}\n\nrender(() => , document.getElementById('app'));" + }, + { + "name": "style", + "type": "css", + "content": "button {\n display: block;\n}\n\n.selected {\n background-color: #ff3e00;\n color: white;\n}" + } + ] +} diff --git a/langs/tl/tutorials/bindings_directives/lesson.json b/langs/tl/tutorials/bindings_directives/lesson.json new file mode 100644 index 00000000..26f8a117 --- /dev/null +++ b/langs/tl/tutorials/bindings_directives/lesson.json @@ -0,0 +1,17 @@ +{ + "files": [ + { + "name": "main", + "content": "// @ts-nocheck\nimport { render } from \"solid-js/web\";\nimport { createSignal, Show } from \"solid-js\";\nimport clickOutside from \"./click-outside\";\nimport \"./style.css\";\n\nfunction App() {\n const [show, setShow] = createSignal(false);\n\n return (\n setShow(true)}>Open Modal}\n >\n
    \n Some Modal\n
    \n \n );\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + }, + { + "name": "click-outside", + "content": "import { onCleanup } from \"solid-js\";\n\nexport default function clickOutside(el, accessor) {\n // implement here\n}\n" + }, + { + "name": "style", + "type": "css", + "content": ".modal {\n padding: 16px;\n border: 1px solid #444;\n box-shadow: 4px 4px #88888866;\n}" + } + ] +} \ No newline at end of file diff --git a/langs/tl/tutorials/bindings_directives/lesson.md b/langs/tl/tutorials/bindings_directives/lesson.md new file mode 100644 index 00000000..05fb8f46 --- /dev/null +++ b/langs/tl/tutorials/bindings_directives/lesson.md @@ -0,0 +1,26 @@ +Solid supports custom directives through the `use:` namespace. This is just a syntactic sugar over `ref`, but is useful in that it resembles typical bindings and there can be multiple bindings on the same element without conflict. This makes it a better tool for reusable DOM element behavior. + +Custom directives are simply functions taking arguments `(element, valueAccesor)` where `element` is the DOM element with the `use:` attribute and `valueAccessor` is a getter function for the value assigned to the attribute. As long as the function is imported in scope, you can use it with `use:`. + +> Important: `use:` is detected by the compiler to be transformed, and the function is required to be in scope, so it cannot be part of spreads or applied to a component. + +In the example, we are going to make a simple wrapper for click-outside behavior to close a popup or modal. First we need to import and use our `clickOutside` directive on our element: + +```jsx + +``` + +Open `click-outside.tsx`, where we will be defining our custom directive. This directive defines a click handler that we bind to the body and cleanup when it is time: + +```jsx +export default function clickOutside(el, accessor) { + const onClick = (e) => !el.contains(e.target) && accessor()?.(); + document.body.addEventListener("click", onClick); + + onCleanup(() => document.body.removeEventListener("click", onClick)); +} +``` + +Now you should be able to go back and forth between opening and closing the modal. diff --git a/langs/tl/tutorials/bindings_directives/solved.json b/langs/tl/tutorials/bindings_directives/solved.json new file mode 100644 index 00000000..84830904 --- /dev/null +++ b/langs/tl/tutorials/bindings_directives/solved.json @@ -0,0 +1,17 @@ +{ + "files": [ + { + "name": "main", + "content": "// @ts-nocheck\nimport { render } from \"solid-js/web\";\nimport { createSignal, Show } from \"solid-js\";\nimport clickOutside from \"./click-outside\";\nimport \"./style.css\";\n\nfunction App() {\n const [show, setShow] = createSignal(false);\n\n return (\n setShow(true)}>Open Modal}\n >\n
    setShow(false)}>\n Some Modal\n
    \n \n );\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + }, + { + "name": "click-outside", + "content": "import { onCleanup } from \"solid-js\";\n\nexport default function clickOutside(el, accessor) {\n const onClick = (e) => !el.contains(e.target) && accessor()?.();\n document.body.addEventListener(\"click\", onClick);\n\n onCleanup(() => document.body.removeEventListener(\"click\", onClick));\n}\n" + }, + { + "name": "style", + "type": "css", + "content": ".modal {\n padding: 16px;\n border: 1px solid #444;\n box-shadow: 4px 4px #88888866;\n}" + } + ] +} diff --git a/langs/tl/tutorials/bindings_events/lesson.json b/langs/tl/tutorials/bindings_events/lesson.json new file mode 100644 index 00000000..0c31e073 --- /dev/null +++ b/langs/tl/tutorials/bindings_events/lesson.json @@ -0,0 +1,13 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal } from \"solid-js\";\n\nimport \"./style.css\";\n\nfunction App() {\n const [pos, setPos] = createSignal({x: 0, y: 0});\n\n function handleMouseMove(event) {\n setPos({\n x: event.clientX,\n y: event.clientY\n });\n }\n\n return (\n
    \n The mouse position is {pos().x} x {pos().y}\n
    \n );\n}\n\nrender(() => , document.getElementById('app'));" + }, + { + "name": "style", + "type": "css", + "content": "div { width: 100%; height: 100%; }" + } + ] +} diff --git a/langs/tl/tutorials/bindings_events/lesson.md b/langs/tl/tutorials/bindings_events/lesson.md new file mode 100644 index 00000000..9fea8f2b --- /dev/null +++ b/langs/tl/tutorials/bindings_events/lesson.md @@ -0,0 +1,22 @@ +Events in Solid are attributes prefixed with `on`. They are treated specially in a few ways. First, they do not follow the normal heuristics for wrapping. In many cases, it is difficult to determine the difference between a Signal and an event handler. And so, since events are called and don't require reactivity to update, they are only bound initially. You can always just have your handler run different code based on the current state of your app. + +Common UI events (that bubble and are composed) are automatically delegated to the document. To improve delegated performance, Solid supports an array syntax to call the handler with data (as the first argument) without creating additional closures: + +```jsx +const handler = (data, event) => /*...*/ + + +``` + +In the example, let's attach the handler to the `mousemove` event: +```jsx +
    + The mouse position is {pos().x} x {pos().y} +
    +``` + +All `on` bindings are case insensitive which means that event names need to be in lowercase. For example, `onMouseMove` monitors the event name `mousemove`. If you need to support other casings or not use event delegation, you can use `on:` namespace to match event handlers that follows the colon: + +```jsx + +``` diff --git a/langs/tl/tutorials/bindings_events/solved.json b/langs/tl/tutorials/bindings_events/solved.json new file mode 100644 index 00000000..913e7869 --- /dev/null +++ b/langs/tl/tutorials/bindings_events/solved.json @@ -0,0 +1,13 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal } from \"solid-js\";\n\nimport \"./style.css\";\n\nfunction App() {\n const [pos, setPos] = createSignal({x: 0, y: 0});\n\n function handleMouseMove(event) {\n setPos({\n x: event.clientX,\n y: event.clientY\n });\n }\n\n return (\n
    \n The mouse position is {pos().x} x {pos().y}\n
    \n );\n}\n\nrender(() => , document.getElementById('app'));" + }, + { + "name": "style", + "type": "css", + "content": "div { width: 100%; height: 100%; }" + } + ] +} diff --git a/langs/tl/tutorials/bindings_forward_refs/lesson.json b/langs/tl/tutorials/bindings_forward_refs/lesson.json new file mode 100644 index 00000000..b47a5c09 --- /dev/null +++ b/langs/tl/tutorials/bindings_forward_refs/lesson.json @@ -0,0 +1,17 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { onMount, onCleanup } from \"solid-js\";\n\nimport Canvas from \"./canvas\";\n\nfunction App() {\n let canvas;\n onMount(() => {\n const ctx = canvas.getContext(\"2d\");\n let frame = requestAnimationFrame(loop);\n\n function loop(t) {\n frame = requestAnimationFrame(loop);\n\n const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);\n\n for (let p = 0; p < imageData.data.length; p += 4) {\n const i = p / 4;\n const x = i % canvas.width;\n const y = (i / canvas.height) >>> 0;\n\n const r = 64 + (128 * x) / canvas.width + 64 * Math.sin(t / 1000);\n const g = 64 + (128 * y) / canvas.height + 64 * Math.cos(t / 1000);\n const b = 128;\n\n imageData.data[p + 0] = r;\n imageData.data[p + 1] = g;\n imageData.data[p + 2] = b;\n imageData.data[p + 3] = 255;\n }\n\n ctx.putImageData(imageData, 0, 0);\n }\n\n onCleanup(() => cancelAnimationFrame(frame));\n });\n\n return ;\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + }, + { + "name": "canvas", + "content": "import \"./style.css\";\n\nexport default function Canvas(props) {\n return ;\n}\n" + }, + { + "name": "style", + "type": "css", + "content": "canvas {\n background-color: #666;\n -webkit-mask: url(https://dev.solidjs.com/img/logo/dark-without-wordmark/logo.svg) 50% 50% no-repeat;\n mask: url(https://dev.solidjs.com/img/logo/dark-without-wordmark/logo.svg) 50% 50% no-repeat;\n}" + } + ] +} diff --git a/langs/tl/tutorials/bindings_forward_refs/lesson.md b/langs/tl/tutorials/bindings_forward_refs/lesson.md new file mode 100644 index 00000000..95a3a203 --- /dev/null +++ b/langs/tl/tutorials/bindings_forward_refs/lesson.md @@ -0,0 +1,9 @@ +On many occassions, you might want to expose a ref from inside a component to a parent. The way we do this is still by using the `ref` attribute. From the outside, using `ref` on a component works very similar to using `ref` on a native element. You can pass it a variable to be assigned or a callback function. + +However, it is the component author's responsibility to connect that `ref` to an internal element to forward it back up. To do so, we leverage `props.ref`. This is a callback form of `ref` if either type of `ref` is given, but this detail is mostly hidden from you as you will more than likely just be assigning it directly to the `ref` attribute of one of the elements or components in this component's JSX. + +To get the logo animating again, we need to forward the ref from `canvas.tsx`: + +```jsx + +``` diff --git a/langs/tl/tutorials/bindings_forward_refs/solved.json b/langs/tl/tutorials/bindings_forward_refs/solved.json new file mode 100644 index 00000000..cf501888 --- /dev/null +++ b/langs/tl/tutorials/bindings_forward_refs/solved.json @@ -0,0 +1,17 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { onMount, onCleanup } from \"solid-js\";\n\nimport Canvas from \"./canvas\";\n\nfunction App() {\n let canvas;\n onMount(() => {\n const ctx = canvas.getContext(\"2d\");\n let frame = requestAnimationFrame(loop);\n\n function loop(t) {\n frame = requestAnimationFrame(loop);\n\n const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);\n\n for (let p = 0; p < imageData.data.length; p += 4) {\n const i = p / 4;\n const x = i % canvas.width;\n const y = (i / canvas.height) >>> 0;\n\n const r = 64 + (128 * x) / canvas.width + 64 * Math.sin(t / 1000);\n const g = 64 + (128 * y) / canvas.height + 64 * Math.cos(t / 1000);\n const b = 128;\n\n imageData.data[p + 0] = r;\n imageData.data[p + 1] = g;\n imageData.data[p + 2] = b;\n imageData.data[p + 3] = 255;\n }\n\n ctx.putImageData(imageData, 0, 0);\n }\n\n onCleanup(() => cancelAnimationFrame(frame));\n });\n\n return ;\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + }, + { + "name": "canvas", + "content": "import \"./style.css\";\n\nexport default function Canvas(props) {\n return ;\n}\n" + }, + { + "name": "style", + "type": "css", + "content": "canvas {\n background-color: #666;\n -webkit-mask: url(https://dev.solidjs.com/img/logo/dark-without-wordmark/logo.svg) 50% 50% no-repeat;\n mask: url(https://dev.solidjs.com/img/logo/dark-without-wordmark/logo.svg) 50% 50% no-repeat;\n}" + } + ] +} \ No newline at end of file diff --git a/langs/tl/tutorials/bindings_refs/lesson.json b/langs/tl/tutorials/bindings_refs/lesson.json new file mode 100644 index 00000000..fc42c8ff --- /dev/null +++ b/langs/tl/tutorials/bindings_refs/lesson.json @@ -0,0 +1,13 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { onMount, onCleanup } from \"solid-js\";\n\nimport \"./style.css\";\n\nfunction App() {\n let canvas;\n onMount(() => {\n const ctx = canvas.getContext(\"2d\");\n let frame = requestAnimationFrame(loop);\n\n function loop(t) {\n frame = requestAnimationFrame(loop);\n\n const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);\n\n for (let p = 0; p < imageData.data.length; p += 4) {\n const i = p / 4;\n const x = i % canvas.width;\n const y = (i / canvas.height) >>> 0;\n\n const r = 64 + (128 * x) / canvas.width + 64 * Math.sin(t / 1000);\n const g = 64 + (128 * y) / canvas.height + 64 * Math.cos(t / 1000);\n const b = 128;\n\n imageData.data[p + 0] = r;\n imageData.data[p + 1] = g;\n imageData.data[p + 2] = b;\n imageData.data[p + 3] = 255;\n }\n\n ctx.putImageData(imageData, 0, 0);\n }\n\n onCleanup(() => cancelAnimationFrame(frame));\n });\n\n return ;\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + }, + { + "name": "style", + "type": "css", + "content": "canvas {\n background-color: #666;\n -webkit-mask: url(https://dev.solidjs.com/img/logo/dark-without-wordmark/logo.svg) 50% 50% no-repeat;\n mask: url(https://dev.solidjs.com/img/logo/dark-without-wordmark/logo.svg) 50% 50% no-repeat;\n}" + } + ] +} diff --git a/langs/tl/tutorials/bindings_refs/lesson.md b/langs/tl/tutorials/bindings_refs/lesson.md new file mode 100644 index 00000000..b46f7f35 --- /dev/null +++ b/langs/tl/tutorials/bindings_refs/lesson.md @@ -0,0 +1,27 @@ +You can always get a reference to a DOM element in Solid through assignment, since JSX creates actual DOM elements. For example: + +```jsx +const myDiv =
    My Element
    ; +``` + +However, there is benefit to not breaking your elements out and instead putting them in a single contiguous JSX template, as it allows Solid to better optimize their creation. + +Instead you can get a reference to an element in Solid using the `ref` attribute. Refs are basically assignments like the example above, which happen at creation time before they are attached to the document DOM. Simply declare a variable and it will be assigned to: + +```jsx +let myDiv; + +
    My Element
    +``` + +So let's get a reference to our canvas element and animate it: + +```jsx + +``` + +Refs can also take the form of a callback function. This can be convenient for encapsulating logic, especially when you don't need to wait until the elements are attached. For example: + +```jsx +
    /* do something with el... */}>My Element
    +``` diff --git a/langs/tl/tutorials/bindings_refs/solved.json b/langs/tl/tutorials/bindings_refs/solved.json new file mode 100644 index 00000000..f1d8fdf5 --- /dev/null +++ b/langs/tl/tutorials/bindings_refs/solved.json @@ -0,0 +1,13 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { onMount, onCleanup } from \"solid-js\";\n\nimport \"./style.css\";\n\nfunction App() {\n let canvas;\n onMount(() => {\n const ctx = canvas.getContext(\"2d\");\n let frame = requestAnimationFrame(loop);\n\n function loop(t) {\n frame = requestAnimationFrame(loop);\n\n const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);\n\n for (let p = 0; p < imageData.data.length; p += 4) {\n const i = p / 4;\n const x = i % canvas.width;\n const y = (i / canvas.height) >>> 0;\n\n const r = 64 + (128 * x) / canvas.width + 64 * Math.sin(t / 1000);\n const g = 64 + (128 * y) / canvas.height + 64 * Math.cos(t / 1000);\n const b = 128;\n\n imageData.data[p + 0] = r;\n imageData.data[p + 1] = g;\n imageData.data[p + 2] = b;\n imageData.data[p + 3] = 255;\n }\n\n ctx.putImageData(imageData, 0, 0);\n }\n\n onCleanup(() => cancelAnimationFrame(frame));\n });\n\n return ;\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + }, + { + "name": "style", + "type": "css", + "content": "canvas {\n background-color: #666;\n -webkit-mask: url(https://dev.solidjs.com/img/logo/dark-without-wordmark/logo.svg) 50% 50% no-repeat;\n mask: url(https://dev.solidjs.com/img/logo/dark-without-wordmark/logo.svg) 50% 50% no-repeat;\n}" + } + ] +} diff --git a/langs/tl/tutorials/bindings_spreads/lesson.json b/langs/tl/tutorials/bindings_spreads/lesson.json new file mode 100644 index 00000000..c89e7f1a --- /dev/null +++ b/langs/tl/tutorials/bindings_spreads/lesson.json @@ -0,0 +1,12 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport Info from \"./info\";\n\nconst pkg = {\n name: \"solid-js\",\n version: 1,\n speed: \"⚡️\",\n website: \"https://solidjs.com\",\n};\n\nfunction App() {\n return (\n \n );\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + }, + { + "name": "info", + "content": "export default function Info(props) {\n return (\n

    \n The {props.name} package is {props.speed} fast. Download\n version {props.version} from{\" \"}\n npm and{\" \"}\n learn more here\n

    \n );\n}\n" + } + ] +} diff --git a/langs/tl/tutorials/bindings_spreads/lesson.md b/langs/tl/tutorials/bindings_spreads/lesson.md new file mode 100644 index 00000000..aa6c72c6 --- /dev/null +++ b/langs/tl/tutorials/bindings_spreads/lesson.md @@ -0,0 +1,9 @@ +Sometimes your components and elements accept a variable number of attributes and it makes sense to pass them down as an object instead of individually. This is especially true when wrapping a DOM element in a component, a common practice when making design systems. + +For this we use the spread operator `...`. + +We can pass an object with a variable number of properties: + +```jsx + +``` diff --git a/langs/tl/tutorials/bindings_spreads/solved.json b/langs/tl/tutorials/bindings_spreads/solved.json new file mode 100644 index 00000000..fd12259f --- /dev/null +++ b/langs/tl/tutorials/bindings_spreads/solved.json @@ -0,0 +1,12 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport Info from \"./info\";\n\nconst pkg = {\n name: \"solid-js\",\n version: 1,\n speed: \"⚡️\",\n website: \"https://solidjs.com\",\n};\n\nfunction App() {\n return ;\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + }, + { + "name": "info", + "content": "export default function Info(props) {\n return (\n

    \n The {props.name} package is {props.speed} fast. Download\n version {props.version} from{\" \"}\n npm and{\" \"}\n learn more here\n

    \n );\n}\n" + } + ] +} diff --git a/langs/tl/tutorials/bindings_style/lesson.json b/langs/tl/tutorials/bindings_style/lesson.json new file mode 100644 index 00000000..11f8610e --- /dev/null +++ b/langs/tl/tutorials/bindings_style/lesson.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal } from \"solid-js\";\n\nfunction App() {\n const [num, setNum] = createSignal(0);\n setInterval(() => setNum((num() + 1) % 255), 30)\n\n return
    Some Text
    ;\n}\n\nrender(() => , document.getElementById('app'));" + } + ] +} diff --git a/langs/tl/tutorials/bindings_style/lesson.md b/langs/tl/tutorials/bindings_style/lesson.md new file mode 100644 index 00000000..03d6c7dd --- /dev/null +++ b/langs/tl/tutorials/bindings_style/lesson.md @@ -0,0 +1,16 @@ +The `style` attribute in Solid accepts either style strings or objects. However the object form differs from `Element.prototype.style` and instead is a wrapper for calling `style.setProperty`. This means that keys take the dash-case form, like "background-color" rather than "backgroundColor". This means that we have the ability to set CSS variables: + +```js +
    +``` + +Let's animate our div with a few inline styles: +```jsx +
    + Some Text +
    +``` \ No newline at end of file diff --git a/langs/tl/tutorials/bindings_style/solved.json b/langs/tl/tutorials/bindings_style/solved.json new file mode 100644 index 00000000..5915cfbf --- /dev/null +++ b/langs/tl/tutorials/bindings_style/solved.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal } from \"solid-js\";\n\nfunction App() {\n const [num, setNum] = createSignal(0);\n setInterval(() => setNum((num() + 1) % 255), 30)\n\n return (\n
    \n Some Text\n
    \n );\n}\n\nrender(() => , document.getElementById('app'));" + } + ] +} diff --git a/langs/tl/tutorials/directory.json b/langs/tl/tutorials/directory.json new file mode 100644 index 00000000..ede4aeaf --- /dev/null +++ b/langs/tl/tutorials/directory.json @@ -0,0 +1,162 @@ +[ + { + "lessonName": "Introduction/Basics", + "internalName": "introduction_basics" + }, + { + "lessonName": "Introduction/JSX", + "internalName": "introduction_jsx" + }, + { + "lessonName": "Introduction/Components", + "internalName": "introduction_components" + }, + { + "lessonName": "Introduction/Signals", + "internalName": "introduction_signals" + }, + { + "lessonName": "Introduction/Effects", + "internalName": "introduction_effects" + }, + { + "lessonName": "Introduction/Derived Signals", + "internalName": "introduction_derived" + }, + { + "lessonName": "Introduction/Memos", + "internalName": "introduction_memos" + }, + { + "lessonName": "Control Flow/Show", + "internalName": "flow_show" + }, + { + "lessonName": "Control Flow/For", + "internalName": "flow_for" + }, + { + "lessonName": "Control Flow/Index", + "internalName": "flow_index" + }, + { + "lessonName": "Control Flow/Switch", + "internalName": "flow_switch" + }, + { + "lessonName": "Control Flow/Dynamic", + "internalName": "flow_dynamic" + }, + { + "lessonName": "Control Flow/Portal", + "internalName": "flow_portal" + }, + { + "lessonName": "Control Flow/Error Boundary", + "internalName": "flow_error_boundary" + }, + { + "lessonName": "Lifecycles/onMount", + "internalName": "lifecycles_onmount" + }, + { + "lessonName": "Lifecycles/onCleanup", + "internalName": "lifecycles_oncleanup" + }, + { + "lessonName": "Bindings/Events", + "internalName": "bindings_events" + }, + { + "lessonName": "Bindings/Style", + "internalName": "bindings_style" + }, + { + "lessonName": "Bindings/ClassList", + "internalName": "bindings_classlist" + }, + { + "lessonName": "Bindings/Refs", + "internalName": "bindings_refs" + }, + { + "lessonName": "Bindings/Forwarding Refs", + "internalName": "bindings_forward_refs" + }, + { + "lessonName": "Bindings/Spreads", + "internalName": "bindings_spreads" + }, + { + "lessonName": "Bindings/Directives", + "internalName": "bindings_directives" + }, + { + "lessonName": "Props/Default Props", + "internalName": "props_defaults" + }, + { + "lessonName": "Props/Splitting Props", + "internalName": "props_split" + }, + { + "lessonName": "Props/Children", + "internalName": "props_children" + }, + { + "lessonName": "Stores/Nested Reactivity", + "internalName": "stores_nested_reactivity" + }, + { + "lessonName": "Stores/Create Store", + "internalName": "stores_createstore" + }, + { + "lessonName": "Stores/Mutation", + "internalName": "stores_mutation" + }, + { + "lessonName": "Stores/Context", + "internalName": "stores_context" + }, + { + "lessonName": "Stores/Immutable Stores", + "internalName": "stores_immutable" + }, + { + "lessonName": "Stores/Without Context", + "internalName": "stores_nocontext" + }, + { + "lessonName": "Reactivity/Batching Updates", + "internalName": "reactivity_batch" + }, + { + "lessonName": "Reactivity/Untrack", + "internalName": "reactivity_untrack" + }, + { + "lessonName": "Reactivity/On", + "internalName": "reactivity_on" + }, + { + "lessonName": "Async/Lazy Components", + "internalName": "async_lazy" + }, + { + "lessonName": "Async/Resources", + "internalName": "async_resources" + }, + { + "lessonName": "Async/Suspense", + "internalName": "async_suspense" + }, + { + "lessonName": "Async/Suspense List", + "internalName": "async_suspense_list" + }, + { + "lessonName": "Async/Transitions", + "internalName": "async_transitions" + } +] diff --git a/langs/tl/tutorials/flow_dynamic/lesson.json b/langs/tl/tutorials/flow_dynamic/lesson.json new file mode 100644 index 00000000..4d9a171d --- /dev/null +++ b/langs/tl/tutorials/flow_dynamic/lesson.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render, Dynamic } from \"solid-js/web\";\nimport { createSignal, Switch, Match, For } from \"solid-js\";\n\nconst RedThing = () => Red Thing;\nconst GreenThing = () => Green Thing;\nconst BlueThing = () => Blue Thing;\n\nconst options = {\n red: RedThing,\n green: GreenThing,\n blue: BlueThing\n}\n\nfunction App() {\n const [selected, setSelected] = createSignal(\"red\");\n\n return (\n <>\n \n }>\n \n \n \n \n );\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + } + ] +} diff --git a/langs/tl/tutorials/flow_dynamic/lesson.md b/langs/tl/tutorials/flow_dynamic/lesson.md new file mode 100644 index 00000000..7a09c912 --- /dev/null +++ b/langs/tl/tutorials/flow_dynamic/lesson.md @@ -0,0 +1,18 @@ +The `` tag is useful when you render from data. It lets you pass either a string for a native element or a component function and it will render that with the rest of the provided props. + +This is often more compact than writing a number of `` or `` components. + +In the example, we can replace the `` statement: + +```jsx +}> + + + +``` + +with: + +```jsx + +``` diff --git a/langs/tl/tutorials/flow_dynamic/solved.json b/langs/tl/tutorials/flow_dynamic/solved.json new file mode 100644 index 00000000..b8a845b8 --- /dev/null +++ b/langs/tl/tutorials/flow_dynamic/solved.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render, Dynamic } from \"solid-js/web\";\nimport { createSignal, For } from \"solid-js\";\n\nconst RedThing = () => Red Thing;\nconst GreenThing = () => Green Thing;\nconst BlueThing = () => Blue Thing;\n\nconst options = {\n red: RedThing,\n green: GreenThing,\n blue: BlueThing\n}\n\nfunction App() {\n const [selected, setSelected] = createSignal(\"red\");\n\n return (\n <>\n \n \n \n );\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + } + ] +} diff --git a/langs/tl/tutorials/flow_error_boundary/lesson.json b/langs/tl/tutorials/flow_error_boundary/lesson.json new file mode 100644 index 00000000..be53fdac --- /dev/null +++ b/langs/tl/tutorials/flow_error_boundary/lesson.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { ErrorBoundary } from \"solid-js\";\n\nconst Broken = (props) => {\n throw new Error(\"Oh No\");\n return <>Never Getting Here\n}\n\nfunction App() {\n return (\n <>\n
    Before
    \n \n
    After
    \n \n );\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + } + ] +} diff --git a/langs/tl/tutorials/flow_error_boundary/lesson.md b/langs/tl/tutorials/flow_error_boundary/lesson.md new file mode 100644 index 00000000..60279577 --- /dev/null +++ b/langs/tl/tutorials/flow_error_boundary/lesson.md @@ -0,0 +1,9 @@ +A JavaScript error originating in the UI shouldn’t break the whole app. Error boundaries are components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed. + +A component has crashed our example. Let's wrap it in an Error Boundary that displays the error. + +```jsx + err}> + + +``` diff --git a/langs/tl/tutorials/flow_error_boundary/solved.json b/langs/tl/tutorials/flow_error_boundary/solved.json new file mode 100644 index 00000000..25ed4b57 --- /dev/null +++ b/langs/tl/tutorials/flow_error_boundary/solved.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { ErrorBoundary } from \"solid-js\";\n\nconst Broken = (props) => {\n throw new Error(\"Oh No\");\n return <>Never Getting Here\n}\n\nfunction App() {\n return (\n <>\n
    Before
    \n err}>\n \n \n
    After
    \n \n );\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + } + ] +} diff --git a/langs/tl/tutorials/flow_for/lesson.json b/langs/tl/tutorials/flow_for/lesson.json new file mode 100644 index 00000000..4b1b4150 --- /dev/null +++ b/langs/tl/tutorials/flow_for/lesson.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from 'solid-js/web';\nimport { createSignal, For } from 'solid-js';\n\nfunction App() {\n const [cats, setCats] = createSignal([\n { id: 'J---aiyznGQ', name: 'Keyboard Cat' },\n { id: 'z_AbfPXTKms', name: 'Maru' },\n { id: 'OUtn3pvWmpg', name: 'Henri The Existential Cat' }\n ]);\n \n return (\n \n );\n}\n\nrender(() => , document.getElementById('app'))\n" + } + ] +} diff --git a/langs/tl/tutorials/flow_for/lesson.md b/langs/tl/tutorials/flow_for/lesson.md new file mode 100644 index 00000000..9657a1a7 --- /dev/null +++ b/langs/tl/tutorials/flow_for/lesson.md @@ -0,0 +1,20 @@ +The `` component is the best way to loop over an array of objects. As the array changes, `` updates or moves items in the DOM rather than recreating them. Let's look at an example. + +```jsx +{(cat, i) => +
  • + + {i() + 1}: {cat.name} + +
  • +}
    +``` + +There is one prop on the `` component: `each`, where you pass the array to loop over. + +Then, instead of writing nodes directly between `` and ``, you a pass a _callback_. This is a function similar to JavaScript's [`map` callback](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map#parameters). For each element in the array, the callback is called with the element as the first argument and the index as the second. (`cat` and `i` in this example.) You can then make use of those in the callback, which should return a node to be rendered. + +Note that the index is a _signal_, not a constant number. This is because `` is "keyed by reference": each node that it renders is coupled to an element in the array. In other words, if an element changes placement in the array, rather than being destroyed and recreated, the corresponding node will move too and its index will change. + + +The `each` prop expects an array, but you can turn other iterable objects into arrays with utilities like [`Array.from`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from), [`Object.keys`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys), or [spread syntax](`https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax`). \ No newline at end of file diff --git a/langs/tl/tutorials/flow_for/solved.json b/langs/tl/tutorials/flow_for/solved.json new file mode 100644 index 00000000..b2b2d8ba --- /dev/null +++ b/langs/tl/tutorials/flow_for/solved.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from 'solid-js/web';\nimport { createSignal, For } from 'solid-js';\n\nfunction App() {\n const [cats, setCats] = createSignal([\n { id: 'J---aiyznGQ', name: 'Keyboard Cat' },\n { id: 'z_AbfPXTKms', name: 'Maru' },\n { id: 'OUtn3pvWmpg', name: 'Henri The Existential Cat' }\n ]);\n \n return (\n \n );\n}\n\nrender(() => , document.getElementById('app'))\n" + } + ] +} diff --git a/langs/tl/tutorials/flow_index/lesson.json b/langs/tl/tutorials/flow_index/lesson.json new file mode 100644 index 00000000..dfffada6 --- /dev/null +++ b/langs/tl/tutorials/flow_index/lesson.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from 'solid-js/web';\nimport { createSignal, Index } from 'solid-js';\n\nfunction App() {\n const [cats, setCats] = createSignal([\n { id: 'J---aiyznGQ', name: 'Keyboard Cat' },\n { id: 'z_AbfPXTKms', name: 'Maru' },\n { id: 'OUtn3pvWmpg', name: 'Henri The Existential Cat' }\n ]);\n \n return (\n \n );\n}\n\nrender(() => , document.getElementById('app'))\n" + } + ] +} diff --git a/langs/tl/tutorials/flow_index/lesson.md b/langs/tl/tutorials/flow_index/lesson.md new file mode 100644 index 00000000..5c00894f --- /dev/null +++ b/langs/tl/tutorials/flow_index/lesson.md @@ -0,0 +1,21 @@ +You now know how to render lists in Solid with ``, but Solid also provides the `` component, which will cause less rerenders in certain situations. + +When the array updates, the `` component uses referential equality to compare elements to the last state of the array. But this isn't always desired. + +In JavaScript, primitives (like strings and numbers) are always compared by value. When using `` with primitive values or arrays of arrays, we could cause a lot of unnecessary rendering. If we used `` to map a list of strings to `` fields that could edit each, every change to that value would cause the `` to be recreated. + +The `` component is provided for these cases. As a rule of thumb, when working with primitives use ``. + +```jsx +{(cat, i) => +
  • + + {i + 1}: {cat().name} + +
  • +}
    +``` + + It has a similar signature to ``, except this time the item is the signal and the index is fixed. Each rendered node corresponds to a spot in the array. Whenever the data in that spot changes, the signal will update. + +`` cares about each piece of data in your array, and the position of that data can change; `` cares about each index in your array, and the content at each index can change. diff --git a/langs/tl/tutorials/flow_index/solved.json b/langs/tl/tutorials/flow_index/solved.json new file mode 100644 index 00000000..0fb0f59e --- /dev/null +++ b/langs/tl/tutorials/flow_index/solved.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from 'solid-js/web';\nimport { createSignal, Index } from 'solid-js';\n\nfunction App() {\n const [cats, setCats] = createSignal([\n { id: 'J---aiyznGQ', name: 'Keyboard Cat' },\n { id: 'z_AbfPXTKms', name: 'Maru' },\n { id: 'OUtn3pvWmpg', name: 'Henri The Existential Cat' }\n ]);\n \n return (\n \n );\n}\n\nrender(() => , document.getElementById('app'))\n" + } + ] +} diff --git a/langs/tl/tutorials/flow_portal/lesson.json b/langs/tl/tutorials/flow_portal/lesson.json new file mode 100644 index 00000000..024fe7fa --- /dev/null +++ b/langs/tl/tutorials/flow_portal/lesson.json @@ -0,0 +1,13 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render, Portal } from \"solid-js/web\";\nimport \"./styles.css\";\n\nfunction App() {\n return (\n
    \n

    Just some text inside a div that has a restricted size.

    \n
    \n

    Popup

    \n

    Some text you might need for something or other.

    \n
    \n
    \n );\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + }, + { + "name": "styles", + "type": "css", + "content": ".app-container {\n width: 200px;\n height: 100px;\n overflow: hidden;\n}\n\n.popup {\n position: relative;\n z-index: 2;\n background: #ddd;\n padding: 1rem;\n min-height: 200px;\n min-width: 200px;\n}\n\n.popup::after {\n content: \" \";\n position: absolute;\n bottom: 100%;\n left: 50%;\n margin-left: -5px;\n border-width: 5px;\n border-style: solid;\n border-color: transparent transparent #ddd transparent;\n}" + } + ] +} diff --git a/langs/tl/tutorials/flow_portal/lesson.md b/langs/tl/tutorials/flow_portal/lesson.md new file mode 100644 index 00000000..81b99ecf --- /dev/null +++ b/langs/tl/tutorials/flow_portal/lesson.md @@ -0,0 +1,14 @@ +Sometimes it's beneficial to insert elements outside the normal flow of the app. Z-indexes are sometimes insufficient to deal with render contexts for floating elements like Modals. + +Solid has a `` component whose child content will be inserted at the location of your choosing. By default, its elements will be rendered in a `
    ` in the `document.body`. + +In the example, we see our information popup get cut off. We can solve this by pulling it out of the flow by wrapping the element in a ``: + +```jsx + + + +``` diff --git a/langs/tl/tutorials/flow_portal/solved.json b/langs/tl/tutorials/flow_portal/solved.json new file mode 100644 index 00000000..2de9f10b --- /dev/null +++ b/langs/tl/tutorials/flow_portal/solved.json @@ -0,0 +1,13 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render, Portal } from \"solid-js/web\";\nimport \"./styles.css\";\n\nfunction App() {\n return (\n
    \n

    Just some text inside a div that has a restricted size.

    \n \n
    \n

    Popup

    \n

    Some text you might need for something or other.

    \n
    \n
    \n
    \n );\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + }, + { + "name": "styles", + "type": "css", + "content": ".app-container {\n width: 200px;\n height: 100px;\n overflow: hidden;\n}\n\n.popup {\n position: relative;\n z-index: 2;\n background: #ddd;\n padding: 1rem;\n min-height: 200px;\n min-width: 200px;\n}\n\n.popup::after {\n content: \" \";\n position: absolute;\n bottom: 100%;\n left: 50%;\n margin-left: -5px;\n border-width: 5px;\n border-style: solid;\n border-color: transparent transparent #ddd transparent;\n}" + } + ] +} diff --git a/langs/tl/tutorials/flow_show/lesson.json b/langs/tl/tutorials/flow_show/lesson.json new file mode 100644 index 00000000..53afe6f3 --- /dev/null +++ b/langs/tl/tutorials/flow_show/lesson.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from 'solid-js/web';\nimport { createSignal, Show } from 'solid-js';\n\nfunction App() {\n const [loggedIn, setLoggedIn] = createSignal(false);\n const toggle = () => setLoggedIn(!loggedIn())\n \n return (\n <>\n \n \n \n );\n}\n\nrender(() => , document.getElementById('app'))\n" + } + ] +} diff --git a/langs/tl/tutorials/flow_show/lesson.md b/langs/tl/tutorials/flow_show/lesson.md new file mode 100644 index 00000000..ca430172 --- /dev/null +++ b/langs/tl/tutorials/flow_show/lesson.md @@ -0,0 +1,16 @@ +JSX allows you to use JavaScript to control the logic flow in the templates. However, without a Virtual DOM, naive use of things like `Array.prototype.map` would wastefully recreate all the DOM nodes on every update. Instead it is common for Reactive libraries to use template helpers. In Solid we wrap them in components. + +The most basic control flow is the conditional. Solid's compiler is smart enough to optimally handle ternaries (`a ? b : c`) and boolean expressions (`a && b`). However, often it is more readable to use Solid's `` component. + +In the example, we would like to show only the appropriate button that reflects the current state (whether the user is logged in). Update the JSX to: +```jsx + } +> + + +``` +The `fallback` prop acts as the `else` and will show when the condition passed to `when` is not truthy. + +Now clicking the button will change back and forth like you would expect. diff --git a/langs/tl/tutorials/flow_show/solved.json b/langs/tl/tutorials/flow_show/solved.json new file mode 100644 index 00000000..9f8ffe63 --- /dev/null +++ b/langs/tl/tutorials/flow_show/solved.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from 'solid-js/web';\nimport { createSignal, Show } from 'solid-js';\n\nfunction App() {\n const [loggedIn, setLoggedIn] = createSignal(false);\n const toggle = () => setLoggedIn(!loggedIn())\n \n return (\n Log in}\n >\n \n \n );\n}\n\nrender(() => , document.getElementById('app'))\n" + } + ] +} diff --git a/langs/tl/tutorials/flow_switch/lesson.json b/langs/tl/tutorials/flow_switch/lesson.json new file mode 100644 index 00000000..7ae3bc59 --- /dev/null +++ b/langs/tl/tutorials/flow_switch/lesson.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal, Show, Switch, Match } from \"solid-js\";\n\nfunction App() {\n const [x] = createSignal(7);\n\n return (\n 10}\n fallback={\n x()}\n fallback={

    {x()} is between 5 and 10

    }\n >\n

    {x()} is less than 5

    \n \n }\n >\n

    {x()} is greater than 10

    \n \n );\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + } + ] +} diff --git a/langs/tl/tutorials/flow_switch/lesson.md b/langs/tl/tutorials/flow_switch/lesson.md new file mode 100644 index 00000000..f460fd82 --- /dev/null +++ b/langs/tl/tutorials/flow_switch/lesson.md @@ -0,0 +1,16 @@ +Sometimes you need to deal with conditionals with more than 2 mutual exclusive outcomes. For this case, we have the `` and `` components modeled roughly after JavaScript's `switch`/`case`. + +It will try in order to match each condition, stopping to render the first that evaluates to true. Failing all of them, it will render the the fallback. + +In the example, we can replace our nested `` components with this: + +```jsx +{x()} is between 5 and 10

    }> + 10}> +

    {x()} is greater than 10

    +
    + x()}> +

    {x()} is less than 5

    +
    +
    +``` diff --git a/langs/tl/tutorials/flow_switch/solved.json b/langs/tl/tutorials/flow_switch/solved.json new file mode 100644 index 00000000..0900e88c --- /dev/null +++ b/langs/tl/tutorials/flow_switch/solved.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal, Switch, Match } from \"solid-js\";\n\nfunction App() {\n const [x] = createSignal(7);\n\n return (\n {x()} is between 5 and 10

    }>\n 10} >\n

    {x()} is greater than 10

    \n
    \n x()}>\n

    {x()} is less than 5

    \n
    \n
    \n );\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + } + ] +} diff --git a/langs/tl/tutorials/introduction_basics/lesson.json b/langs/tl/tutorials/introduction_basics/lesson.json new file mode 100644 index 00000000..5b8b83aa --- /dev/null +++ b/langs/tl/tutorials/introduction_basics/lesson.json @@ -0,0 +1,9 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from 'solid-js/web';\n\nfunction HelloWorld() {\n return
    Hello World!
    ;\n}\n\nrender(() => , document.getElementById('app'))\n" + } + ] +} + diff --git a/langs/tl/tutorials/introduction_basics/lesson.md b/langs/tl/tutorials/introduction_basics/lesson.md new file mode 100644 index 00000000..9072f278 --- /dev/null +++ b/langs/tl/tutorials/introduction_basics/lesson.md @@ -0,0 +1,24 @@ +# Introduction + +Welcome to the Solid tutorial! This tutorial will walk you through Solid's main features. You can also refer to the API and guides to learn more about how Solid works. + +# What is Solid? +Solid is a JavaScript framework for making interactive web applications. +With Solid, you can use your existing HTML and JavaScript knowledge to build components that can be reused throughout your app. +Solid provides the tools to enhance your components with _reactivity_: declarative JavaScript code that links the user interface with the data that it uses and creates. + +# Anatomy of a Solid App + +A Solid App is composed of functions that we call components. Take a look at the `HelloWorld` function on the right: it directly returns a `div`! This mix of HTML and JavaScript is called JSX. Solid ships with a compiler that turns these tags into DOM nodes later on. + +JSX allows you to use most HTML elements in our app, but it also lets you create new elements. Once we've declared our `HelloWorld` function, we can use it as a `` tag throughout our app. + +The entry point for any Solid App is the `render` function. It takes 2 arguments, a function wrapping our application code and an existing element in the HTML to mount to: +```jsx +render(() => , document.getElementById('app')) +``` +# Leveraging this Tutorial + +Each lesson in the tutorial presents a Solid feature and a scenario to try it out. At any point you can click the solve button to see the solution or click reset to start over. The code editor itself has a console and an output tab where you can see the compiled output generated from your code. Look at it if you are curious to see how Solid generates code. + +Have fun! diff --git a/langs/tl/tutorials/introduction_basics/solved.json b/langs/tl/tutorials/introduction_basics/solved.json new file mode 100644 index 00000000..31200eb3 --- /dev/null +++ b/langs/tl/tutorials/introduction_basics/solved.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from 'solid-js/web';\n\nfunction HelloWorld() {\n return
    Hello Solid World!
    ;\n}\n\nrender(() => , document.getElementById('app'))\n" + } + ] +} diff --git a/langs/tl/tutorials/introduction_components/lesson.json b/langs/tl/tutorials/introduction_components/lesson.json new file mode 100644 index 00000000..3e0d70ed --- /dev/null +++ b/langs/tl/tutorials/introduction_components/lesson.json @@ -0,0 +1,12 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\n\nfunction App() {\n return (\n

    This is a Header

    \n );\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + }, + { + "name": "nested", + "content": "export default () =>

    This is a Paragraph

    " + } + ] +} diff --git a/langs/tl/tutorials/introduction_components/lesson.md b/langs/tl/tutorials/introduction_components/lesson.md new file mode 100644 index 00000000..03568b74 --- /dev/null +++ b/langs/tl/tutorials/introduction_components/lesson.md @@ -0,0 +1,24 @@ +As you build your applications, you will want to break apart your code for better modularity and reusability. In Solid, the main way of doing that is by creating components. + +Components are just functions like the `HelloWorld()` one we've been using so far. What makes them special is that they typically return JSX and can be called by JSX in other components. + +In this example, let's add our `Nested` component to our app. We've defined it in another file, though you can put multiple components in the same file. First we must import it: + +```js +import Nested from "./nested"; +``` + +Then we need to add the component to our JSX. Like before, we now have multiple elements we want to return, so we wrap them in a Fragment: + +```jsx +function App() { + return ( + <> +

    This is a Header

    + + + ); +} +``` + +When the parent component first renders, it will execute the `Nested()` function and won't call it ever again. All updates are applied by Solid’s reactivity system which we will cover in the next couple of lessons. diff --git a/langs/tl/tutorials/introduction_components/solved.json b/langs/tl/tutorials/introduction_components/solved.json new file mode 100644 index 00000000..3a259599 --- /dev/null +++ b/langs/tl/tutorials/introduction_components/solved.json @@ -0,0 +1,12 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport Nested from \"./nested\";\n\nfunction App() {\n return (\n <>\n

    This is a Header

    \n \n \n );\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + }, + { + "name": "nested", + "content": "export default () =>

    This is a Paragraph

    " + } + ] +} diff --git a/langs/tl/tutorials/introduction_derived/lesson.json b/langs/tl/tutorials/introduction_derived/lesson.json new file mode 100644 index 00000000..266f7a15 --- /dev/null +++ b/langs/tl/tutorials/introduction_derived/lesson.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal } from \"solid-js\";\n\nfunction Counter() {\n const [count, setCount] = createSignal(0);\n\n setInterval(() => setCount(count() + 1), 1000);\n\n return
    Count: {count()}
    ;\n}\n\nrender(() => , document.getElementById('app'));" + } + ] +} diff --git a/langs/tl/tutorials/introduction_derived/lesson.md b/langs/tl/tutorials/introduction_derived/lesson.md new file mode 100644 index 00000000..eaef05c0 --- /dev/null +++ b/langs/tl/tutorials/introduction_derived/lesson.md @@ -0,0 +1,17 @@ +We've seen that whenever we access a signal in JSX, it will automatically update the view when that signal changes. But the component function itself only executes once. + +We can create new expressions that depend on signals by wrapping a signal in a function. A function that accesses a signal is effectively also a signal: when its wrapped signal changes it will in turn update its readers. + + +Let's update our Counter to count by 2 by introducing a `doubleCount` function: + +```jsx +const doubleCount = () => count() * 2; +``` + +We can then call `doubleCount` just like a signal in our JSX: +```jsx +return
    Count: {doubleCount()}
    ; +``` + +We call functions like these _derived signals_ because they gain their reactivity from the signal they access. They don't themselves store a value (if you create a derived signal but never call it, it will be stripped from Solid's output like any unused function) but they'll update any effects that depend on them, and they'll trigger a rerender if included in a view. \ No newline at end of file diff --git a/langs/tl/tutorials/introduction_derived/solved.json b/langs/tl/tutorials/introduction_derived/solved.json new file mode 100644 index 00000000..71bf7c80 --- /dev/null +++ b/langs/tl/tutorials/introduction_derived/solved.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal } from \"solid-js\";\n\nfunction Counter() {\n const [count, setCount] = createSignal(0);\n const doubleCount = () => count() * 2;\n\n setInterval(() => setCount(count() + 1), 1000);\n\n return
    Count: {doubleCount()}
    ;\n}\n\nrender(() => , document.getElementById('app'));" + } + ] +} diff --git a/langs/tl/tutorials/introduction_effects/lesson.json b/langs/tl/tutorials/introduction_effects/lesson.json new file mode 100644 index 00000000..f5176ea0 --- /dev/null +++ b/langs/tl/tutorials/introduction_effects/lesson.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from 'solid-js/web';\nimport { createSignal, createEffect } from 'solid-js';\n\nfunction Counter() {\n const [count, setCount] = createSignal(0);\n\n return ;\n}\n\nrender(() => , document.getElementById('app'))\n" + } + ] +} diff --git a/langs/tl/tutorials/introduction_effects/lesson.md b/langs/tl/tutorials/introduction_effects/lesson.md new file mode 100644 index 00000000..9eb6fb60 --- /dev/null +++ b/langs/tl/tutorials/introduction_effects/lesson.md @@ -0,0 +1,22 @@ +Signals are trackable values, but they are only one half of the equation. To complement those are observers that can be updated by those trackable values. An _effect_ is one such observer; it runs a side effect that depends on signals. + +An effect can be created by importing `createEffect` from `solid-js` and providing it a function. The effect automatically subscribes to any signal that is read during the function's execution and reruns when any of them change. + +So let's create an Effect that reruns whenever `count` changes: + +```jsx +createEffect(() => { + console.log("The count is now", count()); +}); +``` + +To update our `count` Signal, we'll attach a click handler on our button: + +```jsx + +``` + +Now clicking the button writes to the console. This is a relatively simple example, but to understand how Solid works, you should imagine that every expression in JSX is its own effect that re-executes whenever its dependent signals change. This is how all rendering works in Solid: from Solid's perspective, *all rendering is just a side effect of the reactive system*. + + +> Effects that developers create with `createEffect` run after rendering has completed and are mostly used for scheduling updates that interact with the DOM. If you want to modify the DOM earlier, use [`createRenderEffect`](https://www.solidjs.com/docs/latest/api#createrendereffect). \ No newline at end of file diff --git a/langs/tl/tutorials/introduction_effects/solved.json b/langs/tl/tutorials/introduction_effects/solved.json new file mode 100644 index 00000000..8732c699 --- /dev/null +++ b/langs/tl/tutorials/introduction_effects/solved.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from 'solid-js/web';\nimport { createSignal, createEffect } from 'solid-js';\n\nfunction Counter() {\n const [count, setCount] = createSignal(0);\n createEffect(() => {\n console.log(\"The count is now\", count());\n });\n\n return ;\n}\n\nrender(() => , document.getElementById('app'));\n" + } + ] +} diff --git a/langs/tl/tutorials/introduction_jsx/lesson.json b/langs/tl/tutorials/introduction_jsx/lesson.json new file mode 100644 index 00000000..5837d9df --- /dev/null +++ b/langs/tl/tutorials/introduction_jsx/lesson.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from 'solid-js/web';\n\nfunction HelloWorld() {\n const name = \"Solid\";\n const svg = (
    Replace me with an svg
    )\n\n return (\n <>\n
    Hello {name}!
    \n {svg}\n \n )\n}\n\nrender(() => , document.getElementById('app'))\n" + } + ] +} \ No newline at end of file diff --git a/langs/tl/tutorials/introduction_jsx/lesson.md b/langs/tl/tutorials/introduction_jsx/lesson.md new file mode 100644 index 00000000..e8d3693e --- /dev/null +++ b/langs/tl/tutorials/introduction_jsx/lesson.md @@ -0,0 +1,31 @@ +JSX is the HTML-like syntax we've seen inside these examples and is core to building components in Solid. +JSX adds dynamic expressions that allow you to reference variables and functions within your HTML by using the `{ } ` syntax. +In this example, we include the string `name` in our HTML using `{name}` inside a div. In the same way, we include an HTML element that was directly assigned to the `svg` variable. + +Unlike some other frameworks that use JSX, Solid attempts to stay as close to HTML standards as possible, allowing simple copy and paste from answers on Stack Overflow or from template builders from your designers. + +There are 3 main differences between JSX and HTML that prevent JSX from being seen as a superset of HTML: +1. JSX does not have void elements. This means that all elements must have a closing tag or self-close. Keep this in mind when copying over elements like `` or `
    `. +2. JSX must return a single Element. To represent multiple top level elements, use a Fragment element: + + ```jsx + <> +

    Title

    +

    Some Text

    + + ``` +3. JSX doesn't support HTML Comments `` or special tags like ``. + +But JSX does support SVG. Let's try copying some SVG right into our component: +```jsx + + + + + + + + + Sorry but this browser does not support inline SVG. + +``` diff --git a/langs/tl/tutorials/introduction_jsx/solved.json b/langs/tl/tutorials/introduction_jsx/solved.json new file mode 100644 index 00000000..4a1bd44a --- /dev/null +++ b/langs/tl/tutorials/introduction_jsx/solved.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from 'solid-js/web';\n\nfunction HelloWorld() {\n const name = \"Solid\";\n const svg = (\n \n \n \n \n \n \n \n \n Sorry but this browser does not support inline SVG.\n \n );\n\n return (\n <>\n
    Hello {name}!
    \n {svg}\n \n )\n}\n\nrender(() => , document.getElementById('app'))\n" + } + ] +} \ No newline at end of file diff --git a/langs/tl/tutorials/introduction_memos/lesson.json b/langs/tl/tutorials/introduction_memos/lesson.json new file mode 100644 index 00000000..cc7c690a --- /dev/null +++ b/langs/tl/tutorials/introduction_memos/lesson.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from 'solid-js/web';\nimport { createSignal, createMemo } from 'solid-js';\n\nfunction fibonacci(num) {\n if (num <= 1) return 1;\n\n return fibonacci(num - 1) + fibonacci(num - 2);\n}\n\nfunction Counter() {\n const [count, setCount] = createSignal(10);\n const fib = () => fibonacci(count());\n\n return (\n <>\n \n
    1. {fib()} {fib()} {fib()} {fib()} {fib()}
    \n
    2. {fib()} {fib()} {fib()} {fib()} {fib()}
    \n
    3. {fib()} {fib()} {fib()} {fib()} {fib()}
    \n
    4. {fib()} {fib()} {fib()} {fib()} {fib()}
    \n
    5. {fib()} {fib()} {fib()} {fib()} {fib()}
    \n
    6. {fib()} {fib()} {fib()} {fib()} {fib()}
    \n
    7. {fib()} {fib()} {fib()} {fib()} {fib()}
    \n
    8. {fib()} {fib()} {fib()} {fib()} {fib()}
    \n
    9. {fib()} {fib()} {fib()} {fib()} {fib()}
    \n
    10. {fib()} {fib()} {fib()} {fib()} {fib()}
    \n \n );\n}\n\nrender(() => , document.getElementById('app'))\n" + } + ] +} diff --git a/langs/tl/tutorials/introduction_memos/lesson.md b/langs/tl/tutorials/introduction_memos/lesson.md new file mode 100644 index 00000000..2999d906 --- /dev/null +++ b/langs/tl/tutorials/introduction_memos/lesson.md @@ -0,0 +1,17 @@ +Most of the time, composing derived signals is sufficient. However, it is sometimes beneficial to cache values +in order to reduce duplicated work. We can use memos to evaluate a function and store the result until its dependencies change. This is great for caching calculations for effects that have other dependencies and mitigating the work required for expensive operations like DOM node creation. + +Memos are both an observer, like an effect, and a read-only signal. Since they are aware of both their dependencies and their observers, they can ensure that they run only once for any change. This makes them preferable to registering effects that write to signals. Generally, what can be derived, should be derived. + +Creating a Memo is as simple as passing a function to `createMemo`, imported from `solid-js`. In the example, recalculating the value gets increasingly more expensive with each click. If we wrap it in `createMemo`, it recalculates only once per click: + +```jsx +const fib = createMemo(() => fibonacci(count())); +``` +Place a `console.log` inside the `fib` function to confirm how often it runs: +```jsx +const fib = createMemo(() => { + console.log("Calculating Fibonacci"); + return fibonacci(count()); +}); +``` diff --git a/langs/tl/tutorials/introduction_memos/solved.json b/langs/tl/tutorials/introduction_memos/solved.json new file mode 100644 index 00000000..560fb558 --- /dev/null +++ b/langs/tl/tutorials/introduction_memos/solved.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from 'solid-js/web';\nimport { createSignal, createMemo } from 'solid-js';\n\nfunction fibonacci(num) {\n if (num <= 1) return 1;\n\n return fibonacci(num - 1) + fibonacci(num - 2);\n}\n\nfunction Counter() {\n const [count, setCount] = createSignal(10);\n const fib = createMemo(() => fibonacci(count()));\n\n return (\n <>\n \n
    1. {fib()} {fib()} {fib()} {fib()} {fib()}
    \n
    2. {fib()} {fib()} {fib()} {fib()} {fib()}
    \n
    3. {fib()} {fib()} {fib()} {fib()} {fib()}
    \n
    4. {fib()} {fib()} {fib()} {fib()} {fib()}
    \n
    5. {fib()} {fib()} {fib()} {fib()} {fib()}
    \n
    6. {fib()} {fib()} {fib()} {fib()} {fib()}
    \n
    7. {fib()} {fib()} {fib()} {fib()} {fib()}
    \n
    8. {fib()} {fib()} {fib()} {fib()} {fib()}
    \n
    9. {fib()} {fib()} {fib()} {fib()} {fib()}
    \n
    10. {fib()} {fib()} {fib()} {fib()} {fib()}
    \n \n );\n}\n\nrender(() => , document.getElementById('app'))\n" + } + ] +} diff --git a/langs/tl/tutorials/introduction_signals/lesson.json b/langs/tl/tutorials/introduction_signals/lesson.json new file mode 100644 index 00000000..47186a77 --- /dev/null +++ b/langs/tl/tutorials/introduction_signals/lesson.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal } from \"solid-js\";\n\nfunction Counter() {\n return
    Count: 0
    ;\n}\n\nrender(() => , document.getElementById('app'));" + } + ] +} diff --git a/langs/tl/tutorials/introduction_signals/lesson.md b/langs/tl/tutorials/introduction_signals/lesson.md new file mode 100644 index 00000000..465ba9db --- /dev/null +++ b/langs/tl/tutorials/introduction_signals/lesson.md @@ -0,0 +1,29 @@ +_Signals_ are the cornerstone of reactivity in Solid. They contain values that change over time; when you change a signal's value, it automatically updates anything that uses it. + +To create a signal, let's import `createSignal` from `solid-js` and call it from our Counter component like this: +```jsx +const [count, setCount] = createSignal(0); +``` + +The argument passed to the create call is the initial value, and the return value is an array with two functions, a getter and a setter. By [destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment), we can name these functions whatever we like. In this case, we name the getter `count` and the setter `setCount`. + +It is important to notice that the first returned value is a getter (a function returning the current value) and not the value itself. This is because the framework needs to keep track of where that signal is read so it can update things accordingly. + +In this lesson, we'll use JavaScript's `setInterval` function to create a periodically incrementing counter. We can update our `count` signal every second by adding this code to our Counter component: + +```jsx +setInterval(() => setCount(count() + 1), 1000); +``` + +Each tick, we read the previous count, add 1, and set the new value. + +> Solid's signals also accept a function form where you can use the previous value to set the next value. +> ```jsx +> setCount(c => c + 1); +> ``` + +Finally, we need to read the signal in our JSX code: + +```jsx +return
    Count: {count()}
    ; +``` diff --git a/langs/tl/tutorials/introduction_signals/solved.json b/langs/tl/tutorials/introduction_signals/solved.json new file mode 100644 index 00000000..266f7a15 --- /dev/null +++ b/langs/tl/tutorials/introduction_signals/solved.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal } from \"solid-js\";\n\nfunction Counter() {\n const [count, setCount] = createSignal(0);\n\n setInterval(() => setCount(count() + 1), 1000);\n\n return
    Count: {count()}
    ;\n}\n\nrender(() => , document.getElementById('app'));" + } + ] +} diff --git a/langs/tl/tutorials/lifecycles_oncleanup/lesson.json b/langs/tl/tutorials/lifecycles_oncleanup/lesson.json new file mode 100644 index 00000000..21ee04ad --- /dev/null +++ b/langs/tl/tutorials/lifecycles_oncleanup/lesson.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal, onCleanup } from \"solid-js\";\n\nfunction Counter() {\n const [count, setCount] = createSignal(0);\n\n setInterval(() => setCount(count() + 1), 1000);\n\n return
    Count: {count()}
    ;\n}\n\nrender(() => , document.getElementById('app'));" + } + ] +} diff --git a/langs/tl/tutorials/lifecycles_oncleanup/lesson.md b/langs/tl/tutorials/lifecycles_oncleanup/lesson.md new file mode 100644 index 00000000..0fdc6207 --- /dev/null +++ b/langs/tl/tutorials/lifecycles_oncleanup/lesson.md @@ -0,0 +1,10 @@ +Some frameworks pair their cleanup methods as return values of their side effects or lifecycle methods. Since everything in a Solid render tree is living inside a (possibly inert) Effect and can be nested, we made `onCleanup` a first-class method. You can call it at any scope and it will run when that scope is triggered to re-evaluate and when it is finally disposed. + +Use it in your components or in your Effects. Use it in your custom directives. Use it pretty much anywhere that is part of the synchronous execution of the reactive system. + +The Signal example from the introduction never cleaned up after itself. Let's fix that by replacing the `setInterval` call with this: + +```js +const timer = setInterval(() => setCount(count() + 1), 1000); +onCleanup(() => clearInterval(timer)); +``` \ No newline at end of file diff --git a/langs/tl/tutorials/lifecycles_oncleanup/solved.json b/langs/tl/tutorials/lifecycles_oncleanup/solved.json new file mode 100644 index 00000000..f7fde7ca --- /dev/null +++ b/langs/tl/tutorials/lifecycles_oncleanup/solved.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal, onCleanup } from \"solid-js\";\n\nfunction Counter() {\n const [count, setCount] = createSignal(0);\n\n const timer = setInterval(() => setCount(count() + 1), 1000);\n onCleanup(() => clearInterval(timer));\n\n return
    Count: {count()}
    ;\n}\n\nrender(() => , document.getElementById('app'));" + } + ] +} diff --git a/langs/tl/tutorials/lifecycles_onmount/lesson.json b/langs/tl/tutorials/lifecycles_onmount/lesson.json new file mode 100644 index 00000000..77dd5843 --- /dev/null +++ b/langs/tl/tutorials/lifecycles_onmount/lesson.json @@ -0,0 +1,13 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal, onMount, For } from \"solid-js\";\nimport \"./styles.css\";\n\nfunction App() {\n const [photos, setPhotos] = createSignal([]);\n\n return <>\n

    Photo album

    \n\n
    \n Loading...

    }>{ photo =>\n
    \n {photo.title}\n
    {photo.title}
    \n
    \n }
    \n
    \n ;\n}\n\nrender(() => , document.getElementById('app'));" + }, + { + "name": "styles", + "type": "css", + "content": ".photos {\n width: 100%;\n display: grid;\n grid-template-columns: repeat(5, 1fr);\n grid-gap: 8px;\n}\n\nfigure, img {\n width: 100%;\n margin: 0;\n}" + } + ] +} diff --git a/langs/tl/tutorials/lifecycles_onmount/lesson.md b/langs/tl/tutorials/lifecycles_onmount/lesson.md new file mode 100644 index 00000000..c9e0a4e7 --- /dev/null +++ b/langs/tl/tutorials/lifecycles_onmount/lesson.md @@ -0,0 +1,13 @@ +There are only few Lifecycles in Solid as everything lives and dies by the reactive system. The reactive system is created and updates synchronously, so the only scheduling comes down to Effects which are pushed to the end of the update. + +We've found that developers doing simple tasks don't often think this way, so to make things a little easier we've aliased a non-tracking (never reruns) `createEffect` call with `onMount`. It is just an Effect call but you can use it with confidence that it will run only once for your component, once all initial rendering is done. + +Let's use the `onMount` hook to fetch some photos: +```js +onMount(async () => { + const res = await fetch(`https://jsonplaceholder.typicode.com/photos?_limit=20`); + setPhotos(await res.json()); +}); +``` + +Lifecycles are only run in the browser, so putting code in `onMount` has the benefit of not running on the server during SSR. Even though we are doing data fetching in this example, usually we use Solid's resources for true server/browser coordination. diff --git a/langs/tl/tutorials/lifecycles_onmount/solved.json b/langs/tl/tutorials/lifecycles_onmount/solved.json new file mode 100644 index 00000000..1638c344 --- /dev/null +++ b/langs/tl/tutorials/lifecycles_onmount/solved.json @@ -0,0 +1,13 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal, onMount, For } from \"solid-js\";\nimport \"./styles.css\";\n\nfunction App() {\n const [photos, setPhotos] = createSignal([]);\n\n onMount(async () => {\n const res = await fetch(`https://jsonplaceholder.typicode.com/photos?_limit=20`);\n setPhotos(await res.json());\n });\n\n return <>\n

    Photo album

    \n\n
    \n Loading...

    }>{ photo =>\n
    \n {photo.title}\n
    {photo.title}
    \n
    \n }
    \n
    \n ;\n}\n\nrender(() => , document.getElementById('app'));" + }, + { + "name": "styles", + "type": "css", + "content": ".photos {\n width: 100%;\n display: grid;\n grid-template-columns: repeat(5, 1fr);\n grid-gap: 8px;\n}\n\nfigure, img {\n width: 100%;\n margin: 0;\n}" + } + ] +} diff --git a/langs/tl/tutorials/props_children/lesson.json b/langs/tl/tutorials/props_children/lesson.json new file mode 100644 index 00000000..4d4051be --- /dev/null +++ b/langs/tl/tutorials/props_children/lesson.json @@ -0,0 +1,12 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal, For } from \"solid-js\";\n\nimport ColoredList from \"./colored-list\";\n\nfunction App() {\n const [color, setColor] = createSignal(\"purple\");\n\n return <>\n \n {item =>
    {item}
    }
    \n
    \n \n ;\n}\n\nrender(() => , document.getElementById('app'));" + }, + { + "name": "colored-list", + "content": "import { createEffect, children } from \"solid-js\";\n\nexport default function ColoredList(props) {\n return <>{props.children}\n}" + } + ] +} diff --git a/langs/tl/tutorials/props_children/lesson.md b/langs/tl/tutorials/props_children/lesson.md new file mode 100644 index 00000000..d277a2be --- /dev/null +++ b/langs/tl/tutorials/props_children/lesson.md @@ -0,0 +1,33 @@ +Part of what makes Solid so performant is that our components are basically just function calls. The way we propagate updates is that the compiler wraps potentially reactive expressions in object getters. You can picture the compiler to output: + +```jsx +// this JSX + + + + +// to become +MyComp({ + get dynamic() { return mySignal() }, + get children() { return Child() } +}); +``` +This means that these props are evaluated lazily. Their access is deferred until where they are used. This retains reactivity without introducing extraneous wrappers or synchronization. However, it means that repeat access can lead to recreating child components or elements. + +The vast majority of the time you will just be inserting props into the JSX so there is no problem. However, when you work with children, you need to be careful to avoid creating the children multiple times. + +For that reason, Solid has the `children` helper. This method both creates a memo around the `children` prop and resolves any nested child reactive references so that you can interact with the children directly. + +In the example, we have a dynamic list and we want to set the items' `color` style property. If we interacted with `props.children` directly, not only would we create the nodes multiple times, but we'd find `props.children` to be a function, the Memo returned from ``. + +Instead let's use the `children` helper inside `colored-list.tsx`: +```jsx +export default function ColoredList(props) { + const c = children(() => props.children); + return <>{c()} +} +``` +Now to update our elements, let's create an Effect: +```jsx +createEffect(() => c().forEach(item => item.style.color = props.color)); +``` diff --git a/langs/tl/tutorials/props_children/solved.json b/langs/tl/tutorials/props_children/solved.json new file mode 100644 index 00000000..7f7aa8d5 --- /dev/null +++ b/langs/tl/tutorials/props_children/solved.json @@ -0,0 +1,12 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal, For } from \"solid-js\";\n\nimport ColoredList from \"./colored-list\";\n\nfunction App() {\n const [color, setColor] = createSignal(\"purple\");\n\n return <>\n \n {item =>
    {item}
    }
    \n
    \n \n ;\n}\n\nrender(() => , document.getElementById('app'));" + }, + { + "name": "colored-list", + "content": "import { createEffect, children } from \"solid-js\";\n\nexport default function ColoredList(props) {\n const c = children(() => props.children);\n createEffect(() => c().forEach(item => item.style.color = props.color));\n return <>{c()}\n}" + } + ] +} diff --git a/langs/tl/tutorials/props_defaults/lesson.json b/langs/tl/tutorials/props_defaults/lesson.json new file mode 100644 index 00000000..671b1e57 --- /dev/null +++ b/langs/tl/tutorials/props_defaults/lesson.json @@ -0,0 +1,12 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal } from \"solid-js\";\n\nimport Greeting from \"./greeting\";\n\nfunction App() {\n const [name, setName] = createSignal();\n\n return <>\n \n \n \n \n ;\n}\n\nrender(() => , document.getElementById('app'));" + }, + { + "name": "greeting", + "content": "import { mergeProps } from \"solid-js\";\n\nexport default function Greeting(props) {\n return

    {props.greeting || \"Hi\"} {props.name || \"John\"}

    \n}" + } + ] +} diff --git a/langs/tl/tutorials/props_defaults/lesson.md b/langs/tl/tutorials/props_defaults/lesson.md new file mode 100644 index 00000000..b04f1e71 --- /dev/null +++ b/langs/tl/tutorials/props_defaults/lesson.md @@ -0,0 +1,13 @@ +Props are what we call the object that is passed to our component function on execution that represents all the attributes bound to its JSX. Props objects are readonly and have reactive properties which are wrapped in Object getters. This allows them to have a consistent form regardless of whether the caller used signals, signal expressions, or simple or static values. You simply access them by `props.propName`. + +For this reason it is also very important to not just destructure props objects, as that would lose reactivity if not done within a tracking scope. In general accessing properties on the props object outside of Solid's primitives or JSX can lose reactivity. This applies not just to destructuring, but also to spreads and functions like `Object.assign`. + +Solid has a few utilities to help us when working with props. The first is `mergeProps`, which merges potentially reactive objects together (like a nondestructive `Object.assign`) without losing reactivity. The most common case is when setting default props for our components. + +In the example, in `greetings.tsx`, we inlined the defaults in the template, but we could also use `mergeProps` to keep reactive updates even when setting defaults: + +```jsx +const merged = mergeProps({ greeting: "Hi", name: "John" }, props); + +return

    {merged.greeting} {merged.name}

    +``` diff --git a/langs/tl/tutorials/props_defaults/solved.json b/langs/tl/tutorials/props_defaults/solved.json new file mode 100644 index 00000000..17361fc1 --- /dev/null +++ b/langs/tl/tutorials/props_defaults/solved.json @@ -0,0 +1,12 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal } from \"solid-js\";\n\nimport Greeting from \"./greeting\";\n\nfunction App() {\n const [name, setName] = createSignal();\n\n return <>\n \n \n \n \n ;\n}\n\nrender(() => , document.getElementById('app'));" + }, + { + "name": "greeting", + "content": "import { mergeProps } from \"solid-js\";\n\nexport default function Greeting(props) {\n const merged = mergeProps({ greeting: \"Hi\", name: \"John\" }, props);\n\n return

    {merged.greeting} {merged.name}

    \n}" + } + ] +} diff --git a/langs/tl/tutorials/props_split/lesson.json b/langs/tl/tutorials/props_split/lesson.json new file mode 100644 index 00000000..0439f16a --- /dev/null +++ b/langs/tl/tutorials/props_split/lesson.json @@ -0,0 +1,12 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal } from \"solid-js\";\n\nimport Greeting from \"./greeting\";\n\nfunction App() {\n const [name, setName] = createSignal(\"Jakob\");\n\n return <>\n \n \n ;\n}\n\nrender(() => , document.getElementById('app'));" + }, + { + "name": "greeting", + "content": "import { splitProps } from \"solid-js\";\n\nexport default function Greeting(props) {\n const { greeting, name, ...others } = props;\n return

    {greeting} {name}

    \n}" + } + ] +} diff --git a/langs/tl/tutorials/props_split/lesson.md b/langs/tl/tutorials/props_split/lesson.md new file mode 100644 index 00000000..c7d7f08b --- /dev/null +++ b/langs/tl/tutorials/props_split/lesson.md @@ -0,0 +1,20 @@ +Merging props is not the only operation we can do. Often we use destructuring to use some of the props on the current component but then split off others to pass through to child components. + +For this purpose, Solid has `splitProps`. It takes the props object and one or more arrays of keys that we want to extract into their own props objects. It returns an array of props objects, one per array of specified keys, plus one props object with any remaining keys, similar to the rest parameter. All returned objects preserve reactivity. + +Our example doesn't update when we set the name because of lost reactivity when we destructure in `greeting.tsx`: +```jsx +export default function Greeting(props) { + const { greeting, name, ...others } = props; + return

    {greeting} {name}

    +} +``` + +Instead we can maintain reactivity with `splitProps`: +```jsx +export default function Greeting(props) { + const [local, others] = splitProps(props, ["greeting", "name"]); + return

    {local.greeting} {local.name}

    +} +``` +Now the button works as expected. diff --git a/langs/tl/tutorials/props_split/solved.json b/langs/tl/tutorials/props_split/solved.json new file mode 100644 index 00000000..a6026a0e --- /dev/null +++ b/langs/tl/tutorials/props_split/solved.json @@ -0,0 +1,12 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal } from \"solid-js\";\n\nimport Greeting from \"./greeting\";\n\nfunction App() {\n const [name, setName] = createSignal(\"Jakob\");\n\n return <>\n \n \n ;\n}\n\nrender(() => , document.getElementById('app'));" + }, + { + "name": "greeting", + "content": "import { splitProps } from \"solid-js\";\n\nexport default function Greeting(props) {\n const [local, others] = splitProps(props, [\"greeting\", \"name\"]);\n return

    {local.greeting} {local.name}

    \n}" + } + ] +} \ No newline at end of file diff --git a/langs/tl/tutorials/reactivity_batch/lesson.json b/langs/tl/tutorials/reactivity_batch/lesson.json new file mode 100644 index 00000000..29a90329 --- /dev/null +++ b/langs/tl/tutorials/reactivity_batch/lesson.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal, batch } from \"solid-js\";\n\nconst App = () => {\n const [firstName, setFirstName] = createSignal(\"John\");\n const [lastName, setLastName] = createSignal(\"Smith\");\n const fullName = () => {\n console.log(\"Running FullName\");\n return `${firstName()} ${lastName()}`\n } \n const updateNames = () => {\n console.log(\"Button Clicked\");\n setFirstName(firstName() + \"n\");\n setLastName(lastName() + \"!\");\n }\n \n return \n};\n\nrender(App, document.getElementById(\"app\"));\n" + } + ] +} diff --git a/langs/tl/tutorials/reactivity_batch/lesson.md b/langs/tl/tutorials/reactivity_batch/lesson.md new file mode 100644 index 00000000..0e801e4e --- /dev/null +++ b/langs/tl/tutorials/reactivity_batch/lesson.md @@ -0,0 +1,16 @@ +Solid's reactivity is synchronous which means, by the next line after any change, the DOM will have updated. And for the most part this is perfectly fine, as Solid's granular rendering is just a propagation of the update in the reactive system. Unrelated changes "rendering" twice don't actually mean wasted work. + +What if the changes are related? Solid's `batch` helper allows to queue up multiple changes and then apply them all at the same time before notifying their observers. Within the batch, updated Signal values are not committed until completion. + +In this example, we are assigning both names on a button click and this triggers our rendered update twice. You can see the logs in the console when you click the button. So let's wrap the `set` calls in a batch. + +```js + const updateNames = () => { + console.log("Button Clicked"); + batch(() => { + setFirstName(firstName() + "n"); + setLastName(lastName() + "!"); + }) + } +``` +And that's it. Now we only notify once for the whole changeset. \ No newline at end of file diff --git a/langs/tl/tutorials/reactivity_batch/solved.json b/langs/tl/tutorials/reactivity_batch/solved.json new file mode 100644 index 00000000..bf41d10a --- /dev/null +++ b/langs/tl/tutorials/reactivity_batch/solved.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal, batch } from \"solid-js\";\n\nconst App = () => {\n const [firstName, setFirstName] = createSignal(\"John\");\n const [lastName, setLastName] = createSignal(\"Smith\");\n const fullName = () => {\n console.log(\"Running FullName\");\n return `${firstName()} ${lastName()}`\n } \n const updateNames = () => {\n console.log(\"Button Clicked\");\n batch(() => {\n setFirstName(firstName() + \"n\");\n setLastName(lastName() + \"!\");\n })\n }\n \n return \n};\n\nrender(App, document.getElementById(\"app\"));\n" + } + ] +} diff --git a/langs/tl/tutorials/reactivity_on/lesson.json b/langs/tl/tutorials/reactivity_on/lesson.json new file mode 100644 index 00000000..22900826 --- /dev/null +++ b/langs/tl/tutorials/reactivity_on/lesson.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal, createEffect, on } from \"solid-js\";\n\nconst App = () => {\n const [a, setA] = createSignal(1);\n const [b, setB] = createSignal(1);\n\n createEffect(() => {\n console.log(a(), b());\n });\n\n return <>\n \n \n \n};\n\nrender(App, document.getElementById(\"app\"));\n" + } + ] +} diff --git a/langs/tl/tutorials/reactivity_on/lesson.md b/langs/tl/tutorials/reactivity_on/lesson.md new file mode 100644 index 00000000..1d9deffc --- /dev/null +++ b/langs/tl/tutorials/reactivity_on/lesson.md @@ -0,0 +1,9 @@ +For convenience, Solid has an `on` helper that enables setting explicit dependencies for our computations. This is mostly used as a terse way to be even more explicit about which Signals are tracked (and not track any other Signals, even if they are read). In addition, `on` provides a `defer` option that allows the computation not to execute immediately and only run on first change. + +Let's have our Effect run only when `a` updates, and defer execution until the value changes: + +```js +createEffect(on(a, (a) => { + console.log(a, b()); +}, { defer: true })); +``` diff --git a/langs/tl/tutorials/reactivity_on/solved.json b/langs/tl/tutorials/reactivity_on/solved.json new file mode 100644 index 00000000..21608f91 --- /dev/null +++ b/langs/tl/tutorials/reactivity_on/solved.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal, createEffect, on } from \"solid-js\";\n\nconst App = () => {\n const [a, setA] = createSignal(1);\n const [b, setB] = createSignal(1);\n\n createEffect(on(a, (a) => {\n console.log(a, b());\n }, { defer: true }));\n\n return <>\n \n \n \n};\n\nrender(App, document.getElementById(\"app\"));\n" + } + ] +} diff --git a/langs/tl/tutorials/reactivity_untrack/lesson.json b/langs/tl/tutorials/reactivity_untrack/lesson.json new file mode 100644 index 00000000..01a332e0 --- /dev/null +++ b/langs/tl/tutorials/reactivity_untrack/lesson.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal, createEffect, untrack } from \"solid-js\";\n\nconst App = () => {\n const [a, setA] = createSignal(1);\n const [b, setB] = createSignal(1);\n\n createEffect(() => {\n console.log(a(), b());\n });\n\n return <>\n \n \n \n};\n\nrender(App, document.getElementById(\"app\"));\n" + } + ] +} diff --git a/langs/tl/tutorials/reactivity_untrack/lesson.md b/langs/tl/tutorials/reactivity_untrack/lesson.md new file mode 100644 index 00000000..061f55fd --- /dev/null +++ b/langs/tl/tutorials/reactivity_untrack/lesson.md @@ -0,0 +1,12 @@ +It's sometimes desirable to have Signal reads not be tracked, even inside a reactive context. Solid provides the `untrack` helper as a way to prevent the wrapping computation from tracking any reads. + +Let's suppose we did not want to log in our example when `b` changes. We can untrack the `b` signal by changing our effect to the following: + +```js +createEffect(() => { + console.log(a(), untrack(b)); +}); +``` +Since Signals are functions, they can be passed directly, but `untrack` can wrap functions with more complex behavior. + +Even though `untrack` disables tracking of reads, it has no effect on writes which still happen and notify their observers. diff --git a/langs/tl/tutorials/reactivity_untrack/solved.json b/langs/tl/tutorials/reactivity_untrack/solved.json new file mode 100644 index 00000000..0f66193d --- /dev/null +++ b/langs/tl/tutorials/reactivity_untrack/solved.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { createSignal, createEffect, untrack } from \"solid-js\";\n\nconst App = () => {\n const [a, setA] = createSignal(1);\n const [b, setB] = createSignal(1);\n\n createEffect(() => {\n console.log(a(), untrack(b));\n });\n\n return <>\n \n \n \n};\n\nrender(App, document.getElementById(\"app\"));\n" + } + ] +} diff --git a/langs/tl/tutorials/stores_context/lesson.json b/langs/tl/tutorials/stores_context/lesson.json new file mode 100644 index 00000000..7757ae3c --- /dev/null +++ b/langs/tl/tutorials/stores_context/lesson.json @@ -0,0 +1,16 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport Nested from \"./nested\";\nimport { CounterProvider } from \"./counter\";\n\nfunction App() {\n return <>\n

    Welcome to Counter App

    \n \n \n};\n\nrender(() => , document.getElementById(\"app\"));" + }, + { + "name": "nested", + "content": "import { useCounter } from \"./counter\";\n\nexport default function Nested() {\n return (\n <>\n
    0
    \n \n \n \n );\n};" + }, + { + "name": "counter", + "content": "import { createSignal, createContext, useContext } from \"solid-js\";\n\nconst CounterContext = createContext();\n\nexport function CounterProvider(props) {\n const [count, setCount] = createSignal(props.count || 0),\n store = [\n count,\n {\n increment() {\n setCount(c => c + 1);\n },\n decrement() {\n setCount(c => c - 1);\n }\n }\n ];\n\n return (\n \n {props.children}\n \n );\n}\n\nexport function useCounter() { return useContext(CounterContext); }" + } + ] +} diff --git a/langs/tl/tutorials/stores_context/lesson.md b/langs/tl/tutorials/stores_context/lesson.md new file mode 100644 index 00000000..e76d779e --- /dev/null +++ b/langs/tl/tutorials/stores_context/lesson.md @@ -0,0 +1,28 @@ +Solid provides a Context API to pass data around without relying on passing through props. This is useful for sharing Signals and Stores. Using Context has the benefit of being created as part of the reactive system and managed by it. + +To get started we create a Context object. This object contains a `Provider` component used to inject our data. However, it is common practice to wrap the `Provider` components and `useContext` consumers with versions already configured for the specific Context. + +And that's exactly what we have in this tutorial. You can see the definition for a simple counter store in the `counter.tsx` file. + +To use context, first let's wrap our App component to provide it globally. We will use our wrapped `CounterProvider`. In this case let's give it an initial count of 1. + +```jsx +render(() => ( + + + +), document.getElementById("app")); +``` + +Next we need to consume the counter context in our `nested.tsx` component. We do this by using the wrapped `useCounter` consumer. + +```jsx +const [count, { increment, decrement }] = useCounter(); +return ( + <> +
    {count()}
    + + + +); +``` \ No newline at end of file diff --git a/langs/tl/tutorials/stores_context/solved.json b/langs/tl/tutorials/stores_context/solved.json new file mode 100644 index 00000000..b89b5e06 --- /dev/null +++ b/langs/tl/tutorials/stores_context/solved.json @@ -0,0 +1,16 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport Nested from \"./nested\";\nimport { CounterProvider } from \"./counter\";\n\nfunction App() {\n return <>\n

    Welcome to Counter App

    \n \n \n};\n\nrender(() => (\n \n \n \n), document.getElementById(\"app\"));" + }, + { + "name": "nested", + "content": "import { useCounter } from \"./counter\";\n\nexport default function Nested() {\n const [count, { increment, decrement }] = useCounter();\n return (\n <>\n
    {count()}
    \n \n \n \n );\n};" + }, + { + "name": "counter", + "content": "import { createSignal, createContext, useContext } from \"solid-js\";\n\nconst CounterContext = createContext();\n\nexport function CounterProvider(props) {\n const [count, setCount] = createSignal(props.count || 0),\n store = [\n count,\n {\n increment() {\n setCount(c => c + 1);\n },\n decrement() {\n setCount(c => c - 1);\n }\n }\n ];\n\n return (\n \n {props.children}\n \n );\n}\n\nexport function useCounter() { return useContext(CounterContext); }" + } + ] +} diff --git a/langs/tl/tutorials/stores_createstore/lesson.json b/langs/tl/tutorials/stores_createstore/lesson.json new file mode 100644 index 00000000..9a867b74 --- /dev/null +++ b/langs/tl/tutorials/stores_createstore/lesson.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { For, createSignal } from \"solid-js\";\nimport { createStore } from \"solid-js/store\";\n\nconst App = () => {\n let input;\n let todoId = 0;\n const [todos, setTodos] = createSignal([])\n const addTodo = (text) => {\n setTodos([...todos(), { id: ++todoId, text, completed: false }]);\n }\n const toggleTodo = (id) => {\n setTodos(todos().map((todo) => (\n todo.id !== id ? todo : { ...todo, completed: !todo.completed }\n )));\n }\n\n return (\n <>\n
    \n \n {\n if (!input.value.trim()) return;\n addTodo(input.value);\n input.value = \"\";\n }}\n >\n Add Todo\n \n
    \n \n {(todo) => {\n const { id, text } = todo;\n console.log(`Creating ${text}`)\n return
    \n \n {text}\n
    \n }}\n
    \n \n );\n};\n\nrender(App, document.getElementById(\"app\"));\n" + } + ] +} diff --git a/langs/tl/tutorials/stores_createstore/lesson.md b/langs/tl/tutorials/stores_createstore/lesson.md new file mode 100644 index 00000000..abb3571b --- /dev/null +++ b/langs/tl/tutorials/stores_createstore/lesson.md @@ -0,0 +1,25 @@ +Stores are Solid's answer to nested reactivity. They are proxy objects whose properties can be tracked and can contain other objects which automatically become wrapped in proxies themselves, and so on. + +To keep things light, Solid creates underlying Signals only for properties that are accessed under tracking scopes. And so, all Signals in Stores are created lazily as requested. + +The `createStore` call takes the initial value and returns a read/write tuple similar to Signals. The first element is the store proxy which is readonly, and the second is a setter function. + +The setter function in its most basic form takes an object whose properties will be merged with the current state. It also supports path syntax so that we can do nested updates. In this way we can still maintain control of our reactivity but do pinpoint updates. + +> Solid's path syntax has many forms and includes some powerful syntax to do iteration and ranges. Refer to the API documentation for a full reference. + +Let's look at how much easier it is to achieve nested reactivity with a Store. We can replace the initialization of our component with this: + +```js +const [store, setStore] = createStore({ todos: [] }); +const addTodo = (text) => { + setStore('todos', (todos) => [...todos, { id: ++todoId, text, completed: false }]); +}; +const toggleTodo = (id) => { + setStore('todos', (t) => t.id === id, 'completed', (completed) => !completed); +}; +``` + +We make use of the Store's path syntax with function setters that allow us to take the previous state and return the new state on nested values. + +And that's it. The rest of the template will already react granularly (check the Console on clicking the checkbox). diff --git a/langs/tl/tutorials/stores_createstore/solved.json b/langs/tl/tutorials/stores_createstore/solved.json new file mode 100644 index 00000000..77b2daa9 --- /dev/null +++ b/langs/tl/tutorials/stores_createstore/solved.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { For } from \"solid-js\";\nimport { createStore } from \"solid-js/store\";\n\nconst App = () => {\n let input;\n let todoId = 0;\n const [store, setStore] = createStore({ todos: [] })\n const addTodo = (text) => {\n setStore(\"todos\", todos => [...todos, { id: ++todoId, text, completed: false }]);\n }\n const toggleTodo = (id) => {\n setStore(\"todos\", todo => todo.id === id, \"completed\", completed => !completed);\n }\n\n return (\n <>\n
    \n \n {\n if (!input.value.trim()) return;\n addTodo(input.value);\n input.value = \"\";\n }}\n >\n Add Todo\n \n
    \n \n {(todo) => {\n const { id, text } = todo;\n console.log(`Creating ${text}`)\n return
    \n \n {text}\n
    \n }}\n
    \n \n );\n};\n\nrender(App, document.getElementById(\"app\"));\n" + } + ] +} diff --git a/langs/tl/tutorials/stores_immutable/lesson.json b/langs/tl/tutorials/stores_immutable/lesson.json new file mode 100644 index 00000000..509de763 --- /dev/null +++ b/langs/tl/tutorials/stores_immutable/lesson.json @@ -0,0 +1,20 @@ +{ + "files": [ + { + "name": "main", + "content": "// @ts-nocheck\nimport { render } from \"solid-js/web\";\nimport { For } from \"solid-js\";\n\nimport useRedux from \"./useRedux\";\nimport reduxStore from \"./store\";\nimport actions from \"./actions\";\n\nconst App = () => {\n const [store, { addTodo, toggleTodo }] = useRedux(\n reduxStore,\n actions\n );\n let input;\n return (\n <>\n
    \n \n {\n if (!input.value.trim()) return;\n addTodo(input.value);\n input.value = \"\";\n }}\n >\n Add Todo\n \n
    \n \n {(todo) => {\n const { id, text } = todo;\n console.log(\"Create\", text)\n return
    \n \n {text}\n
    \n }}\n
    \n \n );\n};\n\nrender(App, document.getElementById(\"app\"));\n" + }, + { + "name": "useRedux", + "content": "import { onCleanup } from \"solid-js\";\nimport { createStore, reconcile } from \"solid-js/store\";\n\nexport default function useRedux(store, actions) {\n const [state, setState] = createStore(store.getState());\n const unsubscribe = store.subscribe(\n () => setState(store.getState())\n );\n onCleanup(() => unsubscribe());\n return [\n state,\n mapActions(store, actions)\n ];\n};\n\nfunction mapActions(store, actions) {\n const mapped = {};\n for (const key in actions) {\n mapped[key] = (...args) => store.dispatch(actions[key](...args));\n }\n return mapped;\n}\n" + }, + { + "name": "actions", + "content": "let nextTodoId = 0;\nexport default {\n addTodo: text => ({ type: \"ADD_TODO\", id: ++nextTodoId, text }),\n toggleTodo: id => ({ type: \"TOGGLE_TODO\", id })\n};\n" + }, + { + "name": "store", + "content": "// @ts-nocheck\nimport { createStore } from 'redux';\n\n// todos reducer\nconst todos = (state = { todos: [] }, action) => {\n switch (action.type) {\n case \"ADD_TODO\":\n return { todos: [\n ...state.todos,\n {\n id: action.id,\n text: action.text,\n completed: false\n }\n ]};\n case \"TOGGLE_TODO\":\n return { todos: state.todos.map(todo => \n todo.id === action.id ? { ...todo, completed: !todo.completed } : todo\n )};\n default:\n return state;\n }\n};\n\nexport default createStore(todos);\n" + } + ] +} diff --git a/langs/tl/tutorials/stores_immutable/lesson.md b/langs/tl/tutorials/stores_immutable/lesson.md new file mode 100644 index 00000000..0e978f26 --- /dev/null +++ b/langs/tl/tutorials/stores_immutable/lesson.md @@ -0,0 +1,43 @@ +Stores are most often created in Solid using Solid's Store proxies. Sometimes we wish to interface with immutable libraries like Redux, Apollo, or XState and need to perform granular updates against these. + +In the example, we have a simple wrapper around Redux. You can see the implementation in `useRedux.tsx`. The definition of the store and the actions are in the remaining files. + +The core behavior is that we created a Store object and subscribe to the Redux store to update state on update. + +```js +const [state, setState] = createStore(store.getState()); +const unsubscribe = store.subscribe( + () => setState(store.getState()) +); +``` +If you click around the demo adding items and checking them off it seems to work pretty well. However, what isn't obvious is that the rendering is inefficient. Notice the console.log not only on create but whenever you check the box. + +The reason is that Solid doesn't diff by default. It assumes the new item is new and replaces it. If your data changes granularly, you don't need to diff. But what if you do? + +Solid provides a diffing method `reconcile` that enhances the `setStore` call and lets us diff the data from these immutable sources, only notifying the granular updates. + +Let's update that code to: +```js +const [state, setState] = createStore(store.getState()); +const unsubscribe = store.subscribe( + () => setState(reconcile(store.getState())) +); +``` +Now the example works as you'd expect, only running the create code on create. + +This isn't the only way to solve this and you've seen some frameworks have a `key` property on their template loop flows. The problem is that by making that a default part of the templating, you always need to run list reconciliation and always have to diff all the children for potential changes, even in compiled frameworks. A data-centric approach not only makes this applicable outside of templating but makes it opt in. When you consider that internal state management doesn't need this, it means we default to having the best performance. + +Of course, there's no problem using `reconcile` when you need it. Sometimes a simple reducer makes for a great way to organize data updates. `reconcile` shines here, making your own `useReducer` primitive: + +```js +const useReducer = (reducer, state) => { + const [store, setStore] = createStore(state); + const dispatch = (action) => { + state = reducer(state, action); + setStore(reconcile(state)); + } + return [store, dispatch]; +}; +``` + +The behavior of `reconcile` is configurable. A custom `key` can be set and there is a `merge` option which ignores structural cloning and only diffs the leaves. \ No newline at end of file diff --git a/langs/tl/tutorials/stores_immutable/solved.json b/langs/tl/tutorials/stores_immutable/solved.json new file mode 100644 index 00000000..5ac5aa45 --- /dev/null +++ b/langs/tl/tutorials/stores_immutable/solved.json @@ -0,0 +1,20 @@ +{ + "files": [ + { + "name": "main", + "content": "// @ts-nocheck\nimport { render } from \"solid-js/web\";\nimport { For } from \"solid-js\";\n\nimport useRedux from \"./useRedux\";\nimport reduxStore from \"./store\";\nimport actions from \"./actions\";\n\nconst App = () => {\n const [store, { addTodo, toggleTodo }] = useRedux(\n reduxStore,\n actions\n );\n let input;\n return (\n <>\n
    \n \n {\n if (!input.value.trim()) return;\n addTodo(input.value);\n input.value = \"\";\n }}\n >\n Add Todo\n \n
    \n \n {(todo) => {\n const { id, text } = todo;\n console.log(\"Create\", text)\n return
    \n \n {text}\n
    \n }}\n
    \n \n );\n};\n\nrender(App, document.getElementById(\"app\"));\n" + }, + { + "name": "useRedux", + "content": "import { onCleanup } from \"solid-js\";\nimport { createStore, reconcile } from \"solid-js/store\";\n\nexport default function useRedux(store, actions) {\n const [state, setState] = createStore(store.getState());\n const unsubscribe = store.subscribe(\n () => setState(reconcile(store.getState()))\n );\n onCleanup(() => unsubscribe());\n return [\n state,\n mapActions(store, actions)\n ];\n};\n\nfunction mapActions(store, actions) {\n const mapped = {};\n for (const key in actions) {\n mapped[key] = (...args) => store.dispatch(actions[key](...args));\n }\n return mapped;\n}\n" + }, + { + "name": "actions", + "content": "let nextTodoId = 0;\nexport default {\n addTodo: text => ({ type: \"ADD_TODO\", id: ++nextTodoId, text }),\n toggleTodo: id => ({ type: \"TOGGLE_TODO\", id })\n};\n" + }, + { + "name": "store", + "content": "// @ts-nocheck\nimport { createStore } from 'redux';\n\n// todos reducer\nconst todos = (state = { todos: [] }, action) => {\n switch (action.type) {\n case \"ADD_TODO\":\n return { todos: [\n ...state.todos,\n {\n id: action.id,\n text: action.text,\n completed: false\n }\n ]};\n case \"TOGGLE_TODO\":\n return { todos: state.todos.map(todo => \n todo.id === action.id ? { ...todo, completed: !todo.completed } : todo\n )};\n default:\n return state;\n }\n};\n\nexport default createStore(todos);\n" + } + ] +} diff --git a/langs/tl/tutorials/stores_mutation/lesson.json b/langs/tl/tutorials/stores_mutation/lesson.json new file mode 100644 index 00000000..3addb5e8 --- /dev/null +++ b/langs/tl/tutorials/stores_mutation/lesson.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render, For } from \"solid-js/web\";\nimport { createStore, produce } from \"solid-js/store\";\n\nconst App = () => {\n let input;\n let todoId = 0;\n const [store, setStore] = createStore({ todos: [] })\n const addTodo = (text) => {\n setStore(\"todos\", todos => [...todos, { id: ++todoId, text, completed: false }]);\n }\n const toggleTodo = (id) => {\n setStore(\"todos\", todo => todo.id === id, \"completed\", completed => !completed);\n }\n\n return (\n <>\n
    \n \n {\n if (!input.value.trim()) return;\n addTodo(input.value);\n input.value = \"\";\n }}\n >\n Add Todo\n \n
    \n \n {(todo) => {\n const { id, text } = todo;\n console.log(`Creating ${text}`)\n return
    \n \n {text}\n
    \n }}\n
    \n \n );\n};\n\nrender(App, document.getElementById(\"app\"));\n" + } + ] +} diff --git a/langs/tl/tutorials/stores_mutation/lesson.md b/langs/tl/tutorials/stores_mutation/lesson.md new file mode 100644 index 00000000..aff7eb1c --- /dev/null +++ b/langs/tl/tutorials/stores_mutation/lesson.md @@ -0,0 +1,25 @@ +Solid strongly recommends the use of shallow immutable patterns for updating state. By separating reads and writes we maintain better control over the reactivity of our system without the risk of losing track of changes to our proxy when passed through layers of components. This is much more amplified with Stores compared to Signals. + +Sometimes, however, mutation is just easier to reason about. That's why Solid provides an Immer inspired `produce` store modifier that allows you to mutate a writable proxy version of your Store object inside your `setStore` calls. + +This is a nice tool to have to allow small zones of mutation without relinquishing control. Let's use `produce` on our Todos example by replacing our event handler code with: + +```jsx +const addTodo = (text) => { + setStore( + 'todos', + produce((todos) => { + todos.push({ id: ++todoId, text, completed: false }); + }), + ); +}; +const toggleTodo = (id) => { + setStore( + 'todos', + todo => todo.id === id, + produce((todo) => (todo.completed = !todo.completed)), + ); +}; +``` + +While `produce` with Stores probably handles the vast majority of cases, Solid also has a mutable Store object that is available from `createMutable`. While not the recommended approach for internal APIs, it is sometimes useful for interop or compatibility with 3rd party systems. diff --git a/langs/tl/tutorials/stores_mutation/solved.json b/langs/tl/tutorials/stores_mutation/solved.json new file mode 100644 index 00000000..f1f4f656 --- /dev/null +++ b/langs/tl/tutorials/stores_mutation/solved.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "// @ts-nocheck\nimport { render, For } from \"solid-js/web\";\nimport { createStore, produce } from \"solid-js/store\";\n\nconst App = () => {\n let input;\n let todoId = 0;\n const [store, setStore] = createStore({ todos: [] })\n const addTodo = (text) => {\n setStore(\n 'todos',\n produce((todos) => {\n todos.push({ id: ++todoId, text, completed: false });\n }),\n );\n };\n const toggleTodo = (id) => {\n setStore(\n 'todos',\n todo => todo.id === id,\n produce((todo) => (todo.completed = !todo.completed)),\n );\n };\n\n return (\n <>\n
    \n \n {\n if (!input.value.trim()) return;\n addTodo(input.value);\n input.value = \"\";\n }}\n >\n Add Todo\n \n
    \n \n {(todo) => {\n const { id, text } = todo;\n console.log(`Creating ${text}`)\n return
    \n \n {text}\n
    \n }}\n
    \n \n );\n};\n\nrender(App, document.getElementById(\"app\"));\n" + } + ] +} diff --git a/langs/tl/tutorials/stores_nested_reactivity/lesson.json b/langs/tl/tutorials/stores_nested_reactivity/lesson.json new file mode 100644 index 00000000..32f03a3e --- /dev/null +++ b/langs/tl/tutorials/stores_nested_reactivity/lesson.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { For, createSignal } from \"solid-js\";\n\nconst App = () => {\n const [todos, setTodos] = createSignal([])\n let input;\n let todoId = 0;\n\n const addTodo = (text) => {\n setTodos([...todos(), { id: ++todoId, text, completed: false }]);\n }\n const toggleTodo = (id) => {\n setTodos(todos().map((todo) => (\n todo.id !== id ? todo : { ...todo, completed: !todo.completed }\n )));\n }\n\n return (\n <>\n
    \n \n {\n if (!input.value.trim()) return;\n addTodo(input.value);\n input.value = \"\";\n }}\n >\n Add Todo\n \n
    \n \n {(todo) => {\n const { id, text } = todo;\n console.log(`Creating ${text}`)\n return
    \n \n {text}\n
    \n }}\n
    \n \n );\n};\n\nrender(App, document.getElementById(\"app\"));\n" + } + ] +} diff --git a/langs/tl/tutorials/stores_nested_reactivity/lesson.md b/langs/tl/tutorials/stores_nested_reactivity/lesson.md new file mode 100644 index 00000000..bc48af9f --- /dev/null +++ b/langs/tl/tutorials/stores_nested_reactivity/lesson.md @@ -0,0 +1,33 @@ +One of the reasons for fine-grained reactivity in Solid is that it can handle nested updates independently. You can have a list of users and when we update one name we only update a single location in the DOM without diffing the list itself. Very few (even reactive) UI frameworks can do this. + +But how do we accomplish this? In the example we have a list of todos in a signal. In order to mark a todo as complete, we would need to replace the todo with a clone. This is how most frameworks work, but it's wasteful as we rerun the list diffing and we recreate the DOM elements as illustrated in the `console.log`. + +```js +const toggleTodo = (id) => { + setTodos( + todos().map((todo) => (todo.id !== id ? todo : { ...todo, completed: !todo.completed })), + ); +}; +``` + +Instead, in a fine-grained library like Solid, we initialize the data with nested Signals like this: + +```js +const addTodo = (text) => { + const [completed, setCompleted] = createSignal(false); + setTodos([...todos(), { id: ++todoId, text, completed, setCompleted }]); +}; +``` + +Now we can update the completion state by calling `setCompleted` without any additional diffing. This is because we've moved the complexity to the data rather than the view. And we know exactly how the data changes. + +```js +const toggleTodo = (id) => { + const index = todos().findIndex((t) => t.id === id); + const todo = todos()[index]; + if (todo) todo.setCompleted(!todo.completed()) +} +``` +If you change the remaining references of `todo.completed` to `todo.completed()`, the example should now only run the `console.log` on creation and not when you toggle a todo. + +This of course requires some manual mapping and it was the only choice available to us in the past. But now, thanks to proxies, we can do most of this work in the background without manual intervention. Continue to the next tutorials to see how. diff --git a/langs/tl/tutorials/stores_nested_reactivity/solved.json b/langs/tl/tutorials/stores_nested_reactivity/solved.json new file mode 100644 index 00000000..e4190ad0 --- /dev/null +++ b/langs/tl/tutorials/stores_nested_reactivity/solved.json @@ -0,0 +1,8 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport { For, createSignal } from \"solid-js\";\n\nconst App = () => {\n const [todos, setTodos] = createSignal([])\n let input;\n let todoId = 0;\n\n const addTodo = (text) => {\n const [completed, setCompleted] = createSignal(false); \n setTodos([...todos(), { id: ++todoId, text, completed, setCompleted }]);\n }\n const toggleTodo = (id) => {\n const index = todos().findIndex((t) => t.id === id);\n const todo = todos()[index];\n if (todo) todo.setCompleted(!todo.completed())\n }\n\n return (\n <>\n
    \n \n {\n if (!input.value.trim()) return;\n addTodo(input.value);\n input.value = \"\";\n }}\n >\n Add Todo\n \n
    \n \n {(todo) => {\n const { id, text } = todo;\n console.log(`Creating ${text}`)\n return
    \n \n {text}\n
    \n }}\n
    \n \n );\n};\n\nrender(App, document.getElementById(\"app\"));\n" + } + ] +} diff --git a/langs/tl/tutorials/stores_nocontext/lesson.json b/langs/tl/tutorials/stores_nocontext/lesson.json new file mode 100644 index 00000000..eee66efc --- /dev/null +++ b/langs/tl/tutorials/stores_nocontext/lesson.json @@ -0,0 +1,12 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport counter from \"./counter\"\n\nfunction Counter() {\n return (\n \n );\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + }, + { + "name": "counter", + "content": "import { createSignal, createMemo, createRoot } from \"solid-js\";\n\nfunction createCounter() {\n const [count, setCount] = createSignal(0);\n const increment = () => setCount(count() + 1);\n const doubleCount = createMemo(() => count() * 2);\n return { count, doubleCount, increment };\n}\n\nexport default createRoot(createCounter);" + } + ] +} diff --git a/langs/tl/tutorials/stores_nocontext/lesson.md b/langs/tl/tutorials/stores_nocontext/lesson.md new file mode 100644 index 00000000..3ea581de --- /dev/null +++ b/langs/tl/tutorials/stores_nocontext/lesson.md @@ -0,0 +1,31 @@ +Context is a great tool for stores. It handles injection, ties ownership to the reactive graph, automatically manages disposal, and has no render overhead given Solid's fine-grained rendering. + +However, you could just use the reactive system directly for simple things. It's almost not worth pointing out but a simple writeable store is just a Signal: + +```js +import { createSignal } from 'solid-js'; + +export default createSignal(0); + +// somewhere else: +import counter from './counter'; +const [count, setCount] = counter; +``` + +Solid's reactivity is a universal concept. It doesn't matter if it is inside or outside components. There is no separate concept for global vs local state. It is all the same thing. + +The only restriction is that all computations (Effects/Memos) need to be created under a reactive root (`createRoot`). Solid's `render` does this automatically. + +In this tutorial `counter.tsx` is such a global store. We can use it by modifying our component in `main.tsx` to: + +```jsx +const { count, doubleCount, increment } = counter; + +return ( + +); +``` + +So when using your own more complicated global stores that contain computations, be sure to create a root. Or better yet, do yourself a favor and just use Context. diff --git a/langs/tl/tutorials/stores_nocontext/solved.json b/langs/tl/tutorials/stores_nocontext/solved.json new file mode 100644 index 00000000..956fda6b --- /dev/null +++ b/langs/tl/tutorials/stores_nocontext/solved.json @@ -0,0 +1,12 @@ +{ + "files": [ + { + "name": "main", + "content": "import { render } from \"solid-js/web\";\nimport counter from \"./counter\"\n\nfunction Counter() {\n const { count, doubleCount, increment } = counter;\n\n return (\n \n );\n}\n\nrender(() => , document.getElementById(\"app\"));\n" + }, + { + "name": "counter", + "content": "import { createSignal, createMemo, createRoot } from \"solid-js\";\n\nfunction createCounter() {\n const [count, setCount] = createSignal(0);\n const increment = () => setCount(count() + 1);\n const doubleCount = createMemo(() => count() * 2);\n return { count, doubleCount, increment };\n}\n\nexport default createRoot(createCounter);" + } + ] +} diff --git a/yarn-error.log b/yarn-error.log deleted file mode 100644 index fa2a6417..00000000 --- a/yarn-error.log +++ /dev/null @@ -1,2156 +0,0 @@ -Arguments: - /Users/jutanium/.nvm/versions/node/v16.13.0/bin/node /Users/jutanium/.nvm/versions/node/v16.13.0/bin/yarn doc -- arg - -PATH: - /Users/jutanium/.nvm/versions/node/v16.13.0/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin:/Users/jutanium/.nvm/versions/node/v16.13.0/bin - -Yarn version: - 1.22.17 - -Node version: - 16.13.0 - -Platform: - darwin arm64 - -Trace: - SyntaxError: /Users/jutanium/Documents/Dev/solid-docs/package.json: Unexpected string in JSON at position 800 - at JSON.parse () - at /Users/jutanium/.nvm/versions/node/v16.13.0/lib/node_modules/yarn/lib/cli.js:1625:59 - at Generator.next () - at step (/Users/jutanium/.nvm/versions/node/v16.13.0/lib/node_modules/yarn/lib/cli.js:310:30) - at /Users/jutanium/.nvm/versions/node/v16.13.0/lib/node_modules/yarn/lib/cli.js:321:13 - -npm manifest: - { - "name": "@solid.js/docs", - "version": "1.0.4", - "files": [ - "dist" - ], - "main": "dist/index.js", - "repository": "git@github.com:solidjs/solid-docs.git", - "author": "Solid Team", - "license": "MIT", - "types": "dist/src/index.d.ts", - "devDependencies": { - "@rollup/plugin-dynamic-import-vars": "^1.4.1", - "@rollup/plugin-json": "^4.1.0", - "@types/node": "^16.11.3", - "front-matter": "^4.0.2", - "htmlnano": "^1.1.1", - "jiti": "^1.12.9", - "markdown-it": "^12.2.0", - "markdown-it-anchor": "^8.4.1", - "rollup": "^2.58.3", - "rollup-plugin-typescript2": "^0.30.0", - "shiki": "^0.9.12", - "tslib": "^2.3.1", - "typescript": "^4.4.4" - }, - "scripts": { - "build": "jiti build/buildScript.ts && rollup --config", - "doc": "jiti build/buildScript.ts" - "publish": "npm publish --access public" - }, - "dependencies": { - "@types/markdown-it": "^12.2.3" - } - } - -yarn manifest: - No manifest - -Lockfile: - # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. - # yarn lockfile v1 - - - "@babel/code-frame@^7.0.0": - version "7.15.8" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.15.8.tgz#45990c47adadb00c03677baa89221f7cc23d2503" - integrity sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg== - dependencies: - "@babel/highlight" "^7.14.5" - - "@babel/helper-validator-identifier@^7.14.5": - version "7.15.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" - integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== - - "@babel/highlight@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" - integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== - dependencies: - "@babel/helper-validator-identifier" "^7.14.5" - chalk "^2.0.0" - js-tokens "^4.0.0" - - "@nodelib/fs.scandir@2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" - integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== - dependencies: - "@nodelib/fs.stat" "2.0.5" - run-parallel "^1.1.9" - - "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" - integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== - - "@nodelib/fs.walk@^1.2.3": - version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" - integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== - dependencies: - "@nodelib/fs.scandir" "2.1.5" - fastq "^1.6.0" - - "@rollup/plugin-dynamic-import-vars@^1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@rollup/plugin-dynamic-import-vars/-/plugin-dynamic-import-vars-1.4.1.tgz#ef6d042cd826ba05df6975c8a2612700cf0820ce" - integrity sha512-izHpMs9w8U8CLwyHTXE55H4ytGVaf2ZtlKIWxKigghw6ZC6Mx6AXCsixSY6JOchuX9BN4ZkeN8egLRTS+BxO+w== - dependencies: - "@rollup/pluginutils" "^3.1.0" - estree-walker "^2.0.1" - globby "^11.0.1" - magic-string "^0.25.7" - - "@rollup/plugin-json@^4.1.0": - version "4.1.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-4.1.0.tgz#54e09867ae6963c593844d8bd7a9c718294496f3" - integrity sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw== - dependencies: - "@rollup/pluginutils" "^3.0.8" - - "@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" - integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== - dependencies: - "@types/estree" "0.0.39" - estree-walker "^1.0.1" - picomatch "^2.2.2" - - "@rollup/pluginutils@^4.1.0": - version "4.1.1" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.1.tgz#1d4da86dd4eded15656a57d933fda2b9a08d47ec" - integrity sha512-clDjivHqWGXi7u+0d2r2sBi4Ie6VLEAzWMIkvJLnDmxoOhBYOTfzGbOQBA32THHm11/LiJbd01tJUpJsbshSWQ== - dependencies: - estree-walker "^2.0.1" - picomatch "^2.2.2" - - "@trysound/sax@0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" - integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== - - "@types/estree@0.0.39": - version "0.0.39" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" - integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== - - "@types/linkify-it@*": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-3.0.2.tgz#fd2cd2edbaa7eaac7e7f3c1748b52a19143846c9" - integrity sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA== - - "@types/markdown-it@^12.2.3": - version "12.2.3" - resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-12.2.3.tgz#0d6f6e5e413f8daaa26522904597be3d6cd93b51" - integrity sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ== - dependencies: - "@types/linkify-it" "*" - "@types/mdurl" "*" - - "@types/mdurl@*": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9" - integrity sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA== - - "@types/node@^16.11.3": - version "16.11.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.3.tgz#fad0b069ec205b0e81429c805d306d2c12e26be1" - integrity sha512-aIYL9Eemcecs1y77XzFGiSc+FdfN58k4J23UEe6+hynf4Wd9g4DzQPwIKL080vSMuubFqy2hWwOzCtJdc6vFKw== - - "@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== - - abab@^2.0.0: - version "2.0.5" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" - integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== - - acorn-globals@^4.3.0: - version "4.3.4" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" - integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== - dependencies: - acorn "^6.0.1" - acorn-walk "^6.0.1" - - acorn-walk@^6.0.1: - version "6.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" - integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== - - acorn@^6.0.1, acorn@^6.0.4: - version "6.4.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" - integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== - - ajv@^6.12.3: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - - alphanum-sort@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" - integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= - - 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== - dependencies: - color-convert "^1.9.0" - - argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - - argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - - array-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" - integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= - - array-union@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== - - asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== - dependencies: - safer-buffer "~2.1.0" - - assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - - async-limiter@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" - integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== - - asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - - aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= - - aws4@^1.8.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" - integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== - - balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - - bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= - dependencies: - tweetnacl "^0.14.3" - - boolbase@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" - integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= - - brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - - braces@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - - browser-process-hrtime@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" - integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== - - browserslist@^4.0.0, browserslist@^4.16.0, browserslist@^4.16.6: - version "4.17.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.4.tgz#72e2508af2a403aec0a49847ef31bd823c57ead4" - integrity sha512-Zg7RpbZpIJRW3am9Lyckue7PLytvVxxhJj1CaJVlCWENsGEAOlnlt8X0ZxGRPp7Bt9o8tIRM5SEXy4BCPMJjLQ== - dependencies: - caniuse-lite "^1.0.30001265" - electron-to-chromium "^1.3.867" - escalade "^3.1.1" - node-releases "^2.0.0" - picocolors "^1.0.0" - - buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - - callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - - caniuse-api@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" - integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== - dependencies: - browserslist "^4.0.0" - caniuse-lite "^1.0.0" - lodash.memoize "^4.1.2" - lodash.uniq "^4.5.0" - - caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001265: - version "1.0.30001271" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001271.tgz#0dda0c9bcae2cf5407cd34cac304186616cc83e8" - integrity sha512-BBruZFWmt3HFdVPS8kceTBIguKxu4f99n5JNp06OlPD/luoAMIaIK5ieV5YjnBLH3Nysai9sxj9rpJj4ZisXOA== - - caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - - chalk@^2.0.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - - color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - - color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - - colord@^2.0.1, colord@^2.6: - version "2.9.1" - resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.1.tgz#c961ea0efeb57c9f0f4834458f26cb9cc4a3f90e" - integrity sha512-4LBMSt09vR0uLnPVkOUBnmxgoaeN4ewRbx801wY/bXcltXfpR/G46OdWn96XpYmCWuYvO46aBZP4NgX8HpNAcw== - - combined-stream@^1.0.6, combined-stream@~1.0.6: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - - commander@^2.20.0: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - - commander@^6.0.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" - integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== - - commander@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" - integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== - - commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= - - concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - - core-util-is@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - - cosmiconfig@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" - integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== - 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" - - css-color-names@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-1.0.1.tgz#6ff7ee81a823ad46e020fa2fd6ab40a887e2ba67" - integrity sha512-/loXYOch1qU1biStIFsHH8SxTmOseh1IJqFvy8IujXOm1h+QjUdDhkzOrR5HG8K8mlxREj0yfi8ewCHx0eMxzA== - - css-declaration-sorter@^6.0.3: - version "6.1.3" - resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.1.3.tgz#e9852e4cf940ba79f509d9425b137d1f94438dc2" - integrity sha512-SvjQjNRZgh4ULK1LDJ2AduPKUKxIqmtU7ZAyi47BTV+M90Qvxr9AB6lKlLbDUfXqI9IQeYA8LbAsCZPpJEV3aA== - dependencies: - timsort "^0.3.0" - - css-select@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.3.tgz#a70440f70317f2669118ad74ff105e65849c7067" - integrity sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA== - dependencies: - boolbase "^1.0.0" - css-what "^5.0.0" - domhandler "^4.2.0" - domutils "^2.6.0" - nth-check "^2.0.0" - - css-tree@^1.1.2, css-tree@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" - integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== - dependencies: - mdn-data "2.0.14" - source-map "^0.6.1" - - css-what@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.1.0.tgz#3f7b707aadf633baf62c2ceb8579b545bb40f7fe" - integrity sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw== - - cssesc@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" - integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== - - cssnano-preset-default@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.1.4.tgz#359943bf00c5c8e05489f12dd25f3006f2c1cbd2" - integrity sha512-sPpQNDQBI3R/QsYxQvfB4mXeEcWuw0wGtKtmS5eg8wudyStYMgKOQT39G07EbW1LB56AOYrinRS9f0ig4Y3MhQ== - dependencies: - css-declaration-sorter "^6.0.3" - cssnano-utils "^2.0.1" - postcss-calc "^8.0.0" - postcss-colormin "^5.2.0" - postcss-convert-values "^5.0.1" - postcss-discard-comments "^5.0.1" - postcss-discard-duplicates "^5.0.1" - postcss-discard-empty "^5.0.1" - postcss-discard-overridden "^5.0.1" - postcss-merge-longhand "^5.0.2" - postcss-merge-rules "^5.0.2" - postcss-minify-font-values "^5.0.1" - postcss-minify-gradients "^5.0.2" - postcss-minify-params "^5.0.1" - postcss-minify-selectors "^5.1.0" - postcss-normalize-charset "^5.0.1" - postcss-normalize-display-values "^5.0.1" - postcss-normalize-positions "^5.0.1" - postcss-normalize-repeat-style "^5.0.1" - postcss-normalize-string "^5.0.1" - postcss-normalize-timing-functions "^5.0.1" - postcss-normalize-unicode "^5.0.1" - postcss-normalize-url "^5.0.2" - postcss-normalize-whitespace "^5.0.1" - postcss-ordered-values "^5.0.2" - postcss-reduce-initial "^5.0.1" - postcss-reduce-transforms "^5.0.1" - postcss-svgo "^5.0.2" - postcss-unique-selectors "^5.0.1" - - cssnano-utils@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-2.0.1.tgz#8660aa2b37ed869d2e2f22918196a9a8b6498ce2" - integrity sha512-i8vLRZTnEH9ubIyfdZCAdIdgnHAUeQeByEeQ2I7oTilvP9oHO6RScpeq3GsFUVqeB8uZgOQ9pw8utofNn32hhQ== - - cssnano@^5.0.8: - version "5.0.8" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.0.8.tgz#39ad166256980fcc64faa08c9bb18bb5789ecfa9" - integrity sha512-Lda7geZU0Yu+RZi2SGpjYuQz4HI4/1Y+BhdD0jL7NXAQ5larCzVn+PUGuZbDMYz904AXXCOgO5L1teSvgu7aFg== - dependencies: - cssnano-preset-default "^5.1.4" - is-resolvable "^1.1.0" - lilconfig "^2.0.3" - yaml "^1.10.2" - - csso@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" - integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== - dependencies: - css-tree "^1.1.2" - - cssom@0.3.x, cssom@^0.3.4: - version "0.3.8" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== - - cssstyle@^1.1.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.4.0.tgz#9d31328229d3c565c61e586b02041a28fccdccf1" - integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA== - dependencies: - cssom "0.3.x" - - dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= - dependencies: - assert-plus "^1.0.0" - - data-urls@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" - integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== - dependencies: - abab "^2.0.0" - whatwg-mimetype "^2.2.0" - whatwg-url "^7.0.0" - - deep-is@~0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" - integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== - - delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - - dir-glob@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== - dependencies: - path-type "^4.0.0" - - dom-serializer@^1.0.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" - integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig== - dependencies: - domelementtype "^2.0.1" - domhandler "^4.2.0" - entities "^2.0.0" - - domelementtype@^2.0.1, domelementtype@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" - integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== - - domexception@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" - integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== - dependencies: - webidl-conversions "^4.0.2" - - domhandler@^4.2.0, domhandler@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.2.tgz#e825d721d19a86b8c201a35264e226c678ee755f" - integrity sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w== - dependencies: - domelementtype "^2.2.0" - - domutils@^2.6.0, domutils@^2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" - integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== - dependencies: - dom-serializer "^1.0.1" - domelementtype "^2.2.0" - domhandler "^4.2.0" - - ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - - electron-to-chromium@^1.3.867: - version "1.3.877" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.877.tgz#956870eea7c9d8cf43cc54ea40687fee4dc0c12a" - integrity sha512-fT5mW5Giw5iyVukeHb2XvB4joBKvzHtl8Vs3QzE7APATpFMt/T7RWyUcIKSZzYkKQgpMbu+vDBTCHfQZvh8klA== - - entities@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" - integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== - - entities@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" - integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== - - entities@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" - integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== - - error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - - escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - - 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 sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - - escodegen@^1.11.0: - version "1.14.3" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" - integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== - dependencies: - esprima "^4.0.1" - estraverse "^4.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - - esprima@^4.0.0, esprima@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - - estraverse@^4.2.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - - estree-walker@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" - integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== - - estree-walker@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" - integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== - - esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - - extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - - extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - - extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= - - fast-deep-equal@^3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - - fast-glob@^3.1.1: - version "3.2.7" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1" - integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - - fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - - fast-levenshtein@~2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= - - fastq@^1.6.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" - integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== - dependencies: - reusify "^1.0.4" - - fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - - find-cache-dir@^3.3.1: - version "3.3.2" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" - integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== - dependencies: - commondir "^1.0.1" - make-dir "^3.0.2" - pkg-dir "^4.1.0" - - find-up@^4.0.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" - - forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= - - form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - - front-matter@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/front-matter/-/front-matter-4.0.2.tgz#b14e54dc745cfd7293484f3210d15ea4edd7f4d5" - integrity sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg== - dependencies: - js-yaml "^3.13.1" - - fs-extra@8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - - fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - - fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - - function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - - getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= - dependencies: - assert-plus "^1.0.0" - - glob-parent@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - - glob@^7.0.0, glob@^7.1.4: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - - globby@^11.0.1: - version "11.0.4" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" - integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.1.1" - ignore "^5.1.4" - merge2 "^1.3.0" - slash "^3.0.0" - - graceful-fs@^4.1.6, graceful-fs@^4.2.0: - version "4.2.8" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" - integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== - - har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - - har-validator@~5.1.3: - version "5.1.5" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" - integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== - dependencies: - ajv "^6.12.3" - har-schema "^2.0.0" - - has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - - has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - - html-encoding-sniffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" - integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw== - dependencies: - whatwg-encoding "^1.0.1" - - html-tags@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-1.2.0.tgz#c78de65b5663aa597989dd2b7ab49200d7e4db98" - integrity sha1-x43mW1Zjqll5id0rerSSANfk25g= - - htmlnano@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/htmlnano/-/htmlnano-1.1.1.tgz#aea50e1d7ac156370ea766d4cd75f2d3d1a953cc" - integrity sha512-diMNyqTPx4uGwlxrTs0beZCy8L/GxGIFGHWv20OYhthLcdYkDOP/d4Ja5MbGgVJZMakZUM21KpMk5qWZrBGSdw== - dependencies: - cosmiconfig "^7.0.1" - cssnano "^5.0.8" - postcss "^8.3.6" - posthtml "^0.16.5" - purgecss "^4.0.0" - relateurl "^0.2.7" - srcset "^4.0.0" - svgo "^2.6.1" - terser "^5.8.0" - timsort "^0.3.0" - uncss "^0.17.3" - - htmlparser2@^7.1.1: - version "7.1.2" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-7.1.2.tgz#587923d38f03bc89e03076e00cba2c7473f37f7c" - integrity sha512-d6cqsbJba2nRdg8WW2okyD4ceonFHn9jLFxhwlNcLhQWcFPdxXeJulgOLjLKtAK9T6ahd+GQNZwG9fjmGW7lyg== - dependencies: - domelementtype "^2.0.1" - domhandler "^4.2.2" - domutils "^2.8.0" - entities "^3.0.1" - - http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - - iconv-lite@0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - - ignore@^5.1.4: - version "5.1.8" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" - integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== - - import-fresh@^3.2.1: - version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - - indexes-of@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" - integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= - - inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - - inherits@2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - - is-absolute-url@^3.0.1, is-absolute-url@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" - integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== - - is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= - - is-core-module@^2.2.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.0.tgz#0321336c3d0925e497fd97f5d95cb114a5ccd548" - integrity sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw== - dependencies: - has "^1.0.3" - - is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - - is-glob@^4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - - is-html@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-html/-/is-html-1.1.0.tgz#e04f1c18d39485111396f9a0273eab51af218464" - integrity sha1-4E8cGNOUhRETlvmgJz6rUa8hhGQ= - dependencies: - html-tags "^1.0.0" - - is-json@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-json/-/is-json-2.0.1.tgz#6be166d144828a131d686891b983df62c39491ff" - integrity sha1-a+Fm0USCihMdaGiRuYPfYsOUkf8= - - is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - - is-resolvable@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" - integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== - - is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= - - isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= - - jiti@^1.12.9: - version "1.12.9" - resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.12.9.tgz#2ce45b265cfc8dc91ebd70a5204807cf915291bc" - integrity sha512-TdcJywkQtcwLxogc4rSMAi479G2eDPzfW0fLySks7TPhgZZ4s/tM6stnzayIh3gS/db3zExWJyUx4cNWrwAmoQ== - - js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - - js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - - jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= - - jsdom@^14.1.0: - version "14.1.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-14.1.0.tgz#916463b6094956b0a6c1782c94e380cd30e1981b" - integrity sha512-O901mfJSuTdwU2w3Sn+74T+RnDVP+FuV5fH8tcPWyqrseRAb0s5xOtPgCFiPOtLcyK7CLIJwPyD83ZqQWvA5ng== - dependencies: - abab "^2.0.0" - acorn "^6.0.4" - acorn-globals "^4.3.0" - array-equal "^1.0.0" - cssom "^0.3.4" - cssstyle "^1.1.1" - data-urls "^1.1.0" - domexception "^1.0.1" - escodegen "^1.11.0" - html-encoding-sniffer "^1.0.2" - nwsapi "^2.1.3" - parse5 "5.1.0" - pn "^1.1.0" - request "^2.88.0" - request-promise-native "^1.0.5" - saxes "^3.1.9" - symbol-tree "^3.2.2" - tough-cookie "^2.5.0" - w3c-hr-time "^1.0.1" - w3c-xmlserializer "^1.1.2" - webidl-conversions "^4.0.2" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.3.0" - whatwg-url "^7.0.0" - ws "^6.1.2" - xml-name-validator "^3.0.0" - - json-parse-even-better-errors@^2.3.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" - integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== - - json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - - json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - - json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - - jsonc-parser@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.0.0.tgz#abdd785701c7e7eaca8a9ec8cf070ca51a745a22" - integrity sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA== - - jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= - optionalDependencies: - graceful-fs "^4.1.6" - - jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - - levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - - lilconfig@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.3.tgz#68f3005e921dafbd2a2afb48379986aa6d2579fd" - integrity sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg== - - lines-and-columns@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" - integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= - - linkify-it@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-3.0.3.tgz#a98baf44ce45a550efb4d49c769d07524cc2fa2e" - integrity sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ== - dependencies: - uc.micro "^1.0.1" - - 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" - - lodash.memoize@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= - - lodash.sortby@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" - integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= - - lodash.uniq@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" - integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= - - lodash@^4.17.15, lodash@^4.17.19: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - - lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - - magic-string@^0.25.7: - version "0.25.7" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" - integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== - dependencies: - sourcemap-codec "^1.4.4" - - make-dir@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== - dependencies: - semver "^6.0.0" - - markdown-it-anchor@^8.4.1: - version "8.4.1" - resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-8.4.1.tgz#29e560593f5edb80b25fdab8b23f93ef8a91b31e" - integrity sha512-sLODeRetZ/61KkKLJElaU3NuU2z7MhXf12Ml1WJMSdwpngeofneCRF+JBbat8HiSqhniOMuTemXMrsI7hA6XyA== - - markdown-it@^12.2.0: - version "12.2.0" - resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.2.0.tgz#091f720fd5db206f80de7a8d1f1a7035fd0d38db" - integrity sha512-Wjws+uCrVQRqOoJvze4HCqkKl1AsSh95iFAeQDwnyfxM09divCBSXlDR1uTvyUP3Grzpn4Ru8GeCxYPM8vkCQg== - dependencies: - argparse "^2.0.1" - entities "~2.1.0" - linkify-it "^3.0.1" - mdurl "^1.0.1" - uc.micro "^1.0.5" - - mdn-data@2.0.14: - version "2.0.14" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" - integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== - - mdurl@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" - integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= - - merge2@^1.3.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" - integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== - - micromatch@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" - integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== - dependencies: - braces "^3.0.1" - picomatch "^2.2.3" - - mime-db@1.50.0: - version "1.50.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.50.0.tgz#abd4ac94e98d3c0e185016c67ab45d5fde40c11f" - integrity sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A== - - mime-types@^2.1.12, mime-types@~2.1.19: - version "2.1.33" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.33.tgz#1fa12a904472fafd068e48d9e8401f74d3f70edb" - integrity sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g== - dependencies: - mime-db "1.50.0" - - minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - - nanocolors@^0.1.12: - version "0.1.12" - resolved "https://registry.yarnpkg.com/nanocolors/-/nanocolors-0.1.12.tgz#8577482c58cbd7b5bb1681db4cf48f11a87fd5f6" - integrity sha512-2nMHqg1x5PU+unxX7PGY7AuYxl2qDx7PSrTRjizr8sxdd3l/3hBuWWaki62qmtYm2U5i4Z5E7GbjlyDFhs9/EQ== - - nanoid@^3.1.30: - version "3.1.30" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362" - integrity sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ== - - node-releases@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" - integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== - - normalize-url@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" - integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== - - nth-check@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2" - integrity sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w== - dependencies: - boolbase "^1.0.0" - - nwsapi@^2.1.3: - version "2.2.0" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" - integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== - - oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - - once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - - onigasm@^2.2.5: - version "2.2.5" - resolved "https://registry.yarnpkg.com/onigasm/-/onigasm-2.2.5.tgz#cc4d2a79a0fa0b64caec1f4c7ea367585a676892" - integrity sha512-F+th54mPc0l1lp1ZcFMyL/jTs2Tlq4SqIHKIXGZOR/VkHkF9A7Fr5rRr5+ZG/lWeRsyrClLYRq7s/yFQ/XhWCA== - dependencies: - lru-cache "^5.1.1" - - optionator@^0.8.1: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" - - 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== - - parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - - parse-json@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" - integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== - dependencies: - "@babel/code-frame" "^7.0.0" - error-ex "^1.3.1" - json-parse-even-better-errors "^2.3.0" - lines-and-columns "^1.1.6" - - parse5@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" - integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== - - 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== - - path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - - path-parse@^1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - - path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== - - performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= - - picocolors@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" - integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA== - - picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== - - picomatch@^2.2.2, picomatch@^2.2.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" - integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== - - pkg-dir@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - - pn@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" - integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== - - postcss-calc@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-8.0.0.tgz#a05b87aacd132740a5db09462a3612453e5df90a" - integrity sha512-5NglwDrcbiy8XXfPM11F3HeC6hoT9W7GUH/Zi5U/p7u3Irv4rHhdDcIZwG0llHXV4ftsBjpfWMXAnXNl4lnt8g== - dependencies: - postcss-selector-parser "^6.0.2" - postcss-value-parser "^4.0.2" - - postcss-colormin@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.2.0.tgz#2b620b88c0ff19683f3349f4cf9e24ebdafb2c88" - integrity sha512-+HC6GfWU3upe5/mqmxuqYZ9B2Wl4lcoUUNkoaX59nEWV4EtADCMiBqui111Bu8R8IvaZTmqmxrqOAqjbHIwXPw== - dependencies: - browserslist "^4.16.6" - caniuse-api "^3.0.0" - colord "^2.0.1" - postcss-value-parser "^4.1.0" - - postcss-convert-values@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.0.1.tgz#4ec19d6016534e30e3102fdf414e753398645232" - integrity sha512-C3zR1Do2BkKkCgC0g3sF8TS0koF2G+mN8xxayZx3f10cIRmTaAnpgpRQZjNekTZxM2ciSPoh2IWJm0VZx8NoQg== - dependencies: - postcss-value-parser "^4.1.0" - - postcss-discard-comments@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-5.0.1.tgz#9eae4b747cf760d31f2447c27f0619d5718901fe" - integrity sha512-lgZBPTDvWrbAYY1v5GYEv8fEO/WhKOu/hmZqmCYfrpD6eyDWWzAOsl2rF29lpvziKO02Gc5GJQtlpkTmakwOWg== - - postcss-discard-duplicates@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.1.tgz#68f7cc6458fe6bab2e46c9f55ae52869f680e66d" - integrity sha512-svx747PWHKOGpAXXQkCc4k/DsWo+6bc5LsVrAsw+OU+Ibi7klFZCyX54gjYzX4TH+f2uzXjRviLARxkMurA2bA== - - postcss-discard-empty@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-5.0.1.tgz#ee136c39e27d5d2ed4da0ee5ed02bc8a9f8bf6d8" - integrity sha512-vfU8CxAQ6YpMxV2SvMcMIyF2LX1ZzWpy0lqHDsOdaKKLQVQGVP1pzhrI9JlsO65s66uQTfkQBKBD/A5gp9STFw== - - postcss-discard-overridden@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-5.0.1.tgz#454b41f707300b98109a75005ca4ab0ff2743ac6" - integrity sha512-Y28H7y93L2BpJhrdUR2SR2fnSsT+3TVx1NmVQLbcnZWwIUpJ7mfcTC6Za9M2PG6w8j7UQRfzxqn8jU2VwFxo3Q== - - postcss-merge-longhand@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.0.2.tgz#277ada51d9a7958e8ef8cf263103c9384b322a41" - integrity sha512-BMlg9AXSI5G9TBT0Lo/H3PfUy63P84rVz3BjCFE9e9Y9RXQZD3+h3YO1kgTNsNJy7bBc1YQp8DmSnwLIW5VPcw== - dependencies: - css-color-names "^1.0.1" - postcss-value-parser "^4.1.0" - stylehacks "^5.0.1" - - postcss-merge-rules@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.0.2.tgz#d6e4d65018badbdb7dcc789c4f39b941305d410a" - integrity sha512-5K+Md7S3GwBewfB4rjDeol6V/RZ8S+v4B66Zk2gChRqLTCC8yjnHQ601omj9TKftS19OPGqZ/XzoqpzNQQLwbg== - dependencies: - browserslist "^4.16.6" - caniuse-api "^3.0.0" - cssnano-utils "^2.0.1" - postcss-selector-parser "^6.0.5" - vendors "^1.0.3" - - postcss-minify-font-values@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-5.0.1.tgz#a90cefbfdaa075bd3dbaa1b33588bb4dc268addf" - integrity sha512-7JS4qIsnqaxk+FXY1E8dHBDmraYFWmuL6cgt0T1SWGRO5bzJf8sUoelwa4P88LEWJZweHevAiDKxHlofuvtIoA== - dependencies: - postcss-value-parser "^4.1.0" - - postcss-minify-gradients@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-5.0.2.tgz#7c175c108f06a5629925d698b3c4cf7bd3864ee5" - integrity sha512-7Do9JP+wqSD6Prittitt2zDLrfzP9pqKs2EcLX7HJYxsxCOwrrcLt4x/ctQTsiOw+/8HYotAoqNkrzItL19SdQ== - dependencies: - colord "^2.6" - cssnano-utils "^2.0.1" - postcss-value-parser "^4.1.0" - - postcss-minify-params@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-5.0.1.tgz#371153ba164b9d8562842fdcd929c98abd9e5b6c" - integrity sha512-4RUC4k2A/Q9mGco1Z8ODc7h+A0z7L7X2ypO1B6V8057eVK6mZ6xwz6QN64nHuHLbqbclkX1wyzRnIrdZehTEHw== - dependencies: - alphanum-sort "^1.0.2" - browserslist "^4.16.0" - cssnano-utils "^2.0.1" - postcss-value-parser "^4.1.0" - uniqs "^2.0.0" - - postcss-minify-selectors@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-5.1.0.tgz#4385c845d3979ff160291774523ffa54eafd5a54" - integrity sha512-NzGBXDa7aPsAcijXZeagnJBKBPMYLaJJzB8CQh6ncvyl2sIndLVWfbcDi0SBjRWk5VqEjXvf8tYwzoKf4Z07og== - dependencies: - alphanum-sort "^1.0.2" - postcss-selector-parser "^6.0.5" - - postcss-normalize-charset@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-5.0.1.tgz#121559d1bebc55ac8d24af37f67bd4da9efd91d0" - integrity sha512-6J40l6LNYnBdPSk+BHZ8SF+HAkS4q2twe5jnocgd+xWpz/mx/5Sa32m3W1AA8uE8XaXN+eg8trIlfu8V9x61eg== - - postcss-normalize-display-values@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.1.tgz#62650b965981a955dffee83363453db82f6ad1fd" - integrity sha512-uupdvWk88kLDXi5HEyI9IaAJTE3/Djbcrqq8YgjvAVuzgVuqIk3SuJWUisT2gaJbZm1H9g5k2w1xXilM3x8DjQ== - dependencies: - cssnano-utils "^2.0.1" - postcss-value-parser "^4.1.0" - - postcss-normalize-positions@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-5.0.1.tgz#868f6af1795fdfa86fbbe960dceb47e5f9492fe5" - integrity sha512-rvzWAJai5xej9yWqlCb1OWLd9JjW2Ex2BCPzUJrbaXmtKtgfL8dBMOOMTX6TnvQMtjk3ei1Lswcs78qKO1Skrg== - dependencies: - postcss-value-parser "^4.1.0" - - postcss-normalize-repeat-style@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.1.tgz#cbc0de1383b57f5bb61ddd6a84653b5e8665b2b5" - integrity sha512-syZ2itq0HTQjj4QtXZOeefomckiV5TaUO6ReIEabCh3wgDs4Mr01pkif0MeVwKyU/LHEkPJnpwFKRxqWA/7O3w== - dependencies: - cssnano-utils "^2.0.1" - postcss-value-parser "^4.1.0" - - postcss-normalize-string@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-5.0.1.tgz#d9eafaa4df78c7a3b973ae346ef0e47c554985b0" - integrity sha512-Ic8GaQ3jPMVl1OEn2U//2pm93AXUcF3wz+OriskdZ1AOuYV25OdgS7w9Xu2LO5cGyhHCgn8dMXh9bO7vi3i9pA== - dependencies: - postcss-value-parser "^4.1.0" - - postcss-normalize-timing-functions@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.1.tgz#8ee41103b9130429c6cbba736932b75c5e2cb08c" - integrity sha512-cPcBdVN5OsWCNEo5hiXfLUnXfTGtSFiBU9SK8k7ii8UD7OLuznzgNRYkLZow11BkQiiqMcgPyh4ZqXEEUrtQ1Q== - dependencies: - cssnano-utils "^2.0.1" - postcss-value-parser "^4.1.0" - - postcss-normalize-unicode@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.1.tgz#82d672d648a411814aa5bf3ae565379ccd9f5e37" - integrity sha512-kAtYD6V3pK0beqrU90gpCQB7g6AOfP/2KIPCVBKJM2EheVsBQmx/Iof+9zR9NFKLAx4Pr9mDhogB27pmn354nA== - dependencies: - browserslist "^4.16.0" - postcss-value-parser "^4.1.0" - - postcss-normalize-url@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-5.0.2.tgz#ddcdfb7cede1270740cf3e4dfc6008bd96abc763" - integrity sha512-k4jLTPUxREQ5bpajFQZpx8bCF2UrlqOTzP9kEqcEnOfwsRshWs2+oAFIHfDQB8GO2PaUaSE0NlTAYtbluZTlHQ== - dependencies: - is-absolute-url "^3.0.3" - normalize-url "^6.0.1" - postcss-value-parser "^4.1.0" - - postcss-normalize-whitespace@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.1.tgz#b0b40b5bcac83585ff07ead2daf2dcfbeeef8e9a" - integrity sha512-iPklmI5SBnRvwceb/XH568yyzK0qRVuAG+a1HFUsFRf11lEJTiQQa03a4RSCQvLKdcpX7XsI1Gen9LuLoqwiqA== - dependencies: - postcss-value-parser "^4.1.0" - - postcss-ordered-values@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.0.2.tgz#1f351426977be00e0f765b3164ad753dac8ed044" - integrity sha512-8AFYDSOYWebJYLyJi3fyjl6CqMEG/UVworjiyK1r573I56kb3e879sCJLGvR3merj+fAdPpVplXKQZv+ey6CgQ== - dependencies: - cssnano-utils "^2.0.1" - postcss-value-parser "^4.1.0" - - postcss-reduce-initial@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.0.1.tgz#9d6369865b0f6f6f6b165a0ef5dc1a4856c7e946" - integrity sha512-zlCZPKLLTMAqA3ZWH57HlbCjkD55LX9dsRyxlls+wfuRfqCi5mSlZVan0heX5cHr154Dq9AfbH70LyhrSAezJw== - dependencies: - browserslist "^4.16.0" - caniuse-api "^3.0.0" - - postcss-reduce-transforms@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.1.tgz#93c12f6a159474aa711d5269923e2383cedcf640" - integrity sha512-a//FjoPeFkRuAguPscTVmRQUODP+f3ke2HqFNgGPwdYnpeC29RZdCBvGRGTsKpMURb/I3p6jdKoBQ2zI+9Q7kA== - dependencies: - cssnano-utils "^2.0.1" - postcss-value-parser "^4.1.0" - - postcss-selector-parser@6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c" - integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg== - dependencies: - cssesc "^3.0.0" - indexes-of "^1.0.1" - uniq "^1.0.1" - - postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5: - version "6.0.6" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz#2c5bba8174ac2f6981ab631a42ab0ee54af332ea" - integrity sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg== - dependencies: - cssesc "^3.0.0" - util-deprecate "^1.0.2" - - postcss-svgo@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.0.2.tgz#bc73c4ea4c5a80fbd4b45e29042c34ceffb9257f" - integrity sha512-YzQuFLZu3U3aheizD+B1joQ94vzPfE6BNUcSYuceNxlVnKKsOtdo6hL9/zyC168Q8EwfLSgaDSalsUGa9f2C0A== - dependencies: - postcss-value-parser "^4.1.0" - svgo "^2.3.0" - - postcss-unique-selectors@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-5.0.1.tgz#3be5c1d7363352eff838bd62b0b07a0abad43bfc" - integrity sha512-gwi1NhHV4FMmPn+qwBNuot1sG1t2OmacLQ/AX29lzyggnjd+MnVD5uqQmpXO3J17KGL2WAxQruj1qTd3H0gG/w== - dependencies: - alphanum-sort "^1.0.2" - postcss-selector-parser "^6.0.5" - uniqs "^2.0.0" - - postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" - integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== - - postcss@^7.0.17: - version "7.0.39" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309" - integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== - dependencies: - picocolors "^0.2.1" - source-map "^0.6.1" - - postcss@^8.2.1, postcss@^8.3.6: - version "8.3.11" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.11.tgz#c3beca7ea811cd5e1c4a3ec6d2e7599ef1f8f858" - integrity sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA== - dependencies: - nanoid "^3.1.30" - picocolors "^1.0.0" - source-map-js "^0.6.2" - - posthtml-parser@^0.10.0: - version "0.10.1" - resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.10.1.tgz#63c41931a9339cc2c32aba14f06286d98f107abf" - integrity sha512-i7w2QEHqiGtsvNNPty0Mt/+ERch7wkgnFh3+JnBI2VgDbGlBqKW9eDVd3ENUhE1ujGFe3e3E/odf7eKhvLUyDg== - dependencies: - htmlparser2 "^7.1.1" - - posthtml-render@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/posthtml-render/-/posthtml-render-3.0.0.tgz#97be44931496f495b4f07b99e903cc70ad6a3205" - integrity sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA== - dependencies: - is-json "^2.0.1" - - posthtml@^0.16.5: - version "0.16.5" - resolved "https://registry.yarnpkg.com/posthtml/-/posthtml-0.16.5.tgz#d32f5cf32436516d49e0884b2367d0a1424136f6" - integrity sha512-1qOuPsywVlvymhTFIBniDXwUDwvlDri5KUQuBqjmCc8Jj4b/HDSVWU//P6rTWke5rzrk+vj7mms2w8e1vD0nnw== - dependencies: - posthtml-parser "^0.10.0" - posthtml-render "^3.0.0" - - prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= - - psl@^1.1.28: - version "1.8.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" - integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== - - punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - - purgecss@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/purgecss/-/purgecss-4.0.3.tgz#8147b429f9c09db719e05d64908ea8b672913742" - integrity sha512-PYOIn5ibRIP34PBU9zohUcCI09c7drPJJtTDAc0Q6QlRz2/CHQ8ywGLdE7ZhxU2VTqB7p5wkvj5Qcm05Rz3Jmw== - dependencies: - commander "^6.0.0" - glob "^7.0.0" - postcss "^8.2.1" - postcss-selector-parser "^6.0.2" - - qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== - - queue-microtask@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" - integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== - - relateurl@^0.2.7: - version "0.2.7" - resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" - integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= - - request-promise-core@1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" - integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== - dependencies: - lodash "^4.17.19" - - request-promise-native@^1.0.5: - version "1.0.9" - resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" - integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== - dependencies: - request-promise-core "1.1.4" - stealthy-require "^1.1.1" - tough-cookie "^2.3.3" - - request@^2.88.0: - version "2.88.2" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" - integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.5.0" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - - resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - - resolve@1.20.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== - dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" - - reusify@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== - - rollup-plugin-typescript2@^0.30.0: - version "0.30.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.30.0.tgz#1cc99ac2309bf4b9d0a3ebdbc2002aecd56083d3" - integrity sha512-NUFszIQyhgDdhRS9ya/VEmsnpTe+GERDMmFo0Y+kf8ds51Xy57nPNGglJY+W6x1vcouA7Au7nsTgsLFj2I0PxQ== - dependencies: - "@rollup/pluginutils" "^4.1.0" - find-cache-dir "^3.3.1" - fs-extra "8.1.0" - resolve "1.20.0" - tslib "2.1.0" - - rollup@^2.58.3: - version "2.58.3" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.58.3.tgz#71a08138d9515fb65043b6a18618b2ed9ac8d239" - integrity sha512-ei27MSw1KhRur4p87Q0/Va2NAYqMXOX++FNEumMBcdreIRLURKy+cE2wcDJKBn0nfmhP2ZGrJkP1XPO+G8FJQw== - optionalDependencies: - fsevents "~2.3.2" - - run-parallel@^1.1.9: - version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" - integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== - dependencies: - queue-microtask "^1.2.2" - - safe-buffer@^5.0.1, safe-buffer@^5.1.2: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - - "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - - saxes@^3.1.9: - version "3.1.11" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b" - integrity sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g== - dependencies: - xmlchars "^2.1.1" - - semver@^6.0.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - - shiki@^0.9.12: - version "0.9.12" - resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.9.12.tgz#70cbc8c1bb78ff7b356f84a7eecdb040efddd247" - integrity sha512-VXcROdldv0/Qu0w2XvzU4IrvTeBNs/Kj/FCmtcEXGz7Tic/veQzliJj6tEiAgoKianhQstpYmbPDStHU5Opqcw== - dependencies: - jsonc-parser "^3.0.0" - onigasm "^2.2.5" - vscode-textmate "5.2.0" - - slash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== - - source-map-js@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" - integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug== - - source-map-support@~0.5.20: - version "0.5.20" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.20.tgz#12166089f8f5e5e8c56926b377633392dd2cb6c9" - integrity sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - - source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - - source-map@~0.7.2: - version "0.7.3" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" - integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== - - sourcemap-codec@^1.4.4: - version "1.4.8" - resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" - integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== - - sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - - srcset@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/srcset/-/srcset-4.0.0.tgz#336816b665b14cd013ba545b6fe62357f86e65f4" - integrity sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw== - - sshpk@^1.7.0: - version "1.16.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" - integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - - stable@^0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" - integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== - - stealthy-require@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" - integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= - - stylehacks@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.0.1.tgz#323ec554198520986806388c7fdaebc38d2c06fb" - integrity sha512-Es0rVnHIqbWzveU1b24kbw92HsebBepxfcqe5iix7t9j0PQqhs0IxXVXv0pY2Bxa08CgMkzD6OWql7kbGOuEdA== - dependencies: - browserslist "^4.16.0" - postcss-selector-parser "^6.0.4" - - supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - - svgo@^2.3.0, svgo@^2.6.1: - version "2.7.0" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.7.0.tgz#e164cded22f4408fe4978f082be80159caea1e2d" - integrity sha512-aDLsGkre4fTDCWvolyW+fs8ZJFABpzLXbtdK1y71CKnHzAnpDxKXPj2mNKj+pyOXUCzFHzuxRJ94XOFygOWV3w== - dependencies: - "@trysound/sax" "0.2.0" - commander "^7.2.0" - css-select "^4.1.3" - css-tree "^1.1.3" - csso "^4.2.0" - nanocolors "^0.1.12" - stable "^0.1.8" - - symbol-tree@^3.2.2: - version "3.2.4" - resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" - integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== - - terser@^5.8.0: - version "5.9.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.9.0.tgz#47d6e629a522963240f2b55fcaa3c99083d2c351" - integrity sha512-h5hxa23sCdpzcye/7b8YqbE5OwKca/ni0RQz1uRX3tGh8haaGHqcuSqbGRybuAKNdntZ0mDgFNXPJ48xQ2RXKQ== - dependencies: - commander "^2.20.0" - source-map "~0.7.2" - source-map-support "~0.5.20" - - timsort@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" - integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= - - to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - - tough-cookie@^2.3.3, tough-cookie@^2.5.0, tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - - tr46@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" - integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= - dependencies: - punycode "^2.1.0" - - tslib@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" - integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== - - tslib@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" - integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== - - tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" - - tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= - - type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= - dependencies: - prelude-ls "~1.1.2" - - typescript@^4.4.4: - version "4.4.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c" - integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA== - - uc.micro@^1.0.1, uc.micro@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" - integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== - - uncss@^0.17.3: - version "0.17.3" - resolved "https://registry.yarnpkg.com/uncss/-/uncss-0.17.3.tgz#50fc1eb4ed573ffff763458d801cd86e4d69ea11" - integrity sha512-ksdDWl81YWvF/X14fOSw4iu8tESDHFIeyKIeDrK6GEVTQvqJc1WlOEXqostNwOCi3qAj++4EaLsdAgPmUbEyog== - dependencies: - commander "^2.20.0" - glob "^7.1.4" - is-absolute-url "^3.0.1" - is-html "^1.1.0" - jsdom "^14.1.0" - lodash "^4.17.15" - postcss "^7.0.17" - postcss-selector-parser "6.0.2" - request "^2.88.0" - - uniq@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" - integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= - - uniqs@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" - integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI= - - universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - - uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - - util-deprecate@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - - uuid@^3.3.2: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - - vendors@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e" - integrity sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w== - - verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - - vscode-textmate@5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.2.0.tgz#01f01760a391e8222fe4f33fbccbd1ad71aed74e" - integrity sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ== - - w3c-hr-time@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" - integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== - dependencies: - browser-process-hrtime "^1.0.0" - - w3c-xmlserializer@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794" - integrity sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg== - dependencies: - domexception "^1.0.1" - webidl-conversions "^4.0.2" - xml-name-validator "^3.0.0" - - webidl-conversions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" - integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== - - whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" - integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== - dependencies: - iconv-lite "0.4.24" - - whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" - integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== - - whatwg-url@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" - integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== - dependencies: - lodash.sortby "^4.7.0" - tr46 "^1.0.1" - webidl-conversions "^4.0.2" - - word-wrap@~1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - - wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - - ws@^6.1.2: - version "6.2.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" - integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== - dependencies: - async-limiter "~1.0.0" - - xml-name-validator@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" - integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== - - xmlchars@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" - integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== - - yallist@^3.0.2: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - - yaml@^1.10.0, yaml@^1.10.2: - version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==