diff --git a/.gitignore b/.gitignore index 90ec6574c..d5a05dfcc 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ lib/ .vscode/ coverage/ types/ -.DS_store +.DS_Store packages/solid/src/jsx.d.ts packages/solid/web/server/server.d.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 8007fc502..41853f7d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 1.0.0 + +### Breaking Changes + +#### Options Objects + +Most non-essential arguments on reactive primitives are now living on an options object. This was done to homogenize the API and make it easier to make future additions while remaining backwards compatible. + +#### on + +No longer uses rest parameters for multiple dependencies. Instead pass an array. This facilitates new option to defer execution until dependencies change. + +#### Actions renamed to Directives + +To remove future confusion with other uses of actions the JSX.Actions interace is now the JSX.Directives interface. + ## 0.26.0 - 2021-04-09 This release is about finalizing some API changes on the road to 1.0. This one has one breaking change and not much else. @@ -53,7 +69,7 @@ These are an escape hatch for unusual events. Previously these were custom attri Now that we are supporting SSR for legacy(non-ESM) systems I need to use the main field to indicate a node env. We will be using the "browser" field for the client build in Solid. This straight up breaks Jest which doesn't respect that. I've created `solid-jest` to handle this. -https://github.com/solidui/solid-jest +https://github.com/solidjs/solid-jest ### New Features @@ -63,7 +79,7 @@ Types added for Namespace attributes. You probably won't need most of these beca ```ts declare module "solid-js" { namespace JSX { - interface Actions { // use:____ + interface Directives { // use:____ } interface ExplicitProperties { // prop:____ diff --git a/README.md b/README.md index e06c7bb4c..960cf8e6b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ## Solid
[![Build Status](https://github.com/solidjs/solid/workflows/Solid%20CI/badge.svg)](https://github.com/solidjs/solid/actions/workflows/main-ci.yml) -[![Coverage Status](https://img.shields.io/coveralls/github/solidui/solid.svg?style=flat)](https://coveralls.io/github/solidui/solid?branch=main) +[![Coverage Status](https://img.shields.io/coveralls/github/solidjs/solid.svg?style=flat)](https://coveralls.io/github/solidjs/solid?branch=main) [![NPM Version](https://img.shields.io/npm/v/solid-js.svg?style=flat)](https://www.npmjs.com/package/solid-js) [![](https://img.shields.io/npm/dm/solid-js.svg?style=flat)](https://www.npmjs.com/package/solid-js) [![Discord](https://img.shields.io/discord/722131463138705510)](https://discord.com/invite/solidjs) @@ -20,6 +20,7 @@ Solid is a declarative JavaScript library for creating user interfaces. It does - Small! Completely tree-shakeable Solid's compiler will only include parts of the library you use. - Supports and is built on TypeScript. - Supports modern features like JSX, Fragments, Context, Portals, Suspense, Streaming SSR, Progressive Hydration, Error Boundaries and Concurrent Rendering. +- Works in serverless environments including AWS Lambda and Cloudflare Workers. - Webcomponent friendly and can author Custom Elements - Context API that spans Custom Elements - Implicit event delegation with Shadow DOM Retargeting @@ -65,7 +66,7 @@ Want to see what code Solid generates: ### [Try it Online](https://playground.solidjs.com/) -## Getting Started +## Quick Start > _`npm init solid ` is available with npm 6+._ @@ -104,19 +105,19 @@ For TypeScript remember to set your TSConfig to handle Solid's JSX by: ## Documentation -- [Reactivity](https://github.com/solidjs/solid/blob/main/documentation/reactivity.md) -- [State](https://github.com/solidjs/solid/blob/main/documentation/state.md) -- [JSX Rendering](https://github.com/solidjs/solid/blob/main/documentation/rendering.md) -- [Components](https://github.com/solidjs/solid/blob/main/documentation/components.md) -- [Styling](https://github.com/solidjs/solid/blob/main/documentation/styling.md) -- [Context](https://github.com/solidjs/solid/blob/main/documentation/context.md) -- [Suspense](https://github.com/solidjs/solid/blob/main/documentation/suspense.md) +> This is the documentation for the 1.0.0 RC. THese are significantly slimmed down from what was here previously as we are moving things to the new Docs site. If you wish to see the older docs in the mean time [look here](https://github.com/solidjs/solid/tree/0.x/#documentation). + - [API](https://github.com/solidjs/solid/blob/main/documentation/api.md) - [FAQ](https://github.com/solidjs/solid/blob/main/documentation/faq.md) - [Comparison with other Libraries](https://github.com/solidjs/solid/blob/main/documentation/comparison.md) -- [Storybook](https://github.com/solidjs/solid/blob/main/documentation/storybook.md) -## Resources +### Guides +- [Getting Started](https://github.com/solidjs/solid/blob/main/documentation/guides/getting-started.md) +- [Reactivity](https://github.com/solidjs/solid/blob/main/documentation/guides/reactivity.md) +- [Rendering](https://github.com/solidjs/solid/blob/main/documentation/guides/rendering.md) +- [SSR](https://github.com/solidjs/solid/blob/main/documentation/guides/server.md) + +### Resources - [Examples](https://github.com/solidjs/solid/blob/main/documentation/resources/examples.md) - [Articles](https://github.com/solidjs/solid/blob/main/documentation/resources/articles.md) @@ -195,7 +196,3 @@ Become a sponsor and get your logo on our README on GitHub with a link to your s - -## Status - -Solid is mostly feature complete for its v1.0.0 release. The next releases will be mostly bug fixes and API tweaks on the road to stability. diff --git a/documentation/api.md b/documentation/api.md index 0118bb0bc..bbe3b6420 100644 --- a/documentation/api.md +++ b/documentation/api.md @@ -1,98 +1,1269 @@ -# Core API +# Basic Reactivity -### `createSignal(initialValue, boolean | comparatorFn): [getValueFn, setValueFn]` +## `createSignal` -This is the smallest and most primitive reactive atom used to track a single value. By default signals always notify on setting a value. You can have it only notify on changes if you pass true to the second parameter. Or a custom comparator can be passed in to indicate whether the values should be considered equal and listeners not notified. +```ts +export function createSignal( + value: T, + options?: { name?: string; equals?: false | ((prev: T, next: T) => boolean) } +): [get: () => T, set: (v: T) => T]; +``` -### `createMemo(prev => , initialValue, boolean | comparatorFn): getValueFn` +This is the most basic reactive primitive used to track a single value that changes over time. The create function returns a get and set pair of functions to access and update the signal. -Creates a readonly derived signal that recalculates it's value whenever the executed codes dependencies update. By default memos always notify on updating a value. You can have it only notify on changes if you pass true to the third parameter. Or a custom comparator can be passed in to indicate whether the values should be considered equal and listeners not notified. +```js +const [getValue, setValue] = createSignal(initialValue); -### `createEffect(prev => , initialValue): void` +// read value +getValue(); -Creates a new computation that automatically tracks dependencies and runs after each render where a dependency has changed. Ideal for using `ref`s and managing other side effects. 2nd argument is the initial value. +// set value +setValue(nextValue); +``` -### `onMount(() => )` +Remember to access signals under a tracking scope if you wish them to react to updates. -Registers a method that runs after initial render and elements have been mounted. Ideal for using `ref`s and managing other one time side effects. +## `createEffect` -### `onCleanup(() => )` +```ts +export function createEffect(fn: (v: T) => T, value?: T, options?: { name?: string }): void; +``` -Registers a cleanup method that executes on disposal or recalculation of the current context. Can be used in components or computations. +Creates a new computation that automatically tracks dependencies and runs after each render where a dependency has changed. Ideal for using `ref`s and managing other side effects. -### `createState(initValue): [state, setState]` +```js +const [a, setA] = createSignal(initialValue); -Creates a new State proxy object and setState pair. State only triggers update on values changing. Tracking is done by intercepting property access and automatically tracks deep nesting via proxy. +// effect that depends on signal `a` +createEffect(() => doSideEffect(a())); +``` -### `createContext(defaultContext): Context` +The effect function is called with the value returned from the effect function's last execution. This value can be initialized as an optional 2nd argument. This can be useful for diffing without creating an additional closure. -Creates a new context object that can be used with useContext and the Provider control flow. Default Context is used when no Provider is found above in the hierarchy. +```js +createEffect(prev => { + const sum = a() + b(); + if (sum !== prev) console.log(sum); + return sum; +}, 0); +``` -### `useContext(Context): any` +## `createMemo` -Hook to grab context to allow for deep passing of props with hierarchal resolution of dependencies without having to pass them through each Component function. +```ts +export function createMemo( + fn: (v: T) => T, + value?: T, + options?: { name?: string; equals?: false | ((prev: T, next: T) => boolean) } +): () => T; +``` -# Additional API +Creates a readonly derived signal that recalculates it's value whenever the executed code's dependencies update. -The following are not required to build simple applications but allow a lot more power and control. +```js +const getValue = createMemo(() => computeExpensiveValue(a(), b())); -### `createRoot(disposer => )` +// read value +getValue(); +``` -Creates a new non-tracked context that doesn't auto-dispose. All Solid code should be wrapped in one of these top level as they ensure that all memory/computations are freed up. +The memo function is called with the value returned from the memo function's last execution. This value can be initialized as an optional 2nd argument. This is useful for reducing computations. -### `untrack(() => ): any` +```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; name?: string } +): ResourceReturn; + +export function createResource( + source: U | false | null | (() => U | false | null), + fetcher: (k: U, getPrev: () => T | undefined) => T | Promise, + options?: { initialValue?: T; name?: string } +): ResourceReturn; +``` + +Creates a signal that can manage async requests. The `fetcher` is an async function that accepts return value of the `source` if provided and returns a Promise whose resolved value is set in the resource. The fetcher is not reactive so use the optional first argument if you want it to run more than once. If the source resolves to false, null, or undefined will not to fetch. + +```js +const [data, { mutate, refetch }] = createResource(getQuery, fetchData); + +// read value +data(); + +// check if loading +data.loading; + +// check if errored +data.error; + +// directly set value without creating promise +mutate(optimisticValue); + +// refetch last request just because +refetch(); +``` + +`loading` and `error` are reactive getters and can be tracked. + +## `createState` + +```ts +export function createState( + state: T | State, + options?: { name?: string } +): [get: State, set: SetStateFunction]; +``` + +This creates a tree of signals as proxy that allows individual values in nested data structures to be independently tracked. The create function returns a readonly proxy object, and a state setter function. + +```js +const [state, setState] = createState(initialValue); + +// read value +state.someValue; + +// set value +setState({ merge: "thisValue" }); + +setState("path", "to", "value", newValue); +``` + +### Getters + +State objects support the use of getters to store calculated values. + +```js +const [state, setState] = createState({ + 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] = createState({ + user: { + firstName: "John", + lastName: "Smith", + get fullName() { + return fullName(); + } + } +}); +fullName = createMemo(() => `${state.firstName} ${state.lastName}`); +``` + +### Setting State + +Changes can take the form of function that passes previous state and returns new state or a value. Objects are always merged. Set values to `undefined` to delete them from state. + +```js +const [state, setState] = createState({ 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] = createState({ + 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] = createState({ + 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 } +// ] +// } +``` + +# Lifecycles + +## `onMount` + +```ts +export function onMount(fn: () => void): void; +``` + +Registers a method that runs after initial render and elements have been mounted. Ideal for using `ref`s and managing other one time side effects. It is equivalent to a `createEffect` which does not have any dependencies. + +## `onCleanup` + +```ts +export function onCleanup(fn: () => void): void; +``` + +Registers a cleanup method that executes on disposal or recalculation of the current reactive scope. Can be used in any Component or Effect. + +## `onError` + +```ts +export function onError(fn: (err: any) => void): void; +``` + +Registers an error handler method that executes when child scope errors. Only the nearest scope error handlers execute. Rethrow to trigger up the line. + +# Reactive Utilities + +These helpers provide the ability to better schedule updates and control how reactivity is tracked. + +## `untrack` + +```ts +export function untrack(fn: () => T): T; +``` Ignores tracking any of the dependencies in the executing code block and returns the value. -### `batch(() => ): any` +## `batch` + +```ts +export function batch(fn: () => T): T; +``` + +Holds committing updates within the block until the end to prevent unnecessary recalculation. This means that reading values on the next line will not have updated yet. Solid State's `setState` method and Effects automatically wrap their code in a batch. + +## `on` + +```ts +export function on any> | (() => any), U>( + deps: T, + fn: (value: T, prev: T, prevResults?: U) => U, + options: { defer?: boolean } = {} +): (prev?: U) => U | undefined; +``` + +`on` is designed to be passed into a computation to make its dependencies explicit. If an array of dependencies is passed, value and prevValue are arrays. + +```js +createEffect(on(a, v => console.log(v, b()))); + +// is equivalent to: +createEffect(() => { + const v = a(); + untrack(() => console.log(v, b())); +}); +``` + +You can also not run the compoutation immediately and instead opt in for it to only run on change by setting the defer option to true. + +```js +// doesn't run immediately +createEffect(on(a, v => console.log(v), { defer: true })); + +setA("new"); // now it runs +``` + +## `createRoot` + +```ts +export function createRoot(fn: (dispose: () => void) => T): T; +``` + +Creates a new non-tracked context that doesn't auto-dispose. This useful for nested reactive context that you do not wish to release when the parent re-evaluates. This is a powerful pattern for caching. + +All Solid code should be wrapped in one of these top level as they ensure that all memory/computations are freed up. Normally you do not need to worry about this as `createRoot` is embedded into all `render` entry functions. + +## `mergeProps` + +```ts +export function mergeProps(...sources: any): any; +``` + +A reactive object `merge` method. Useful for setting default props for components in case caller doesn't provide them. Or cloning the props object including reactive properties. + +This method works by using a proxy and resolving properties in reverse order. This allows for dynamic tracking of properties that aren't present when the prop object is first merged. + +```js +// default props +props = mergeProps({ name: "Smith" }, props); + +// clone props +newProps = mergeProps(props); + +// merge props +props = mergeProps(props, otherProps); +``` + +## `splitProps` + +```ts +export function splitProps(props: T, ...keys: Array<(keyof T)[]>): [...parts: Partial]; +``` + +This is the replacement for destructuring. It splits a reactive object by keys while maintaining reactivity. + +```js +const [local, others] = splitProps(props, ["children"]); + +<> + +
{local.children}
+ +``` + +## `useTransition` + +```ts +export function useTransition(): [() => boolean, (fn: () => void, cb?: () => void) => void]; +``` + +Used to batch async updates in a transaction deferring commit until all async processes are complete. This is tied into Suspense and only tracks resources read under Suspense boundaries. + +```js +const [isPending, start] = createTransition(); + +// check if transitioning +isPending(); + +// wrap in transition +start(() => setSignal(newValue), () => /* transition is done */) +``` + +## `observable` + +```ts +export function observable(input: () => T): Observable; +``` + +This method takes a signal and produces a simple Observable. Consume it from the Observable library of your choice with typically with the `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 that caches each item by reference to reduce unnecessary mapping on updates. It only runs the mapping function once per value and then moves or removes it as needed. The index argument is a signal. The map function itself is not tracking. + +Underlying helper for the `` 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[]; +``` + +Similar to `mapArray` except it maps by index. The item is a signal and the index is now the constant. + +Underlying helper for the `` 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}`; + }, + } +}); +``` + +# State Modifiers -Ensures that all notification of updates within the block happen at the same time to prevent unnecessary recalculation. Solid State's setState method and computations(useEffect, useMemo) automatically wrap their code in a batch. +Used to add additional behavior to `setState` function created with `createState`. -### `on(...args, (value, prevValue, prevResult) => result): (prev) => value` +## `produce` -`on` is designed to be passed into a computation to make its deps explicit. If more than one dep is passed, value and prevValue are arrays. +```ts +export function produce( + fn: (state: T) => void +): (state: T extends NotWrappable ? T : State) => T extends NotWrappable ? T : State; +``` -### `onError((err: any) => )` +Immer inspired API for Solid's state objects that allows for localized mutation. -Registers an error handler method that executes when child context errors. Only nearest context error handlers execute. Rethrow to trigger up the line. +```js +setState( + produce(s => { + s.user.name = "Frank"; + s.list.push("Pencil Crayon"); + }) +); +``` -### `createMutable(initValue): state` +## `reconcile` + +```ts +export function reconcile( + value: T | State, + options?: { + key?: string | null; + merge?: boolean; + } = { key: "id" } +): (state: T extends NotWrappable ? T : State) => T extends NotWrappable ? T : State; +``` + +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()); +``` + +# 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] = createState({ count: props.count || 0 }); + const store = [ + state, + { + increment() { + setState("count", c => c + 1); + }, + decrement() { + setState("count", c => c - 1); + } + } + ]; + + return {props.children}; +} +``` + +## `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 useful tools to have. + +## `createMutable` + +```ts +export function createMutable( + state: T | State, + options?: { name?: string } +): State { +``` Creates a new mutable State proxy object. State only triggers update on values changing. Tracking is done by intercepting property access and automatically tracks deep nesting via proxy. -### `createDeferred(() => , options: { timeoutMs: number }): getValueFn` +Useful for integrating external systems or as a compatibility layer with MobX/Vue. + +> **Note:** As mutable state can be passed around and mutated anywhere, which can make it harder to follow and easier to break unidirectional flow, it generally recommended to use `createState` instead. The `produce` state 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); +``` -Creates memo that only notifies downstream changes when the browser is idle. `timeoutMs` is the maximum time to wait before forcing the update. +Mutables support setters along with getters. -### `createComputed(prev => , initialValue): void` +```js +const user = createMutable({ + firstName: "John", + lastName: "Smith", + get fullName() { + return `${this.firstName} ${this.lastName}`; + }, + set fullName(value) { + [this.firstName, this.lastName] = value.split(" "); + } +}); +``` -Creates a new computation that automatically tracks dependencies and runs immediately. Use this to write to other reactive primitives or to reactively trigger async data loading before render. 2nd argument is the initial value. +## `createDeferred` -### `createRenderEffect(prev => , initialValue): void` +```ts +export function createDeferred( + source: () => T, + options?: { timeoutMs?: number; name?: string; 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, options?: { name?: string }): 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, + options?: { name?: string } +): 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(() => , comparatorFn?): (key) => boolean` +## `createSelector` -Creates a conditional signal that only notifies subscribers when entering or exiting their key matching the value. Useful for delegated selection state. +```ts +export function createSelector( + source: () => T, + fn?: (a: U, b: T) => boolean, + options?: { name?: string } +): (k: U) => boolean; +``` -### `createResource(fetcher, { initialValue }): [getValueFn, { mutate, refetch }]` -### `createResource(source, fetcher, { initialValue }): [getValueFn, { mutate, refetch }]` +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). -Creates a new resource signal that can hold an async resource. Resources when read while loading trigger Suspense. The `fetcher` is a function that accepts return value of the `source` if provided and returns a Promise whose resolved value is set in the resource. The fetcher is not reactive so use the optional first argument if you want it to run more than once. If provided false, null, or undefined signals not to fetch. +```js +const isSelected = createSelector(selectedId); -### `lazy(() => ): Component` +{item =>
  • {item.name}
  • }
    ; +``` -Used to lazy load components to allow for things like code splitting and Suspense. +# Rendering -### `useTransition(): [isPending, startTransition]` +These imports are exposed from `solid-js/web`. -Used to batch async updates deferring commit until all async processes are complete. +## `render` -### `mergeProps(...sources): target` +```ts +export function render(code: () => JSX.Element, element: MountableElement): () => void; +``` -A reactive object `merge` method. Useful for setting default props for components in case caller doesn't provide them. Or cloning the props object including reactive properties. +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; + } +): { html: string; script: 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, script } = renderToString(App); +``` + +## `renderToStringAsync` + +```ts +export function renderToStringAsync( + fn: () => T, + options?: { + eventNames?: string[]; + timeoutMs?: number; + nonce?: string; + } +): Promise<{ html: string; script: string }>; +``` + +Same as `renderToString` except it 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, script } = await renderToStringAsync(App); +``` + +## `renderToNodeStream` + +```ts +export function renderToNodeStream( + fn: () => T, + options?: { + eventNames?: string[]; + nonce?: string; + } +): { + stream: NodeJS.ReadableStream; + script: string; +}; +``` + +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 +const { stream, script } = renderToNodeStream(App); +``` + +## `renderToWebStream` + +```ts +export function renderToWebStream( + fn: () => T, + options?: { + eventNames?: string[]; + nonce?: string; + } +): { + writeTo: (writer: WritableStreamDefaultWriter) => Promise; + script: string; +}; +``` + +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. The `writeTo` method takes a WritableStream writer. + +```js +const { readable, writable } = new TransformStream(); +const writer = writable.getWriter(); +const encoder = new TextEncoder(); + +const { writeTo, script } = renderToWebStream(App); + +// Write the head of your document +writer.write(encoder.encode(htmlStart)); +writeTo(writer).then(() => { + // Write the end of your document + writer.write(encoder.encode(htmlEnd)); + writer.close(); +}); +``` + +## `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 + +Solid uses components for control flow. The reason is that with reactivity to be performant we have to control how elements are created. For example with a list a simple `map` is inefficient as it always maps everything. This means helper functions. + +Wrapping these in components is convenient way for terse templating and allows users to compose and build their own control flows. + +These built-in control flows 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 control flow. + +```jsx +Loading...
    }> + {item =>
    {item}
    } + +``` + +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 is similar to the ternary operator(`a ? b : c`) 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 (rows keyed to index). This useful when there is no conceptual key, like if the data is 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}
    }> + +
    +``` + +## `` + +```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. The event name is always lowercased. 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 highly optimized way delegating events. + +```jsx +function handler(itemId, e) { + /*...*/ +} + +
      + {item =>
    • } +
    ; +``` + +Events can not be rebound and the bindings are not reactive. The reason is that it is generally more expensive to attach/detach listeners. Since events naturally are called there is no need for reactivity simply shortcut your handler if desired. + +```jsx +// if defined call it, otherwised don't. +
    props.handleClick?.()} /> +``` + +## `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; +``` + +These functions run at render time and you can do whatever you want in them. Create signals and effects, register cleanup functions, whatever you desire. + +```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. -### `splitProps(props, ...keyArrays): [...splitProps]` +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 */. -Splits a reactive object by keys while maintaining reactivity. +```jsx + +``` +This also works on children. +```jsx +{/*@once*/state.wontUpdate} +``` \ No newline at end of file diff --git a/documentation/comparison.md b/documentation/comparison.md index f5f003ccc..ea19136db 100644 --- a/documentation/comparison.md +++ b/documentation/comparison.md @@ -9,7 +9,7 @@ React has had a big influence on Solid. Its unidirectional flow and explicit seg However, as much as Solid aligns with React's design philosophy, it works fundamentally different. 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. Whereas Solid renders each Template once in entirety constructing its reactive graph and afterwords only 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`. Watch out for destructuring or early property access losing reactivity. You don't need explicit keys on list rows to have "keyed" behavior. Finally, there is no VDOM so imperative VDOM APIs like `React.Children` and `React.cloneElement` make no sense. I encourage finding different ways to solve problems that use these declaratively. +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`. Watch out for destructuring or early property access losing reactivity. 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. Finally, there is no VDOM so imperative VDOM APIs like `React.Children` and `React.cloneElement` make no sense. I encourage finding different ways to solve problems that use these declaratively. ## Vue @@ -39,9 +39,9 @@ Knockout's bindings are just strings in HTML which are walked over at runtime. T 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 Knockout, Solid's primitives might look strange to you. The read/write separation is intentional and not just to make life hard. Look to group adopting a state/action (Flux) mental model. While the libraries look similar they ppromote different best practices. +If you are used Knockout, Solid's primitives might look strange to you. The read/write separation is intentional and not just to make life hard. Look to adopting a state/action (Flux) mental model. While the libraries look similar they ppromote different best practices. -## lit-html & LighterHTML +## 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. diff --git a/documentation/context.md b/documentation/context.md deleted file mode 100644 index 266d8d97b..000000000 --- a/documentation/context.md +++ /dev/null @@ -1,75 +0,0 @@ -# Context - -Solid has Context API for dependency injection which comprises of `createContext`, `Provider` control flow, and `useContext`. `createContext` lets you create the Context Object. When Solid renders a component that subscribes to this Context object it will read the current context value from the closest matching Provider above it in the tree. If there is not provider above it will use the default value. - -Example below using Solid's own state mechanism to create a global store that wraps the whole app. Notice that the `Nested` component can access the counter without it being passed down via Props. While you could arguably use a singleton, this pattern gives clear ownership and allows hierarchical store contexts. You could use stores in this case for common data concerns that could appear multiple times on the same page like pagination, scrolling, etc, and have child components that know how to interact with their associated stores. This is a very powerful pattern as you compose patterns that wrap Solid's primitives and Context to create reusable injectable multi component driving modules. - -> **For React Users:** Although the API surface is the same, Solid's Context API can house just about anything. The reactivity comes from the primitives not from the top down hierarchy. This means that it isn't the parent components driving reactivity, so there aren't any performance concerns, but context only updates when the primitives you use update. - -```jsx -// counter-context.js -import { createState, createContext } from "solid-js"; - -export const CounterContext = createContext([{ count: 0 }, {}]); - -export function CounterProvider(props) { - const [state, setState] = createState({ count: props.count || 0 }), - store = [ - state, - { - increment() { - setState("count", c => c + 1); - }, - decrement() { - setState("count", c => c - 1); - } - } - ]; - - return ( - - {props.children} - - ); -} - -// index.js -import { render } from "solid-js/web"; -import { CounterProvider } from "./counter-context"; -import App from "./app"; - -render( - () => ( - // start counter at 2 - - - - ), - document.getElementById("app") -); - -// app.js -import Nested from "./nested"; - -export default const App = () => ( - <> -

    Welcome to Counter App

    - - -); - -// nested.js -import { useContext } from "solid-js"; -import { CounterContext } from "./counter-context"; - -export default const Nested = () => { - const [counter, { increment, decrement }] = useContext(CounterContext); - return ( - <> -
    {counter.count}
    - - - - ); -}; -``` diff --git a/documentation/faq.md b/documentation/faq.md index 654c2069d..5809ba93d 100644 --- a/documentation/faq.md +++ b/documentation/faq.md @@ -56,9 +56,9 @@ Being able to pass the ability to update state is arguably even more important t ### 9. Can I use Solid's reactivity on its own? -Of course. While I 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/solidui/solid) and it's underlying [DOM Expressions library](https://github.com/ryansolid/dom-expressions) in the first place to make a renderer purely from the reactive system. +Of course. While I 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 it's 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/solidui/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 good way to get a feel. +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 good way to get a feel. ### 10. Does Solid have a Next.js or Material Components like library I can use? diff --git a/documentation/guides/getting-started.md b/documentation/guides/getting-started.md new file mode 100644 index 000000000..ca20592b0 --- /dev/null +++ b/documentation/guides/getting-started.md @@ -0,0 +1,84 @@ +# Getting Started + +## Try Solid + +By far the easiest way to get started with Solid is trying 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](../resources/examples.md). + +Alternatively you can use our CLI to bootstrap client-side a project (based on [Create React App](https://github.com/facebook/create-react-app)). + +> _`npm init solid ` is available with npm 6+._ + +You can get started with a simple app with the CLI with by running: + +```sh +> npm init solid app my-app +``` + +Or for a TypeScript starter: + +```sh +> npm init solid app-ts my-app +``` + +## Learn Solid + +Solid is all about small composable pieces that serve as building blocks for our applications. These pieces are mostly functions which make up many shallow top-level APIs. Fortunately to start you won't need to know about most of them. + +The two main types of building blocks you have at your disposal are Components and Reactive Primitves. + +Components are functions that accept a props object and return JSX elements including native DOM elements and other components. They can be expressed as Pascal Cased JSX Elements: + +```jsx +function MyComponent(props) { + return
    Hello {props.name}
    +} + + +``` + +Components are lightweight as they are not stateful themselves and have no instances but instead serve as factory functions for our DOM elements and reactive primitives. + +Solid's fine-grained reactivity is built on 3 simple primitives, Signals, Memos, and Effects. 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 here about [Solid's Reactivity](reactivity.md) and [Solid's Rendering](rendering.md). + +You can also view our full [API Reference](../api.md) + +## Think Solid + +Solid's design carries several opinions on what principles and values help us best make websites and applications. It is easier to learn and use Solid when 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 exists 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 built 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) let's 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 streaming rendering on the server you get to write your code one way and have it execute on the server. Things 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. + +For more information read the [SSR guide](./server.md) diff --git a/documentation/guides/reactivity.md b/documentation/guides/reactivity.md new file mode 100644 index 000000000..c4f9b8610 --- /dev/null +++ b/documentation/guides/reactivity.md @@ -0,0 +1,86 @@ +# 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 primitves, 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 a get and set 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-excute when ever 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 and re-execute 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. As a 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 it 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. Super simple yet effective. The actual implementation is much more complicated but that is the guts of what is going on. + +# 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, State, 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. \ No newline at end of file diff --git a/documentation/components.md b/documentation/guides/rendering.md similarity index 53% rename from documentation/components.md rename to documentation/guides/rendering.md index 3a6cdb2b1..ecec1034d 100644 --- a/documentation/components.md +++ b/documentation/guides/rendering.md @@ -1,6 +1,47 @@ -# Components +# Rendering -Components in Solid are just Pascal(Capital) cased functions. Their first argument is an props object and they return real DOM nodes. +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 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 = () => ( @@ -19,11 +60,11 @@ const Label = props => ( ); ``` -Since all nodes from JSX are actual DOM nodes, the only responsibility of top level Components is appending to the DOM. +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, you can define properties on your components which allow a parent component to pass data to a child component. Here a parent is passing the string "Hello" to the `Label` component via a `greeting` property. +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 = () => ( @@ -62,7 +103,7 @@ const Label = props => ( ); ``` -Unlike in some other frameworks, you cannot use object destructuring on the `props` of a component. This is because the `props` object is, behind the scenes, relies on Object getters to lazily retrieve values. Using object destructuring breaks the reactivity of `props`. +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: @@ -79,9 +120,9 @@ This example shows the wrong way of accessing props in Solid: 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. +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 desired. +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"; @@ -116,7 +157,7 @@ const BasicComponent = props => { }; ``` -This is equivalently can be hoisted into a function: +This, equivalently, can be hoisted into a function: ```jsx const BasicComponent = props => { @@ -126,7 +167,7 @@ const BasicComponent = props => { }; ``` -Another option if it is an expensive computation to use `createMemo`. For example: +Another option, if it is an expensive computation, is to use `createMemo`. For example: ```jsx const BasicComponent = props => { @@ -164,7 +205,7 @@ const BasicComponent = props => { }; ``` -Solid's Components are the key part of its performance. Solid's approach is "Vanishing" Components 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. In so we defer 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. +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 ; @@ -183,7 +224,7 @@ untrack(() => ); ``` -To help maintain reactivity Solid has a couple prop helpers: +To help maintain reactivity Solid has a couple of prop helpers: ```jsx // default props @@ -202,7 +243,7 @@ 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 is an array. 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. +Solid handles JSX Children similar to React. A single child is a single value on `props.children` and multiple children is handled 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 @@ -237,51 +278,4 @@ const List = (props) => { ; ``` -**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 luxury of creating Virtual DOM nodes ahead of time 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. - -## Lifecycle - -All lifecycles in Solid are tied to the lifecycle of the reactive system. - -If you wish to perform some side effect on mount or after update use `createEffect` after render has complete: - -```jsx -import { createSignal, createEffect } from "solid-js"; - -function Example() { - const [count, setCount] = createSignal(0); - - createEffect(() => { - document.title = `You clicked ${count()} times`; - }); - - return ( -
    -

    You clicked {count()} times

    - -
    - ); -} -``` - -For convenience if you need to only run it once you can use `onMount` which is the same as `createEffect` but will only run once. Keep in mind Solid's cleanup is independent of this mechanism. So if you aren't reading the DOM you don't need to use these. - -If you wish to release something on the Component being destroyed, simply wrap in an `onCleanup`. - -```jsx -const Ticker = () => { - const [state, setState] = createState({ count: 0 }), - t = setInterval(() => setState({ count: state.count + 1 }), 1000); - - // remove interval when Component destroyed: - onCleanup(() => clearInterval(t)); - - return
    {state.count}
    ; -}; -``` - -## Web Components - -Since change management is independent of code modularization, Solid Templates are sufficient as is to act as Components, or Solid fits easily into other Component structures like Web Components. - -[Solid Element](https://github.com/solidui/solid/tree/main/packages/solid-element) Provides an out of the box solution for wrapping your Solid Components as Custom Elements. +**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. \ No newline at end of file diff --git a/documentation/guides/server.md b/documentation/guides/server.md new file mode 100644 index 000000000..2eda52082 --- /dev/null +++ b/documentation/guides/server.md @@ -0,0 +1,80 @@ +# 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.getElementById("main")); +``` + +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, script } = renderToString(() => ); + +// Asynchronous string rendering +const { html, script } = await renderToStringAsync(() => ); + +// Node Stream API +const { stream, script } = renderToNodeStream(() => ); +stream.pipe(res, { end: false }); // pipe it to the response + +// Web Stream API (for like Cloudflare Workers) +const { writeTo, script } = renderToWebStream(() => ); +const { readable, writable } = new TransformStream(); +const writer = writable.getWriter(); +writeTo(writer).then(() => writer.close()); +``` +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 +} +``` + +## 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. + +## 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/documentation/reactivity.md b/documentation/reactivity.md deleted file mode 100644 index a045b1c0a..000000000 --- a/documentation/reactivity.md +++ /dev/null @@ -1,175 +0,0 @@ -# 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")); -``` - -> **For React Users:** This looks like React Hooks, but it is very different. There are no Hook rules, or concern about stale closures because your Component only runs once. It is only the "Hooks" that re-execute. So they always have the latest references. - -## Signals - -Signals are the glue that hold the library together. They are a simple primitive that contain values that change over time. Each Signal comes with a separate setter in a tuple. This setter is the only way to update the Signal's value. - -With Signals you can track all sorts of changes from various sources in your applications. They are not tied to any specific component and can be used wherever whenever. - -```js -import { createSignal, onCleanup } from "solid-js"; - -function createTick(delay) { - const [getCount, setCount] = createSignal(0), - handle = setInterval(() => setCount(getCount() + 1), delay); - onCleanup(() => clearInterval(handle)); - return getCount; -} -``` -## Effects - -When we want our Signals to affect the world we use Effects. Effects wrap expressions that contain Signals and re-execute them everytime those Signals change. The most common ones are JSX bindings we can manually create them as well. - -```js -import { createSignal, onCleanup } from "solid-js"; - -function logTick(delay) { - const [getCount, setCount] = createSignal(0), - handle = setInterval(() => setCount(getCount() + 1), delay); - onCleanup(() => clearInterval(handle)); - - // logs the count every `delay` interval - createEffect(() => console.log(getCount())); -} -``` - -Effects are what allow the DOM to stay up to date. While you don't see them, everytime you write an expression in the JSX(code between the parenthesis `{}`), the compiler is wrapping it in a function and passing it to a `createEffect` call. - -## Memos - -Wrapping a Signal read in a thunk `() => signal()` creates a derived signal that can be tracked as well. The same holds true for accessing props or Solid's reactive State proxies. Want to use state as a signal just wrap it in a function. Any pure function that wraps one ore more Signal executions is also a Signal. - -```js -import { createSignal, createEffect } from "solid-js"; - -const [count, setCount] = createSignal(1); -const doubleCount = () => count() * 2; - -createEffect(() => console.log(doubleCount())); -setCount(count() + 1); - -// 2 -// 4 -``` - -Memos allow us to store and access values without re-evaluating them until their dependencies change. They are very similar to derived Signals mentioned above except they only re-evaluate when their dependencies change and return the last cached value on read. - -Keep in mind memos are only necessary if you wish to prevent re-evaluation when the value is read. Useful for expensive operations like DOM Node creation. Otherwise they are not very different from other Signals. - -```js -import { createSignal, createMemo, createEffect } from "solid-js"; - -const [count, setCount] = createSignal(1); -const expensiveCount = createMemo(() => expensiveCalculation(count())); - -createEffect(() => console.log(expensiveCount())); -setCount(count() + 1); -``` - -Memos also pass the previous value on each execution. This is useful for reducing operations (obligatory Redux in a couple lines example): - -```js -// reducer -const reducer = (state, action = {}) => { - switch (action.type) { - case "LIST/ADD": - return { ...state, list: [...state.list, action.payload] }; - default: - return state; - } -}; - -// initial state -const state = { list: [] }; - -// redux -const [getAction, dispatch] = createSignal(), - getStore = createMemo(state => reducer(state, getAction()), state); - -// subscribe and dispatch -createEffect(() => console.log(getStore().list)); -dispatch({ type: "LIST/ADD", payload: { id: 1, title: "New Value" } }); -``` - -That being said there are plenty of reasons to use actual Redux. - -## Managing Dependencies and Updates - -Sometimes we want to be explicit about what triggers Effects and Memos to update and nothing else. Solid offers ways to explicitly set dependencies or to not track under a tracking scope at all. - -```js -const [a, setA] = createSignal(1); -const [b, setB] = createSignal(1); - -createEffect(() => { - const v = a(); - untrack(() => console.log(v, b())); -}); // 1, 1 - -setA(2); // 2, 1 -setB(2); // (does not trigger) -setA(3); // 3, 2 (still reads latest values) -``` - -For convenience Solid provides an `on` operator to set up explict dependencies for any computation. - -```js -// equivalent to above -createEffect(on(a, v => console.log(v, b()))); -``` - -Solid executes synchronously but sometimes you want to apply multiple changes at once. `batch` allows us to do that without triggering updates multiple times. - -```js -batch(() => { - setA(4); - setB(6); -}); -``` - -## Cleanup - -While Solid does not have Component lifecyles in the traditional sense, it still needs to handle cleaning up reactive dependencies. The way Solid works is that each nested computation is owned by it's parent reactive scope. In so all computations must be created as part of a root. This detail is generally taken care of for you as the `render` method contains a `createRoot` call. But it can be called directly for cases where you need to control disposal outside of this cycle. - -Once inside a scope whenever the scope is re-evaluated or disposed of itself, all children computations will be disposed. In addition you can register a `onCleanup` method that will execute as part of this disposal cycle. - -_Note: Solid's graph is synchronously executed so any starting point that isn't caused by a reactive update (perhaps an asynchronous entry) should start from its own root. There are other ways to handle asynchronicity as shown in the [Suspense Docs](./suspense.md)_ - -## Composition - -Solid's primitives combine wonderfully. They encourage composing more sophisticated patterns to fit developer need. - -```js -// Solid's fine-grained exivalent to React's `useReducer` Hook -const useReducer = (reducer, state) => { - const [store, setStore] = createState(state); - const dispatch = (action) => { - state = reducer(state, action); - setStore(reconcile(state)); - } - return [store, dispatch]; -}; -``` diff --git a/documentation/rendering.md b/documentation/rendering.md deleted file mode 100644 index 67e33e9f7..000000000 --- a/documentation/rendering.md +++ /dev/null @@ -1,314 +0,0 @@ -# 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, 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? - -Still there is some confusion as to what JSX is and is not. JSX is an XML-like syntax extension to EcmaScript (https://facebook.github.io/jsx/). It is not a language or runtime. Those can be refered to as HyperScript. So while Solid's JSX and might resemble React it by no means works like React and there should be no illusions that a JSX library will just work with Solid. Afterall, there are no JSX libraries, as they all work without JSX, only HyperScript ones. - -## 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 both is more performant and produces less code then creating each element one by one with document.createElement. - -More documentation is available at: [babel-plugin-jsx-dom-expressions](https://github.com/ryansolid/dom-expressions/tree/main/packages/babel-plugin-jsx-dom-expressions) - -## 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 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 - -``` -> Support for namespace in JSX is coming in TS 4.2. - -### Note on binding order - -Static attributes are created as part of the html template together. 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. - -### Note on forms - -Solid expects the UI to reflect its state. This means updating state on form actions. Failing to do so can cause unexpected behavior as setting state to the same value will not trigger an update even if the DOM value has diverged. In general it is recommended you handle forms in this "controlled" manner. - -In some cases it might make sense to manage the form state outside of Solid via refs. These "uncontrolled" forms can also work. Just be conscious of the difference as mixing approaches can lead to unexpected results. - -## 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")); -``` - -## Events - -`on_____` handlers are event handlers expecting a function. The compiler will delegate events where possible (Events that can be composed and bubble) else it will fall back `el.addEventListener`. - -If you wish to bind a value to events pass an array handler instead and the second argument will be passed to your event handler as the first argument (the event will be second). This can improve performance in large lists when the event is delegated. - -```jsx -function handler(itemId, e) {/*...*/} - -
      - {item =>
    • } -
    -``` - -This delegation solution works with Web Components and the Shadow DOM as well if the events are composed. That limits the list to custom events and most UA UI events like onClick, onKeyUp, onKeyDown, onDblClick, onInput, onMouseDown, onMouseUp, etc.. - -To allow for casing to work all custom events should follow the all lowercase convention of native events. If you want to use different event convention (or use Level 3 Events "addEventListener") use the "on" or "oncapture" namespace binding. - -```jsx -
    alert(e.detail)} /> -``` - -## Spreads - -Solid supports spread operator on native elements and Components. While not able to be optimized by the compiler these are useful for forwarding data through. Especially when the intermediary is unsure of what possible props are being passed down. - -```js -function MyDiv(props) { - return
    -} -``` - -Solid supports dynamically changing which values are on the spread object value on native elements. However, currently there is a limitation on Components that while they support dynamic changes, all properties must be on the props object at first render. - -## Control Flow - -While you could use a map function to loop, they aren't optimized. It is perhaps not as big of a deal in VDOM-based libraries (like React), since they always execute all the code from top down repeatedly anyway. But Solid is designed to _avoid_ doing that, so we rely on techniques like isolated contexts and memoization. This is complicated and require special methods which Solid exposes through JSX control flow syntax. - -```jsx -
      - No Users
    }> - {user => ( -
  • -
    {user.firstName}
    - 100}> -
    Verified
    -
    -
  • - )} - - -``` - -Control flows can be imported from `solid-js` but as a convenience the compiler will automatically import them from `solid-js/web`. - -### For - -Keyed list iteration: -```jsx -Loading...
    }> - {item =>
    {item}
    } - -``` - -Optional second argument is an index signal: -```jsx -Loading...
    }> - {(item, index) =>
    #{index()} {item}
    } - -``` - -### Show - -Conditionally control content (make sure `when` is boolean): -```jsx - 0} fallback={
    Loading...
    }> -
    My Content
    -
    -``` - -Or as a way of keying blocks: -```jsx -Loading...
    }> - {user =>
    {user.firstName}
    } - -``` - -_Note Show is designed to handle more complex scenarios like Component insertions. For simple dynamic expressions use boolean or ternary operator._ - -### Switch/Match - -```jsx -Not Found
    }> - - - - - - - -``` - -### Suspense - -```jsx -Loading...
    }> - - -``` - -### SuspenseList - -```jsx - - - Loading posts...}> - - - Loading fun facts...}> - - - -``` - -### ErrorBoundary - -Catches uncaught errors and renders fallback content. -```jsx -Something went terribly wrong
    }> - - -``` - -### Index - -Non-Keyed list iteration (rows keyed to index). This useful when there is no conceptual key, like if the data is primitives and it is the index that is fixed rather than the value. Useful nested reactivity when the data is simple strings/numbers and not models. - -The item is a signal: -```jsx -Loading...
    }> - {item =>
    {item()}
    } - -``` - -Optional second argument is an index number: -```jsx -Loading...
    }> - {(item, index) =>
    #{index} {item()}
    } - -``` - -Also available from `solid-js/web`: - -### Dynamic - -This component lets you insert an arbitrary Component or tag and passes the props through to it. - -```jsx - -``` - -### Portal - -This inserts the element in the mount node. Useful for inserting Modals outside of the page layout. Events still propagate through the Component Hierarchy. - -```jsx - -
    My Content
    -
    -``` - -## Refs - -Refs come in 2 flavours. `ref` which directly assigns the value, and which calls a callback `(ref) => void` with the reference. - -### `ref` - -```jsx -function MyComp() { - let myDiv; - createEffect(() => console.log(myDiv.clientWidth)); - return
    ; -} -``` - -On a native intrinsic element as the element executes the provided variable will be assigned. This form usually is used in combination with `createEffect` to do work after the component has mounted. Like do a DOM measurement or attach DOM plugins etc... - -When applied to a Component it acts similarly but also passes a prop in that is a function that is expected to be called with a ref to forward the ref (more on this in the next section): - -```jsx -function App() { - let myDiv; - createEffect(() => console.log(myDiv.clientWidth)); - return ; -} -``` - -Callback form expects a function like React's callback refs. Original use case is like described above: - -```jsx -function MyComp(props) { - return
    ; -} - -function App() { - let myDiv; - createEffect(() => console.log(myDiv.clientWidth)); - return ; -} -``` - -You can also apply a callback `ref` on a Component: - -```jsx -function App() { - return console.log(ref.clientWidth)} />; -} -``` - -This just passes the function through as `props.ref` again and work similar to the example above except it would run synchronously during render. You can use this to chain as many `ref` up a Component chain as you wish. - -## Actions - -> Support for Namespaced JSX Attributes is available in TypeScript 4.2 - -Creating a Component is the cleanest way to package reusable functionality data and view behavior. Reactive primitive composition is often the best way to reuse data behavior. However sometimes there is a need for behavior that can be re-used cross DOM element. - -Solid provides a custom directive syntax for adding additional behavior to native elements as a syntax sugar over `ref` making it easy to combine multiple on a single element. - -```jsx -
    - -const [name, setName] = createSignal(""); - -``` - -To create a directive simply expose a function with this signature `(el: HTMLElement, valueAccessor: () => /*binding value*/) => {}`. Value accessor lets you track it if you wish to. And you can register `onCleanup` methods to cleanup any side effects you create. - -```jsx -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 Actions { - draggable: boolean; - model: [() => any, (v: any) => any]; - } - } -} -``` - -## Server Side Rendering (Experimental) - -See [solid-ssr](https://github.com/solidui/solid/blob/main/packages/solid-ssr) diff --git a/documentation/resources/articles.md b/documentation/resources/articles.md index c41425ee0..a06cd4fb4 100644 --- a/documentation/resources/articles.md +++ b/documentation/resources/articles.md @@ -12,6 +12,7 @@ ## Latest Articles +- [Components are Pure Overhead](https://dev.to/this-is-learning/components-are-pure-overhead-hpm) Unnecessary boundaries have real cost. - [5 Places SolidJS is not the Best](https://dev.to/this-is-learning/5-places-solidjs-is-not-the-best-5019) It's important to know the limitations of your tools. - [Building a Reactive Library from Scratch](https://dev.to/ryansolid/building-a-reactive-library-from-scratch-1i0p) Build a reactive library in only 50 lines of code. - [A Hands-on Introduction to Fine-Grained Reactivity](https://dev.to/ryansolid/a-hands-on-introduction-to-fine-grained-reactivity-3ndf) Learn reactivity by example. diff --git a/documentation/resources/examples.md b/documentation/resources/examples.md index 254469a47..a44d85c9e 100644 --- a/documentation/resources/examples.md +++ b/documentation/resources/examples.md @@ -11,18 +11,19 @@ - [Styled JSX](https://codesandbox.io/s/solid-styled-jsx-xgx6b) A simple example of using Styled JSX with Solid. - [Counter Context](https://codesandbox.io/s/counter-context-gur76) Implement a global store with Context API - [Async Resource](https://codesandbox.io/s/2o4wmxj9zy) Ajax requests to SWAPI with Promise cancellation +- [Async Resource GraphQL](https://codesandbox.io/s/async-resource-graphql-r4rcx?file=/index.js) Simple resource for handling graphql request. - [Suspense](https://codesandbox.io/s/5v67oym224) Various Async loading with Solid's Suspend control flow - [Suspense Tabs](https://codesandbox.io/s/solid-suspense-tabs-vkgpj) Defered loading spinners for smooth UX. - [SuspenseList](https://codesandbox.io/s/solid-suspenselist-eorvk) Orchestrating multiple Suspense Components. - [Redux Undoable Todos](https://codesandbox.io/s/pkjw38r8mj) Example from Redux site done with Solid. - [Simple Todos Template Literals](https://codesandbox.io/s/jpm68z1q33) Simple Todos using Lit DOM Expressions - [Simple Todos HyperScript](https://codesandbox.io/s/0vmjlmq94v) Simple Todos using Hyper DOM Expressions -- [Simple async resource for calling graphql endpoint](https://codesandbox.io/s/async-resource-graphql-r4rcx?file=/index.js) Simple async resource for handling graphql endpoint. ## Demos -- [TodoMVC](https://github.com/solidui/solid-todomvc) Classic TodoMVC example -- [Real World Demo](https://github.com/solidui/solid-realworld) Real World Demo for Solid -- [Hacker News](https://github.com/solidui/solid-hackernews) Hacker News Clone for Solid +- [TodoMVC](https://github.com/solidjs/solid-todomvc) Classic TodoMVC example +- [Real World Demo](https://github.com/solidjs/solid-realworld) Real World Demo for Solid +- [Hacker News](https://github.com/solidjs/solid-hackernews) Hacker News Clone for Solid +- [Storybook](https://github.com/rturnq/storybook-solid) Solid with Storybook ## Benchmarks - [JS Framework Benchmark](https://github.com/krausest/js-framework-benchmark/tree/main/frameworks/keyed/solid) The one and only diff --git a/documentation/resources/projects.md b/documentation/resources/projects.md index a2db406d6..b3671f699 100644 --- a/documentation/resources/projects.md +++ b/documentation/resources/projects.md @@ -2,16 +2,16 @@ ## Libraries -- [Solid Element](https://github.com/solidui/solid/blob/main/packages/solid-element) +- [Solid Element](https://github.com/solidjs/solid/blob/main/packages/solid-element) Extensions to Solid.js that add a Web Component wrapper. - [Lume Element](https://github.com/lume/element) Build Web Components with simple templates and reactivity. -- [Solid Styled Components](https://github.com/solidui/solid-styled-components) +- [Solid Styled Components](https://github.com/solidjs/solid-styled-components) Styled Components for Solid using 1kb library Goober. -- [Solid Styled JSX](https://github.com/solidui/solid-styled-jsx) - Wrapper for using Solid with Zeit's Styled JSX. -- [Solid Meta](https://github.com/solidui/solid/blob/main/packages/solid-meta) +- [Solid Styled JSX](https://github.com/solidjs/solid-styled-jsx) + Wrapper for using Solid with Vercel's Styled JSX. +- [Solid Meta](https://github.com/solidjs/solid/blob/main/packages/solid-meta) Library for adding metadata tags to the document head. -- [Solid Transition Group](https://github.com/solidui/solid-transition-group) Simple animations for Solid +- [Solid Transition Group](https://github.com/solidjs/solid-transition-group) Simple animations for Solid - [Storeon Solid](https://github.com/storeon/solidjs) Solid bindings for Storeon - [Reatom Solid](https://github.com/skrylnikov/reatom-solid) Solid bindings for Reatom - [Solid Orbit](https://github.com/andgate/solid-orbit) Solid bindings for Orbit @@ -19,13 +19,13 @@ - [Solid i18n](https://github.com/amoutonbrady/solid-i18n) Tiny translation library for solid-js inspired by rosetta - [Solid Chart](https://github.com/MrFoxPro/solid-chart.js) Solid Chart.js wrapper - [Solid HeroIcons](https://github.com/amoutonbrady/solid-heroicons) HeroIcons for Solid -- [React Solid State](https://github.com/solidui/react-solid-state) +- [React Solid State](https://github.com/solidjs/react-solid-state) React Hooks API to use Solid.js paradigm in your existing React apps. ## Routers - [Solid Router](https://github.com/rturnq/solid-router) A React Router inspired router for Solid -- [Solid App Router](https://github.com/solidui/solid-app-router) A configuration driven nested router for Solid +- [Solid App Router](https://github.com/solidjs/solid-app-router) A configuration driven nested router for Solid - [Solid TypeFu Router5](https://github.com/mikeplus64/solid-typefu-router5) A TypeScript forward router - [Solid Router5](https://github.com/zxlib/solid-router5) A Solid router built on Router5. - [Solid Router](https://github.com/mduclehcm/solid-router) A declarative router for Solid @@ -46,12 +46,12 @@ - [Vite Template Solid](https://github.com/amoutonbrady/vite-template-solid) - Vite ## Tooling -- [Solid Refresh](https://github.com/solidui/solid-refresh) Universal plugin for HMR -- [Solid Jest](https://github.com/solidui/solid-jest) Presets to test Solid for Browser or Node -- [Solid Testing Library](https://github.com/solidui/solid-testing-library) Solid's Testing Library -- [Create Solid](https://github.com/solidui/create-solid) Solid's port of Create React App. +- [Solid Refresh](https://github.com/solidjs/solid-refresh) Universal plugin for HMR +- [Solid Jest](https://github.com/solidjs/solid-jest) Presets to test Solid for Browser or Node +- [Solid Testing Library](https://github.com/solidjs/solid-testing-library) Solid's Testing Library +- [Create Solid](https://github.com/solidjs/create-solid) Solid's port of Create React App. - [Vite Plugin Solid](https://github.com/amoutonbrady/vite-plugin-solid) Solid plugin for Vite. -- [Solid Playground](https://github.com/solidui/solid-playground) A playground for Solid. +- [Solid Playground](https://github.com/solidjs/solid-playground) A playground for Solid. - [Solid Debug](https://github.com/amoutonbrady/solid-debug) Simple visual debugger for Solid. - [Solid Esbuild Plugin](https://github.com/amoutonbrady/esbuild-plugin-solid) Solid plugin for Esbuild. diff --git a/documentation/state.md b/documentation/state.md deleted file mode 100644 index 9d94f7f3b..000000000 --- a/documentation/state.md +++ /dev/null @@ -1,219 +0,0 @@ -# State - -State is a core work horse of Solid. It is composed of many on demand reactive Signals through a proxy object. It is deeply nested reactivity, and lazily creates Signals on demand. - -The advantage is that it is automatically reactive and resembles data structures you may already have. It removes the classic issues with fine-grained reactivity around mapping reactive structures and serializing JSON. And as a structure itself it can be diffed allowing interaction with immutable data and snapshots. - -Through the use of proxies and explicit setters it gives the control of an immutable interface and the performance of a mutable one. The setters support a variety of forms, but to get started set and update state with an object. - -> Note: State objects themselves aren't reactive. Only the property access on them are. So destructuring in non-tracked scopes will not track updates. Also passing the state object directly to bindings will not track unless those bindings explicitly access properties. Finally, while nested state objects will be notified when new properties are added, top level state cannot be tracked so adding properties will not trigger updates when iterating over keys. This is the primary reason state does benefit from being created as a top level array. - -### `createState(object)` - -Solid's state object are deeply nested reactive data trees useful for global stores, model caches, and 3rd party immutable data interopt. They have a much more powerful setter that allows to specify nested changes and use value and function forms for updates. - -They can be used in Components as well and is the go to choice when data gets more complicated (nested). - -```jsx -import { createState } from "solid-js"; -import { render } from "solid-js/web"; - -const App = () => { - const [state, setState] = createState({ - user: { - firstName: "John", - lastName: "Smith", - get fullName() { - return `${this.firstName} ${this.lastName}`; - } - } - }); - - return ( -
    setState("user", "lastName", value => value + "!")}> - {state.user.fullName} -
    - ); -}; - -render(() => , document.getElementById("app")); -``` - -Remember if you destructure or spread a state object outside of a computation or JSX reactivity is lost. However, unlike Vue we don't separate our `setup` from our view code so there is little concern about transforming or transfering these reactive atoms around. Just access the properties where you need them. - -With Solid State and Context API you really don't need 3rd party global stores. These proxies are optimized part of the reactive system and lend to creating controlled unidirectional patterns. - -### `setState(changes)` - -### `setState(...path, changes)` - -This merges the changes into the path on the state object. All changes made in a single setState command are applied syncronously (ie all changes see each other at the same time). Changes can take the form of function that passes previous state and returns new state or a value. Objects are always merged. Set values to `undefined` to delete them from state. - -```js -const [state, setState] = createState({ 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' }) -``` - -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] = createState({ - 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] = createState({ - 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 } -// ] -// } -``` - -## Modifiers - -This library also provides a state setter modifiers which can optionally be included to provide different behavior when setting state. - -### `produce(state => void)` - -Solid supports a ImmerJS style mutable form with the produce modifier. - -```js -const [state, setState] = createState({ - counter: 2, - list: [ - { id: 23, title: 'Birds' } - { id: 27, title: 'Fish' } - ] -}); - -setState(produce(s => { - s.counter = s.counter * 3; - s.list[1].title += '!'; -})); -// { -// counter: 6, -// list: [ -// { id: 23, title: 'Birds' } -// { id: 27, title: 'Fish!' } -// ] -// } -``` - -### `reconcile(value, options)` - -`setState` on it's own does a replace(or shallow merge). This only triggers the reactivity at that point that the change occurs. But what if that data is larger and we do not know what has changed? It can be inefficient to trigger everything starting from that higher level point. - -`reconcile` can be used to do deep diffs by applying the changes from a new State value. This is useful when pulling in immutable data trees from stores like Redux, Apollo(GraphQL), RxJS or any large data snapshot(maybe from the server) to ensure the least amount of mutations to your state. That instead of replacing the whole value, we should attempt to update only what has changed. - -By default `reconcile` will try to use referential equality and failing that will fall back to using a key property in the data to match items in the new input value. The new input state can be any shape and `reconcile` will deeply diff it for changes. - -However `reconcile` is configurable to change that key or aggressively merge every field. This pushes all change to the leaves which is non-keyed, but could be useful for certain situations. - -```js -// subscribing to an observable -const unsubscribe = store.subscribe(({ todos }) => ( - setState('todos', reconcile(todos))); -); -onCleanup(() => unsubscribe()); -``` - -The second parameter are options to configure the diff algorithm: - -```js -setState('users', reconcile( - store.get('users'), - { - key: '_id' // does a keyed comparison - default: 'id' - merge: false // overwrites rather than detects array position changes when not keyed - default: false - } -)) -``` - -### Mutable State - -Sometimes it makes sense especially when interopting with 3rd parties or legacy systems to use mutable state. Solid provides a `createMutable` for this purpose. It allows direct mutation much like MobX's Observables and Vue's Reactive. While less than ideal for managing global state, passing data to component children, these can often the most unobtrusive way to deal with an external library. - -> Use with caution as it can promote difficult to reason about code, anti-patterns, and unexpected performance cliffs. Keep in mind Vue and MobX care less about these inefficient patterns since they have a VDOM safety net. We do not. For advanced users only. - -```js -const user = createMutable({ - firstName: "John", - lastName: "Smith", - get fullName() { - return `${this.firstName} ${this.lastName}`; - }, - set fullName(value) { - const parts = value.split(" "); - batch(() => { - this.firstName = parts[0]; - this.lastName = parts[1]; - }); - } -}); - -user.firstName = "Jake"; -``` - -Along with getters Mutable state supports setters. Setters are atomic but remember that you need to `batch` your changes if you don't want to waste work on multiple updates. `setState` does this automatically with Immutable State. diff --git a/documentation/storybook.md b/documentation/storybook.md deleted file mode 100644 index f7c184aeb..000000000 --- a/documentation/storybook.md +++ /dev/null @@ -1,74 +0,0 @@ -# Storybook - -This is the guide for setting up [storybook](https://storybook.js.org/) for solid components. - -### Step 1: Install storybook/html - -```sh -> npx -p @storybook/cli sb init --type html -``` - -### Step 2: Add babel-preset-solid to .babelrc - -```json -{ - "presets": ["solid"] -} -``` - -### Step 3: Update .storybook/config.js to setup solid Root for each story - -Replace the auto generated config with this: - -```js -import { addDecorator, configure } from "@storybook/html"; -import { createRoot } from "solid-js"; - -// automatically import all files ending in *.stories.js -configure(require.context("../stories", true, /\.stories\.js$/), module); - -addDecorator(story => { - return createRoot(() => story()); -}); -``` - -### Step 4: Update stories/index.stories.js - -Replace the auto generated html story with this: - -```js -import { console } from "global"; -import { createState, onCleanup } from "solid-js"; - -export default { - title: "Demo" -}; - -export const heading = () =>

    Hello World

    ; - -export const button = () => { - return ; -}; - -function Counter() { - const [state, setState] = createState({ count: 0 }); - - const timer = setInterval(() => { - setState({ count: state.count + 1 }); - }, 1000); - - onCleanup(() => { - clearInterval(timer); - }); - - return
    {state.count}
    ; -} - -export const counter = () => { - return ; -}; -``` - -### Step 5: npm run storybook - -Storybook will be started at port 6006 diff --git a/documentation/styling.md b/documentation/styling.md deleted file mode 100644 index 8710556e7..000000000 --- a/documentation/styling.md +++ /dev/null @@ -1,83 +0,0 @@ -# Styling - -Styling is not any different in Solid than most libraries. You can use CSS inserted in the head of your document. Or you can use CSS Modules with your Webpack or Rollup config, you can use preprocessor like SASS or PostCSS. Libraries like Bootstrap or Tailwind are compatible. Solid also supports generic CSS in JS libraries like Emotion. - -In addition Solid has 2 libraries to support some more common CSS in JS patterns with [solid-styled-components](https://github.com/solidui/solid-styled-components), a Styled Component library that works with Solid's Component system and [solid-styled-jsx](https://github.com/solidui/solid-styled-jsx) a wrapper on Zeit's Styled JSX to work with Solid. - -Styled Components: - -```jsx -import { styled } from "solid-styled-components"; - -const Btn = styled("button")` - border-radius: ${props => props.size}px; -`; - -; -``` - -Styled JSX: - -```jsx -function Button() { - const [isLoggedIn, login] = createSignal(false); - return ( - <> - - - - ); -} -``` - -## Inline Styles - -Solid supports both string styles and style objects (using style property form .. ie hyphenated not camel cased). - -```jsx -
    - -
    -``` - -This also means it can support stuff like CSS variables: - -```jsx -
    -``` - -In addition to supporting CSS variables it puts things consistent with CSS string and SSR versions. -[`setProperty`](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration/setProperty) is also more performant. This does mean that you must use units like "px" or "em" on properties expecting sizes. - -_Note: The compiler automatically optimizes object form when declared inline unrolling any iteration._ - -## Classes - -Solid supports declaring classes with both `className` and `class`: - -```jsx -
    - -
    -``` - -Solid also provides an additional binding for toggling multiple classes `classList`: - -```jsx -
    -``` diff --git a/documentation/suspense.md b/documentation/suspense.md deleted file mode 100644 index 600477d41..000000000 --- a/documentation/suspense.md +++ /dev/null @@ -1,284 +0,0 @@ -# Suspense - -Suspense is an API designed to seamlessly integrate asynchronous processes into Solid's synchronous reactive system. - -## Asynchronous Rendering - -In Solid all reactive nodes must be created as part of execution of another reactive node starting at the root node that is created in the `render` method. Even though the graph must be created synchronously updates to a particular node can be triggered asynchronously. Once a node is running more reactive nodes can be created. In so, rendering can be achieved piecewise as long as the next node is created as part of the current execution. This is essentially asynchronous rendering. - -> **For React Users:** Time splitting is trivial for a library like Solid since the primitives are already finer grained. While it is true there is always the cost of initial rendering, unlike React doing part of the work and then triggering an update to do the rest of the work doesn't cause the whole tree to reconcile again. - -A simple example of this would be something like this: - -```jsx -function Deferred(props) { - const [resume, setResume] = createSignal(false); - setTimeout(() => setResume(true), 0); - - return {props.children}; -} - -// somewhere in a view -<> - - - {/* doesn't render right away */} - - -; -``` - -Solid does include a scheduler similar to React's Concurrent Mode scheduler which allows work to be scheduled in idle frames. The easiest way to leverage it is to use `createDeferred` which creates a memo that will only be read as the cpu is available. - -```jsx -function App() { - const [text, setText] = createSignal("hello"); - const deferredText = createDeferred(text, { timeoutMs: 2000 }); - - return ( -
    - {/* Keep passing the current text to the input */} - - ... - {/* But the list is allowed to "lag behind" when necessary */} - -
    - ); -} -``` - -## Placeholders & Transitions(Experimental) - -But what if we don't control when the asynchronous action returns and we need to deal with potentially multiple asynchronous actions. This is where Suspense comes in. It inverts the control, so that the child can manage its asynchronous needs and the parent just sets the rules of how to display the fallback content that is shown as these processes complete. Unlike conditional control flow (like the `Deferred` component above) the children are not blocked and get execute their asynchronous actions triggering Suspense rather than pushing the responsibility on the parent. Suspense consists of 2 states: - -- normal - This is when no asynchronous actions are being processed and the system is working as normal. -- fallback - This is the fallback content (like a loading spinner) to show in place of the loading if the asynchronous actions have not completed by the time the delay has run out. - -Consider the simple case of switching between 3 tabs which have asynchronous loaded tabs. To use Suspense you need to use the `Suspense` Component to wrap the asynchronous activity. - -```jsx -import { createState, Suspense } from "solid-js"; - -function App() { - const [state, setState] = createState({ activeTab: 1 }); - - return ( - <> -
      -
    • setState({ activeTab: 1 })}>Tab1
    • -
    • setState({ activeTab: 2 })}>Tab2
    • -
    • setState({ activeTab: 3 })}>Tab3
    • -
    - }> - - ... - ... - ... - - - - ); -} -``` - -In this case if the tab hasn't loaded you will see a `LoadingSpinner` and as you switch you will see another `LoadingSpinner` as it moves in and out of suspended state. - -But we can do more when we already have data loaded. We can avoid going back to the fallback state by leveraging `useTransition`. It returns a method to wrap state updates that can be deferred and an method that tracks whether the transition is currently active. 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 the transition. Any new nested `Suspense` components with drop to "fallback" - -```jsx -import { createState, useTransition, Suspense } from "solid-js"; - -function App() { - const [state, setState] = createState({ activeTab: 1 }), - [isPending, startTransition] = useTransition(); - - return ( - <> -
      -
    • startTransition(() => setState({ activeTab: 1 }))}>Tab1
    • -
    • startTransition(() => setState({ activeTab: 2 }))}>Tab2
    • -
    • startTransition(() => setState({ activeTab: 3 }))}>Tab3
    • -
    - }> - - ... - ... - ... - - - - ); -} -``` - -> **For React Users:** Given the nature of Solid's Reactive system, the throw a promise approach React uses doesn't make sense here. React just re-runs that part of the tree again, whereas Solid cannot pickup from where it left off. Instead Solid's Suspense mechanism ties into the Context API. Like React it is the closest Suspense Component that handles the Suspense state. - -## Code Splitting - -The first use for using Suspense is lazy loading components. This easily allows the browser or bundlers like Webpack to code split. That way the page can be loaded with part of the JavaScript code and the rest can be loaded separately as needed. Solid's `lazy` takes the dynamic import of Component that is the default export of a module and turns it into a Component you can define in your JSX view. You can pass props and interact with it as if it were the Component you were importing itself. However, this Component doesn't render until its code has been asynchronously loaded and doesn't trigger loading until it is to be rendered. - -```jsx -import { lazy } from "solid-js"; -const OtherComponent = lazy(() => import("./OtherComponent")); - -function MyComponent() { - return ( -
    - Loading...
    }> - - -
    - ); -} -``` - -There are lots of potential patterns for code splitting, but routing is a good start. For instance taking the example from the previous section, we can defer loading our Component to when the corresponding tab becomes active: - -```jsx -import { Suspense } from "solid-js"; - -const ComponentA = lazy(() => import("./ComponentA")); -const ComponentB = lazy(() => import("./ComponentB")); -const ComponentC = lazy(() => import("./ComponentC")); - -const App = () => { - const [state, setState] = createState({ activeTab: 1 }); - - return ( - } maxDuration={300}> - - - - - - - - - - - - - ); -}; -``` - -## Data Loading - -Solid ships with a primitive to handle async data loading, `createResource`. It has a trackable `loading` and `error` properties as well. - -```jsx -import { createResource } from "solid-js"; - -// notice returns a function that returns a promise -const fetchJSON = query => fetch(query).then(r => r.json()); - -export default const UserPanel = props => { - let [user] = createResource(() => `https://swapi.co/api/people/${props.userId}/`, fetchJSON); - - return
    - - Loading... - Something Went Wrong - {({ name, height, mass, birthYear }) => - <> -

    {name}

    -
      -
    • Height: {height}
    • -
    • Mass: {mass}
    • -
    • Birth Year: {birthYear}
    • -
    - - }
    -
    -
    -} -``` - -This example handles the different loading states. However, you can expand this example to use Suspense instead by wrapping with the `Suspense` Component. - -Resource also returns actions that can be performed, like `refetch` and `mutate`. And if absent using `fetch` to return JSON is the default fetcher. - -```js -let [user, { refetch, mutate }] = createResource( - () => `https://swapi.co/api/people/${props.userId}/` -fetchJSON); -``` - -The first argument is the value to be passed into the fetcher. If it is a function it will be tracked. If it is null or undefined the fetcher will not be executed. It can be omitted commpletely as well if the resource only fires once and you wish to put all your logic in the fetcher. - -> **For React Users:** At the time of writing this React has not completely settled how their Data Fetching API will look. Solid ships with this feature today, and it might differ from what React ultimately lands on. - -## Render as you Fetch - -It is important to note that Suspense is tracked based on data requirements of the the reactive graph not the fact data is being fetched. Suspense is enacted when a child of a Suspense Component accesses a Resource not when the fetch occurs. In so, it is possible to start loading the Component data and lazy load the Component itself at the same time, instead of waiting for the Component to load to start loading the data. - -```jsx -// start loading data before any part of the page is executed. -const [user] = createResource(fetchUser); -const [posts] = createResource(fetchPost); - -function ProfilePage() { - return ( - Loading profile...}> - - Loading posts...}> - - - - ); -} - -function ProfileDetails() { - // Try to read user info, although it might not have loaded yet - return

    {state.user?.name}

    ; -} - -function ProfileTimeline() { - // Try to read posts, although they might not have loaded yet - return ( -
      - {post =>
    • {post.text}
    • }
      -
    - ); -} - -render(ProfilePage, document.getElementById("app")); -``` - -## Coordinating Suspense Components with SuspenseList (Experimental) - -Sometimes you have multiple `Suspense` Components you wish to coordinate. Sure you could put everything under a single `Suspense` but that limits us to a single loading behavior. A single fallback state and everything always needs to wait until the last thing is loaded. Instead we introduce the `SuspenseList` Component to coordinate. Consider: - -```jsx -function ProfilePage(props) { - return ( - <> - - Loading posts...}> - - - Loading fun facts...}> - - - - ); -} -``` - -If we wrap this 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 page jumping around. You can set `revealOrder` to `backwards` and `together` as well, which reverse this order, or wait for all Suspense Components to load respectively. 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`. - -A `SuspenseList` can contain other `SuspenseList`'s to create flowing tables or grids etc. - -```jsx - - - Loading posts...}> - - - Loading fun facts...}> - - - -``` - -> **For React Users:** Again this works a bit different than its React counterpart as it uses the Context API. In so nesting Suspense Components are perfectly fine. However, do not put them under dynamic areas like control flows as order is based on execution so conditional rendering can cause unpredictable behavior. Also unlike the current Suspense implication even if you are not seeing the "next" Suspense element they are all evaluated immediately on render. This unblocking behavior allows further downstream evaluation that currently does not happen in React. diff --git a/documentation/troubleshooting.md b/documentation/troubleshooting.md deleted file mode 100644 index 81ddc4131..000000000 --- a/documentation/troubleshooting.md +++ /dev/null @@ -1,8 +0,0 @@ -# Help! - -## Reactivity is not working - -- If you application has duplicate instances of the `solid-js` package inside `node_modules`, this can cause reactivity to break. - - This can happen when you've `npm link`ed dependencies into your project. - - If you're using Webpack with `babel-preset-solid`, you may have luck fixing the issue with using the [`RootMostResolvePlugin`](https://github.com/webpack/webpack/issues/985#issuecomment-260230782) - - If you're using Node.js `require` in Electron (for example), you'll need to write a Require hook (see the `pirates` package, for example) to make it import the root-most version of `solid-js`. (I'm not sure if such a hook already exists). diff --git a/lerna.json b/lerna.json index eb0712100..c07ece9ee 100644 --- a/lerna.json +++ b/lerna.json @@ -2,5 +2,5 @@ "packages": [ "packages/*" ], - "version": "0.26.5" + "version": "1.0.0-rc.1" } diff --git a/package-lock.json b/package-lock.json index 809cdbc5b..f995c730f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "solid-js", - "version": "0.26.0", + "version": "1.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "solid-js", - "version": "0.26.0", + "version": "1.0.0", "license": "MIT", "dependencies": { "component-register": "~0.8.0", @@ -14,51 +14,50 @@ "solid-styled-components": "^0.26.3" }, "devDependencies": { - "@babel/cli": "^7.13.14", - "@babel/core": "^7.13.15", - "@babel/preset-env": "^7.13.15", + "@babel/cli": "^7.14.3", + "@babel/core": "^7.14.3", + "@babel/preset-env": "^7.14.2", "@babel/preset-typescript": "^7.13.0", "@rollup/plugin-babel": "5.3.0", - "@rollup/plugin-commonjs": "18.0.0", + "@rollup/plugin-commonjs": "19.0.0", "@rollup/plugin-json": "4.1.0", - "@rollup/plugin-node-resolve": "11.2.1", + "@rollup/plugin-node-resolve": "13.0.0", "@rollup/plugin-replace": "2.4.2", - "@types/jest": "^26.0.22", + "@types/jest": "^26.0.23", "@types/shelljs": "^0.8.8", "babel-jest": "^26.6.3", - "babel-plugin-jsx-dom-expressions": "~0.26.3", + "babel-plugin-jsx-dom-expressions": "^0.27.7", "coveralls": "^3.1.0", - "dom-expressions": "0.26.4", - "gitly": "^2.0.2", - "hyper-dom-expressions": "0.26.4", + "dom-expressions": "0.27.7", + "gitly": "^2.1.0", + "hyper-dom-expressions": "0.27.7", "jest": "~26.6.3", "jest-ts-webcompat-resolver": "^1.0.0", "lerna": "^3.22.1", - "lit-dom-expressions": "0.26.4", + "lit-dom-expressions": "0.27.7", "ncp": "2.0.0", "npm-run-all": "^4.1.5", "rimraf": "^3.0.2", - "rollup": "^2.45.1", + "rollup": "^2.48.0", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-copy": "^3.4.0", "shelljs": "^0.8.4", - "solid-jest": "^0.0.2", + "solid-jest": "^0.0.3", "symlink-dir": "^4.1.0", "tsconfig-replace-paths": "0.0.5", "typescript": "~4.2.4" } }, "node_modules/@babel/cli": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.13.14.tgz", - "integrity": "sha512-zmEFV8WBRsW+mPQumO1/4b34QNALBVReaiHJOkxhUsdo/AvYM62c+SKSuLi2aZ42t3ocK6OI0uwUXRvrIbREZw==", + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.14.3.tgz", + "integrity": "sha512-zU4JLvwk32ay1lhhyGfqiRUSPoltVDjhYkA3aQq8+Yby9z30s/EsFw1EPOHxWG9YZo2pAGfgdRNeHZQAYU5m9A==", "dev": true, "dependencies": { "commander": "^4.0.1", "convert-source-map": "^1.1.0", "fs-readdir-recursive": "^1.1.0", "glob": "^7.0.0", - "lodash": "^4.17.19", "make-dir": "^2.1.0", "slash": "^2.0.0", "source-map": "^0.5.0" @@ -85,26 +84,26 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.13.15.tgz", - "integrity": "sha512-ltnibHKR1VnrU4ymHyQ/CXtNXI6yZC0oJThyW78Hft8XndANwi+9H+UIklBDraIjFEJzw8wmcM427oDd9KS5wA==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.0.tgz", + "integrity": "sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q==", "dev": true }, "node_modules/@babel/core": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.13.15.tgz", - "integrity": "sha512-6GXmNYeNjS2Uz+uls5jalOemgIhnTMeaXo+yBUA72kC2uX/8VW6XyhVIo2L8/q0goKQA3EVKx0KOQpVKSeWadQ==", + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.3.tgz", + "integrity": "sha512-jB5AmTKOCSJIZ72sd78ECEhuPiDMKlQdDI/4QRI6lzYATx5SSogS1oQA2AoPecRCknm30gHi2l+QVvNUu3wZAg==", "dev": true, "dependencies": { "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.13.9", - "@babel/helper-compilation-targets": "^7.13.13", - "@babel/helper-module-transforms": "^7.13.14", - "@babel/helpers": "^7.13.10", - "@babel/parser": "^7.13.15", + "@babel/generator": "^7.14.3", + "@babel/helper-compilation-targets": "^7.13.16", + "@babel/helper-module-transforms": "^7.14.2", + "@babel/helpers": "^7.14.0", + "@babel/parser": "^7.14.3", "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.15", - "@babel/types": "^7.13.14", + "@babel/traverse": "^7.14.2", + "@babel/types": "^7.14.2", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -148,12 +147,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.13.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.13.9.tgz", - "integrity": "sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw==", + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.3.tgz", + "integrity": "sha512-bn0S6flG/j0xtQdz3hsjJ624h3W0r3llttBMfyHX3YrZ/KtLYr15bjA0FXkgW7FpvrDuTuElXeVjiKlYRpnOFA==", "dev": true, "dependencies": { - "@babel/types": "^7.13.0", + "@babel/types": "^7.14.2", "jsesc": "^2.5.1", "source-map": "^0.5.0" } @@ -178,12 +177,12 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.13.13", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.13.tgz", - "integrity": "sha512-q1kcdHNZehBwD9jYPh3WyXcsFERi39X4I59I3NadciWtNDyZ6x+GboOxncFK0kXlKIv6BJm5acncehXWUjWQMQ==", + "version": "7.13.16", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz", + "integrity": "sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.13.12", + "@babel/compat-data": "^7.13.15", "@babel/helper-validator-option": "^7.12.17", "browserslist": "^4.14.5", "semver": "^6.3.0" @@ -202,16 +201,20 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.13.8.tgz", - "integrity": "sha512-qioaRrKHQbn4hkRKDHbnuQ6kAxmmOF+kzKGnIfxPK4j2rckSJCpKzr/SSTlohSCiE3uAQpNDJ9FIh4baeE8W+w==", + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.3.tgz", + "integrity": "sha512-BnEfi5+6J2Lte9LeiL6TxLWdIlEv9Woacc1qXzXBgbikcOzMRM2Oya5XGg/f/ngotv1ej2A/b+3iJH8wbS1+lQ==", "dev": true, "dependencies": { - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-member-expression-to-functions": "^7.13.0", + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-function-name": "^7.14.2", + "@babel/helper-member-expression-to-functions": "^7.13.12", "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/helper-replace-supers": "^7.13.0", + "@babel/helper-replace-supers": "^7.14.3", "@babel/helper-split-export-declaration": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-create-regexp-features-plugin": { @@ -285,14 +288,14 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", - "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.2.tgz", + "integrity": "sha512-NYZlkZRydxw+YT56IlhIcS8PAhb+FEUiOzuhFTfqDyPmzAhRge6ua0dQYT/Uh0t/EDHq05/i+e5M2d4XvjgarQ==", "dev": true, "dependencies": { "@babel/helper-get-function-arity": "^7.12.13", "@babel/template": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/types": "^7.14.2" } }, "node_modules/@babel/helper-get-function-arity": { @@ -333,19 +336,19 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.13.14.tgz", - "integrity": "sha512-QuU/OJ0iAOSIatyVZmfqB0lbkVP0kDRiKj34xy+QNsnVZi/PA6BoSoreeqnxxa9EHFAIL0R9XOaAR/G9WlIy5g==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.2.tgz", + "integrity": "sha512-OznJUda/soKXv0XhpvzGWDnml4Qnwp16GN+D/kZIdLsWoHj05kyu8Rm5kXmMef+rVJZ0+4pSGLkeixdqNUATDA==", "dev": true, "dependencies": { "@babel/helper-module-imports": "^7.13.12", "@babel/helper-replace-supers": "^7.13.12", "@babel/helper-simple-access": "^7.13.12", "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/helper-validator-identifier": "^7.12.11", + "@babel/helper-validator-identifier": "^7.14.0", "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.13", - "@babel/types": "^7.13.14" + "@babel/traverse": "^7.14.2", + "@babel/types": "^7.14.2" } }, "node_modules/@babel/helper-optimise-call-expression": { @@ -375,15 +378,15 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz", - "integrity": "sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw==", + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.3.tgz", + "integrity": "sha512-Rlh8qEWZSTfdz+tgNV/N4gz1a0TMNwCUcENhMjHTHKp3LseYH5Jha0NSlyTQWMnjbYcwFt+bqAMqSLHVXkQ6UA==", "dev": true, "dependencies": { "@babel/helper-member-expression-to-functions": "^7.13.12", "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.12" + "@babel/traverse": "^7.14.2", + "@babel/types": "^7.14.2" } }, "node_modules/@babel/helper-simple-access": { @@ -414,9 +417,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", + "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", "dev": true }, "node_modules/@babel/helper-validator-option": { @@ -438,14 +441,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.13.10.tgz", - "integrity": "sha512-4VO883+MWPDUVRF3PhiLBUFHoX/bsLTGFpFK/HqvvfBZz2D57u9XzPVNFVBTc0PW/CWR9BXTOKt8NF4DInUHcQ==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.0.tgz", + "integrity": "sha512-+ufuXprtQ1D1iZTO/K9+EBRn+qPWMJjZSw/S0KlFrxCw4tkrzv9grgpDHkY9MeQTjTY8i2sp7Jep8DfU6tN9Mg==", "dev": true, "dependencies": { "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.0" + "@babel/traverse": "^7.14.0", + "@babel/types": "^7.14.0" } }, "node_modules/@babel/highlight": { @@ -460,9 +463,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.15.tgz", - "integrity": "sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ==", + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.3.tgz", + "integrity": "sha512-7MpZDIfI7sUC5zWo2+foJ50CSI5lcqDehZ0lVgIhSi4bFEk94fLAKlF3Q0nzSQQ+ca0lm+O6G9ztKVBeu8PMRQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -486,9 +489,9 @@ } }, "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.13.15.tgz", - "integrity": "sha512-VapibkWzFeoa6ubXy/NgV5U2U4MVnUlvnx6wo1XhlsaTrLYWE0UFpDQsVrmn22q5CzeloqJ8gEMHSKxuee6ZdA==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.14.2.tgz", + "integrity": "sha512-b1AM4F6fwck4N8ItZ/AtC4FP/cqZqmKRQ4FaTDutwSYyjuhtvsGEMLK4N/ztV/ImP40BjIDyMgBQAeAMsQYVFQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.13.0", @@ -509,93 +512,131 @@ "@babel/helper-plugin-utils": "^7.13.0" } }, + "node_modules/@babel/plugin-proposal-class-static-block": { + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.14.3.tgz", + "integrity": "sha512-HEjzp5q+lWSjAgJtSluFDrGGosmwTgKwCXdDQZvhKsRlwv3YdkUEqxNrrjesJd+B9E9zvr1PVPVBvhYZ9msjvQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.14.3", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-class-static-block": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, "node_modules/@babel/plugin-proposal-dynamic-import": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.13.8.tgz", - "integrity": "sha512-ONWKj0H6+wIRCkZi9zSbZtE/r73uOhMVHh256ys0UzfM7I3d4n+spZNWjOnJv2gzopumP2Wxi186vI8N0Y2JyQ==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.14.2.tgz", + "integrity": "sha512-oxVQZIWFh91vuNEMKltqNsKLFWkOIyJc95k2Gv9lWVyDfPUQGSSlbDEgWuJUU1afGE9WwlzpucMZ3yDRHIItkA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.13.0", "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-export-namespace-from": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.13.tgz", - "integrity": "sha512-INAgtFo4OnLN3Y/j0VwAgw3HDXcDtX+C/erMvWzuV9v71r7urb6iyMXu7eM9IgLr1ElLlOkaHjJ0SbCmdOQ3Iw==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.14.2.tgz", + "integrity": "sha512-sRxW3z3Zp3pFfLAgVEvzTFutTXax837oOatUIvSG9o5gRj9mKwm3br1Se5f4QalTQs9x4AzlA/HrCWbQIHASUQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-json-strings": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.13.8.tgz", - "integrity": "sha512-w4zOPKUFPX1mgvTmL/fcEqy34hrQ1CRcGxdphBc6snDnnqJ47EZDIyop6IwXzAC8G916hsIuXB2ZMBCExC5k7Q==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.14.2.tgz", + "integrity": "sha512-w2DtsfXBBJddJacXMBhElGEYqCZQqN99Se1qeYn8DVLB33owlrlLftIbMzn5nz1OITfDVknXF433tBrLEAOEjA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.13.0", "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.13.8.tgz", - "integrity": "sha512-aul6znYB4N4HGweImqKn59Su9RS8lbUIqxtXTOcAGtNIDczoEFv+l1EhmX8rUBp3G1jMjKJm8m0jXVp63ZpS4A==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.14.2.tgz", + "integrity": "sha512-1JAZtUrqYyGsS7IDmFeaem+/LJqujfLZ2weLR9ugB0ufUPjzf8cguyVT1g5im7f7RXxuLq1xUxEzvm68uYRtGg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.13.0", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.13.8.tgz", - "integrity": "sha512-iePlDPBn//UhxExyS9KyeYU7RM9WScAG+D3Hhno0PLJebAEpDZMocbDe64eqynhNAnwz/vZoL/q/QB2T1OH39A==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.14.2.tgz", + "integrity": "sha512-ebR0zU9OvI2N4qiAC38KIAK75KItpIPTpAtd2r4OZmMFeKbKJpUFLYP2EuDut82+BmYi8sz42B+TfTptJ9iG5Q==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.13.0", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-numeric-separator": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.13.tgz", - "integrity": "sha512-O1jFia9R8BUCl3ZGB7eitaAPu62TXJRHn7rh+ojNERCFyqRwJMTmhz+tJ+k0CwI6CLjX/ee4qW74FSqlq9I35w==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.14.2.tgz", + "integrity": "sha512-DcTQY9syxu9BpU3Uo94fjCB3LN9/hgPS8oUL7KrSW3bA2ePrKZZPJcc5y0hoJAM9dft3pGfErtEUvxXQcfLxUg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.13.8.tgz", - "integrity": "sha512-DhB2EuB1Ih7S3/IRX5AFVgZ16k3EzfRbq97CxAVI1KSYcW+lexV8VZb7G7L8zuPVSdQMRn0kiBpf/Yzu9ZKH0g==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.2.tgz", + "integrity": "sha512-hBIQFxwZi8GIp934+nj5uV31mqclC1aYDhctDu5khTi9PCCUOczyy0b34W0oE9U/eJXiqQaKyVsmjeagOaSlbw==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.13.8", - "@babel/helper-compilation-targets": "^7.13.8", + "@babel/compat-data": "^7.14.0", + "@babel/helper-compilation-targets": "^7.13.16", "@babel/helper-plugin-utils": "^7.13.0", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.13.0" + "@babel/plugin-transform-parameters": "^7.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-optional-catch-binding": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.13.8.tgz", - "integrity": "sha512-0wS/4DUF1CuTmGo+NiaHfHcVSeSLj5S3e6RivPTg/2k3wOv3jO35tZ6/ZWsQhQMvdgI7CwphjQa/ccarLymHVA==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.14.2.tgz", + "integrity": "sha512-XtkJsmJtBaUbOxZsNk0Fvrv8eiqgneug0A6aqLFZ4TSkar2L5dSXWcnUKHgmjJt49pyB/6ZHvkr3dPgl9MOWRQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.13.0", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.13.12.tgz", - "integrity": "sha512-fcEdKOkIB7Tf4IxrgEVeFC4zeJSTr78no9wTdBuZZbqF64kzllU0ybo2zrzm7gUQfxGhBgq4E39oRs8Zx/RMYQ==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.14.2.tgz", + "integrity": "sha512-qQByMRPwMZJainfig10BoaDldx/+VDtNcrA7qdNaEOAj6VXud+gfrkA8j4CRAU5HjnWREXqIpSpH30qZX1xivA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.13.0", @@ -616,6 +657,21 @@ "@babel/helper-plugin-utils": "^7.13.0" } }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.14.0.tgz", + "integrity": "sha512-59ANdmEwwRUkLjB7CRtwJxxwtjESw+X2IePItA+RGQh+oy5RmpCh/EvVVvh5XQc3yxsm5gtv0+i9oBZhaDNVTg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-create-class-features-plugin": "^7.14.0", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-private-property-in-object": "^7.14.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-proposal-unicode-property-regex": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.13.tgz", @@ -656,6 +712,18 @@ "@babel/helper-plugin-utils": "^7.12.13" } }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.12.13.tgz", + "integrity": "sha512-ZmKQ0ZXR0nYpHZIIuj9zE7oIqCx2hw9TKi+lIo73NNrMPAZGHfS92/VRV0ZmPj6H2ffBgyFHXvJ5NYsNeEaP2A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-dynamic-import": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", @@ -663,6 +731,9 @@ "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-export-namespace-from": { @@ -672,6 +743,9 @@ "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-import-meta": { @@ -755,6 +829,18 @@ "@babel/helper-plugin-utils": "^7.8.0" } }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.0.tgz", + "integrity": "sha512-bda3xF8wGl5/5btF794utNOL0Jw+9jE5C1sLZcoK7c4uonE/y3iQiyG+KbkF3WBV/paX58VCpjhxLPkdj5Fe4w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-top-level-await": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz", @@ -803,27 +889,33 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.13.tgz", - "integrity": "sha512-Pxwe0iqWJX4fOOM2kEZeUuAxHMWb9nK+9oh5d11bsLoB0xMg+mkDpt0eYuDZB7ETrY9bbcVlKUGTOGWy7BHsMQ==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.2.tgz", + "integrity": "sha512-neZZcP19NugZZqNwMTH+KoBjx5WyvESPSIOQb4JHpfd+zPfqcH65RMu5xJju5+6q/Y2VzYrleQTr+b6METyyxg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.13.0.tgz", - "integrity": "sha512-9BtHCPUARyVH1oXGcSJD3YpsqRLROJx5ZNP6tN5vnk17N0SVf9WCtf8Nuh1CFmgByKKAIMstitKduoCmsaDK5g==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.14.2.tgz", + "integrity": "sha512-7oafAVcucHquA/VZCsXv/gmuiHeYd64UJyyTYU+MPfNu0KeNlxw06IeENBO8bJjXVbolu+j1MM5aKQtH1OMCNg==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-function-name": "^7.12.13", + "@babel/helper-function-name": "^7.14.2", "@babel/helper-optimise-call-expression": "^7.12.13", "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-replace-supers": "^7.13.0", + "@babel/helper-replace-supers": "^7.13.12", "@babel/helper-split-export-declaration": "^7.12.13", "globals": "^11.1.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-computed-properties": { @@ -836,12 +928,15 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.0.tgz", - "integrity": "sha512-zym5em7tePoNT9s964c0/KU3JPPnuq7VhIxPRefJ4/s82cD+q1mgKfuGRDMCPL0HTyKz4dISuQlCusfgCJ86HA==", + "version": "7.13.17", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.17.tgz", + "integrity": "sha512-UAUqiLv+uRLO+xuBKKMEpC+t7YRNVRqBsWWq1yKXbBZBje/t3IXCiSinZhjn/DC3qzBfICeYd2EFGEbHsh5RLA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-dotall-regex": { @@ -911,26 +1006,32 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.13.0.tgz", - "integrity": "sha512-EKy/E2NHhY/6Vw5d1k3rgoobftcNUmp9fGjb9XZwQLtTctsRBOTRO7RHHxfIky1ogMN5BxN7p9uMA3SzPfotMQ==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.14.2.tgz", + "integrity": "sha512-hPC6XBswt8P3G2D1tSV2HzdKvkqOpmbyoy+g73JG0qlF/qx2y3KaMmXb1fLrpmWGLZYA0ojCvaHdzFWjlmV+Pw==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.13.0", + "@babel/helper-module-transforms": "^7.14.2", "@babel/helper-plugin-utils": "^7.13.0", "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.13.8.tgz", - "integrity": "sha512-9QiOx4MEGglfYZ4XOnU79OHr6vIWUakIj9b4mioN8eQIoEh+pf5p/zEB36JpDFWA12nNMiRf7bfoRvl9Rn79Bw==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.0.tgz", + "integrity": "sha512-EX4QePlsTaRZQmw9BsoPeyh5OCtRGIhwfLquhxGp5e32w+dyL8htOcDwamlitmNFK6xBZYlygjdye9dbd9rUlQ==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.13.0", + "@babel/helper-module-transforms": "^7.14.0", "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-simple-access": "^7.12.13", + "@babel/helper-simple-access": "^7.13.12", "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-modules-systemjs": { @@ -947,13 +1048,16 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.13.0.tgz", - "integrity": "sha512-D/ILzAh6uyvkWjKKyFE/W0FzWwasv6vPTSqPcjxFqn6QpX3u8DjRVliq4F2BamO2Wee/om06Vyy+vPkNrd4wxw==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.14.0.tgz", + "integrity": "sha512-nPZdnWtXXeY7I87UZr9VlsWme3Y0cfFFE41Wbxz4bbaexAjNMInXPFUpRRUJ8NoMm0Cw+zxbqjdPmLhcjfazMw==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.13.0", + "@babel/helper-module-transforms": "^7.14.0", "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { @@ -985,12 +1089,15 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.13.0.tgz", - "integrity": "sha512-Jt8k/h/mIwE2JFEOb3lURoY5C85ETcYPnbuAJ96zRBzh1XHtQZfs62ChZ6EP22QlC8c7Xqr9q+e1SU5qttwwjw==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.14.2.tgz", + "integrity": "sha512-NxoVmA3APNCC1JdMXkdYXuQS+EMdqy0vIwyDHeKHiJKRxmp1qGSdb0JLEIoPRhkx6H/8Qi3RJ3uqOCYw8giy9A==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.13.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-property-literals": { @@ -1100,31 +1207,34 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.13.15.tgz", - "integrity": "sha512-D4JAPMXcxk69PKe81jRJ21/fP/uYdcTZ3hJDF5QX2HSI9bBxxYw/dumdR6dGumhjxlprHPE4XWoPaqzZUVy2MA==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.14.2.tgz", + "integrity": "sha512-7dD7lVT8GMrE73v4lvDEb85cgcQhdES91BSD7jS/xjC6QY8PnRhux35ac+GCpbiRhp8crexBvZZqnaL6VrY8TQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.13.15", - "@babel/helper-compilation-targets": "^7.13.13", + "@babel/compat-data": "^7.14.0", + "@babel/helper-compilation-targets": "^7.13.16", "@babel/helper-plugin-utils": "^7.13.0", "@babel/helper-validator-option": "^7.12.17", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.13.12", - "@babel/plugin-proposal-async-generator-functions": "^7.13.15", + "@babel/plugin-proposal-async-generator-functions": "^7.14.2", "@babel/plugin-proposal-class-properties": "^7.13.0", - "@babel/plugin-proposal-dynamic-import": "^7.13.8", - "@babel/plugin-proposal-export-namespace-from": "^7.12.13", - "@babel/plugin-proposal-json-strings": "^7.13.8", - "@babel/plugin-proposal-logical-assignment-operators": "^7.13.8", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8", - "@babel/plugin-proposal-numeric-separator": "^7.12.13", - "@babel/plugin-proposal-object-rest-spread": "^7.13.8", - "@babel/plugin-proposal-optional-catch-binding": "^7.13.8", - "@babel/plugin-proposal-optional-chaining": "^7.13.12", + "@babel/plugin-proposal-class-static-block": "^7.13.11", + "@babel/plugin-proposal-dynamic-import": "^7.14.2", + "@babel/plugin-proposal-export-namespace-from": "^7.14.2", + "@babel/plugin-proposal-json-strings": "^7.14.2", + "@babel/plugin-proposal-logical-assignment-operators": "^7.14.2", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.2", + "@babel/plugin-proposal-numeric-separator": "^7.14.2", + "@babel/plugin-proposal-object-rest-spread": "^7.14.2", + "@babel/plugin-proposal-optional-catch-binding": "^7.14.2", + "@babel/plugin-proposal-optional-chaining": "^7.14.2", "@babel/plugin-proposal-private-methods": "^7.13.0", + "@babel/plugin-proposal-private-property-in-object": "^7.14.0", "@babel/plugin-proposal-unicode-property-regex": "^7.12.13", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.12.13", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", "@babel/plugin-syntax-json-strings": "^7.8.3", @@ -1134,14 +1244,15 @@ "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.0", "@babel/plugin-syntax-top-level-await": "^7.12.13", "@babel/plugin-transform-arrow-functions": "^7.13.0", "@babel/plugin-transform-async-to-generator": "^7.13.0", "@babel/plugin-transform-block-scoped-functions": "^7.12.13", - "@babel/plugin-transform-block-scoping": "^7.12.13", - "@babel/plugin-transform-classes": "^7.13.0", + "@babel/plugin-transform-block-scoping": "^7.14.2", + "@babel/plugin-transform-classes": "^7.14.2", "@babel/plugin-transform-computed-properties": "^7.13.0", - "@babel/plugin-transform-destructuring": "^7.13.0", + "@babel/plugin-transform-destructuring": "^7.13.17", "@babel/plugin-transform-dotall-regex": "^7.12.13", "@babel/plugin-transform-duplicate-keys": "^7.12.13", "@babel/plugin-transform-exponentiation-operator": "^7.12.13", @@ -1149,14 +1260,14 @@ "@babel/plugin-transform-function-name": "^7.12.13", "@babel/plugin-transform-literals": "^7.12.13", "@babel/plugin-transform-member-expression-literals": "^7.12.13", - "@babel/plugin-transform-modules-amd": "^7.13.0", - "@babel/plugin-transform-modules-commonjs": "^7.13.8", + "@babel/plugin-transform-modules-amd": "^7.14.2", + "@babel/plugin-transform-modules-commonjs": "^7.14.0", "@babel/plugin-transform-modules-systemjs": "^7.13.8", - "@babel/plugin-transform-modules-umd": "^7.13.0", + "@babel/plugin-transform-modules-umd": "^7.14.0", "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.13", "@babel/plugin-transform-new-target": "^7.12.13", "@babel/plugin-transform-object-super": "^7.12.13", - "@babel/plugin-transform-parameters": "^7.13.0", + "@babel/plugin-transform-parameters": "^7.14.2", "@babel/plugin-transform-property-literals": "^7.12.13", "@babel/plugin-transform-regenerator": "^7.13.15", "@babel/plugin-transform-reserved-words": "^7.12.13", @@ -1168,7 +1279,7 @@ "@babel/plugin-transform-unicode-escapes": "^7.12.13", "@babel/plugin-transform-unicode-regex": "^7.12.13", "@babel/preset-modules": "^0.1.4", - "@babel/types": "^7.13.14", + "@babel/types": "^7.14.2", "babel-plugin-polyfill-corejs2": "^0.2.0", "babel-plugin-polyfill-corejs3": "^0.2.0", "babel-plugin-polyfill-regenerator": "^0.2.0", @@ -1233,17 +1344,17 @@ } }, "node_modules/@babel/traverse": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.15.tgz", - "integrity": "sha512-/mpZMNvj6bce59Qzl09fHEs8Bt8NnpEDQYleHUPZQ3wXUMvXi+HJPLars68oAbmp839fGoOkv2pSL2z9ajCIaQ==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.2.tgz", + "integrity": "sha512-TsdRgvBFHMyHOOzcP9S6QU0QQtjxlRpEYOy3mcCO5RgmC305ki42aSAmfZEMSSYBla2oZ9BMqYlncBaKmD/7iA==", "dev": true, "dependencies": { "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.13.9", - "@babel/helper-function-name": "^7.12.13", + "@babel/generator": "^7.14.2", + "@babel/helper-function-name": "^7.14.2", "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.13.15", - "@babel/types": "^7.13.14", + "@babel/parser": "^7.14.2", + "@babel/types": "^7.14.2", "debug": "^4.1.0", "globals": "^11.1.0" } @@ -1267,13 +1378,12 @@ "dev": true }, "node_modules/@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.2.tgz", + "integrity": "sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.14.0", "to-fast-properties": "^2.0.0" } }, @@ -3786,9 +3896,9 @@ } }, "node_modules/@rollup/plugin-commonjs": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-18.0.0.tgz", - "integrity": "sha512-fj92shhg8luw7XbA0HowAqz90oo7qtLGwqTKbyZ8pmOyH8ui5e+u0wPEgeHLH3djcVma6gUCUrjY6w5R2o1u6g==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-19.0.0.tgz", + "integrity": "sha512-adTpD6ATGbehdaQoZQ6ipDFhdjqsTgpOAhFiPwl+dzre4pPshsecptDPyEFb61JMJ1+mGljktaC4jI8ARMSNyw==", "dev": true, "dependencies": { "@rollup/pluginutils": "^3.1.0", @@ -3803,7 +3913,7 @@ "node": ">= 8.0.0" }, "peerDependencies": { - "rollup": "^2.30.0" + "rollup": "^2.38.3" } }, "node_modules/@rollup/plugin-commonjs/node_modules/estree-walker": { @@ -3822,10 +3932,11 @@ } }, "node_modules/@rollup/plugin-node-resolve": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", - "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.0.0.tgz", + "integrity": "sha512-41X411HJ3oikIDivT5OKe9EZ6ud6DXudtfNrGbC4nniaxx2esiWjkLOzgnZsWq1IM8YIeL2rzRGLZLBjlhnZtQ==", "dev": true, + "license": "MIT", "dependencies": { "@rollup/pluginutils": "^3.1.0", "@types/resolve": "1.17.1", @@ -3838,7 +3949,7 @@ "node": ">= 10.0.0" }, "peerDependencies": { - "rollup": "^1.20.0||^2.0.0" + "rollup": "^2.42.0" } }, "node_modules/@rollup/plugin-replace": { @@ -3986,9 +4097,9 @@ } }, "node_modules/@types/jest": { - "version": "26.0.22", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.22.tgz", - "integrity": "sha512-eeWwWjlqxvBxc4oQdkueW5OF/gtfSceKk4OnOAGlUSwS/liBRtZppbJuz1YkgbrbfGOoeBHun9fOvXnjNwrSOw==", + "version": "26.0.23", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.23.tgz", + "integrity": "sha512-ZHLmWMJ9jJ9PTiT58juykZpL7KjwJywFN3Rr2pTSkyQfydf/rk22yS7W8p5DaVUMQ2BQC7oYiU3FjbTM/mYrOA==", "dev": true, "dependencies": { "jest-diff": "^26.0.0", @@ -4581,10 +4692,11 @@ } }, "node_modules/babel-plugin-jsx-dom-expressions": { - "version": "0.26.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.26.3.tgz", - "integrity": "sha512-TtcqjKrCzP9m8eMKBTxwdqTK5PlySn6+mYt6vJTbgQ7cCNtnurLUqPFN+ObG1dY60D2ZgUGc86/7O6plXDjsXg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.27.7.tgz", + "integrity": "sha512-YNa62veWtrZ1fEBsohDol2Vw37z07AEM8mTNY1OPpnXl9P30iIaOQ3DdHAuIv0YIBDp9IygWpQB9ZJeLbS6Rjw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.10.4", "@babel/plugin-syntax-jsx": "^7.10.4", @@ -4678,6 +4790,28 @@ "node": ">= 10.14.2" } }, + "node_modules/babel-preset-solid": { + "version": "0.26.5", + "resolved": "https://registry.npmjs.org/babel-preset-solid/-/babel-preset-solid-0.26.5.tgz", + "integrity": "sha512-SXMSmp3iL3FTbcy2QB4poHSbsPMrNSHOk8vIgQSMI/8UwxbxjjAG1iDDcqjsgWw6DqndxM6Z6nID9+94aT8NCQ==", + "dev": true, + "peer": true, + "dependencies": { + "babel-plugin-jsx-dom-expressions": "~0.26.3" + } + }, + "node_modules/babel-preset-solid/node_modules/babel-plugin-jsx-dom-expressions": { + "version": "0.26.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.26.3.tgz", + "integrity": "sha512-TtcqjKrCzP9m8eMKBTxwdqTK5PlySn6+mYt6vJTbgQ7cCNtnurLUqPFN+ObG1dY60D2ZgUGc86/7O6plXDjsXg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-module-imports": "^7.10.4", + "@babel/plugin-syntax-jsx": "^7.10.4", + "@babel/types": "^7.11.5" + } + }, "node_modules/balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -4873,22 +5007,26 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.16.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz", - "integrity": "sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==", + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", "dev": true, "dependencies": { - "caniuse-lite": "^1.0.30001181", - "colorette": "^1.2.1", - "electron-to-chromium": "^1.3.649", + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", "escalade": "^3.1.1", - "node-releases": "^1.1.70" + "node-releases": "^1.1.71" }, "bin": { "browserslist": "cli.js" }, "engines": { "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" } }, "node_modules/bser": { @@ -5105,10 +5243,14 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001192", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001192.tgz", - "integrity": "sha512-63OrUnwJj5T1rUmoyqYTdRWBqFFxZFlyZnRRjDR8NSUQFB6A+j/uBORU/SyJ5WzDLg4SPiZH40hQCBNdZ/jmAw==", - "dev": true + "version": "1.0.30001230", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz", + "integrity": "sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } }, "node_modules/capture-exit": { "version": "2.0.0", @@ -6698,11 +6840,10 @@ } }, "node_modules/dom-expressions": { - "version": "0.26.4", - "resolved": "https://registry.npmjs.org/dom-expressions/-/dom-expressions-0.26.4.tgz", - "integrity": "sha512-dFQR5OulHj/CUDIUG31W3aadME4XefF+fVf8Wpi3oVOFGCiWSCrBqE5/gj42YeJsSnSFXmPhdbfg7+badoFb+A==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/dom-expressions/-/dom-expressions-0.27.7.tgz", + "integrity": "sha512-t1gdvx04HVNcEdguqNZrLT41VssMUFny1+PAbS1TzaofrJfdplTMz+plMAkX4/d929RckWX3TYpUygkDq1TKIw==", "dev": true, - "license": "MIT", "dependencies": { "babel-plugin-transform-rename-import": "^2.3.0" } @@ -6774,9 +6915,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "node_modules/electron-to-chromium": { - "version": "1.3.676", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.676.tgz", - "integrity": "sha512-t0eEgYCP+XEbH/KwxWYZDY0XKwzmokDAsjFJ2rBstp2XuwuBCUZ+ni5qXI6XDRNkvDpVJcAOp2aJxkSkshKkmw==", + "version": "1.3.741", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.741.tgz", + "integrity": "sha512-4i3T0cwnHo1O4Mnp9JniEco8bZiXoqbm3PhW5hv7uu8YLg35iajYrRnNyKFaN8/8SSTskU2hYqVTeYVPceSpUA==", "dev": true }, "node_modules/emittery": { @@ -8662,13 +8803,14 @@ } }, "node_modules/gitly": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/gitly/-/gitly-2.0.2.tgz", - "integrity": "sha512-OVdJHahWJBPR0NNCSA8hhCvdeI4u9kEYg87pB7o/Fi3dgB8rrF7QR1oXCjPpycnx/t2IEkD4h/WhYFvL1wAQJw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/gitly/-/gitly-2.1.0.tgz", + "integrity": "sha512-0TL0ZGT9bdmfa+dylBpwp1ip3FQ+CQsINM4Ih+2Z+XKkDasOWSk7MborpRPsdYnKO/1LuC5HoGQg/JAVIwb0qg==", "dev": true, + "license": "MIT", "dependencies": { - "axios": "^0.21.0", - "tar": "^6.0.5" + "axios": "^0.21.1", + "tar": "^6.1.0" } }, "node_modules/gitly/node_modules/chownr": { @@ -8983,9 +9125,9 @@ } }, "node_modules/hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, "node_modules/html-encoding-sniffer": { @@ -9116,9 +9258,9 @@ } }, "node_modules/hyper-dom-expressions": { - "version": "0.26.4", - "resolved": "https://registry.npmjs.org/hyper-dom-expressions/-/hyper-dom-expressions-0.26.4.tgz", - "integrity": "sha512-fzWXD5/+ExcHo4XmLnL61dP6VbuzvTpow8yAxgWVIMZKDE/cjJIhiQf1mekCY49oQqir5rU9DhJoxnc2z6TsTQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/hyper-dom-expressions/-/hyper-dom-expressions-0.27.7.tgz", + "integrity": "sha512-ci+VTGLtpLE3OgGH/yHnUK1M0FOGvyVESvbSAXhxgTzlmqIseXDDCah7viAxP9HqxAHtlCn/Lz862iTfmJs2DA==", "dev": true, "license": "MIT" }, @@ -12209,9 +12351,9 @@ "dev": true }, "node_modules/lit-dom-expressions": { - "version": "0.26.4", - "resolved": "https://registry.npmjs.org/lit-dom-expressions/-/lit-dom-expressions-0.26.4.tgz", - "integrity": "sha512-VsH2YhEOvIlaqP9pZbJCq0jEjVcN8wcxu1mNX/xulYi+VCEIK2/tiGbVFk8IuYpvOuGDgd2hlY+o1Qud+QIolA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/lit-dom-expressions/-/lit-dom-expressions-0.27.7.tgz", + "integrity": "sha512-Fs0ozluQUaB7+S//6ttVe5iUvj+My1CpXnkIYTfsBadztGieEGvt9kN/vtk4apDg/rHJWTouAiS4BSWcxUfg8g==", "dev": true }, "node_modules/load-json-file": { @@ -14651,9 +14793,9 @@ } }, "node_modules/rollup": { - "version": "2.45.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.45.1.tgz", - "integrity": "sha512-vPD+JoDj3CY8k6m1bLcAFttXMe78P4CMxoau0iLVS60+S9kLsv2379xaGy4NgYWu+h2WTlucpoLPAoUoixFBag==", + "version": "2.48.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.48.0.tgz", + "integrity": "sha512-wl9ZSSSsi5579oscSDYSzGn092tCS076YB+TQrzsGuSfYyJeep8eEWj0eaRjuC5McuMNmcnR8icBqiE/FWNB1A==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -15355,29 +15497,33 @@ } }, "node_modules/solid-jest": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/solid-jest/-/solid-jest-0.0.2.tgz", - "integrity": "sha512-/B6iOCnopUwvG0sJ9r0ML3ESY/yujUJhrj/TX8N190WVpqhCxoIAvs3q5bnuzR4ZT0YASN9+Fx30nOxZXPK8Sw==", + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/solid-jest/-/solid-jest-0.0.3.tgz", + "integrity": "sha512-GNGyiwo9jmZjnTLRYBokLOsyYWwaK3UxN2Riy8qOPHx1u5KbyLnFhiaOfab+5GGhsaqPJJN3eCtg5INIbfQ7RA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/preset-env": "^7.13.9", "babel-jest": "^26.6.3", "enhanced-resolve-jest": "^1.1.0" + }, + "peerDependencies": { + "babel-preset-solid": "*" } }, "node_modules/solid-js": { - "version": "0.26.3", - "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-0.26.3.tgz", - "integrity": "sha512-ixl/SWvkZjQrNBS5DrD/IqdgphTOnC0/JtEPisx8c9/L5O0xQjlR0WljD4DLwzXFN9wfmLakpfreWO0cy+ckfw==", + "version": "0.26.5", + "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-0.26.5.tgz", + "integrity": "sha512-cMjxcVoyRBgnfSpwYxXPM5WF800guR+x/01RDBFQjAAkqU7X28GbRkTNKcyQ1KHcFOnzEsG18J+JJ9/PvqyNmw==", "peer": true }, "node_modules/solid-styled-components": { - "version": "0.26.3", - "resolved": "https://registry.npmjs.org/solid-styled-components/-/solid-styled-components-0.26.3.tgz", - "integrity": "sha512-IHkd42WVpKh4XI1Dub1KFyjrQnGkKbMoGNqC1UQL04rfKxiBNp0V+i+I7DJVtc1leVjguMfMZ9PVcqtkoUEP0Q==", + "version": "0.26.4", + "resolved": "https://registry.npmjs.org/solid-styled-components/-/solid-styled-components-0.26.4.tgz", + "integrity": "sha512-Ol91K36yZpDALopC05hyXSrI25AdtBpgqiBjefD2DAFpQqN31NyodA6IywQAqDQJ0jq6DvCjJiqeGcYXAta77w==", "dependencies": { "csstype": "^2.6.2", - "goober": "^2.0.35" + "goober": "^2.0.37" }, "peerDependencies": { "solid-js": ">= 0.26.0" @@ -17172,9 +17318,9 @@ }, "dependencies": { "@babel/cli": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.13.14.tgz", - "integrity": "sha512-zmEFV8WBRsW+mPQumO1/4b34QNALBVReaiHJOkxhUsdo/AvYM62c+SKSuLi2aZ42t3ocK6OI0uwUXRvrIbREZw==", + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.14.3.tgz", + "integrity": "sha512-zU4JLvwk32ay1lhhyGfqiRUSPoltVDjhYkA3aQq8+Yby9z30s/EsFw1EPOHxWG9YZo2pAGfgdRNeHZQAYU5m9A==", "dev": true, "requires": { "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents", @@ -17183,7 +17329,6 @@ "convert-source-map": "^1.1.0", "fs-readdir-recursive": "^1.1.0", "glob": "^7.0.0", - "lodash": "^4.17.19", "make-dir": "^2.1.0", "slash": "^2.0.0", "source-map": "^0.5.0" @@ -17199,26 +17344,26 @@ } }, "@babel/compat-data": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.13.15.tgz", - "integrity": "sha512-ltnibHKR1VnrU4ymHyQ/CXtNXI6yZC0oJThyW78Hft8XndANwi+9H+UIklBDraIjFEJzw8wmcM427oDd9KS5wA==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.0.tgz", + "integrity": "sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q==", "dev": true }, "@babel/core": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.13.15.tgz", - "integrity": "sha512-6GXmNYeNjS2Uz+uls5jalOemgIhnTMeaXo+yBUA72kC2uX/8VW6XyhVIo2L8/q0goKQA3EVKx0KOQpVKSeWadQ==", + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.14.3.tgz", + "integrity": "sha512-jB5AmTKOCSJIZ72sd78ECEhuPiDMKlQdDI/4QRI6lzYATx5SSogS1oQA2AoPecRCknm30gHi2l+QVvNUu3wZAg==", "dev": true, "requires": { "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.13.9", - "@babel/helper-compilation-targets": "^7.13.13", - "@babel/helper-module-transforms": "^7.13.14", - "@babel/helpers": "^7.13.10", - "@babel/parser": "^7.13.15", + "@babel/generator": "^7.14.3", + "@babel/helper-compilation-targets": "^7.13.16", + "@babel/helper-module-transforms": "^7.14.2", + "@babel/helpers": "^7.14.0", + "@babel/parser": "^7.14.3", "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.15", - "@babel/types": "^7.13.14", + "@babel/traverse": "^7.14.2", + "@babel/types": "^7.14.2", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -17251,12 +17396,12 @@ } }, "@babel/generator": { - "version": "7.13.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.13.9.tgz", - "integrity": "sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw==", + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.3.tgz", + "integrity": "sha512-bn0S6flG/j0xtQdz3hsjJ624h3W0r3llttBMfyHX3YrZ/KtLYr15bjA0FXkgW7FpvrDuTuElXeVjiKlYRpnOFA==", "dev": true, "requires": { - "@babel/types": "^7.13.0", + "@babel/types": "^7.14.2", "jsesc": "^2.5.1", "source-map": "^0.5.0" } @@ -17281,12 +17426,12 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.13.13", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.13.tgz", - "integrity": "sha512-q1kcdHNZehBwD9jYPh3WyXcsFERi39X4I59I3NadciWtNDyZ6x+GboOxncFK0kXlKIv6BJm5acncehXWUjWQMQ==", + "version": "7.13.16", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz", + "integrity": "sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA==", "dev": true, "requires": { - "@babel/compat-data": "^7.13.12", + "@babel/compat-data": "^7.13.15", "@babel/helper-validator-option": "^7.12.17", "browserslist": "^4.14.5", "semver": "^6.3.0" @@ -17301,15 +17446,16 @@ } }, "@babel/helper-create-class-features-plugin": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.13.8.tgz", - "integrity": "sha512-qioaRrKHQbn4hkRKDHbnuQ6kAxmmOF+kzKGnIfxPK4j2rckSJCpKzr/SSTlohSCiE3uAQpNDJ9FIh4baeE8W+w==", + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.3.tgz", + "integrity": "sha512-BnEfi5+6J2Lte9LeiL6TxLWdIlEv9Woacc1qXzXBgbikcOzMRM2Oya5XGg/f/ngotv1ej2A/b+3iJH8wbS1+lQ==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-member-expression-to-functions": "^7.13.0", + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-function-name": "^7.14.2", + "@babel/helper-member-expression-to-functions": "^7.13.12", "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/helper-replace-supers": "^7.13.0", + "@babel/helper-replace-supers": "^7.14.3", "@babel/helper-split-export-declaration": "^7.12.13" } }, @@ -17372,14 +17518,14 @@ } }, "@babel/helper-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", - "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.2.tgz", + "integrity": "sha512-NYZlkZRydxw+YT56IlhIcS8PAhb+FEUiOzuhFTfqDyPmzAhRge6ua0dQYT/Uh0t/EDHq05/i+e5M2d4XvjgarQ==", "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.12.13", "@babel/template": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/types": "^7.14.2" } }, "@babel/helper-get-function-arity": { @@ -17420,19 +17566,19 @@ } }, "@babel/helper-module-transforms": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.13.14.tgz", - "integrity": "sha512-QuU/OJ0iAOSIatyVZmfqB0lbkVP0kDRiKj34xy+QNsnVZi/PA6BoSoreeqnxxa9EHFAIL0R9XOaAR/G9WlIy5g==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.2.tgz", + "integrity": "sha512-OznJUda/soKXv0XhpvzGWDnml4Qnwp16GN+D/kZIdLsWoHj05kyu8Rm5kXmMef+rVJZ0+4pSGLkeixdqNUATDA==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.13.12", "@babel/helper-replace-supers": "^7.13.12", "@babel/helper-simple-access": "^7.13.12", "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/helper-validator-identifier": "^7.12.11", + "@babel/helper-validator-identifier": "^7.14.0", "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.13", - "@babel/types": "^7.13.14" + "@babel/traverse": "^7.14.2", + "@babel/types": "^7.14.2" } }, "@babel/helper-optimise-call-expression": { @@ -17462,15 +17608,15 @@ } }, "@babel/helper-replace-supers": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz", - "integrity": "sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw==", + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.14.3.tgz", + "integrity": "sha512-Rlh8qEWZSTfdz+tgNV/N4gz1a0TMNwCUcENhMjHTHKp3LseYH5Jha0NSlyTQWMnjbYcwFt+bqAMqSLHVXkQ6UA==", "dev": true, "requires": { "@babel/helper-member-expression-to-functions": "^7.13.12", "@babel/helper-optimise-call-expression": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.12" + "@babel/traverse": "^7.14.2", + "@babel/types": "^7.14.2" } }, "@babel/helper-simple-access": { @@ -17501,9 +17647,9 @@ } }, "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", + "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", "dev": true }, "@babel/helper-validator-option": { @@ -17525,14 +17671,14 @@ } }, "@babel/helpers": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.13.10.tgz", - "integrity": "sha512-4VO883+MWPDUVRF3PhiLBUFHoX/bsLTGFpFK/HqvvfBZz2D57u9XzPVNFVBTc0PW/CWR9BXTOKt8NF4DInUHcQ==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.0.tgz", + "integrity": "sha512-+ufuXprtQ1D1iZTO/K9+EBRn+qPWMJjZSw/S0KlFrxCw4tkrzv9grgpDHkY9MeQTjTY8i2sp7Jep8DfU6tN9Mg==", "dev": true, "requires": { "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.0" + "@babel/traverse": "^7.14.0", + "@babel/types": "^7.14.0" } }, "@babel/highlight": { @@ -17547,9 +17693,9 @@ } }, "@babel/parser": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.15.tgz", - "integrity": "sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ==", + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.3.tgz", + "integrity": "sha512-7MpZDIfI7sUC5zWo2+foJ50CSI5lcqDehZ0lVgIhSi4bFEk94fLAKlF3Q0nzSQQ+ca0lm+O6G9ztKVBeu8PMRQ==", "dev": true }, "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { @@ -17564,9 +17710,9 @@ } }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.13.15.tgz", - "integrity": "sha512-VapibkWzFeoa6ubXy/NgV5U2U4MVnUlvnx6wo1XhlsaTrLYWE0UFpDQsVrmn22q5CzeloqJ8gEMHSKxuee6ZdA==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.14.2.tgz", + "integrity": "sha512-b1AM4F6fwck4N8ItZ/AtC4FP/cqZqmKRQ4FaTDutwSYyjuhtvsGEMLK4N/ztV/ImP40BjIDyMgBQAeAMsQYVFQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.13.0", @@ -17584,10 +17730,21 @@ "@babel/helper-plugin-utils": "^7.13.0" } }, + "@babel/plugin-proposal-class-static-block": { + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.14.3.tgz", + "integrity": "sha512-HEjzp5q+lWSjAgJtSluFDrGGosmwTgKwCXdDQZvhKsRlwv3YdkUEqxNrrjesJd+B9E9zvr1PVPVBvhYZ9msjvQ==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.14.3", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-class-static-block": "^7.12.13" + } + }, "@babel/plugin-proposal-dynamic-import": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.13.8.tgz", - "integrity": "sha512-ONWKj0H6+wIRCkZi9zSbZtE/r73uOhMVHh256ys0UzfM7I3d4n+spZNWjOnJv2gzopumP2Wxi186vI8N0Y2JyQ==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.14.2.tgz", + "integrity": "sha512-oxVQZIWFh91vuNEMKltqNsKLFWkOIyJc95k2Gv9lWVyDfPUQGSSlbDEgWuJUU1afGE9WwlzpucMZ3yDRHIItkA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.13.0", @@ -17595,19 +17752,19 @@ } }, "@babel/plugin-proposal-export-namespace-from": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.13.tgz", - "integrity": "sha512-INAgtFo4OnLN3Y/j0VwAgw3HDXcDtX+C/erMvWzuV9v71r7urb6iyMXu7eM9IgLr1ElLlOkaHjJ0SbCmdOQ3Iw==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.14.2.tgz", + "integrity": "sha512-sRxW3z3Zp3pFfLAgVEvzTFutTXax837oOatUIvSG9o5gRj9mKwm3br1Se5f4QalTQs9x4AzlA/HrCWbQIHASUQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" } }, "@babel/plugin-proposal-json-strings": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.13.8.tgz", - "integrity": "sha512-w4zOPKUFPX1mgvTmL/fcEqy34hrQ1CRcGxdphBc6snDnnqJ47EZDIyop6IwXzAC8G916hsIuXB2ZMBCExC5k7Q==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.14.2.tgz", + "integrity": "sha512-w2DtsfXBBJddJacXMBhElGEYqCZQqN99Se1qeYn8DVLB33owlrlLftIbMzn5nz1OITfDVknXF433tBrLEAOEjA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.13.0", @@ -17615,9 +17772,9 @@ } }, "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.13.8.tgz", - "integrity": "sha512-aul6znYB4N4HGweImqKn59Su9RS8lbUIqxtXTOcAGtNIDczoEFv+l1EhmX8rUBp3G1jMjKJm8m0jXVp63ZpS4A==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.14.2.tgz", + "integrity": "sha512-1JAZtUrqYyGsS7IDmFeaem+/LJqujfLZ2weLR9ugB0ufUPjzf8cguyVT1g5im7f7RXxuLq1xUxEzvm68uYRtGg==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.13.0", @@ -17625,9 +17782,9 @@ } }, "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.13.8.tgz", - "integrity": "sha512-iePlDPBn//UhxExyS9KyeYU7RM9WScAG+D3Hhno0PLJebAEpDZMocbDe64eqynhNAnwz/vZoL/q/QB2T1OH39A==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.14.2.tgz", + "integrity": "sha512-ebR0zU9OvI2N4qiAC38KIAK75KItpIPTpAtd2r4OZmMFeKbKJpUFLYP2EuDut82+BmYi8sz42B+TfTptJ9iG5Q==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.13.0", @@ -17635,32 +17792,32 @@ } }, "@babel/plugin-proposal-numeric-separator": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.13.tgz", - "integrity": "sha512-O1jFia9R8BUCl3ZGB7eitaAPu62TXJRHn7rh+ojNERCFyqRwJMTmhz+tJ+k0CwI6CLjX/ee4qW74FSqlq9I35w==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.14.2.tgz", + "integrity": "sha512-DcTQY9syxu9BpU3Uo94fjCB3LN9/hgPS8oUL7KrSW3bA2ePrKZZPJcc5y0hoJAM9dft3pGfErtEUvxXQcfLxUg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", "@babel/plugin-syntax-numeric-separator": "^7.10.4" } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.13.8.tgz", - "integrity": "sha512-DhB2EuB1Ih7S3/IRX5AFVgZ16k3EzfRbq97CxAVI1KSYcW+lexV8VZb7G7L8zuPVSdQMRn0kiBpf/Yzu9ZKH0g==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.2.tgz", + "integrity": "sha512-hBIQFxwZi8GIp934+nj5uV31mqclC1aYDhctDu5khTi9PCCUOczyy0b34W0oE9U/eJXiqQaKyVsmjeagOaSlbw==", "dev": true, "requires": { - "@babel/compat-data": "^7.13.8", - "@babel/helper-compilation-targets": "^7.13.8", + "@babel/compat-data": "^7.14.0", + "@babel/helper-compilation-targets": "^7.13.16", "@babel/helper-plugin-utils": "^7.13.0", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.13.0" + "@babel/plugin-transform-parameters": "^7.14.2" } }, "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.13.8.tgz", - "integrity": "sha512-0wS/4DUF1CuTmGo+NiaHfHcVSeSLj5S3e6RivPTg/2k3wOv3jO35tZ6/ZWsQhQMvdgI7CwphjQa/ccarLymHVA==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.14.2.tgz", + "integrity": "sha512-XtkJsmJtBaUbOxZsNk0Fvrv8eiqgneug0A6aqLFZ4TSkar2L5dSXWcnUKHgmjJt49pyB/6ZHvkr3dPgl9MOWRQ==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.13.0", @@ -17668,9 +17825,9 @@ } }, "@babel/plugin-proposal-optional-chaining": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.13.12.tgz", - "integrity": "sha512-fcEdKOkIB7Tf4IxrgEVeFC4zeJSTr78no9wTdBuZZbqF64kzllU0ybo2zrzm7gUQfxGhBgq4E39oRs8Zx/RMYQ==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.14.2.tgz", + "integrity": "sha512-qQByMRPwMZJainfig10BoaDldx/+VDtNcrA7qdNaEOAj6VXud+gfrkA8j4CRAU5HjnWREXqIpSpH30qZX1xivA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.13.0", @@ -17688,6 +17845,18 @@ "@babel/helper-plugin-utils": "^7.13.0" } }, + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.14.0.tgz", + "integrity": "sha512-59ANdmEwwRUkLjB7CRtwJxxwtjESw+X2IePItA+RGQh+oy5RmpCh/EvVVvh5XQc3yxsm5gtv0+i9oBZhaDNVTg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.12.13", + "@babel/helper-create-class-features-plugin": "^7.14.0", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-private-property-in-object": "^7.14.0" + } + }, "@babel/plugin-proposal-unicode-property-regex": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.13.tgz", @@ -17725,6 +17894,15 @@ "@babel/helper-plugin-utils": "^7.12.13" } }, + "@babel/plugin-syntax-class-static-block": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.12.13.tgz", + "integrity": "sha512-ZmKQ0ZXR0nYpHZIIuj9zE7oIqCx2hw9TKi+lIo73NNrMPAZGHfS92/VRV0ZmPj6H2ffBgyFHXvJ5NYsNeEaP2A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, "@babel/plugin-syntax-dynamic-import": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", @@ -17824,6 +18002,15 @@ "@babel/helper-plugin-utils": "^7.8.0" } }, + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.0.tgz", + "integrity": "sha512-bda3xF8wGl5/5btF794utNOL0Jw+9jE5C1sLZcoK7c4uonE/y3iQiyG+KbkF3WBV/paX58VCpjhxLPkdj5Fe4w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.13.0" + } + }, "@babel/plugin-syntax-top-level-await": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz", @@ -17872,25 +18059,25 @@ } }, "@babel/plugin-transform-block-scoping": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.13.tgz", - "integrity": "sha512-Pxwe0iqWJX4fOOM2kEZeUuAxHMWb9nK+9oh5d11bsLoB0xMg+mkDpt0eYuDZB7ETrY9bbcVlKUGTOGWy7BHsMQ==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.2.tgz", + "integrity": "sha512-neZZcP19NugZZqNwMTH+KoBjx5WyvESPSIOQb4JHpfd+zPfqcH65RMu5xJju5+6q/Y2VzYrleQTr+b6METyyxg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.12.13" + "@babel/helper-plugin-utils": "^7.13.0" } }, "@babel/plugin-transform-classes": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.13.0.tgz", - "integrity": "sha512-9BtHCPUARyVH1oXGcSJD3YpsqRLROJx5ZNP6tN5vnk17N0SVf9WCtf8Nuh1CFmgByKKAIMstitKduoCmsaDK5g==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.14.2.tgz", + "integrity": "sha512-7oafAVcucHquA/VZCsXv/gmuiHeYd64UJyyTYU+MPfNu0KeNlxw06IeENBO8bJjXVbolu+j1MM5aKQtH1OMCNg==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.12.13", - "@babel/helper-function-name": "^7.12.13", + "@babel/helper-function-name": "^7.14.2", "@babel/helper-optimise-call-expression": "^7.12.13", "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-replace-supers": "^7.13.0", + "@babel/helper-replace-supers": "^7.13.12", "@babel/helper-split-export-declaration": "^7.12.13", "globals": "^11.1.0" } @@ -17905,9 +18092,9 @@ } }, "@babel/plugin-transform-destructuring": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.0.tgz", - "integrity": "sha512-zym5em7tePoNT9s964c0/KU3JPPnuq7VhIxPRefJ4/s82cD+q1mgKfuGRDMCPL0HTyKz4dISuQlCusfgCJ86HA==", + "version": "7.13.17", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.17.tgz", + "integrity": "sha512-UAUqiLv+uRLO+xuBKKMEpC+t7YRNVRqBsWWq1yKXbBZBje/t3IXCiSinZhjn/DC3qzBfICeYd2EFGEbHsh5RLA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.13.0" @@ -17980,25 +18167,25 @@ } }, "@babel/plugin-transform-modules-amd": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.13.0.tgz", - "integrity": "sha512-EKy/E2NHhY/6Vw5d1k3rgoobftcNUmp9fGjb9XZwQLtTctsRBOTRO7RHHxfIky1ogMN5BxN7p9uMA3SzPfotMQ==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.14.2.tgz", + "integrity": "sha512-hPC6XBswt8P3G2D1tSV2HzdKvkqOpmbyoy+g73JG0qlF/qx2y3KaMmXb1fLrpmWGLZYA0ojCvaHdzFWjlmV+Pw==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.13.0", + "@babel/helper-module-transforms": "^7.14.2", "@babel/helper-plugin-utils": "^7.13.0", "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.13.8.tgz", - "integrity": "sha512-9QiOx4MEGglfYZ4XOnU79OHr6vIWUakIj9b4mioN8eQIoEh+pf5p/zEB36JpDFWA12nNMiRf7bfoRvl9Rn79Bw==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.14.0.tgz", + "integrity": "sha512-EX4QePlsTaRZQmw9BsoPeyh5OCtRGIhwfLquhxGp5e32w+dyL8htOcDwamlitmNFK6xBZYlygjdye9dbd9rUlQ==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.13.0", + "@babel/helper-module-transforms": "^7.14.0", "@babel/helper-plugin-utils": "^7.13.0", - "@babel/helper-simple-access": "^7.12.13", + "@babel/helper-simple-access": "^7.13.12", "babel-plugin-dynamic-import-node": "^2.3.3" } }, @@ -18016,12 +18203,12 @@ } }, "@babel/plugin-transform-modules-umd": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.13.0.tgz", - "integrity": "sha512-D/ILzAh6uyvkWjKKyFE/W0FzWwasv6vPTSqPcjxFqn6QpX3u8DjRVliq4F2BamO2Wee/om06Vyy+vPkNrd4wxw==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.14.0.tgz", + "integrity": "sha512-nPZdnWtXXeY7I87UZr9VlsWme3Y0cfFFE41Wbxz4bbaexAjNMInXPFUpRRUJ8NoMm0Cw+zxbqjdPmLhcjfazMw==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.13.0", + "@babel/helper-module-transforms": "^7.14.0", "@babel/helper-plugin-utils": "^7.13.0" } }, @@ -18054,9 +18241,9 @@ } }, "@babel/plugin-transform-parameters": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.13.0.tgz", - "integrity": "sha512-Jt8k/h/mIwE2JFEOb3lURoY5C85ETcYPnbuAJ96zRBzh1XHtQZfs62ChZ6EP22QlC8c7Xqr9q+e1SU5qttwwjw==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.14.2.tgz", + "integrity": "sha512-NxoVmA3APNCC1JdMXkdYXuQS+EMdqy0vIwyDHeKHiJKRxmp1qGSdb0JLEIoPRhkx6H/8Qi3RJ3uqOCYw8giy9A==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.13.0" @@ -18166,31 +18353,34 @@ } }, "@babel/preset-env": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.13.15.tgz", - "integrity": "sha512-D4JAPMXcxk69PKe81jRJ21/fP/uYdcTZ3hJDF5QX2HSI9bBxxYw/dumdR6dGumhjxlprHPE4XWoPaqzZUVy2MA==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.14.2.tgz", + "integrity": "sha512-7dD7lVT8GMrE73v4lvDEb85cgcQhdES91BSD7jS/xjC6QY8PnRhux35ac+GCpbiRhp8crexBvZZqnaL6VrY8TQ==", "dev": true, "requires": { - "@babel/compat-data": "^7.13.15", - "@babel/helper-compilation-targets": "^7.13.13", + "@babel/compat-data": "^7.14.0", + "@babel/helper-compilation-targets": "^7.13.16", "@babel/helper-plugin-utils": "^7.13.0", "@babel/helper-validator-option": "^7.12.17", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.13.12", - "@babel/plugin-proposal-async-generator-functions": "^7.13.15", + "@babel/plugin-proposal-async-generator-functions": "^7.14.2", "@babel/plugin-proposal-class-properties": "^7.13.0", - "@babel/plugin-proposal-dynamic-import": "^7.13.8", - "@babel/plugin-proposal-export-namespace-from": "^7.12.13", - "@babel/plugin-proposal-json-strings": "^7.13.8", - "@babel/plugin-proposal-logical-assignment-operators": "^7.13.8", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8", - "@babel/plugin-proposal-numeric-separator": "^7.12.13", - "@babel/plugin-proposal-object-rest-spread": "^7.13.8", - "@babel/plugin-proposal-optional-catch-binding": "^7.13.8", - "@babel/plugin-proposal-optional-chaining": "^7.13.12", + "@babel/plugin-proposal-class-static-block": "^7.13.11", + "@babel/plugin-proposal-dynamic-import": "^7.14.2", + "@babel/plugin-proposal-export-namespace-from": "^7.14.2", + "@babel/plugin-proposal-json-strings": "^7.14.2", + "@babel/plugin-proposal-logical-assignment-operators": "^7.14.2", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.2", + "@babel/plugin-proposal-numeric-separator": "^7.14.2", + "@babel/plugin-proposal-object-rest-spread": "^7.14.2", + "@babel/plugin-proposal-optional-catch-binding": "^7.14.2", + "@babel/plugin-proposal-optional-chaining": "^7.14.2", "@babel/plugin-proposal-private-methods": "^7.13.0", + "@babel/plugin-proposal-private-property-in-object": "^7.14.0", "@babel/plugin-proposal-unicode-property-regex": "^7.12.13", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.12.13", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", "@babel/plugin-syntax-json-strings": "^7.8.3", @@ -18200,14 +18390,15 @@ "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.0", "@babel/plugin-syntax-top-level-await": "^7.12.13", "@babel/plugin-transform-arrow-functions": "^7.13.0", "@babel/plugin-transform-async-to-generator": "^7.13.0", "@babel/plugin-transform-block-scoped-functions": "^7.12.13", - "@babel/plugin-transform-block-scoping": "^7.12.13", - "@babel/plugin-transform-classes": "^7.13.0", + "@babel/plugin-transform-block-scoping": "^7.14.2", + "@babel/plugin-transform-classes": "^7.14.2", "@babel/plugin-transform-computed-properties": "^7.13.0", - "@babel/plugin-transform-destructuring": "^7.13.0", + "@babel/plugin-transform-destructuring": "^7.13.17", "@babel/plugin-transform-dotall-regex": "^7.12.13", "@babel/plugin-transform-duplicate-keys": "^7.12.13", "@babel/plugin-transform-exponentiation-operator": "^7.12.13", @@ -18215,14 +18406,14 @@ "@babel/plugin-transform-function-name": "^7.12.13", "@babel/plugin-transform-literals": "^7.12.13", "@babel/plugin-transform-member-expression-literals": "^7.12.13", - "@babel/plugin-transform-modules-amd": "^7.13.0", - "@babel/plugin-transform-modules-commonjs": "^7.13.8", + "@babel/plugin-transform-modules-amd": "^7.14.2", + "@babel/plugin-transform-modules-commonjs": "^7.14.0", "@babel/plugin-transform-modules-systemjs": "^7.13.8", - "@babel/plugin-transform-modules-umd": "^7.13.0", + "@babel/plugin-transform-modules-umd": "^7.14.0", "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.13", "@babel/plugin-transform-new-target": "^7.12.13", "@babel/plugin-transform-object-super": "^7.12.13", - "@babel/plugin-transform-parameters": "^7.13.0", + "@babel/plugin-transform-parameters": "^7.14.2", "@babel/plugin-transform-property-literals": "^7.12.13", "@babel/plugin-transform-regenerator": "^7.13.15", "@babel/plugin-transform-reserved-words": "^7.12.13", @@ -18234,7 +18425,7 @@ "@babel/plugin-transform-unicode-escapes": "^7.12.13", "@babel/plugin-transform-unicode-regex": "^7.12.13", "@babel/preset-modules": "^0.1.4", - "@babel/types": "^7.13.14", + "@babel/types": "^7.14.2", "babel-plugin-polyfill-corejs2": "^0.2.0", "babel-plugin-polyfill-corejs3": "^0.2.0", "babel-plugin-polyfill-regenerator": "^0.2.0", @@ -18295,17 +18486,17 @@ } }, "@babel/traverse": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.15.tgz", - "integrity": "sha512-/mpZMNvj6bce59Qzl09fHEs8Bt8NnpEDQYleHUPZQ3wXUMvXi+HJPLars68oAbmp839fGoOkv2pSL2z9ajCIaQ==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.2.tgz", + "integrity": "sha512-TsdRgvBFHMyHOOzcP9S6QU0QQtjxlRpEYOy3mcCO5RgmC305ki42aSAmfZEMSSYBla2oZ9BMqYlncBaKmD/7iA==", "dev": true, "requires": { "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.13.9", - "@babel/helper-function-name": "^7.12.13", + "@babel/generator": "^7.14.2", + "@babel/helper-function-name": "^7.14.2", "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.13.15", - "@babel/types": "^7.13.14", + "@babel/parser": "^7.14.2", + "@babel/types": "^7.14.2", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -18328,13 +18519,12 @@ } }, "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.2.tgz", + "integrity": "sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.14.0", "to-fast-properties": "^2.0.0" } }, @@ -20460,9 +20650,9 @@ } }, "@rollup/plugin-commonjs": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-18.0.0.tgz", - "integrity": "sha512-fj92shhg8luw7XbA0HowAqz90oo7qtLGwqTKbyZ8pmOyH8ui5e+u0wPEgeHLH3djcVma6gUCUrjY6w5R2o1u6g==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-19.0.0.tgz", + "integrity": "sha512-adTpD6ATGbehdaQoZQ6ipDFhdjqsTgpOAhFiPwl+dzre4pPshsecptDPyEFb61JMJ1+mGljktaC4jI8ARMSNyw==", "dev": true, "requires": { "@rollup/pluginutils": "^3.1.0", @@ -20492,9 +20682,9 @@ } }, "@rollup/plugin-node-resolve": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", - "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.0.0.tgz", + "integrity": "sha512-41X411HJ3oikIDivT5OKe9EZ6ud6DXudtfNrGbC4nniaxx2esiWjkLOzgnZsWq1IM8YIeL2rzRGLZLBjlhnZtQ==", "dev": true, "requires": { "@rollup/pluginutils": "^3.1.0", @@ -20644,9 +20834,9 @@ } }, "@types/jest": { - "version": "26.0.22", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.22.tgz", - "integrity": "sha512-eeWwWjlqxvBxc4oQdkueW5OF/gtfSceKk4OnOAGlUSwS/liBRtZppbJuz1YkgbrbfGOoeBHun9fOvXnjNwrSOw==", + "version": "26.0.23", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.23.tgz", + "integrity": "sha512-ZHLmWMJ9jJ9PTiT58juykZpL7KjwJywFN3Rr2pTSkyQfydf/rk22yS7W8p5DaVUMQ2BQC7oYiU3FjbTM/mYrOA==", "dev": true, "requires": { "jest-diff": "^26.0.0", @@ -21140,9 +21330,9 @@ } }, "babel-plugin-jsx-dom-expressions": { - "version": "0.26.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.26.3.tgz", - "integrity": "sha512-TtcqjKrCzP9m8eMKBTxwdqTK5PlySn6+mYt6vJTbgQ7cCNtnurLUqPFN+ObG1dY60D2ZgUGc86/7O6plXDjsXg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.27.7.tgz", + "integrity": "sha512-YNa62veWtrZ1fEBsohDol2Vw37z07AEM8mTNY1OPpnXl9P30iIaOQ3DdHAuIv0YIBDp9IygWpQB9ZJeLbS6Rjw==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.10.4", @@ -21224,6 +21414,30 @@ "babel-preset-current-node-syntax": "^1.0.0" } }, + "babel-preset-solid": { + "version": "0.26.5", + "resolved": "https://registry.npmjs.org/babel-preset-solid/-/babel-preset-solid-0.26.5.tgz", + "integrity": "sha512-SXMSmp3iL3FTbcy2QB4poHSbsPMrNSHOk8vIgQSMI/8UwxbxjjAG1iDDcqjsgWw6DqndxM6Z6nID9+94aT8NCQ==", + "dev": true, + "peer": true, + "requires": { + "babel-plugin-jsx-dom-expressions": "~0.26.3" + }, + "dependencies": { + "babel-plugin-jsx-dom-expressions": { + "version": "0.26.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.26.3.tgz", + "integrity": "sha512-TtcqjKrCzP9m8eMKBTxwdqTK5PlySn6+mYt6vJTbgQ7cCNtnurLUqPFN+ObG1dY60D2ZgUGc86/7O6plXDjsXg==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@babel/plugin-syntax-jsx": "^7.10.4", + "@babel/types": "^7.11.5" + } + } + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -21392,16 +21606,16 @@ "dev": true }, "browserslist": { - "version": "4.16.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.3.tgz", - "integrity": "sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==", + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001181", - "colorette": "^1.2.1", - "electron-to-chromium": "^1.3.649", + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", "escalade": "^3.1.1", - "node-releases": "^1.1.70" + "node-releases": "^1.1.71" } }, "bser": { @@ -21586,9 +21800,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001192", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001192.tgz", - "integrity": "sha512-63OrUnwJj5T1rUmoyqYTdRWBqFFxZFlyZnRRjDR8NSUQFB6A+j/uBORU/SyJ5WzDLg4SPiZH40hQCBNdZ/jmAw==", + "version": "1.0.30001230", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz", + "integrity": "sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ==", "dev": true }, "capture-exit": { @@ -22869,9 +23083,9 @@ } }, "dom-expressions": { - "version": "0.26.4", - "resolved": "https://registry.npmjs.org/dom-expressions/-/dom-expressions-0.26.4.tgz", - "integrity": "sha512-dFQR5OulHj/CUDIUG31W3aadME4XefF+fVf8Wpi3oVOFGCiWSCrBqE5/gj42YeJsSnSFXmPhdbfg7+badoFb+A==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/dom-expressions/-/dom-expressions-0.27.7.tgz", + "integrity": "sha512-t1gdvx04HVNcEdguqNZrLT41VssMUFny1+PAbS1TzaofrJfdplTMz+plMAkX4/d929RckWX3TYpUygkDq1TKIw==", "dev": true, "requires": { "babel-plugin-transform-rename-import": "^2.3.0" @@ -22937,9 +23151,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { - "version": "1.3.676", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.676.tgz", - "integrity": "sha512-t0eEgYCP+XEbH/KwxWYZDY0XKwzmokDAsjFJ2rBstp2XuwuBCUZ+ni5qXI6XDRNkvDpVJcAOp2aJxkSkshKkmw==", + "version": "1.3.741", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.741.tgz", + "integrity": "sha512-4i3T0cwnHo1O4Mnp9JniEco8bZiXoqbm3PhW5hv7uu8YLg35iajYrRnNyKFaN8/8SSTskU2hYqVTeYVPceSpUA==", "dev": true }, "emittery": { @@ -24433,13 +24647,13 @@ } }, "gitly": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/gitly/-/gitly-2.0.2.tgz", - "integrity": "sha512-OVdJHahWJBPR0NNCSA8hhCvdeI4u9kEYg87pB7o/Fi3dgB8rrF7QR1oXCjPpycnx/t2IEkD4h/WhYFvL1wAQJw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/gitly/-/gitly-2.1.0.tgz", + "integrity": "sha512-0TL0ZGT9bdmfa+dylBpwp1ip3FQ+CQsINM4Ih+2Z+XKkDasOWSk7MborpRPsdYnKO/1LuC5HoGQg/JAVIwb0qg==", "dev": true, "requires": { - "axios": "^0.21.0", - "tar": "^6.0.5" + "axios": "^0.21.1", + "tar": "^6.1.0" }, "dependencies": { "chownr": { @@ -24689,9 +24903,9 @@ } }, "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, "html-encoding-sniffer": { @@ -24809,9 +25023,9 @@ } }, "hyper-dom-expressions": { - "version": "0.26.4", - "resolved": "https://registry.npmjs.org/hyper-dom-expressions/-/hyper-dom-expressions-0.26.4.tgz", - "integrity": "sha512-fzWXD5/+ExcHo4XmLnL61dP6VbuzvTpow8yAxgWVIMZKDE/cjJIhiQf1mekCY49oQqir5rU9DhJoxnc2z6TsTQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/hyper-dom-expressions/-/hyper-dom-expressions-0.27.7.tgz", + "integrity": "sha512-ci+VTGLtpLE3OgGH/yHnUK1M0FOGvyVESvbSAXhxgTzlmqIseXDDCah7viAxP9HqxAHtlCn/Lz862iTfmJs2DA==", "dev": true }, "iconv-lite": { @@ -27257,9 +27471,9 @@ "dev": true }, "lit-dom-expressions": { - "version": "0.26.4", - "resolved": "https://registry.npmjs.org/lit-dom-expressions/-/lit-dom-expressions-0.26.4.tgz", - "integrity": "sha512-VsH2YhEOvIlaqP9pZbJCq0jEjVcN8wcxu1mNX/xulYi+VCEIK2/tiGbVFk8IuYpvOuGDgd2hlY+o1Qud+QIolA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/lit-dom-expressions/-/lit-dom-expressions-0.27.7.tgz", + "integrity": "sha512-Fs0ozluQUaB7+S//6ttVe5iUvj+My1CpXnkIYTfsBadztGieEGvt9kN/vtk4apDg/rHJWTouAiS4BSWcxUfg8g==", "dev": true }, "load-json-file": { @@ -29257,9 +29471,9 @@ } }, "rollup": { - "version": "2.45.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.45.1.tgz", - "integrity": "sha512-vPD+JoDj3CY8k6m1bLcAFttXMe78P4CMxoau0iLVS60+S9kLsv2379xaGy4NgYWu+h2WTlucpoLPAoUoixFBag==", + "version": "2.48.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.48.0.tgz", + "integrity": "sha512-wl9ZSSSsi5579oscSDYSzGn092tCS076YB+TQrzsGuSfYyJeep8eEWj0eaRjuC5McuMNmcnR8icBqiE/FWNB1A==", "dev": true, "requires": { "fsevents": "~2.3.1" @@ -29819,9 +30033,9 @@ } }, "solid-jest": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/solid-jest/-/solid-jest-0.0.2.tgz", - "integrity": "sha512-/B6iOCnopUwvG0sJ9r0ML3ESY/yujUJhrj/TX8N190WVpqhCxoIAvs3q5bnuzR4ZT0YASN9+Fx30nOxZXPK8Sw==", + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/solid-jest/-/solid-jest-0.0.3.tgz", + "integrity": "sha512-GNGyiwo9jmZjnTLRYBokLOsyYWwaK3UxN2Riy8qOPHx1u5KbyLnFhiaOfab+5GGhsaqPJJN3eCtg5INIbfQ7RA==", "dev": true, "requires": { "@babel/preset-env": "^7.13.9", @@ -29830,18 +30044,18 @@ } }, "solid-js": { - "version": "0.26.3", - "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-0.26.3.tgz", - "integrity": "sha512-ixl/SWvkZjQrNBS5DrD/IqdgphTOnC0/JtEPisx8c9/L5O0xQjlR0WljD4DLwzXFN9wfmLakpfreWO0cy+ckfw==", + "version": "0.26.5", + "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-0.26.5.tgz", + "integrity": "sha512-cMjxcVoyRBgnfSpwYxXPM5WF800guR+x/01RDBFQjAAkqU7X28GbRkTNKcyQ1KHcFOnzEsG18J+JJ9/PvqyNmw==", "peer": true }, "solid-styled-components": { - "version": "0.26.3", - "resolved": "https://registry.npmjs.org/solid-styled-components/-/solid-styled-components-0.26.3.tgz", - "integrity": "sha512-IHkd42WVpKh4XI1Dub1KFyjrQnGkKbMoGNqC1UQL04rfKxiBNp0V+i+I7DJVtc1leVjguMfMZ9PVcqtkoUEP0Q==", + "version": "0.26.4", + "resolved": "https://registry.npmjs.org/solid-styled-components/-/solid-styled-components-0.26.4.tgz", + "integrity": "sha512-Ol91K36yZpDALopC05hyXSrI25AdtBpgqiBjefD2DAFpQqN31NyodA6IywQAqDQJ0jq6DvCjJiqeGcYXAta77w==", "requires": { "csstype": "^2.6.2", - "goober": "^2.0.35" + "goober": "^2.0.37" } }, "sort-keys": { diff --git a/package.json b/package.json index 636f2dd86..297cbb2f2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "solid-js", "description": "A declarative JavaScript library for building user interfaces.", - "version": "0.26.0", + "version": "1.0.0", "author": "Ryan Carniato", "license": "MIT", "repository": { @@ -21,35 +21,35 @@ "publish:release": "lerna run build && lerna publish" }, "devDependencies": { - "@babel/cli": "^7.13.14", - "@babel/core": "^7.13.15", - "@babel/preset-env": "^7.13.15", + "@babel/cli": "^7.14.3", + "@babel/core": "^7.14.3", + "@babel/preset-env": "^7.14.2", "@babel/preset-typescript": "^7.13.0", "@rollup/plugin-babel": "5.3.0", - "@rollup/plugin-commonjs": "18.0.0", + "@rollup/plugin-commonjs": "19.0.0", "@rollup/plugin-json": "4.1.0", - "@rollup/plugin-node-resolve": "11.2.1", + "@rollup/plugin-node-resolve": "13.0.0", "@rollup/plugin-replace": "2.4.2", - "@types/jest": "^26.0.22", + "@types/jest": "^26.0.23", "@types/shelljs": "^0.8.8", "babel-jest": "^26.6.3", - "babel-plugin-jsx-dom-expressions": "~0.26.3", + "babel-plugin-jsx-dom-expressions": "^0.27.7", "coveralls": "^3.1.0", - "dom-expressions": "0.26.4", - "gitly": "^2.0.2", - "hyper-dom-expressions": "0.26.4", + "dom-expressions": "0.27.7", + "gitly": "^2.1.0", + "hyper-dom-expressions": "0.27.7", "jest": "~26.6.3", "jest-ts-webcompat-resolver": "^1.0.0", "lerna": "^3.22.1", - "lit-dom-expressions": "0.26.4", + "lit-dom-expressions": "0.27.7", "ncp": "2.0.0", "npm-run-all": "^4.1.5", "rimraf": "^3.0.2", - "rollup": "^2.45.1", + "rollup": "^2.48.0", "rollup-plugin-cleanup": "^3.2.1", "rollup-plugin-copy": "^3.4.0", "shelljs": "^0.8.4", - "solid-jest": "^0.0.2", + "solid-jest": "^0.0.3", "symlink-dir": "^4.1.0", "tsconfig-replace-paths": "0.0.5", "typescript": "~4.2.4" diff --git a/packages/babel-preset-solid/package.json b/packages/babel-preset-solid/package.json index 7723dc40f..724f6530e 100644 --- a/packages/babel-preset-solid/package.json +++ b/packages/babel-preset-solid/package.json @@ -1,11 +1,11 @@ { "name": "babel-preset-solid", - "version": "0.26.5", + "version": "1.0.0-rc.1", "description": "Babel preset to transform JSX for Solid.js", "author": "Ryan Carniato ", - "homepage": "https://github.com/solidui/solid/blob/main/packages/babel-preset-solid#readme", + "homepage": "https://github.com/solidjs/solid/blob/main/packages/babel-preset-solid#readme", "license": "MIT", - "repository": "https://github.com/solidui/solid/blob/main/packages/babel-preset-solid", + "repository": "https://github.com/solidjs/solid/blob/main/packages/babel-preset-solid", "main": "index.js", "files": [ "index.js" @@ -14,6 +14,6 @@ "test": "node test.js" }, "dependencies": { - "babel-plugin-jsx-dom-expressions": "~0.26.3" + "babel-plugin-jsx-dom-expressions": "^0.27.7" } } diff --git a/packages/solid-element/README.md b/packages/solid-element/README.md index 9c6c974c2..743f53ecc 100644 --- a/packages/solid-element/README.md +++ b/packages/solid-element/README.md @@ -1,10 +1,10 @@ # Solid Element -[![Build Status](https://github.com/solidui/solid/workflows/Solid%20CI/badge.svg)](https://github.com/solidui/solid/actions/workflows/main-ci.yml) +[![Build Status](https://github.com/solidjs/solid/workflows/Solid%20CI/badge.svg)](https://github.com/solidjs/solid/actions/workflows/main-ci.yml) [![NPM Version](https://img.shields.io/npm/v/solid-element.svg?style=flat)](https://www.npmjs.com/package/solid-element) ![](https://img.shields.io/librariesio/release/npm/solid-element) ![](https://img.shields.io/npm/dm/solid-element.svg?style=flat) -This library extends [Solid](https://github.com/solidui/solid) by adding Custom Web Components and extensions to manage modular behaviors and composition. It uses [Component Register](https://github.com/ryansolid/component-register) to create Web Components and its composed mixin pattern to construct modular re-usable behaviors. This allows your code to available as simple HTML elements for library interopt and to leverage Shadow DOM style isolation. Solid already supports binding to Web Components so this fills the gap allowing full modular applications to be built out of nested Web Components. Component Register makes use of the V1 Standards and on top of being compatible with the common webcomponent.js polyfills, has a solution for Polyfilling Shadow DOM CSS using the ShadyCSS Parser from Polymer in a generic framework agnostic way (unlike the ShadyCSS package). +This library extends [Solid](https://github.com/solidjs/solid) by adding Custom Web Components and extensions to manage modular behaviors and composition. It uses [Component Register](https://github.com/ryansolid/component-register) to create Web Components and its composed mixin pattern to construct modular re-usable behaviors. This allows your code to available as simple HTML elements for library interopt and to leverage Shadow DOM style isolation. Solid already supports binding to Web Components so this fills the gap allowing full modular applications to be built out of nested Web Components. Component Register makes use of the V1 Standards and on top of being compatible with the common webcomponent.js polyfills, has a solution for Polyfilling Shadow DOM CSS using the ShadyCSS Parser from Polymer in a generic framework agnostic way (unlike the ShadyCSS package). ## Example diff --git a/packages/solid-element/package.json b/packages/solid-element/package.json index 5107ed9e5..733d08757 100644 --- a/packages/solid-element/package.json +++ b/packages/solid-element/package.json @@ -3,8 +3,8 @@ "description": "Webcomponents wrapper for Solid", "author": "Ryan Carniato", "license": "MIT", - "version": "0.26.5", - "homepage": "https://github.com/solidui/solid/blob/main/packages/solid-element#readme", + "version": "1.0.0-rc.1", + "homepage": "https://github.com/solidjs/solid/blob/main/packages/solid-element#readme", "type": "module", "main": "dist/index.js", "module": "dist/index.js", @@ -25,6 +25,6 @@ "solid-js": ">= 0.26.0" }, "devDependencies": { - "solid-js": "^0.26.5" + "solid-js": "^1.0.0-rc.1" } } diff --git a/packages/solid-meta/README.md b/packages/solid-meta/README.md deleted file mode 100644 index 34e6e8e63..000000000 --- a/packages/solid-meta/README.md +++ /dev/null @@ -1,71 +0,0 @@ -# solid-meta [![npm Version](https://img.shields.io/npm/v/solid-meta.svg?style=flat-square)](https://www.npmjs.org/package/solid-meta) - -Asynchronous SSR-ready Document Head management for Solid based on [React Head](https://github.com/tizmagik/react-head) - -## Motivation - -This module allows you to define `document.head` tags anywhere in your component hierarchy. The motivations are similar to [react-helmet](https://github.com/nfl/react-helmet) in that you may only have the information for certain tags contextually deep in your component hiearchy. There are no dependencies and it should work fine with asynchronous rendering. - -## Installation - -```sh -npm i solid-meta -``` - -## How it works - -1. You wrap your App with `` -1. From the server, you pass `tags[]` array to `` -1. Then call `renderTags(tags)` and include in the `` block of your server template -1. To insert head tags within your app, just render one of ``, `<Meta />`, `<Style />`, `<Link />`, and `<Base />` components as often as needed. - -On the server, the tags are collected in the `tags[]` array, and then on the client the server-generated tags are removed in favor of the client-rendered tags so that SPAs still work as expected (e.g. in cases where subsequent page loads need to change the head tags). - -### Server setup - -Wrap your app with `<MetaProvider />` on the server, using a `tags[]` array to pass down as part of your server-rendered payload. When rendered, the component mutates this array to contain the tags. - -```js -import { renderToString } from 'solid-js/web'; -import { MetaProvider, renderTags } from 'solid-meta'; -import App from './App'; - -// ... within the context of a request ... - -const tags = []; // mutated during render so you can include in server-rendered template later -const app = renderToString( - <MetaProvider tags={tags}> - <App /> - </MetaProvider> -); - -res.send(` - <!doctype html> - <head> - ${renderTags(tags)} - </head> - <body> - <div id="root">${app}</div> - </body> - </html> -`); -``` - -### Client setup - -There is nothing special required on the client, just render one of head tag components whenever you want to inject a tag in the `<head />`. - -```js -import { MetaProvider, Title, Link, Meta } from 'solid-meta'; - -const App = () => ( - <MetaProvider> - <div class="Home"> - <Title>Title of page - - - // ... -
    - -); -``` \ No newline at end of file diff --git a/packages/solid-meta/babel.config.cjs b/packages/solid-meta/babel.config.cjs deleted file mode 100644 index f81ba9392..000000000 --- a/packages/solid-meta/babel.config.cjs +++ /dev/null @@ -1,21 +0,0 @@ -module.exports = { - env: { - test: { - presets: [["@babel/preset-env", { targets: { node: "current" } }], "@babel/preset-typescript"] - }, - development: { - presets: ["@babel/preset-typescript"], - plugins: [ - [ - "babel-plugin-jsx-dom-expressions", - { - moduleName: "solid-js/web", - contextToCustomElements: true, - wrapConditionals: true, - builtIns: ["For", "Show", "Switch", "Match", "Suspense", "SuspenseList", "Portal"] - } - ] - ] - } - } -}; diff --git a/packages/solid-meta/package.json b/packages/solid-meta/package.json deleted file mode 100644 index fbfa2399f..000000000 --- a/packages/solid-meta/package.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "solid-meta", - "description": "Write meta tags to the document head", - "version": "0.26.5", - "author": "Ryan Carniato", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/solidui/solid/blob/main/packages/solid-meta" - }, - "type": "module", - "main": "dist/index.js", - "exports": { - ".": { - "solid": "./dist/index.jsx", - "default": "./dist/index.js" - } - }, - "types": "dist/index.d.ts", - "files": [ - "dist" - ], - "sideEffects": false, - "scripts": { - "prebuild": "npm run clean", - "clean": "rimraf dist/", - "build": "tsc && babel src/index.tsx --out-file dist/index.js", - "test": "jest && npm run test:types", - "test:types": "tsc --project tsconfig.test.json" - }, - "peerDependencies": { - "solid-js": ">= 0.26.0" - }, - "devDependencies": { - "babel-preset-solid": "^0.26.5", - "solid-js": "^0.26.5" - }, - "jest": { - "preset": "solid-jest/preset/browser" - } -} diff --git a/packages/solid-meta/src/index.tsx b/packages/solid-meta/src/index.tsx deleted file mode 100644 index 9b01611cf..000000000 --- a/packages/solid-meta/src/index.tsx +++ /dev/null @@ -1,153 +0,0 @@ -import { - createContext, - createState, - createComputed, - onMount, - onCleanup, - splitProps, - useContext, - Component, - JSX, - mergeProps -} from "solid-js"; -import { isServer, Show, Portal, Dynamic } from "solid-js/web"; - -interface TagDescription { - tag: string; - props: Record; -} - -interface MetaContextType { - addClientTag: (tag: string, name: string) => number; - - shouldRenderTag: (tag: string, index: number) => boolean; - - removeClientTag: (tag: string, index: number) => void; - - addServerTag?: (tagDesc: TagDescription) => void; -} - -const MetaContext = createContext(); - -const cascadingTags = ["title", "meta"]; - -const MetaProvider: Component<{ tags?: Array }> = props => { - const indices = new Map(), - [state, setState] = createState<{ [k: string]: (string | null)[] }>({}); - - onMount(() => { - const ssrTags = document.head.querySelectorAll(`[data-sm=""]`); - // `forEach` on `NodeList` is not supported in Googlebot, so use a workaround - Array.prototype.forEach.call(ssrTags, (ssrTag: Node) => ssrTag.parentNode!.removeChild(ssrTag)); - }); - - const actions: MetaContextType = { - addClientTag: (tag: string, name: string) => { - // consider only cascading tags - if (cascadingTags.indexOf(tag) !== -1) { - setState(state => { - const names = state[tag] || []; - return { [tag]: [...names, name] }; - }); - // track indices synchronously - const index = indices.has(tag) ? indices.get(tag) + 1 : 0; - indices.set(tag, index); - return index; - } - return -1; - }, - - shouldRenderTag: (tag: string, index: number) => { - if (cascadingTags.indexOf(tag) !== -1) { - const names = state[tag]; - // check if the tag is the last one of similar - return names && names.lastIndexOf(names[index]) === index; - } - return true; - }, - - removeClientTag: (tag: string, index: number) => { - setState(tag, (names: any) => { - if (names) return { [index]: null }; - return names; - }); - } - }; - - if (isServer) { - actions.addServerTag = (tagDesc: TagDescription) => { - const { tags = [] } = props; - // tweak only cascading tags - if (cascadingTags.indexOf(tagDesc.tag) !== -1) { - const index = tags.findIndex(prev => { - const prevName = prev.props.name || prev.props.property; - const nextName = tagDesc.props.name || tagDesc.props.property; - return prev.tag === tagDesc.tag && prevName === nextName; - }); - if (index !== -1) { - tags.splice(index, 1); - } - } - tags.push(tagDesc); - } - - if (Array.isArray(props.tags) === false) { - throw Error("tags array should be passed to in node"); - } - } - - return {props.children}; -}; - -const MetaTag: Component<{ [k: string]: any }> = props => { - const c = useContext(MetaContext); - if (!c) throw new Error(" should be in the tree"); - const { addClientTag, removeClientTag, addServerTag, shouldRenderTag } = c; - - let index = -1; - createComputed(() => { - index = addClientTag(props.tag, props.name || props.property); - onCleanup(() => removeClientTag(props.tag, index)); - }); - - const [internal, rest] = splitProps(props, ["tag"]); - if (isServer) { - addServerTag!({ tag: internal.tag, props: rest }); - return null; - } - return ( - - - - - - ); -}; - -export { MetaProvider }; - -export function renderTags(tags: Array) { - return tags - .map(tag => { - const keys = Object.keys(tag.props); - return `<${tag.tag} data-sm=""${keys.map(k => - k === "children" ? "" : ` ${k}="${tag.props[k]}"` - )}>${tag.props.children || ""}`; - }) - .join(""); -} - -export const Title: Component> = props => - MetaTag(mergeProps({ tag: "title" }, props)); - -export const Style: Component> = props => - MetaTag(mergeProps({ tag: "style" }, props)); - -export const Meta: Component> = props => - MetaTag(mergeProps({ tag: "meta" }, props)); - -export const Link: Component> = props => - MetaTag(mergeProps({ tag: "link" }, props)); - -export const Base: Component> = props => - MetaTag(mergeProps({ tag: "base" }, props)); diff --git a/packages/solid-meta/test/index.spec.tsx b/packages/solid-meta/test/index.spec.tsx deleted file mode 100644 index f1daf53fe..000000000 --- a/packages/solid-meta/test/index.spec.tsx +++ /dev/null @@ -1,155 +0,0 @@ -/* @jsxImportSource solid-js */ -import { createSignal } from "solid-js"; -import { render, Show } from "solid-js/web"; -import { MetaProvider, Title, Style, Meta, Link, Base } from '../src'; - -global.queueMicrotask = setImmediate; - -test('renders into document.head portal', () => { - let div = document.createElement("div"); - const snapshot = "Test title" - const dispose = render(() => - -
    - Yes render - Test title - - - - -
    -
    - , div); - expect(document.head.innerHTML).toBe(snapshot); - dispose(); -}); - -test('renders only the last title', () => { - let div = document.createElement("div"); - const snapshot = "Title 3"; - const dispose = render(() => - -
    - Title 1 -
    -
    - Title 2 -
    -
    - Title 3 -
    -
    - , div); - expect(document.head.innerHTML).toBe(snapshot); - dispose(); -}); - -test('mounts and unmounts title', () => { - let div = document.createElement("div"); - const snapshot1 = "Static"; - const snapshot2 = "Dynamic"; - const [visible, setVisible] = createSignal(false) - const dispose = render(() => - - Static - - Dynamic - - - , div); - - expect(document.head.innerHTML).toBe(snapshot1); - setVisible(true); - expect(document.head.innerHTML).toBe(snapshot2); - setVisible(false); - expect(document.head.innerHTML).toBe(snapshot1); - dispose(); -}); - -test('switches between titles', () => { - let div = document.createElement("div"); - const snapshot1 = "Title 1"; - const snapshot2 = "Title 2"; - const [visible, setVisible] = createSignal(true) - const dispose = render(() => - - Static - Title 2}> - Title 1 - - - , div); - expect(document.head.innerHTML).toBe(snapshot1); - setVisible(false); - expect(document.head.innerHTML).toBe(snapshot2); - dispose(); -}); - -test('renders only the last meta with the same name', () => { - let div = document.createElement("div"); - - /* Something weird in this env - const snapshot1 = "Static 1Static 2"; - const snapshot2 = "Static 1Dynamic 1"; - const snapshot3 = "Dynamic 2Dynamic 1"; - */ - - const snapshot1 = ""; - const snapshot2 = ""; - const snapshot3 = ""; - const snapshot4 = ""; - - const [visible1, setVisible1] = createSignal(false); - const [visible2, setVisible2] = createSignal(false); - const dispose = render(() => - - Static 1 - Static 2 - - Dynamic 1 - - - Dynamic 2 - - - , div); - expect(document.head.innerHTML).toBe(snapshot1); - // mount first - setVisible1(true); - expect(document.head.innerHTML).toBe(snapshot2); - // mount second - setVisible2(true) - expect(document.head.innerHTML).toBe(snapshot3); - // unmount second - setVisible2(false); - expect(document.head.innerHTML).toBe(snapshot4); - // unmount first - setVisible1(false); - expect(document.head.innerHTML).toBe(snapshot1); - dispose(); -}); - -test('renders only last meta with the same property', () => { - let div = document.createElement("div"); - // something weird with meta tag stringification in this env - const snapshot = ""; - const dispose = render(() => - - Meta 1 - Meta 2 - Meta 3 - Meta 4 - Meta 5 - Meta 6 - - , div); - expect(document.head.innerHTML).toBe(snapshot); - dispose(); -}); - -test('throws error if head tag is rendered without MetaProvider', () => { - expect(() => { - let div = document.createElement("div"); - render(() => , div); - }).toThrowError(/ should be in the tree/); -}); \ No newline at end of file diff --git a/packages/solid-meta/tsconfig.json b/packages/solid-meta/tsconfig.json deleted file mode 100644 index 837cc5254..000000000 --- a/packages/solid-meta/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "emitDeclarationOnly": false, - "jsx": "preserve", - "jsxImportSource": "solid-js" - }, - "include": [ - "./src" - ] -} \ No newline at end of file diff --git a/packages/solid-meta/tsconfig.test.json b/packages/solid-meta/tsconfig.test.json deleted file mode 100644 index be1b449e6..000000000 --- a/packages/solid-meta/tsconfig.test.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.test.json", - "compilerOptions": { - "jsxImportSource": "solid-js" - }, - "include": [ - "./test" - ] - } \ No newline at end of file diff --git a/packages/solid-rx/README.md b/packages/solid-rx/README.md deleted file mode 100644 index 0cda4e4b5..000000000 --- a/packages/solid-rx/README.md +++ /dev/null @@ -1,157 +0,0 @@ -# `solid-rx`(Deprecated) - -### This package is no longer maintained and may not work properly with future versions of Solid - -[![Build Status](https://github.com/solidui/solid/workflows/Solid%20CI/badge.svg)](https://github.com/solidui/solid/actions/workflows/main-ci.yml) -[![NPM Version](https://img.shields.io/npm/v/solid-rx.svg?style=flat)](https://www.npmjs.com/package/solid-rx) -![](https://img.shields.io/librariesio/release/npm/solid-rx) -![](https://img.shields.io/npm/dm/solid-rx.svg?style=flat) - -Functional Reactive Extensions for Solid.js. This package contains a number of operators intended to be use with Solid's `createMemo`, `createComputed`, and `createEffect` to create reactive transformations. - -Example: - -```js -import { createSignal, createMemo, createEffect } from "solid-js"; -import { pipe, tap, map, filter } from "solid-rx"; - -const doubleEven = pipe( - tap(console.log), - filter(t => t % 2 === 0), - map(t => t * 2) -); - -const [number, setNumber] = createSignal(0), - result = createMemo(doubleEven(number)); -// 0 - -createEffect(() => console.log("transformed", result())); -// transformed 0 -setNumber(1); -// 1 -setNumber(2); -// 2 -// transformed 4 -setNumber(3); -// 3 -``` - -These can also be useful for use with control flow. For example if you wished to delegate applying a class to any row with a model property that matched the current selection (without adding the selected state to the model): - -```jsx -function selectClass(selected, className) { - return list => { - createEffect( - transform( - list, - // wrap selection in accessor function and merge since map operators are not tracked - // find selected element - mergeMap(list => () => list.find(el => el.model === selected())), - // group prev value with current - pairwise(), - // tap value for side effect of setting `className` - tap(([prevEl, el]) => { - prevEl && (prevEl.className = ""); - el && (el.className = className); - }) - ) - ); - // return the original signal - return list; - }; -} - -const ForWithSelection = props => { - const applyClass = selectClass(() => props.selected, "active"); - return applyClass({props.children}); -}; - -// in a component somewhere: - - {row =>
    {row.description}
    } -
    ; -``` - -## Why? - -Truthfully nothing in this package is necessary. Solid's auto dependency tracking computations do not need to take a formal functional programming approach to be expressive and succint. If anything this can make simple expressions more complicated. Compare: - -```js -// functional -createEffect(transform( - signal, - filter(t => t % 2 === 0), - map(t => t * 2) - tap(console.log) -)); - -// imperative -createEffect(() => { - const s = signal(); - s % 2 === 0 && console.log(s * 2); -}) -``` - -Obviously `map` and `tap` could have been combined in the functional example, but the point still stands. The reason you look at a library like this is that sometimes more complicated problems can easier be modelled as a transformation stream, and that this approach is very composable allowing constructing patterns for code reuse. - -# Documentation - -## Utilities - -### `from(setter => dispose) => signal` - -This operator is useful to create signals from any sort of data structure. You pass in a function that provides a setter. Use the setter to set any value to pass to the signal, from various sources like events, promises, observables, timers etc... - -### `pipe(...operators) => sourceSignal => outSignal` - -### `transform(sourceSignal, ...operators) => outSignal` - -Tbese operators are responsible for chaining together transformations. The only difference is piped if curried and used for composition, whereas transform includes the source as an argument. - -### `observable(signal) => Observable` - -Connects a signal to a TC39 observable. Whenever the signal is updated the change will be propagated to the observable. This observable can be used with libraries like RxJS which unlock a whole number of operators and functionality. Going the opposite direction is just passing a signal setter to the observable subscribe method. - -## Operators - -All operators support curried and non-curried (signal passed as first argument) forms. Curried form is what is listed here. - -### `delay(timeMs: number)` - -Delay value propagation by the given time in milliseconds. - -### `defer({ timeoutMs: number })` - -Defers propagation until CPU is idle unless optional timeout has expired. - -### `map(v => any)` - -Map value to another value. - -### `mergeMap(v => signal | () => any)` - -Project inside signal or accessor function to output signal. - -### `tap(v => void)` - -Does not affect value propagation. Useful for side effects or debugging. - -### `pairwise()` - -Combines previous value with current value as an array. - -### `scan((accumulator, value) => result, seed)` - -Accumulators the result of each value propagation and feeds it to the next. - -### `filter(v => boolean)` - -Propagate value change if condition is true. - -## Observables - -Signals and Observable are similar concepts that can work together but there are a few key differences. Observables are as defined by the [TC39 Proposal](https://github.com/tc39/proposal-observable). These are a standard way of representing streams, and follow a few key conventions. Mostly that they are cold, unicast, and push-based by default. What this means is that they do not do anything until subscribed to at which point they create the source, and do so for each subscription. So if you had an Observable from a DOM Event, subscribing would add an event listener for each function you pass. In so being unicast they aren't managing a list of subscribers. Finally being push you don't ask for the latest value, they tell you. - -Observables track next value, errors, and completion. This is very useful for tracking discreet events over time. Signals are much simpler. They are hot and multicast in nature and while capable of pushing values over time aren't aware of it themselves. They are simple and synchronous. They don't complete, they exist or they don't exist. - -Observables can work well with Signals as being a source that feeds data into them. Like State, Observables are another tool that allow more control in a specific aspect of your application. Where State is valuable for reconciling multiple Signals together into a serializable structure to keep managing Component or Store code simple, Observables are useful for transforming Async data pipelines like handling Data Communication services. diff --git a/packages/solid-rx/babel.config.cjs b/packages/solid-rx/babel.config.cjs deleted file mode 100644 index 72681ff93..000000000 --- a/packages/solid-rx/babel.config.cjs +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - env: { - test: { - presets: [ - ["@babel/preset-env", { targets: { node: "current" } }], - "@babel/preset-typescript" - ] - } - } -}; diff --git a/packages/solid-rx/package.json b/packages/solid-rx/package.json deleted file mode 100644 index d8f5b5c1d..000000000 --- a/packages/solid-rx/package.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "solid-rx", - "version": "0.26.5", - "description": "Functionally reactive extensions for Solid.js", - "author": "Ryan Carniato ", - "homepage": "https://github.com/solidui/solid/blob/main/packages/solid-rx#readme", - "license": "MIT", - "type": "module", - "main": "dist/solid-rx.js", - "module": "dist/solid-rx.js", - "types": "types/index.d.ts", - "files": [ - "dist", - "types" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/solidui/solid.git" - }, - "scripts": { - "prebuild": "npm run clean", - "clean": "rimraf dist/ types/ coverage/", - "build": "rollup -c && tsc", - "test": "jest" - }, - "bugs": { - "url": "https://github.com/solidui/solid/issues" - }, - "peerDependencies": { - "solid-js": ">= 0.26.0" - }, - "devDependencies": { - "solid-js": "^0.26.5" - }, - "jest": { - "preset": "solid-jest/preset/browser" - } -} diff --git a/packages/solid-rx/rollup.config.js b/packages/solid-rx/rollup.config.js deleted file mode 100644 index b832bb915..000000000 --- a/packages/solid-rx/rollup.config.js +++ /dev/null @@ -1,24 +0,0 @@ -import nodeResolve from "@rollup/plugin-node-resolve"; -import babel from '@rollup/plugin-babel'; - -const plugins = [ - nodeResolve({ - extensions: [".js", ".ts"] - }), - babel({ - extensions: ['.ts', '.js'], - babelHelpers: "bundled", - presets: ["@babel/preset-typescript"], - exclude: 'node_modules/**' - }) -]; - -export default { - input: 'src/index.ts', - output: [{ - file: 'dist/solid-rx.js', - format: 'es' - }], - external: ['solid-js'], - plugins -}; \ No newline at end of file diff --git a/packages/solid-rx/src/defer.ts b/packages/solid-rx/src/defer.ts deleted file mode 100644 index 249ef1094..000000000 --- a/packages/solid-rx/src/defer.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { createDeferred } from "solid-js"; - -export function defer(options?: { timeoutMs: number }): (fn: () => T) => () => T; -export function defer(fn: () => T, options: { timeoutMs: number }): () => T; -export function defer(fn: any, options?: any): any { - if (typeof fn === "function") { - return createDeferred(fn, options); - } - options = fn; - return (signal: () => T) => createDeferred(signal, options); -} diff --git a/packages/solid-rx/src/delay.ts b/packages/solid-rx/src/delay.ts deleted file mode 100644 index 4b9fd36c2..000000000 --- a/packages/solid-rx/src/delay.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { createSignal, createComputed } from "solid-js"; - -export function delay(timeMs: number): (v: () => T) => () => T; -export function delay(input: () => T, timeMs: number): () => T; -export function delay(input: any, timeMs?: number): any { - if (arguments.length === 1) { - timeMs = input; - return delay; - } - return delay(input); - - function delay(input: () => T) { - const [s, set] = createSignal(); - createComputed(() => { - const value = input(); - setTimeout(() => set(value), timeMs); - }); - return s; - } -} diff --git a/packages/solid-rx/src/filter.ts b/packages/solid-rx/src/filter.ts deleted file mode 100644 index e58c3c830..000000000 --- a/packages/solid-rx/src/filter.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createMemo, untrack } from "solid-js"; - -export function filter(fn: (v: T) => boolean): (v: () => T) => () => T; -export function filter(input: () => T, fn: (v: T) => boolean): () => T; -export function filter(input: any, fn?: (v: T) => boolean): any { - if (arguments.length === 1) { - fn = input; - return filter; - } - return filter(input); - - function filter(input: () => T) { - let value: T; - const trigger = createMemo( - () => { - value = input(); - return untrack(() => fn!(value)); - }, - undefined, - (_, next) => next === false - ); - return () => trigger() && value; - } -} diff --git a/packages/solid-rx/src/from.ts b/packages/solid-rx/src/from.ts deleted file mode 100644 index ee2e9b9ef..000000000 --- a/packages/solid-rx/src/from.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { createSignal, onCleanup } from "solid-js"; - -export function from(fn: (setter: (v: T) => void) => (() => void) | void) { - const [s, set] = createSignal(), - disposer = fn(set); - if (disposer) onCleanup(disposer); - return s; -} diff --git a/packages/solid-rx/src/index.ts b/packages/solid-rx/src/index.ts deleted file mode 100644 index 2e690e005..000000000 --- a/packages/solid-rx/src/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -export { defer } from "./defer"; -export { delay } from "./delay"; -export { filter } from "./filter"; -export { from } from "./from"; -export { map } from "./map"; -export { mergeMap } from "./mergeMap"; -export { observable } from "./observable"; -export type { ObservableObserver } from "./observable"; -export { pairwise } from "./pairwise"; -export { pipe } from "./pipe"; -export { reduceArray } from "./reduceArray"; -export { scan } from "./scan"; -export { tap } from "./tap"; -export { transform } from "./transform"; diff --git a/packages/solid-rx/src/map.ts b/packages/solid-rx/src/map.ts deleted file mode 100644 index e35b2af76..000000000 --- a/packages/solid-rx/src/map.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { untrack } from "solid-js"; - -export function map(fn: (v: T) => U): (v: () => T) => () => U; -export function map(input: () => T, fn: (v: T) => U): () => U; -export function map(input: any, fn?: (v: T) => U): any { - if (arguments.length === 1) { - fn = input; - return map; - } - return map(input); - - function map(input: () => T) { - return () => { - const value = input(); - return untrack(() => fn!(value)); - }; - } -} diff --git a/packages/solid-rx/src/mergeMap.ts b/packages/solid-rx/src/mergeMap.ts deleted file mode 100644 index 7bee34cf9..000000000 --- a/packages/solid-rx/src/mergeMap.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { untrack, createMemo } from "solid-js"; - -export function mergeMap(fn: (v: T) => () => U): (v: () => T) => () => U; -export function mergeMap(input: () => T, fn: (v: T) => () => U): () => U; -export function mergeMap(input: any, fn?: (v: T) => () => U): any { - if (arguments.length === 1) { - fn = input; - return mergeMap; - } - return mergeMap(input); - - function mergeMap(input: () => T) { - const mapped = createMemo(() => { - const value = input(); - return untrack(() => fn!(value)); - }); - return () => { - const m = mapped(); - return m ? m() : undefined; - }; - } -} diff --git a/packages/solid-rx/src/pairwise.ts b/packages/solid-rx/src/pairwise.ts deleted file mode 100644 index 9fab1ce12..000000000 --- a/packages/solid-rx/src/pairwise.ts +++ /dev/null @@ -1,15 +0,0 @@ -export function pairwise(): (v: () => T) => () => [T, T]; -export function pairwise(input: () => T): () => [T, T]; -export function pairwise(input?: any): any { - return arguments.length === 0 ? pairwise : pairwise(input); - - function pairwise(input: () => T) { - let prevValue: T; - return () => { - const value = input(), - result = [prevValue, value]; - prevValue = value; - return result; - }; - } -} diff --git a/packages/solid-rx/src/pipe.ts b/packages/solid-rx/src/pipe.ts deleted file mode 100644 index 20a627e00..000000000 --- a/packages/solid-rx/src/pipe.ts +++ /dev/null @@ -1,78 +0,0 @@ -type Operator = (seq: () => T) => () => U; - -export function pipe(): Operator; -export function pipe(fn1: Operator): Operator; -export function pipe(fn1: Operator, fn2: Operator): Operator; -export function pipe( - fn1: Operator, - fn2: Operator, - fn3: Operator -): Operator; -export function pipe( - fn1: Operator, - fn2: Operator, - fn3: Operator, - fn4: Operator -): Operator; -export function pipe( - fn1: Operator, - fn2: Operator, - fn3: Operator, - fn4: Operator, - fn5: Operator -): Operator; -export function pipe( - fn1: Operator, - fn2: Operator, - fn3: Operator, - fn4: Operator, - fn5: Operator, - fn6: Operator -): Operator; -export function pipe( - fn1: Operator, - fn2: Operator, - fn3: Operator, - fn4: Operator, - fn5: Operator, - fn6: Operator, - fn7: Operator -): Operator; -export function pipe( - fn1: Operator, - fn2: Operator, - fn3: Operator, - fn4: Operator, - fn5: Operator, - fn6: Operator, - fn7: Operator, - fn8: Operator -): Operator; -export function pipe( - fn1: Operator, - fn2: Operator, - fn3: Operator, - fn4: Operator, - fn5: Operator, - fn6: Operator, - fn7: Operator, - fn8: Operator, - fn9: Operator -): Operator; -export function pipe( - fn1: Operator, - fn2: Operator, - fn3: Operator, - fn4: Operator, - fn5: Operator, - fn6: Operator, - fn7: Operator, - fn8: Operator, - fn9: Operator, - ...fns: Operator[] -): Operator; -export function pipe(...fns: Array>): Operator { - if (!fns) return i => i; - if (fns.length === 1) return fns[0]; - return input => fns.reduce((prev, fn) => fn(prev), input); -} diff --git a/packages/solid-rx/src/reduceArray.ts b/packages/solid-rx/src/reduceArray.ts deleted file mode 100644 index 5f0dbd8d2..000000000 --- a/packages/solid-rx/src/reduceArray.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { untrack } from "solid-js"; - -export function reduceArray( - fn: (memo: U, value: T, i: number) => U, - seed: U -): (list: () => T[]) => () => U; -export function reduceArray( - list: () => T[], - fn: (memo: U, value: T, i: number) => U, - seed: U -): () => U; -export function reduceArray(list: any, fn: any, seed?: any): any { - if (arguments.length < 3) { - seed = fn; - fn = list; - return reducer; - } - return reducer(list); - - function reducer(list: () => T[]) { - return () => { - let newList = list() || [], - result = seed; - return untrack(() => { - for (let i = 0; i < newList.length; i++) { - result = fn(result, newList[i], i); - } - return result; - }); - }; - } -} \ No newline at end of file diff --git a/packages/solid-rx/src/scan.ts b/packages/solid-rx/src/scan.ts deleted file mode 100644 index c1f7882db..000000000 --- a/packages/solid-rx/src/scan.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { untrack } from "solid-js"; - -export function scan(fn: (a: U, v: T) => U, seed: U): (v: () => T) => () => U; -export function scan(input: () => T, fn: (a: U, v: T) => U, seed: U): () => U; -export function scan(input: any, fn: any, seed?: U): any { - if (arguments.length === 2) { - seed = fn; - fn = input; - return scan; - } - return scan(input); - - function scan(input: () => T) { - return () => { - const value = input(); - return untrack(() => (seed = fn(seed, value))); - }; - } -} diff --git a/packages/solid-rx/src/tap.ts b/packages/solid-rx/src/tap.ts deleted file mode 100644 index 5c89a76c0..000000000 --- a/packages/solid-rx/src/tap.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { untrack } from "solid-js"; - -export function tap(fn: (v: T) => void): (v: () => T) => () => T; -export function tap(input: () => T, fn: (v: T) => void): () => T; -export function tap(input: any, fn?: any): any { - if (arguments.length === 1) { - fn = input; - return tap; - } - return tap(input); - - function tap(input: () => T) { - return () => { - const value = input(); - untrack(() => fn(value)); - return value; - }; - } -} diff --git a/packages/solid-rx/src/transform.ts b/packages/solid-rx/src/transform.ts deleted file mode 100644 index fef1c7dec..000000000 --- a/packages/solid-rx/src/transform.ts +++ /dev/null @@ -1,90 +0,0 @@ -type Operator = (seq: () => T) => () => U; - -export function transform(source: () => T): () => T; -export function transform(source: () => T, fn1: Operator): () => A; -export function transform( - source: () => T, - fn1: Operator, - fn2: Operator -): () => B; -export function transform( - source: () => T, - fn1: Operator, - fn2: Operator, - fn3: Operator -): () => C; -export function transform( - source: () => T, - fn1: Operator, - fn2: Operator, - fn3: Operator, - fn4: Operator -): () => D; -export function transform( - source: () => T, - fn1: Operator, - fn2: Operator, - fn3: Operator, - fn4: Operator, - fn5: Operator -): () => E; -export function transform( - source: () => T, - fn1: Operator, - fn2: Operator, - fn3: Operator, - fn4: Operator, - fn5: Operator, - fn6: Operator -): () => F; -export function transform( - source: () => T, - fn1: Operator, - fn2: Operator, - fn3: Operator, - fn4: Operator, - fn5: Operator, - fn6: Operator, - fn7: Operator -): () => G; -export function transform( - source: () => T, - fn1: Operator, - fn2: Operator, - fn3: Operator, - fn4: Operator, - fn5: Operator, - fn6: Operator, - fn7: Operator, - fn8: Operator -): () => H; -export function transform( - source: () => T, - fn1: Operator, - fn2: Operator, - fn3: Operator, - fn4: Operator, - fn5: Operator, - fn6: Operator, - fn7: Operator, - fn8: Operator, - fn9: Operator -): () => I; -export function transform( - source: () => T, - fn1: Operator, - fn2: Operator, - fn3: Operator, - fn4: Operator, - fn5: Operator, - fn6: Operator, - fn7: Operator, - fn8: Operator, - fn9: Operator, - ...fns: Operator[] -): () => any; -export function transform(source: () => any, ...fns: Array>): () => any { - if (!fns) return source; - if (fns.length === 1) return fns[0](source); - return fns.reduce((prev, fn) => fn(prev), source); -} diff --git a/packages/solid-rx/test/MessageChannel.ts b/packages/solid-rx/test/MessageChannel.ts deleted file mode 100644 index d59cacd79..000000000 --- a/packages/solid-rx/test/MessageChannel.ts +++ /dev/null @@ -1,9 +0,0 @@ -// @ts-ignore -globalThis.MessageChannel = class { - port1: { onmessage?: any } = {}; - port2 = { - postMessage: () => { - setTimeout(this.port1.onmessage, 0); - } - }; -} \ No newline at end of file diff --git a/packages/solid-rx/test/defer.spec.ts b/packages/solid-rx/test/defer.spec.ts deleted file mode 100644 index c00fa32bf..000000000 --- a/packages/solid-rx/test/defer.spec.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { createRoot, createSignal, createMemo } from "solid-js"; -import { defer } from "../src"; -import "./MessageChannel"; - -describe("Defer operator", () => { - test("simple defer", done => { - createRoot(() => { - const [s, set] = createSignal(), - r = createMemo(defer(s, { timeoutMs: 20 })); - expect(r()).not.toBeDefined(); - set("Hi"); - expect(r()).not.toBeDefined(); - setTimeout(() => { - expect(r()).toBe("Hi"); - done(); - }, 25); - }); - }); - - test("simple defer curried", done => { - createRoot(() => { - const [s, set] = createSignal(), - deferred = defer({ timeoutMs: 20 }), - r = createMemo(deferred(s)); - expect(r()).not.toBeDefined(); - set("Hi"); - expect(r()).not.toBeDefined(); - setTimeout(() => { - expect(r()).toBe("Hi"); - done(); - }, 25); - }); - }); -}); diff --git a/packages/solid-rx/test/delay.spec.ts b/packages/solid-rx/test/delay.spec.ts deleted file mode 100644 index 10d6af58b..000000000 --- a/packages/solid-rx/test/delay.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { createRoot, createSignal, createMemo } from "solid-js"; -import { delay } from "../src"; - -describe("Delay operator", () => { - test("simple delay", done => { - createRoot(() => { - const [s, set] = createSignal(), - r = createMemo(delay(s, 10)); - expect(r()).not.toBeDefined(); - set("Hi"); - expect(r()).not.toBeDefined(); - setTimeout(() => { - expect(r()).toBe("Hi"); - done(); - }, 15); - }); - }); - - test("simple delay curried", done => { - createRoot(() => { - const [s, set] = createSignal(), - delayed = delay(10), - r = createMemo(delayed(s)); - expect(r()).not.toBeDefined(); - set("Hi"); - expect(r()).not.toBeDefined(); - setTimeout(() => { - expect(r()).toBe("Hi"); - done(); - }, 15); - }); - }); -}); diff --git a/packages/solid-rx/test/filter.spec.ts b/packages/solid-rx/test/filter.spec.ts deleted file mode 100644 index bf0a72f06..000000000 --- a/packages/solid-rx/test/filter.spec.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { createRoot, createSignal, createMemo, createComputed } from "solid-js"; -import { filter } from "../src"; - -describe("Filter operator", () => { - test("simple filter", () => { - createRoot(() => { - const [s, set] = createSignal(0), - r = createMemo(filter(s, n => n % 2 === 0)); - let executions = 0; - createComputed(() => { - r(); - executions++; - }); - expect(r()).toBe(0); - expect(executions).toBe(1); - set(1); - expect(r()).toBe(0); - expect(executions).toBe(1); - set(2); - expect(r()).toBe(2); - expect(executions).toBe(2); - set(3); - expect(r()).toBe(2); - expect(executions).toBe(2); - set(4); - expect(r()).toBe(4); - expect(executions).toBe(3); - }); - }); - - test("simple filter curried", () => { - createRoot(() => { - const [s, set] = createSignal(0), - even = filter(n => n % 2 === 0), - r = createMemo(even(s)); - let executions = 0; - createComputed(() => { - r(); - executions++; - }); - expect(r()).toBe(0); - expect(executions).toBe(1); - set(1); - expect(r()).toBe(0); - expect(executions).toBe(1); - set(2); - expect(r()).toBe(2); - expect(executions).toBe(2); - set(3); - expect(r()).toBe(2); - expect(executions).toBe(2); - set(4); - expect(r()).toBe(4); - expect(executions).toBe(3); - }); - }); -}); diff --git a/packages/solid-rx/test/from.spec.ts b/packages/solid-rx/test/from.spec.ts deleted file mode 100644 index 5c6cb6f5c..000000000 --- a/packages/solid-rx/test/from.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { createRoot } from "solid-js"; -import { from } from "../src"; - -describe("From operator", () => { - test("from promise", async () => { - let resolve; - const signal = from(set => { - new Promise(r => (resolve = r)).then(set); - }); - expect(signal()).toBeUndefined(); - resolve("Hi"); - await Promise.resolve(); - expect(signal()).toBe("Hi"); - }); - - test("from interval", done => { - createRoot(dispose => { - let i = 0, - disposed = false; - const signal = from(set => { - const n = setInterval(() => set(++i), 20); - return () => ((disposed = true), clearInterval(n)); - }); - expect(signal()).toBeUndefined(); - expect(disposed).toBe(false); - setTimeout(() => expect(signal()).toBe(1), 35); - setTimeout(() => expect(signal()).toBe(2), 55); - setTimeout(() => { - expect(signal()).toBe(3); - dispose(); - expect(disposed).toBe(true); - done(); - }, 75); - }); - }); -}); diff --git a/packages/solid-rx/test/map.spec.ts b/packages/solid-rx/test/map.spec.ts deleted file mode 100644 index 54621b748..000000000 --- a/packages/solid-rx/test/map.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { createRoot, createSignal, createMemo } from "solid-js"; -import { map } from "../src"; - -describe("Map operator", () => { - test("simple map", () => { - createRoot(() => { - const [s, set] = createSignal("Hi"), - r = createMemo(map(s, t => t + " " + t)); - expect(r()).toBe("Hi Hi"); - set("Lo"); - expect(r()).toBe("Lo Lo"); - }); - }); - - test("simple map curried", () => { - createRoot(() => { - const [s, set] = createSignal("Hi"), - repeat = map(t => t + " " + t), - r = createMemo(repeat(s)); - expect(r()).toBe("Hi Hi"); - set("Lo"); - expect(r()).toBe("Lo Lo"); - }); - }); -}); diff --git a/packages/solid-rx/test/mergeMap.spec.ts b/packages/solid-rx/test/mergeMap.spec.ts deleted file mode 100644 index a99718a20..000000000 --- a/packages/solid-rx/test/mergeMap.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { createRoot, createSignal, createMemo } from "solid-js"; -import { mergeMap } from "../src"; - -describe("Merge Map operator", () => { - test("simple mergeMap", () => { - createRoot(() => { - const [s, set] = createSignal(1), - [s2, set2] = createSignal(1), - r = createMemo(mergeMap(s, t => () => t * s2())); - expect(r()).toBe(1); - set2(2); - expect(r()).toBe(2); - set(2); - expect(r()).toBe(4); - set2(3); - expect(r()).toBe(6); - }); - }); - - test("simple mergeMap curried", () => { - createRoot(() => { - const [s, set] = createSignal(1), - [s2, set2] = createSignal(1), - multiply = mergeMap(t => () => t * s2()), - r = createMemo(multiply(s)); - expect(r()).toBe(1); - set2(2); - expect(r()).toBe(2); - set(2); - expect(r()).toBe(4); - set2(3); - expect(r()).toBe(6); - }); - }); -}); diff --git a/packages/solid-rx/test/pairwise.spec.ts b/packages/solid-rx/test/pairwise.spec.ts deleted file mode 100644 index 1776a5d9c..000000000 --- a/packages/solid-rx/test/pairwise.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { createRoot, createSignal, createMemo } from "solid-js"; -import { pairwise } from "../src"; - -describe("Pairwise operator", () => { - test("simple pairwise", () => { - createRoot(() => { - const [s, set] = createSignal("Hi"), - r = createMemo(pairwise(s)); - expect(r()).toEqual([undefined, "Hi"]); - set("Lo"); - expect(r()).toEqual(["Hi", "Lo"]); - }); - }); - - test("simple pairwise curried", () => { - createRoot(() => { - const [s, set] = createSignal("Hi"), - pair = pairwise(), - r = createMemo(pair(s)); - expect(r()).toEqual([undefined, "Hi"]); - set("Lo"); - expect(r()).toEqual(["Hi", "Lo"]); - }); - }); -}); diff --git a/packages/solid-rx/test/pipe.spec.ts b/packages/solid-rx/test/pipe.spec.ts deleted file mode 100644 index 25747a3cd..000000000 --- a/packages/solid-rx/test/pipe.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { createRoot, createSignal, createMemo } from "solid-js"; -import { pipe } from "../src"; - -describe("Pipe operator", () => { - const multiply = (m: number) => (s: () => number) => () => s() * m; - test("no ops", () => { - createRoot(() => { - const [s, set] = createSignal(0), - r = createMemo(pipe()(s)); - expect(r()).toBe(0); - set(2); - expect(r()).toBe(2); - }); - }); - - test("single op", () => { - createRoot(() => { - const [s, set] = createSignal(1), - r = createMemo(pipe(multiply(2))(s)); - expect(r()).toBe(2); - set(2); - expect(r()).toBe(4); - }); - }); - - test("multiple ops", () => { - createRoot(() => { - const [s, set] = createSignal(1), - r = createMemo(pipe(multiply(2), multiply(3))(s)); - expect(r()).toBe(6); - set(2); - expect(r()).toBe(12); - }); - }); -}); diff --git a/packages/solid-rx/test/reduceArray.spec.ts b/packages/solid-rx/test/reduceArray.spec.ts deleted file mode 100644 index cd95b1a2b..000000000 --- a/packages/solid-rx/test/reduceArray.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { createRoot, createSignal, createMemo } from "solid-js"; -import { reduceArray } from "../src"; - -describe("Reduce operator", () => { - test("simple addition", () => { - createRoot(() => { - const [s, set] = createSignal([1, 2, 3, 4]), - r = createMemo(reduceArray(s, (m: number, v: number) => m + v, 0)); - expect(r()).toBe(10); - set([3, 4, 5]); - expect(r()).toBe(12); - }); - }); - - test("simple addition curried", () => { - createRoot(() => { - const [s, set] = createSignal([1, 2, 3, 4]), - sum = reduceArray((m: number, v: number) => m + v, 0), - r = createMemo(sum(s)); - expect(r()).toBe(10); - set([3, 4, 5]); - expect(r()).toBe(12); - }); - }); - - test("filter list", () => { - createRoot(() => { - const [s, set] = createSignal([1, 2, 3, 4]), - filterOdd = reduceArray((m: number[], v: number) => (v % 2 ? [...m, v] : m), []), - r = createMemo(filterOdd(s)); - expect(r()).toEqual([1, 3]); - set([3, 4, 5]); - expect(r()).toEqual([3, 5]); - }); - }); -}); diff --git a/packages/solid-rx/test/scan.spec.ts b/packages/solid-rx/test/scan.spec.ts deleted file mode 100644 index dd0405af5..000000000 --- a/packages/solid-rx/test/scan.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { createRoot, createSignal, createMemo } from "solid-js"; -import { scan } from "../src"; - -describe("Scan operator", () => { - test("simple scan", () => { - createRoot(() => { - const [s, set] = createSignal(1), - r = createMemo(scan(s, (m, t) => m + t, 1)); - expect(r()).toBe(2); - set(2); - expect(r()).toBe(4); - }); - }); - - test("simple scan curried", () => { - createRoot(() => { - const [s, set] = createSignal(1), - sum = scan((m, t) => m + t, 1), - r = createMemo(sum(s)); - expect(r()).toBe(2); - set(2); - expect(r()).toBe(4); - }); - }); -}); diff --git a/packages/solid-rx/test/tap.spec.ts b/packages/solid-rx/test/tap.spec.ts deleted file mode 100644 index 4b7035d7d..000000000 --- a/packages/solid-rx/test/tap.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { createRoot, createSignal, createComputed } from "solid-js"; -import { tap } from "../src"; - -describe("Tap operator", () => { - test("simple tap", () => { - createRoot(() => { - const [s, set] = createSignal("Hi"); - let out: string; - createComputed(tap(s, t => (out = t + " " + t))); - expect(out).toBe("Hi Hi"); - set("Lo"); - expect(out).toBe("Lo Lo"); - }); - }); - - test("simple tap curried", () => { - createRoot(() => { - let out: string; - const [s, set] = createSignal("Hi"), - repeat = tap(t => (out = t + " " + t)); - createComputed(repeat(s)); - expect(out).toBe("Hi Hi"); - set("Lo"); - expect(out).toBe("Lo Lo"); - }); - }); -}); diff --git a/packages/solid-rx/test/transform.spec.ts b/packages/solid-rx/test/transform.spec.ts deleted file mode 100644 index c693fe34f..000000000 --- a/packages/solid-rx/test/transform.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { createRoot, createSignal, createMemo } from "solid-js"; -import { transform } from "../src"; - -describe("Transform operator", () => { - const multiply = (m: number) => (s: () => number) => () => s() * m; - test("no ops", () => { - createRoot(() => { - const [s, set] = createSignal(0), - r = createMemo(transform(s)); - expect(r()).toBe(0); - set(2); - expect(r()).toBe(2); - }); - }); - - test("single op", () => { - createRoot(() => { - const [s, set] = createSignal(1), - r = createMemo(transform(s, multiply(2))); - expect(r()).toBe(2); - set(2); - expect(r()).toBe(4); - }); - }); - - test("multiple ops", () => { - createRoot(() => { - const [s, set] = createSignal(1), - r = createMemo(transform(s, multiply(2), multiply(3))); - expect(r()).toBe(6); - set(2); - expect(r()).toBe(12); - }); - }); -}); diff --git a/packages/solid-rx/tsconfig.json b/packages/solid-rx/tsconfig.json deleted file mode 100644 index 9dafbe55f..000000000 --- a/packages/solid-rx/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./types" - }, - "include": [ - "./src" - ] - } \ No newline at end of file diff --git a/packages/solid-ssr/README.md b/packages/solid-ssr/README.md index feef2a49a..5faf9713a 100644 --- a/packages/solid-ssr/README.md +++ b/packages/solid-ssr/README.md @@ -1,10 +1,36 @@ # `solid-ssr` -This library provides tools to help with SSR. So far it's a simple Static Generator. This project is still in progress. +This library provides tools to help with SSR. So far it's a simple Static Generator. But will add more tools in the future. -Look at the examples to best understand how to use it. Important I make use of conditional export maps here in node. You need the latest version of Node 14 (or latest Node 12 but this example doesn't work in Node 12, sorry). +## solid-ssr/static + +This is a simple runner that renders files and writes them to disk. It exports a single export that can be used in either CJS or ESM. + +```js +renderStatic( + PAGES.map(p => ({ + entry: pathToServer, + output: path.join(pathToPublic, `${p}.html`), + url: `/${p}` + })) +); +``` + +Each entry expects 3 values: +* entry: path to the server entry point you will be using to render the page +* output: path to the location of the html file you wish to write +* url: the url that will be passed on the faux request object -### Examples +Entry files should be async functions that return the html string in the form: +```js +export default async function(req) { + return "My Page" +} +``` + +## Examples + +Look at the examples to best understand how to use it. Important I make use of conditional export maps here in node. You need the latest version of Node 14 (or latest Node 12 but this example doesn't work in Node 12, sorry). There are 4 examples all using the same shared source. It is an isomorphically routed tab navigation using Suspense, Lazy Components, and Data Fetching. They are just compiled differently and have slightly different server entry points. diff --git a/packages/solid-ssr/package.json b/packages/solid-ssr/package.json index b87f3573a..f54822c80 100644 --- a/packages/solid-ssr/package.json +++ b/packages/solid-ssr/package.json @@ -1,15 +1,19 @@ { "name": "solid-ssr", - "description": "Patches node to work with Solid's SSR", - "version": "0.26.5", + "description": "Utilities to help with SSR", + "version": "1.0.0-rc.1", "author": "Ryan Carniato", "license": "MIT", "repository": { "type": "git", - "url": "https://github.com/solidui/solid/solid-ssr" + "url": "https://github.com/solidjs/solid/blob/main/packages/solid-ssr" + }, + "exports": { + "./static": { + "require": "./static/index.js", + "import": "./static/index.mjs" + } }, - "main": "index.js", - "types": "index.d.ts", "files": [ "static" ], @@ -24,12 +28,9 @@ "start:example:stream": "node examples/stream/lib/index.js" }, "devDependencies": { - "babel-preset-solid": "^0.26.5", + "babel-preset-solid": "^1.0.0-rc.1", "express": "^4.17.1", - "solid-js": "^0.26.5", + "solid-js": "^1.0.0-rc.1", "solid-styled-components": "^0.26.3" - }, - "peerDependencies": { - "solid-js": "*" } } diff --git a/packages/solid-ssr/static/index.mjs b/packages/solid-ssr/static/index.mjs new file mode 100644 index 000000000..e89eff460 --- /dev/null +++ b/packages/solid-ssr/static/index.mjs @@ -0,0 +1,20 @@ +import { resolve, dirname } from "path"; +import { fileURLToPath } from "url"; +import { execFile } from "child_process"; +import { promisify } from "util"; + +const exec = promisify(execFile); +const __dirname = dirname(fileURLToPath(import.meta.url)); +const pathToRunner = resolve(__dirname, "writeToDisk.mjs"); + +async function run({ entry, output, url }) { + const { stdout, stderr } = await exec("node", [pathToRunner, entry, output, url, "--trace-warnings"]); + if (stdout.length) console.log(stdout); + if (stderr.length) console.log(stderr); +} + +export default async function renderStatic(config) { + if (Array.isArray(config)) { + await Promise.all(config.map(run)); + } else await run(config); +}; \ No newline at end of file diff --git a/packages/solid-ssr/static/writeToDisk.js b/packages/solid-ssr/static/writeToDisk.js index 6cb85aded..492d414d5 100644 --- a/packages/solid-ssr/static/writeToDisk.js +++ b/packages/solid-ssr/static/writeToDisk.js @@ -1,8 +1,11 @@ const fs = require("fs"); +const path = require("path"); const server = require(process.argv[2]); async function write() { const res = await server({ url: process.argv[4] }); - fs.writeFile(process.argv[3], res, () => process.exit(0)); + fs.mkdir(path.dirname(process.argv[3]), {recursive: true}, () => + fs.writeFile(process.argv[3], res, () => process.exit(0)) + ); } write(); diff --git a/packages/solid-ssr/static/writeToDisk.mjs b/packages/solid-ssr/static/writeToDisk.mjs new file mode 100644 index 000000000..d4cee000f --- /dev/null +++ b/packages/solid-ssr/static/writeToDisk.mjs @@ -0,0 +1,11 @@ +import { dirname } from "path"; +import { writeFile, mkdir } from "fs" + +async function write() { + const server = (await import(process.argv[2])).default; + const res = await server({ url: process.argv[4] }); + mkdir(dirname(process.argv[3]), {recursive: true}, () => + writeFile(process.argv[3], res, () => process.exit(0)) + ); +} +write(); \ No newline at end of file diff --git a/packages/solid/README.md b/packages/solid/README.md index 106f12a6f..8d748f10a 100644 --- a/packages/solid/README.md +++ b/packages/solid/README.md @@ -1,7 +1,7 @@ -## Solid
    +## Solid
    -[![Build Status](https://github.com/solidui/solid/workflows/Solid%20CI/badge.svg)](https://github.com/solidui/solid/actions/workflows/main-ci.yml) -[![Coverage Status](https://img.shields.io/coveralls/github/solidui/solid.svg?style=flat)](https://coveralls.io/github/solidui/solid?branch=main) +[![Build Status](https://github.com/solidjs/solid/workflows/Solid%20CI/badge.svg)](https://github.com/solidjs/solid/actions/workflows/main-ci.yml) +[![Coverage Status](https://img.shields.io/coveralls/github/solidjs/solid.svg?style=flat)](https://coveralls.io/github/solidjs/solid?branch=main) [![NPM Version](https://img.shields.io/npm/v/solid-js.svg?style=flat)](https://www.npmjs.com/package/solid-js) [![](https://img.shields.io/npm/dm/solid-js.svg?style=flat)](https://www.npmjs.com/package/solid-js) [![Discord](https://img.shields.io/discord/722131463138705510)](https://discord.com/invite/solidjs) @@ -20,6 +20,7 @@ Solid is a declarative JavaScript library for creating user interfaces. It does - Small! Completely tree-shakeable Solid's compiler will only include parts of the library you use. - Supports and is built on TypeScript. - Supports modern features like JSX, Fragments, Context, Portals, Suspense, Streaming SSR, Progressive Hydration, Error Boundaries and Concurrent Rendering. +- Works in serverless environments including AWS Lambda and Cloudflare Workers. - Webcomponent friendly and can author Custom Elements - Context API that spans Custom Elements - Implicit event delegation with Shadow DOM Retargeting @@ -65,7 +66,7 @@ Want to see what code Solid generates: ### [Try it Online](https://playground.solidjs.com/) -## Getting Started +## Quick Start > _`npm init solid ` is available with npm 6+._ @@ -104,23 +105,23 @@ For TypeScript remember to set your TSConfig to handle Solid's JSX by: ## Documentation -- [Reactivity](https://github.com/solidui/solid/blob/main/documentation/reactivity.md) -- [State](https://github.com/solidui/solid/blob/main/documentation/state.md) -- [JSX Rendering](https://github.com/solidui/solid/blob/main/documentation/rendering.md) -- [Components](https://github.com/solidui/solid/blob/main/documentation/components.md) -- [Styling](https://github.com/solidui/solid/blob/main/documentation/styling.md) -- [Context](https://github.com/solidui/solid/blob/main/documentation/context.md) -- [Suspense](https://github.com/solidui/solid/blob/main/documentation/suspense.md) -- [API](https://github.com/solidui/solid/blob/main/documentation/api.md) -- [FAQ](https://github.com/solidui/solid/blob/main/documentation/faq.md) -- [Comparison with other Libraries](https://github.com/solidui/solid/blob/main/documentation/comparison.md) -- [Storybook](https://github.com/solidui/solid/blob/main/documentation/storybook.md) +> This is the documentation for the 1.0.0 RC. THese are significantly slimmed down from what was here previously as we are moving things to the new Docs site. If you wish to see the older docs in the mean time [look here](https://github.com/solidjs/solid/tree/0.x/#documentation). -## Resources +- [API](https://github.com/solidjs/solid/blob/main/documentation/api.md) +- [FAQ](https://github.com/solidjs/solid/blob/main/documentation/faq.md) +- [Comparison with other Libraries](https://github.com/solidjs/solid/blob/main/documentation/comparison.md) -- [Examples](https://github.com/solidui/solid/blob/main/documentation/resources/examples.md) -- [Articles](https://github.com/solidui/solid/blob/main/documentation/resources/articles.md) -- [Projects](https://github.com/solidui/solid/blob/main/documentation/resources/projects.md) +### Guides +- [Getting Started](https://github.com/solidjs/solid/blob/main/documentation/guides/getting-started.md) +- [Reactivity](https://github.com/solidjs/solid/blob/main/documentation/guides/reactivity.md) +- [Rendering](https://github.com/solidjs/solid/blob/main/documentation/guides/rendering.md) +- [SSR](https://github.com/solidjs/solid/blob/main/documentation/guides/server.md) + +### Resources + +- [Examples](https://github.com/solidjs/solid/blob/main/documentation/resources/examples.md) +- [Articles](https://github.com/solidjs/solid/blob/main/documentation/resources/articles.md) +- [Projects](https://github.com/solidjs/solid/blob/main/documentation/resources/projects.md) ## No Compilation? @@ -148,7 +149,7 @@ You can run them straight from the browser using SkyPack: ``` -Remember you still need the corresponding DOM Expressions library for these to work with TypeScript. Tagged Template Literals [Lit DOM Expressions](https://github.com/solidui/dom-expressions/tree/main/packages/lit-dom-expressions) or HyperScript with [Hyper DOM Expressions](https://github.com/solidui/dom-expressions/tree/main/packages/hyper-dom-expressions). +Remember you still need the corresponding DOM Expressions library for these to work with TypeScript. Tagged Template Literals [Lit DOM Expressions](https://github.com/solidjs/dom-expressions/tree/main/packages/lit-dom-expressions) or HyperScript with [Hyper DOM Expressions](https://github.com/solidjs/dom-expressions/tree/main/packages/hyper-dom-expressions). ## Browser Support @@ -162,7 +163,7 @@ Come chat with us on [Discord](https://discord.com/invite/solidjs) ### Contributors - + ### Open Collective @@ -195,7 +196,3 @@ Become a sponsor and get your logo on our README on GitHub with a link to your s - -## Status - -Solid is mostly feature complete for its v1.0.0 release. The next releases will be mostly bug fixes and API tweaks on the road to stability. diff --git a/packages/solid/dev/package.json b/packages/solid/dev/package.json deleted file mode 100644 index 1a62a344b..000000000 --- a/packages/solid/dev/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "solid-js/dev", - "main": "./dist/dev.cjs", - "module": "./dist/dev.js", - "type": "module", - "sideEffects": false -} diff --git a/packages/solid/package.json b/packages/solid/package.json index f515214ef..703f972ba 100644 --- a/packages/solid/package.json +++ b/packages/solid/package.json @@ -1,13 +1,13 @@ { "name": "solid-js", "description": "A declarative JavaScript library for building user interfaces.", - "version": "0.26.5", + "version": "1.0.0-rc.1", "author": "Ryan Carniato", "license": "MIT", - "homepage": "https://github.com/solidui/solid#readme", + "homepage": "https://github.com/solidjs/solid#readme", "repository": { "type": "git", - "url": "https://github.com/solidui/solid" + "url": "https://github.com/solidjs/solid" }, "main": "dist/static.cjs", "module": "dist/static.js", @@ -15,6 +15,7 @@ "./dist/static.cjs": "./dist/solid.cjs", "./dist/static.js": "./dist/solid.js" }, + "unpkg": "./dist/solid.cjs", "types": "types/index.d.ts", "sideEffects": false, "type": "module", @@ -27,12 +28,14 @@ "html/dist", "html/types", "types", - "dom", - "dev", "jsx-runtime.d.ts" ], "exports": { ".": { + "development": { + "import": "./dist/dev.js", + "require": "./dist/dev.cjs" + }, "browser": { "import": "./dist/solid.js", "require": "./dist/solid.cjs" @@ -44,7 +47,12 @@ "import": "./dist/solid.js", "require": "./dist/solid.cjs" }, + "./dist/*": "./dist/*", "./web": { + "development": { + "import": "./web/dist/dev.js", + "require": "./web/dist/dev.cjs" + }, "browser": { "import": "./web/dist/web.js", "require": "./web/dist/web.cjs" @@ -66,16 +74,11 @@ "import": "./html/dist/html.js", "require": "./html/dist/html.cjs" }, - "./html/dist/*": "./html/dist/*", - "./dev": { - "import": "./dev/dist/dev.js", - "require": "./dev/dist/dev.cjs" - }, - "./dev/dist/*": "./dev/dist/*" + "./html/dist/*": "./html/dist/*" }, "scripts": { "prebuild": "npm run clean", - "clean": "rimraf dist/ types/ coverage/ dev/dist/ web/dist/ web/types/ h/dist/ h/types/ html/dist/ html/types/", + "clean": "rimraf dist/ types/ coverage/ web/dist/ web/types/ h/dist/ h/types/ html/dist/ html/types/", "build": "npm-run-all -cnl build:*", "build:link": "symlink-dir . node_modules/solid-js", "build:js": "ncp ../../node_modules/dom-expressions/src/jsx.d.ts ./src/jsx.d.ts && rollup -c", diff --git a/packages/solid/rollup.config.js b/packages/solid/rollup.config.js index a98c9e6c6..85932c966 100644 --- a/packages/solid/rollup.config.js +++ b/packages/solid/rollup.config.js @@ -25,6 +25,7 @@ const plugins = [ ] }), cleanup({ + comments: ["some", /PURE/], extensions: [".js", ".ts"] }) ]; @@ -76,11 +77,11 @@ export default [ input: "src/index.ts", output: [ { - file: "dev/dist/dev.cjs", + file: "dist/dev.cjs", format: "cjs" }, { - file: "dev/dist/dev.js", + file: "dist/dev.js", format: "es" } ], @@ -100,6 +101,10 @@ export default [ ], external: ["solid-js"], plugins: [ + replace({ + '"_DX_DEV_"': false, + delimiters: ["", ""] + }), copy({ targets: [ { @@ -110,6 +115,43 @@ export default [ ] }) ].concat(plugins) + }, { + input: "web/server/index.ts", + output: [ + { + file: "web/dist/server.cjs", + format: "cjs" + }, + { + file: "web/dist/server.js", + format: "es" + } + ], + external: ["solid-js", "stream"], + plugins: [ + copy({ + targets: [ + { + src: ["../../node_modules/dom-expressions/src/server.d.ts"], + dest: "./web/server" + } + ] + }) + ].concat(plugins) + }, { + input: "web/src/index.ts", + output: [ + { + file: "web/dist/dev.cjs", + format: "cjs" + }, + { + file: "web/dist/dev.js", + format: "es" + } + ], + external: ["solid-js"], + plugins }, { input: "html/src/index.ts", @@ -142,29 +184,5 @@ export default [ ], external: ["solid-js/web"], plugins - }, - { - input: "web/server/index.ts", - output: [ - { - file: "web/dist/server.cjs", - format: "cjs" - }, - { - file: "web/dist/server.js", - format: "es" - } - ], - external: ["solid-js", "stream"], - plugins: [ - copy({ - targets: [ - { - src: ["../../node_modules/dom-expressions/src/server.d.ts"], - dest: "./web/server" - } - ] - }) - ].concat(plugins) } ]; diff --git a/packages/solid/src/index.ts b/packages/solid/src/index.ts index fc9250ad5..447543439 100644 --- a/packages/solid/src/index.ts +++ b/packages/solid/src/index.ts @@ -8,7 +8,6 @@ export { createSelector, createMemo, createResource, - getListener, onMount, onCleanup, onError, @@ -19,31 +18,44 @@ export { createContext, useContext, children, + getListener, getOwner, runWithOwner, equalFn } from "./reactive/signal"; -export type { Resource } from "./reactive/signal"; +export type { Accessor, Resource, ResourceReturn, Context } from "./reactive/signal"; export { createState, unwrap, $RAW } from "./reactive/state"; -export type { State, SetStateFunction } from "./reactive/state"; +export type { + State, + SetStateFunction, + NotWrappable, + StateNode, + StateSetter, + StatePathRange, + ArrayFilterFn, + Part, + Next +} from "./reactive/state"; export * from "./reactive/mutable"; - -export { reconcile, produce } from "./reactive/stateModifiers"; - +export * from "./reactive/observable"; +export * from "./reactive/stateModifiers"; export * from "./reactive/scheduler"; export * from "./reactive/array"; export * from "./render"; -export type { JSX } from "./jsx"; + +import type { JSX } from "./jsx"; +type JSXElement = JSX.Element; +export type { JSXElement, JSX }; // mock server endpoint for dom-expressions export function awaitSuspense() {} // dev import { writeSignal, serializeGraph } from "./reactive/signal"; -let DEV: { writeSignal: typeof writeSignal, serializeGraph: typeof serializeGraph }; +let DEV: { writeSignal: typeof writeSignal; serializeGraph: typeof serializeGraph }; if ("_SOLID_DEV_") { - DEV = { writeSignal, serializeGraph } + DEV = { writeSignal, serializeGraph }; } export { DEV }; diff --git a/packages/solid/src/reactive/array.ts b/packages/solid/src/reactive/array.ts index 8da39932d..1041aeb4f 100644 --- a/packages/solid/src/reactive/array.ts +++ b/packages/solid/src/reactive/array.ts @@ -1,12 +1,12 @@ -import { onCleanup, createRoot, untrack, createSignal, Owner } from "./signal"; +import { onCleanup, createRoot, untrack, createSignal, Owner, Accessor } from "./signal"; const FALLBACK = Symbol("fallback"); // Modified version of mapSample from S-array[https://github.com/adamhaile/S-array] by Adam Haile export function mapArray( - list: () => readonly T[], - mapFn: (v: T, i: () => number) => U, - options: { fallback?: () => any } = {} + list: Accessor, + mapFn: (v: T, i: Accessor) => U, + options: { fallback?: Accessor } = {} ): () => U[] { let items: (T | typeof FALLBACK)[] = [], mapped: U[] = [], @@ -135,9 +135,9 @@ export function mapArray( } export function indexArray( - list: () => readonly T[], - mapFn: (v: () => T, i: number) => U, - options: { fallback?: () => any } = {} + list: Accessor, + mapFn: (v: Accessor, i: number) => U, + options: { fallback?: Accessor } = {} ): () => U[] { let items: (T | typeof FALLBACK)[] = [], mapped: U[] = [], @@ -196,7 +196,7 @@ export function indexArray( }); function mapper(disposer: () => void) { disposers[i] = disposer; - const [s, set] = createSignal(newItems[i], false); + const [s, set] = createSignal(newItems[i]); signals[i] = set; return mapFn(s, i); } diff --git a/packages/solid-rx/src/observable.ts b/packages/solid/src/reactive/observable.ts similarity index 72% rename from packages/solid-rx/src/observable.ts rename to packages/solid/src/reactive/observable.ts index fb99fae2c..dacc11887 100644 --- a/packages/solid-rx/src/observable.ts +++ b/packages/solid/src/reactive/observable.ts @@ -1,36 +1,39 @@ -import { createComputed, untrack } from "solid-js"; - -const SymbolCopy = Symbol as any; -const $$observable = (() => (typeof SymbolCopy === 'function' && SymbolCopy.observable) || '@@observable')(); - -export type ObservableObserver = - | ((v: T) => void) - | { - next: (v: T) => void; - error?: (v: any) => void; - complete?: (v: boolean) => void; - }; -export function observable(input: () => T) { - return { - subscribe(observer: ObservableObserver) { - if (!(observer instanceof Object) || observer == null) { - throw new TypeError("Expected the observer to be an object."); - } - const handler = "next" in observer ? observer.next : observer; - let complete = false; - createComputed(() => { - const v = input(); - if (complete) return; - untrack(() => handler(v)); - }); - return { - unsubscribe() { - complete = true; - } - }; - }, - [$$observable]() { - return this; - } - }; -} +import { createComputed, untrack, Accessor } from "./signal"; + +function getSymbol() { + const SymbolCopy = Symbol as any; + return SymbolCopy.observable || '@@observable'; +} + +export type ObservableObserver = + | ((v: T) => void) + | { + next: (v: T) => void; + error?: (v: any) => void; + complete?: (v: boolean) => void; + }; +export function observable(input: Accessor) { + const $$observable = getSymbol(); + return { + subscribe(observer: ObservableObserver) { + if (!(observer instanceof Object) || observer == null) { + throw new TypeError("Expected the observer to be an object."); + } + const handler = "next" in observer ? observer.next : observer; + let complete = false; + createComputed(() => { + if (complete) return; + const v = input(); + untrack(() => handler(v)); + }); + return { + unsubscribe() { + complete = true; + } + }; + }, + [$$observable]() { + return this; + } + }; +} \ No newline at end of file diff --git a/packages/solid/src/reactive/signal.ts b/packages/solid/src/reactive/signal.ts index 1ba150a7c..78ff32bd2 100644 --- a/packages/solid/src/reactive/signal.ts +++ b/packages/solid/src/reactive/signal.ts @@ -3,7 +3,9 @@ import { requestCallback, Task } from "./scheduler"; import { sharedConfig } from "../render/hydration"; import type { JSX } from "../jsx"; +export type Accessor = () => T; export const equalFn = (a: T, b: T) => a === b; +const signalOptions = { equals: equalFn }; let ERROR: symbol | null = null; let runEffects = runQueue; @@ -99,23 +101,25 @@ export function createRoot(fn: (dispose: () => void) => T, detachedOwner?: Ow return result!; } -export function createSignal(): [get: () => T | undefined, set: (v?: U) => U]; +export function createSignal(): [ + get: Accessor, + set: (v?: U) => U +]; export function createSignal( value: T, - areEqual?: boolean | ((prev: T, next: T) => boolean), - options?: { name?: string; internal?: boolean } -): [get: () => T, set: (v: T) => T]; + options?: { equals?: false | ((prev: T, next: T) => boolean); name?: string; internal?: boolean } +): [get: Accessor, set: (v: T) => T]; export function createSignal( value?: T, - areEqual: boolean | ((prev: T, next: T) => boolean) = true, - options?: { name?: string; internal?: boolean } -): [get: () => T, set: (v: T) => T] { + options?: { equals?: false | ((prev: T, next: T) => boolean); name?: string; internal?: boolean } +): [get: Accessor, set: (v: T) => T] { + options = options ? Object.assign({}, signalOptions, options) : signalOptions; const s: Signal = { value, observers: null, observerSlots: null, pending: NOTPENDING, - comparator: areEqual ? (typeof areEqual === "function" ? areEqual : equalFn) : undefined + comparator: options.equals || undefined }; if ("_SOLID_DEV_" && (!options || !options.internal)) s.name = registerGraph((options && options.name) || hashValue(value), s as { value: unknown }); @@ -123,69 +127,229 @@ export function createSignal( return [readSignal.bind(s), writeSignal.bind(s)]; } -export function createComputed(fn: (v: T) => T, value: T): void; export function createComputed(fn: (v?: T) => T | undefined): void; -export function createComputed(fn: (v?: T) => T, value?: T): void { - updateComputation(createComputation(fn, value, true)); +export function createComputed(fn: (v: T) => T, value: T, options?: { name?: string }): void; +export function createComputed(fn: (v?: T) => T, value?: T, options?: { name?: string }): void { + updateComputation(createComputation(fn, value, true, "_SOLID_DEV_" ? options : undefined)); } -export function createRenderEffect(fn: (v: T) => T, value: T): void; export function createRenderEffect(fn: (v?: T) => T | undefined): void; -export function createRenderEffect(fn: (v?: T) => T, value?: T): void { - updateComputation(createComputation(fn, value, false)); +export function createRenderEffect(fn: (v: T) => T, value: T, options?: { name?: string }): void; +export function createRenderEffect( + fn: (v?: T) => T, + value?: T, + options?: { name?: string } +): void { + updateComputation(createComputation(fn, value, false, "_SOLID_DEV_" ? options : undefined)); } -export function createEffect(fn: (v: T) => T, value: T): void; export function createEffect(fn: (v?: T) => T | undefined): void; -export function createEffect(fn: (v?: T) => T, value?: T): void { +export function createEffect(fn: (v: T) => T, value: T, options?: { name?: string }): void; +export function createEffect(fn: (v?: T) => T, value?: T, options?: { name?: string }): void { runEffects = runUserEffects; - const c = createComputation(fn, value, false), + const c = createComputation(fn, value, false, "_SOLID_DEV_" ? options : undefined), s = SuspenseContext && lookup(Owner, SuspenseContext.id); if (s) c.suspense = s; c.user = true; Effects && Effects.push(c); } -export function resumeEffects(e: Computation[]) { - Transition && (Transition.running = true); - Effects!.push.apply(Effects, e); - e.length = 0; -} - export function createMemo( fn: (v?: T) => T, value?: undefined, - areEqual?: boolean | ((prev: T, next: T) => boolean) -): () => T; + options?: { equals?: false | ((prev: T, next: T) => boolean); name?: string } +): Accessor; export function createMemo( fn: (v: T) => T, value: T, - areEqual?: boolean | ((prev: T, next: T) => boolean) -): () => T; + options?: { equals?: false | ((prev: T, next: T) => boolean); name?: string } +): Accessor; export function createMemo( fn: (v?: T) => T, value?: T, - areEqual: boolean | ((prev: T, next: T) => boolean) = true -): () => T { - const c: Partial> = createComputation(fn, value, true); + options?: { equals?: false | ((prev: T, next: T) => boolean); name?: string } +): Accessor { + options = options ? Object.assign({}, signalOptions, options) : signalOptions; + const c: Partial> = createComputation( + fn, + value, + true, + "_SOLID_DEV_" ? options : undefined + ); c.pending = NOTPENDING; c.observers = null; c.observerSlots = null; c.state = 0; - c.comparator = areEqual ? (typeof areEqual === "function" ? areEqual : equalFn) : undefined; + c.comparator = options.equals || undefined; updateComputation(c as Memo); return readSignal.bind(c as Memo); } -export function createDeferred(source: () => T, options?: { timeoutMs: number }) { +export interface Resource extends Accessor { + loading: boolean; + error: any; +} + +export type ResourceReturn = [ + Resource, + { + mutate: (v: T | undefined) => T | undefined; + refetch: () => void; + } +]; + +export function createResource( + fetcher: (k: U, getPrev: Accessor) => T | Promise, + options?: { initialValue?: T; name?: string } +): ResourceReturn; +export function createResource( + source: U | false | null | (() => U | false | null), + fetcher: (k: U, getPrev: Accessor) => T | Promise, + options?: { initialValue?: T; name?: string } +): ResourceReturn; +export function createResource( + source: + | U + | false + | true + | null + | (() => U | false | null) + | ((k: U, getPrev: Accessor) => T | Promise), + fetcher?: ((k: U, getPrev: Accessor) => T | Promise) | { initialValue?: T }, + options: { initialValue?: T; name?: string } = {} +): ResourceReturn { + if (arguments.length === 2) { + if (typeof fetcher === "object") { + options = fetcher; + fetcher = source as (k: U, getPrev: Accessor) => T | Promise; + source = true; + } + } else if (arguments.length === 1) { + fetcher = source as (k: U, getPrev: Accessor) => T | Promise; + source = true; + } + const contexts = new Set(), + [s, set] = createSignal(options!.initialValue), + [track, trigger] = createSignal(undefined, { equals: false }), + [loading, setLoading] = createSignal(false), + [error, setError] = createSignal(); + + let err: any = undefined, + pr: Promise | null = null, + initP: Promise | null = null, + id: string | null = null, + loadedUnderTransition = false, + dynamic = typeof source === "function"; + + if (sharedConfig.context) { + id = `${sharedConfig.context!.id}${sharedConfig.context!.count++}`; + if (sharedConfig.context.loadResource) { + initP = sharedConfig.context.loadResource!(id!); + } else if (sharedConfig.resources && id && id in sharedConfig.resources) { + initP = sharedConfig.resources![id]; + delete sharedConfig.resources![id]; + } + } + function loadEnd(p: Promise | null, v: T, e?: any) { + if (pr === p) { + setError((err = e)); + pr = null; + if (Transition && p && loadedUnderTransition) { + Transition.promises.delete(p); + loadedUnderTransition = false; + runUpdates(() => { + Transition!.running = true; + if (!Transition!.promises.size) { + Effects!.push.apply(Effects, Transition!.effects); + Transition!.effects = []; + } + completeLoad(v); + }, false); + } else completeLoad(v); + } + return v; + } + function completeLoad(v: T) { + batch(() => { + set(v); + setLoading(false); + for (let c of contexts.keys()) c.decrement!(); + contexts.clear(); + }); + } + + function read() { + const c = SuspenseContext && lookup(Owner, SuspenseContext.id), + v = s(); + if (err) throw err; + if (Listener && !Listener.user && c) { + createComputed(() => { + track(); + if (pr) { + if (c.resolved && Transition) Transition.promises.add(pr!); + else if (!contexts.has(c)) { + c.increment!(); + contexts.add(c); + } + } + }); + } + return v; + } + function load() { + setError((err = undefined)); + let lookup = dynamic ? (source as () => U)() : (source as U); + loadedUnderTransition = (Transition && Transition.running) as boolean; + if (lookup == null || (lookup as any) === false) { + loadEnd(pr, untrack(s)!); + return; + } + if (Transition && pr) Transition.promises.delete(pr); + const p = + initP || (fetcher as (k: U, getPrev: Accessor) => T | Promise)(lookup, s); + initP = null; + if (typeof p !== "object" || !("then" in p)) { + loadEnd(pr, p); + return; + } + pr = p; + batch(() => { + setLoading(true); + trigger(); + }); + p.then( + v => loadEnd(p as Promise, v), + e => loadEnd(p as Promise, e, e) + ); + } + Object.defineProperties(read, { + loading: { + get() { + return loading(); + } + }, + error: { + get() { + return error(); + } + } + }); + if (dynamic) createComputed(load); + else load(); + return [read as Resource, { refetch: load, mutate: set }]; +} + +export function createDeferred( + source: Accessor, + options?: { equals?: false | ((prev: T, next: T) => boolean); name?: string; timeoutMs?: number } +) { let t: Task, timeout = options ? options.timeoutMs : undefined; - const [deferred, setDeferred] = createSignal(); const node = createComputation( () => { if (!t || !t.fn) t = requestCallback( - () => setDeferred(node.value), + () => setDeferred(node.value as T), timeout !== undefined ? { timeout } : undefined ); return source(); @@ -193,15 +357,17 @@ export function createDeferred(source: () => T, options?: { timeoutMs: number undefined, true ); + const [deferred, setDeferred] = createSignal(node.value as T, options); updateComputation(node); - setDeferred(node.value); + setDeferred(node.value as T); return deferred; } export function createSelector( - source: () => T, - fn: (a: U, b: T) => boolean = equalFn as any -) { + source: Accessor, + fn: (a: U, b: T) => boolean = equalFn as any, + options?: { name?: string } +): (key: U) => boolean { let subs = new Map>>(); const node = createComputation( (p: T | undefined) => { @@ -218,7 +384,8 @@ export function createSelector( return v; }, undefined, - true + true, + "_SOLID_DEV_" ? options : undefined ); updateComputation(node); return (key: U) => { @@ -259,30 +426,7 @@ export function batch(fn: () => T): T { return result; } -export function useTransition(): [() => boolean, (fn: () => void, cb?: () => void) => void] { - return [ - transPending, - (fn: () => void, cb?: () => void) => { - if (SuspenseContext) { - Transition || - (Transition = { - sources: new Set(), - effects: [], - promises: new Set(), - disposed: new Set(), - running: true, - cb: [] - }); - cb && Transition.cb.push(cb); - Transition.running = true; - } - batch(fn); - if (!SuspenseContext && cb) cb(); - } - ]; -} - -export function untrack(fn: () => T): T { +export function untrack(fn: Accessor): T { let result: T, listener = Listener; @@ -293,27 +437,24 @@ export function untrack(fn: () => T): T { return result; } -type ReturnTypeArray = { [P in keyof T]: T[P] extends () => infer U ? U : never }; -export function on T>, U>( - ...args: X["length"] extends 1 - ? [w: () => T, fn: (v: T, prev: T | undefined, prevResults?: U) => U] - : [...w: X, fn: (v: ReturnTypeArray, prev: ReturnTypeArray | [], prevResults?: U) => U] -): (prev?: U) => U { - const fn = args.pop() as (v: T | Array, p?: T | Array, r?: U) => U; - let deps: (() => T) | Array<() => T>; - let isArray = true; - let prev: T | T[]; - if (args.length < 2) { - deps = args[0] as () => T; - isArray = false; - } else deps = args as Array<() => T>; +export function on( + deps: Array<() => T> | (() => T), + fn: (value: Array | T, prev: Array | T, prevResults?: U) => U, + options?: { defer?: boolean } +): (prev?: U) => U | undefined { + let isArray = Array.isArray(deps); + let prev: Array | T; + let defer = options && options.defer; return prevResult => { let value: T | Array; if (isArray) { value = []; - if (!prev) prev = []; for (let i = 0; i < deps.length; i++) value.push((deps as Array<() => T>)[i]()); } else value = (deps as () => T)(); + if (defer) { + defer = false; + return undefined; + } const result = untrack(() => fn!(value, prev, prevResult)); prev = value; return result; @@ -361,6 +502,37 @@ export function runWithOwner(o: Owner, fn: () => any) { } } +// Transitions +export function useTransition(): [Accessor, (fn: () => void, cb?: () => void) => void] { + return [ + transPending, + (fn: () => void, cb?: () => void) => { + if (SuspenseContext) { + Transition || + (Transition = { + sources: new Set(), + effects: [], + promises: new Set(), + disposed: new Set(), + running: true, + cb: [] + }); + cb && Transition.cb.push(cb); + Transition.running = true; + } + batch(fn); + if (!SuspenseContext && cb) cb(); + } + ]; +} + +export function resumeEffects(e: Computation[]) { + Transition && (Transition.running = true); + Effects!.push.apply(Effects, e); + e.length = 0; +} + +// Dev export function devComponent(Comp: (props: T) => JSX.Element, props: T) { const c: Partial> = createComputation( () => untrack(() => Comp(props)), @@ -373,21 +545,28 @@ export function devComponent(Comp: (props: T) => JSX.Element, props: T) { c.state = 0; c.componentName = Comp.name; updateComputation(c as Memo); - return c.tValue !== undefined ? c.tValue : c.value;; + return c.tValue !== undefined ? c.tValue : c.value; } -export function hashValue(v: any) { - const s = new Set() - return "s" + (typeof v === "string" ? hash(v) : hash(JSON.stringify(v, (k, v) => { - if (typeof v === 'object' && v != null) { - if (s.has(v)) return; - s.add(v); - } - return v; - }) || "")); +export function hashValue(v: any): string { + const s = new Set(); + return ( + "s" + + (typeof v === "string" + ? hash(v) + : hash( + JSON.stringify(v, (k, v) => { + if (typeof v === "object" && v != null) { + if (s.has(v)) return; + s.add(v); + } + return v; + }) || "" + )) + ); } -export function registerGraph(name: string, value: { value: unknown }) { +export function registerGraph(name: string, value: { value: unknown }): string { let tryName = name; if (Owner) { let i = 0; @@ -427,7 +606,7 @@ export function useContext(context: Context): T { return lookup(Owner, context.id) || context.defaultValue; } -export function children(fn: () => any) { +export function children(fn: Accessor): Accessor { const children = createMemo(fn); return createMemo(() => resolveChildren(children())); } @@ -451,160 +630,7 @@ export function getSuspenseContext() { return SuspenseContext || (SuspenseContext = createContext({})); } -export interface Resource { - (): T | undefined; - loading: boolean; - error: any; -} - -type ResourceReturn = [ - Resource, - { - 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( - fn: U | false | (() => U | false), - fetcher: (k: U, getPrev: () => T | undefined) => T | Promise, - options?: { initialValue?: T } -): ResourceReturn; -export function createResource( - fn: - | U - | true - | false - | (() => U | false) - | ((k: U, getPrev: () => T | undefined) => T | Promise), - fetcher?: ((k: U, getPrev: () => T | undefined) => T | Promise) | { initialValue?: T }, - options: { initialValue?: T } = {} -): ResourceReturn { - if (arguments.length === 2) { - if (typeof fetcher === "object") { - options = fetcher; - fetcher = fn as (k: U, getPrev: () => T | undefined) => T | Promise; - fn = true; - } - } else if (arguments.length === 1) { - fetcher = fn as (k: U, getPrev: () => T | undefined) => T | Promise; - fn = true; - } - const contexts = new Set(), - [s, set] = createSignal(options!.initialValue), - [track, trigger] = createSignal(undefined, false), - [loading, setLoading] = createSignal(false), - [error, setError] = createSignal(); - - let err: any = undefined, - pr: Promise | null = null, - initP: Promise | null = null, - id: string | null = null, - loadedUnderTransition = false, - dynamic = typeof fn === "function"; - - if (sharedConfig.context) { - id = `${sharedConfig.context!.id}${sharedConfig.context!.count++}`; - if (sharedConfig.context.loadResource) { - initP = sharedConfig.context.loadResource!(id!); - } else if (sharedConfig.resources && id && id in sharedConfig.resources) { - initP = sharedConfig.resources![id]; - delete sharedConfig.resources![id]; - } - } - function loadEnd(p: Promise | null, v: T, e?: any) { - if (pr === p) { - setError((err = e)); - pr = null; - if (Transition && p && loadedUnderTransition) { - Transition.promises.delete(p); - loadedUnderTransition = false; - runUpdates(() => { - Transition!.running = true; - if (!Transition!.promises.size) { - Effects!.push.apply(Effects, Transition!.effects); - Transition!.effects = []; - } - completeLoad(v); - }, false); - } else completeLoad(v); - } - return v; - } - function completeLoad(v: T) { - batch(() => { - set(v); - setLoading(false); - for (let c of contexts.keys()) c.decrement!(); - contexts.clear(); - }); - } - - function read() { - const c = SuspenseContext && lookup(Owner, SuspenseContext.id), - v = s(); - if (err) throw err; - if (Listener && !Listener.user && c) { - createComputed(() => { - track(); - if (pr) { - if (c.resolved && Transition) Transition.promises.add(pr!); - else if (!contexts.has(c)) { - c.increment!(); - contexts.add(c); - } - } - }); - } - return v; - } - function load() { - setError((err = undefined)); - let lookup = dynamic ? (fn as () => U)() : (fn as U); - loadedUnderTransition = (Transition && Transition.running) as boolean; - if (lookup == null || (lookup as any) === false) { - loadEnd(pr, untrack(s)!); - return; - } - if (Transition && pr) Transition.promises.delete(pr); - const p = - initP || (fetcher as (k: U, getPrev: () => T | undefined) => T | Promise)(lookup, s); - initP = null; - if (typeof p !== "object" || !("then" in p)) { - loadEnd(pr, p); - return; - } - pr = p; - batch(() => { - setLoading(true); - trigger(); - }); - p.then( - v => loadEnd(p as Promise, v), - e => loadEnd(p as Promise, e, e) - ); - } - Object.defineProperties(read, { - loading: { - get() { - return loading(); - } - }, - error: { - get() { - return error(); - } - } - }); - if (dynamic) createComputed(load); - else load(); - return [read as Resource, { refetch: load, mutate: set }]; -} - +// Internal function readSignal(this: Signal | Memo) { if ((this as Memo).state && (this as Memo).sources) { const updates = Updates; @@ -665,7 +691,8 @@ export function writeSignal(this: Signal | Memo, value: any, isComp?: } if (Updates!.length > 10e5) { Updates = []; - throw new Error("Potential Infinite Loop Detected."); + if ("_SOLID_DEV_") throw new Error("Potential Infinite Loop Detected."); + throw new Error(); } }, false); } @@ -708,7 +735,12 @@ function runComputation(node: Computation, value: any, time: number) { } } -function createComputation(fn: (v?: T) => T, init: T | undefined, pure: boolean) { +function createComputation( + fn: (v?: T) => T, + init: T | undefined, + pure: boolean, + options?: { name?: string } +) { const c: Computation = { fn, state: STALE, @@ -737,9 +769,10 @@ function createComputation(fn: (v?: T) => T, init: T | undefined, pure: boole } if ("_SOLID_DEV_") c.name = + (options && options.name) || ((Owner as Computation).name || "c") + - "-" + - (Owner.owned || (Owner as Memo).tOwned!).length; + "-" + + (Owner.owned || (Owner as Memo).tOwned!).length; } return c; } diff --git a/packages/solid/src/reactive/state.ts b/packages/solid/src/reactive/state.ts index 85b21e040..fc6ad82b1 100644 --- a/packages/solid/src/reactive/state.ts +++ b/packages/solid/src/reactive/state.ts @@ -108,8 +108,8 @@ export function proxyDescriptor(target: StateNode, property: string | number | s export function createDataNode() { const [s, set] = ("_SOLID_DEV_" - ? createSignal(undefined, false, { internal: true }) - : createSignal(undefined, false)) as [{ (): void; set: () => void }, () => void]; + ? createSignal(undefined, { equals: false, internal: true }) + : createSignal(undefined, { equals: false })) as [{ (): void; set: () => void }, () => void]; s.set = set; return s; } @@ -218,19 +218,19 @@ export function updatePath(current: StateNode, path: any[], traversed: (number | } else setProperty(current, part, value); } -type StateSetter = +export type StateSetter = | Partial | (( prevState: T extends NotWrappable ? T : State, traversed?: (string | number)[] ) => Partial | void); -type StatePathRange = { from?: number; to?: number; by?: number }; +export type StatePathRange = { from?: number; to?: number; by?: number }; -type ArrayFilterFn = (item: T extends any[] ? T[number] : never, index: number) => boolean; +export type ArrayFilterFn = (item: T extends any[] ? T[number] : never, index: number) => boolean; -type Part = keyof T | Array | StatePathRange | ArrayFilterFn; // changing this to "T extends any[] ? ArrayFilterFn : never" results in depth limit errors +export type Part = keyof T | Array | StatePathRange | ArrayFilterFn; // changing this to "T extends any[] ? ArrayFilterFn : never" results in depth limit errors -type Next = K extends keyof T +export type Next = K extends keyof T ? T[K] : K extends Array ? T[K[number]] diff --git a/packages/solid/src/reactive/stateModifiers.ts b/packages/solid/src/reactive/stateModifiers.ts index d398f3dff..430d26e39 100644 --- a/packages/solid/src/reactive/stateModifiers.ts +++ b/packages/solid/src/reactive/stateModifiers.ts @@ -1,6 +1,6 @@ import { setProperty, unwrap, isWrappable, State, NotWrappable, StateNode, $RAW } from "./state"; -type ReconcileOptions = { +export type ReconcileOptions = { key?: string | null; merge?: boolean; }; diff --git a/packages/solid/src/render/Suspense.ts b/packages/solid/src/render/Suspense.ts index 96896125d..910584f58 100644 --- a/packages/solid/src/render/Suspense.ts +++ b/packages/solid/src/render/Suspense.ts @@ -7,18 +7,19 @@ import { useContext, getSuspenseContext, resumeEffects, - createMemo + createMemo, + Accessor } from "../reactive/signal"; import type { JSX } from "../jsx"; type SuspenseListRegistryItem = { - inFallback: () => boolean; + inFallback: Accessor; showContent: (v: boolean) => void; showFallback: (v: boolean) => void; }; type SuspenseListContextType = { - register: (inFallback: () => boolean) => [() => boolean, () => boolean]; + register: (inFallback: Accessor) => [Accessor, Accessor]; }; const SuspenseListContext = createContext(); @@ -29,8 +30,8 @@ export function SuspenseList(props: { }) { let index = 0, suspenseSetter: (s: boolean) => void, - showContent: () => boolean, - showFallback: () => boolean; + showContent: Accessor, + showFallback: Accessor; // Nested SuspenseList support const listContext = useContext(SuspenseListContext); @@ -43,7 +44,7 @@ export function SuspenseList(props: { const registry: SuspenseListRegistryItem[] = [], comp = createComponent(SuspenseListContext.Provider, { value: { - register: (inFallback: () => boolean) => { + register: (inFallback: Accessor) => { const [showingContent, showContent] = createSignal(false), [showingFallback, showFallback] = createSignal(false); registry[index++] = { inFallback, showContent, showFallback }; @@ -97,8 +98,8 @@ export function SuspenseList(props: { export function Suspense(props: { fallback?: JSX.Element; children: JSX.Element }) { let counter = 0, - showContent: () => boolean, - showFallback: () => boolean; + showContent: Accessor, + showFallback: Accessor; const [inFallback, setFallback] = createSignal(false), SuspenseContext = getSuspenseContext(), store = { diff --git a/packages/solid/src/render/component.ts b/packages/solid/src/render/component.ts index dd229302f..d5bf992dc 100644 --- a/packages/solid/src/render/component.ts +++ b/packages/solid/src/render/component.ts @@ -9,7 +9,7 @@ import { $PROXY } from "../reactive/state"; import { sharedConfig, nextHydrateContext, setHydrateContext } from "./hydration"; import type { JSX } from "../jsx"; -type PropsWithChildren

    = P & { children?: JSX.Element }; +export type PropsWithChildren

    = P & { children?: JSX.Element }; export type Component

    = (props: PropsWithChildren

    ) => JSX.Element; /** * Takes the props of the passed component and returns its type @@ -157,7 +157,17 @@ export function splitProps(props: T, ...keys: Array<(keyof T)[]>) { const clone = {}; for (let i = 0; i < k.length; i++) { const key = k[i]; - if (descriptors[key]) Object.defineProperty(clone, key, descriptors[key]); + Object.defineProperty( + clone, + key, + descriptors[key] + ? descriptors[key] + : { + get() { + return props[key]; + } + } + ); } return clone; }); diff --git a/packages/solid/src/render/flow.ts b/packages/solid/src/render/flow.ts index 0f8c61a7a..835e1f41c 100644 --- a/packages/solid/src/render/flow.ts +++ b/packages/solid/src/render/flow.ts @@ -1,28 +1,32 @@ -import { createMemo, untrack, createSignal, onError, children } from "../reactive/signal"; +import { createMemo, untrack, createSignal, onError, children, Accessor } from "../reactive/signal"; import { mapArray, indexArray } from "../reactive/array"; import type { JSX } from "../jsx"; export function For(props: { each: readonly T[]; fallback?: JSX.Element; - children: (item: T, index: () => number) => U; + children: (item: T, index: Accessor) => U; }) { const fallback = "fallback" in props && { fallback: () => props.fallback }; return createMemo( - mapArray(() => props.each, props.children, fallback ? fallback : undefined) - , undefined, false); + mapArray(() => props.each, props.children, fallback ? fallback : undefined), + undefined, + { equals: false } + ); } // non-keyed export function Index(props: { each: readonly T[]; fallback?: JSX.Element; - children: (item: () => T, index: number) => U; + children: (item: Accessor, index: number) => U; }) { const fallback = "fallback" in props && { fallback: () => props.fallback }; return createMemo( - indexArray(() => props.each, props.children, fallback ? fallback : undefined) - , undefined, false); + indexArray(() => props.each, props.children, fallback ? fallback : undefined), + undefined, + { equals: false } + ); } export function Show(props: { @@ -31,11 +35,9 @@ export function Show(props: { children: JSX.Element | ((item: T) => JSX.Element); }) { let strictEqual = false; - const condition = createMemo( - () => props.when, - undefined, - (a, b) => (strictEqual ? a === b : !a === !b) - ); + const condition = createMemo(() => props.when, undefined, { + equals: (a, b) => (strictEqual ? a === b : !a === !b) + }); return createMemo(() => { const c = condition(); if (c) { @@ -48,7 +50,10 @@ export function Show(props: { }) as () => JSX.Element; } -export function Switch(props: { fallback?: JSX.Element; children: JSX.Element }) { +export function Switch(props: { + fallback?: JSX.Element; + children: JSX.Element; +}): Accessor { let strictEqual = false; const conditions = children(() => props.children) as () => MatchProps[], evalConditions = createMemo<[number, unknown?, MatchProps?]>( @@ -62,8 +67,10 @@ export function Switch(props: { fallback?: JSX.Element; children: JSX.Element }) return [-1]; }, undefined, - (a: [number, unknown?, unknown?], b: [number, unknown?, unknown?]) => - a && a[0] === b[0] && (strictEqual ? a[1] === b[1] : !a[1] === !b[1]) && a[2] === b[2] + { + equals: (a: [number, unknown?, unknown?], b: [number, unknown?, unknown?]) => + a && a[0] === b[0] && (strictEqual ? a[1] === b[1] : !a[1] === !b[1]) && a[2] === b[2] + } ); return createMemo(() => { const [index, when, cond] = evalConditions(); @@ -75,7 +82,7 @@ export function Switch(props: { fallback?: JSX.Element; children: JSX.Element }) }); } -type MatchProps = { +export type MatchProps = { when: T | undefined | null | false; children: JSX.Element | ((item: T) => JSX.Element); }; @@ -86,7 +93,7 @@ export function Match(props: MatchProps) { export function ErrorBoundary(props: { fallback: JSX.Element | ((err: any, reset: () => void) => JSX.Element); children: JSX.Element; -}) { +}): Accessor { const [errored, setErrored] = createSignal(); onError(setErrored); let e: any; @@ -96,5 +103,5 @@ export function ErrorBoundary(props: { return typeof f === "function" && f.length ? untrack(() => f(e, () => setErrored(null))) : f; } return props.children; - }) as () => JSX.Element; + }) as Accessor; } diff --git a/packages/solid/src/static/reactive.ts b/packages/solid/src/static/reactive.ts index 035da76f0..5cb91983d 100644 --- a/packages/solid/src/static/reactive.ts +++ b/packages/solid/src/static/reactive.ts @@ -1,3 +1,4 @@ +import type { JSX } from "../jsx"; export const equalFn = (a: T, b: T) => a === b; const ERROR = Symbol("error"); @@ -27,10 +28,7 @@ export function createRoot(fn: (dispose: () => void) => T, detachedOwner?: Ow return result!; } -export function createSignal( - value?: T, - areEqual?: boolean | ((prev: T, next: T) => boolean) -): [() => T, (v: T) => T] { +export function createSignal(value?: T): [() => T, (v: T) => T] { return [() => value as T, (v: T) => (value = v)]; } @@ -44,58 +42,42 @@ export const createRenderEffect = createComputed; export function createEffect(fn: (v?: T) => T, value?: T): void {} -export function createMemo( - fn: (v?: T) => T, - value?: T, - areEqual?: boolean | ((prev: T, next: T) => boolean) -): () => T { +export function createMemo(fn: (v?: T) => T, value?: T): () => T { Owner = { owner: Owner, context: null }; const v = fn(value); Owner = Owner.owner; return () => v; } -export function createDeferred(source: () => T, options?: { timeoutMs: number }) { +export function createDeferred(source: () => T) { return source; } -export function createSelector( - source: () => T, - fn: (k: T, value: T, prevValue: T | undefined) => boolean -) { - return source; +export function createSelector(source: () => T, fn: (k: T, value: T) => boolean) { + return (k: T) => fn(k, source()); } export function batch(fn: () => T): T { return fn(); } -export function untrack(fn: () => T): T { - return fn(); -} +export const untrack = batch; -type ReturnTypeArray = { [P in keyof T]: T[P] extends (() => infer U) ? U : never }; -export function on T>, U>( - ...args: X['length'] extends 1 - ? [w: () => T, fn: (v: T, prev: T | undefined, prevResults?: U) => U] - : [...w: X, fn: (v: ReturnTypeArray, prev: ReturnTypeArray | [], prevResults?: U) => U] -): (prev?: U) => U { - const fn = args.pop() as (v: T | Array, p?: T | Array, r?: U) => U; - let deps: (() => T) | Array<() => T>; - let isArray = true; - let prev: T | T[]; - if (args.length < 2) { - deps = args[0] as () => T; - isArray = false; - } else deps = args as Array<() => T>; - return prevResult => { - let value: T | Array; +export function on( + deps: Array<() => T> | (() => T), + fn: (value: Array | T, prev: Array | T, prevResults?: U) => U, + options: { defer?: boolean } = {} +): (prev?: U) => U | undefined { + let isArray = Array.isArray(deps); + let defer = options.defer; + return () => { + if (defer) return undefined; + let value: Array | T; if (isArray) { value = []; - if (!prev) prev = []; for (let i = 0; i < deps.length; i++) value.push((deps as Array<() => T>)[i]()); } else value = (deps as () => T)(); - return fn!(value, prev, prevResult); + return fn!(value, undefined); }; } @@ -137,7 +119,7 @@ export function getOwner() { } export function children(fn: () => any) { - return resolveChildren(fn()) + return createMemo(() => resolveChildren(fn())); } export function runWithOwner(o: Owner, fn: () => any) { @@ -156,8 +138,8 @@ export function lookup(owner: Owner | null, key: symbol | string): any { ); } -function resolveChildren(children: any): any { - if (typeof children === "function") return resolveChildren(children()); +function resolveChildren(children: any): unknown { + if (typeof children === "function" && !children.length) return resolveChildren(children()); if (Array.isArray(children)) { const results: any[] = []; for (let i = 0; i < children.length; i++) { @@ -171,12 +153,10 @@ function resolveChildren(children: any): any { function createProvider(id: symbol) { return function provider(props: { value: unknown; children: any }) { - let rendered; - createRenderEffect(() => { + return (createMemo(() => { Owner!.context = { [id]: props.value }; - rendered = resolveChildren(props.children); - }); - return rendered; + return children(() => props.children); + }) as unknown) as JSX.Element; }; } diff --git a/packages/solid/test/dev.spec.ts b/packages/solid/test/dev.spec.ts index b5ca34dbf..76d969372 100644 --- a/packages/solid/test/dev.spec.ts +++ b/packages/solid/test/dev.spec.ts @@ -26,7 +26,7 @@ describe("Dev features", () => { const [s, set] = createSignal(5); const [s2] = createSignal(5); createEffect(() => { - const [s] = createSignal(6, false, { name: "explicit" }); + const [s] = createSignal(6, { name: "explicit" }); }); createComponent(CustomComponent, {}); set1 = set; @@ -45,7 +45,7 @@ describe("Dev features", () => { const [s, set] = createSignal(5); const [s2] = createSignal(5); createEffect(() => { - const [s] = createSignal(6, false, { name: "explicit" }); + const [s] = createSignal(6, { name: "explicit" }); }); const [state, setState] = createState({ firstName: "John", lastName: "Smith" }); createEffect(() => { diff --git a/packages/solid-rx/test/observable.spec.ts b/packages/solid/test/observable.test.ts similarity index 51% rename from packages/solid-rx/test/observable.spec.ts rename to packages/solid/test/observable.test.ts index 116812a57..64e29fa83 100644 --- a/packages/solid-rx/test/observable.spec.ts +++ b/packages/solid/test/observable.test.ts @@ -1,19 +1,18 @@ -import { createSignal, createRoot } from "solid-js"; -import { observable } from "../src"; +import { createSignal, createRoot, observable } from "../src"; describe("Observable operator", () => { test("to observable", async () => { let out: string; - let set: (string) => void; + let set: (v: string) => void; createRoot(() => { const [s, _set] = createSignal("Hi"), - obsv$ = observable(s); + obsv$ = observable(s); set = _set; obsv$.subscribe({ next: v => (out = v) }); }); - expect(out).toBe("Hi"); - set("John"); - expect(out).toBe("John"); + expect(out!).toBe("Hi"); + set!("John"); + expect(out!).toBe("John"); }); }); diff --git a/packages/solid/test/signals.memo.spec.ts b/packages/solid/test/signals.memo.spec.ts index c9a8bc967..0f4ad02e9 100644 --- a/packages/solid/test/signals.memo.spec.ts +++ b/packages/solid/test/signals.memo.spec.ts @@ -4,14 +4,12 @@ describe("createMemo", () => { describe("executing propagating", () => { it("does not trigger downstream computations unless changed", () => { createRoot(() => { - let [s1, set] = createSignal(1, false); + let [s1, set] = createSignal(1, { equals: false }); let order = ""; - let t1 = createMemo( - () => { - order += "t1"; - return s1(); - } - ); + let t1 = createMemo(() => { + order += "t1"; + return s1(); + }); createComputed(() => { order += "c1"; t1(); @@ -30,12 +28,10 @@ describe("createMemo", () => { createRoot(() => { let [s1, set] = createSignal(0); let order = ""; - let t1 = createMemo( - () => { - order += "t1"; - return s1() === 0; - } - ); + let t1 = createMemo(() => { + order += "t1"; + return s1() === 0; + }); createComputed(() => { order += "c1"; return s1(); @@ -57,12 +53,10 @@ describe("createMemo", () => { let [s1, set] = createSignal(0); let [s2] = createSignal(0); let order = ""; - let t1 = createMemo( - () => { - order += "t1"; - return s1() === 0; - } - ); + let t1 = createMemo(() => { + order += "t1"; + return s1() === 0; + }); createComputed(() => { order += "c1"; return s1(); @@ -94,12 +88,10 @@ describe("createMemo", () => { [t, setT] = createSignal(1); [e, setE] = createSignal(2); fevals = 0; - f = createMemo( - () => { - fevals++; - return i() ? t() : e(); - } - ); + f = createMemo(() => { + fevals++; + return i() ? t() : e(); + }); fevals = 0; } @@ -145,33 +137,25 @@ describe("createMemo", () => { createRoot(() => { var order = "", [a, setA] = createSignal(0), - b = createMemo( - () => { - order += "b"; - return a() + 1; - }, - ), - c = createMemo( - () => { - order += "c"; - const check = b(); - if (check) { - return check; - } - return e(); - } - ), - d = createMemo( - () => { - return a(); + b = createMemo(() => { + order += "b"; + return a() + 1; + }), + c = createMemo(() => { + order += "c"; + const check = b(); + if (check) { + return check; } - ), - e = createMemo( - () => { - order += "d"; - return d() + 10; - } - ); + return e(); + }), + d = createMemo(() => { + return a(); + }), + e = createMemo(() => { + order += "d"; + return d() + 10; + }); expect(order).toBe("bcd"); @@ -205,18 +189,14 @@ describe("createMemo", () => { c3 [PN,PN,STL,void] */ - let t1 = createMemo(() => s1() > 0, undefined, true); - let t2 = createMemo(() => s1() > 0, undefined, true); + let t1 = createMemo(() => s1() > 0); + let t2 = createMemo(() => s1() > 0); let c1 = createMemo(() => s1()); - let t3 = createMemo( - () => { - let a = s1(); - let b = s2(); - return a && b; - }, - undefined, - true - ); + let t3 = createMemo(() => { + let a = s1(); + let b = s2(); + return a && b; + }); createComputed(() => { t1(); t2(); @@ -276,14 +256,10 @@ describe("createMemo", () => { let [s2] = createSignal(1); let count = 0; let c1: () => number; - createMemo( - () => { - c1 = createMemo(() => s2(), undefined, true); - return s1(); - }, - undefined, - true - ); + createMemo(() => { + c1 = createMemo(() => s2()); + return s1(); + }); createComputed(() => { count++; c1(); @@ -297,20 +273,14 @@ describe("createMemo", () => { createRoot(() => { let [s1, set] = createSignal(true); let order = ""; - let t1 = createMemo( - () => { - order += "t1"; - return s1(); - }, - undefined, - true - ); - let t2 = createMemo( - () => { - order += "t2"; - return s1(); - } - ); + let t1 = createMemo(() => { + order += "t1"; + return s1(); + }); + let t2 = createMemo(() => { + order += "t2"; + return s1(); + }); createComputed(() => { t1(); t2(); @@ -333,22 +303,14 @@ describe("createMemo", () => { c2(); } }); - let t1 = createMemo( - () => { - order += "t1"; - return s1() < 3; - }, - undefined, - true - ); - let t2 = createMemo( - () => { - order += "t2"; - return t1(); - }, - undefined, - true - ); + let t1 = createMemo(() => { + order += "t1"; + return s1() < 3; + }); + let t2 = createMemo(() => { + order += "t2"; + return t1(); + }); c2 = createMemo(() => { order += "c2"; return t2(); @@ -366,12 +328,10 @@ describe("createMemo", () => { createRoot(() => { let [s1, set] = createSignal(1); let order = ""; - let t1 = createMemo( - () => { - order += "t1"; - return s1(); - } - ); + let t1 = createMemo(() => { + order += "t1"; + return s1(); + }); let c1 = createMemo(() => { order += "c1"; return t1(); @@ -397,13 +357,9 @@ describe("createMemo", () => { var [d, set] = createSignal(1); expect(() => { - createMemo( - () => { - return set(d() + 1); - }, - undefined, - true - ); + createMemo(() => { + return set(d() + 1); + }); }).toThrow(); }); }); @@ -412,19 +368,15 @@ describe("createMemo", () => { createRoot(() => { let i = 2; var [d, set] = createSignal(1), - f1 = createMemo(() => d(), undefined, true), - f2 = createMemo(() => f1(), undefined, true), - f3 = createMemo(() => f2(), undefined, true); + f1 = createMemo(() => d()), + f2 = createMemo(() => f1()), + f3 = createMemo(() => f2()); expect(() => { - createMemo( - () => { - f3(); - set(i++); - }, - undefined, - true - ); + createMemo(() => { + f3(); + set(i++); + }); }).toThrow(); }); }); @@ -434,7 +386,7 @@ describe("createMemo", () => { it("throws when cycle created by modifying a branch", () => { createRoot(() => { var [d, set] = createSignal(1), - f: () => number = createMemo(() => (f ? f() : d()), undefined, false); + f: () => number = createMemo(() => (f ? f() : d()), undefined, { equals: false }); expect(() => { set(0); diff --git a/packages/solid/test/signals.spec.ts b/packages/solid/test/signals.spec.ts index 289226958..9861927d7 100644 --- a/packages/solid/test/signals.spec.ts +++ b/packages/solid/test/signals.spec.ts @@ -25,7 +25,7 @@ describe("Create signals", () => { expect(value()).toBe(5); }); test("Create and read a Signal with comparator", () => { - const [value] = createSignal(5, (a, b) => a === b); + const [value] = createSignal(5, { equals: (a, b) => a === b }); expect(value()).toBe(5); }); test("Create and read a Memo", () => { @@ -40,27 +40,37 @@ describe("Create signals", () => { expect(memo()).toBe("Hello John"); }); }); - test("Create an Effect", () => { + test("Create onMount", () => { let temp: string; createRoot(() => { - onMount(() => (temp = "unpure")); + onMount(() => (temp = "impure")); }); - expect(temp!).toBe("unpure"); + expect(temp!).toBe("impure"); }); test("Create a Computed with explicit deps", () => { createRoot(() => { let temp: string; const [sign] = createSignal("thoughts"); - createComputed(on(sign, v => (temp = "unpure " + v))); - expect(temp!).toBe("unpure thoughts"); + createComputed(on(sign, v => (temp = "impure " + v))); + expect(temp!).toBe("impure thoughts"); }); }); test("Create a Computed with multiple explicit deps", () => { createRoot(() => { let temp: string; const [sign] = createSignal("thoughts"); - createComputed(on(sign, sign, v => (temp = "unpure " + v[1]))); - expect(temp!).toBe("unpure thoughts"); + createComputed(on([sign, sign], v => (temp = "impure " + v[1]))); + expect(temp!).toBe("impure thoughts"); + }); + }); + test("Create a Computed with explicit deps and lazy evaluation", () => { + createRoot(() => { + let temp: string; + const [sign, set] = createSignal("thoughts"); + createComputed(on(sign, v => (temp = "impure " + v), { defer: true })); + expect(temp!).toBeUndefined(); + set("minds"); + expect(temp!).toBe("impure minds"); }); }); }); @@ -71,13 +81,13 @@ describe("Update signals", () => { setValue(10); expect(value()).toBe(10); }); - test("Create Signal with comparator and set different value", () => { - const [value, setValue] = createSignal(5, (a, b) => a === b); + test("Create Signal and set different value", () => { + const [value, setValue] = createSignal(5); setValue(10); expect(value()).toBe(10); }); - test("Create Signal with comparator and set equivalent value", () => { - const [value, setValue] = createSignal(5, (a, b) => a > b); + test("Create Signal and set equivalent value", () => { + const [value, setValue] = createSignal(5, { equals: (a, b) => a > b }); setValue(3); expect(value()).toBe(5); }); @@ -441,16 +451,16 @@ describe("createSelector", () => { const [s, set] = createSignal(-1), isSelected = createSelector(s); let count = 0; - const list = Array.from({ length: 100 }, (_, i) => - [createMemo(() => { + const list = Array.from({ length: 100 }, (_, i) => [ + createMemo(() => { count++; return isSelected(i) ? "selected" : "no"; }), createMemo(() => { count++; return isSelected(i) ? "oui" : "non"; - })] - ); + }) + ]); expect(count).toBe(200); expect(list[3][0]()).toBe("no"); expect(list[3][1]()).toBe("non"); @@ -502,8 +512,8 @@ describe("createSelector", () => { describe("create and use context", () => { test("createContext without arguments defaults to undefined", () => { - const context = createContext() + const context = createContext(); const res = useContext(context); - expect(res).toBe(undefined) - }) -}) + expect(res).toBe(undefined); + }); +}); diff --git a/packages/solid/tsconfig.test.json b/packages/solid/tsconfig.test.json index c3e9256df..f22e8f93e 100644 --- a/packages/solid/tsconfig.test.json +++ b/packages/solid/tsconfig.test.json @@ -7,6 +7,7 @@ } }, "include": [ - "./test" + "./test", + "./web/test" ] } \ No newline at end of file diff --git a/packages/solid/web/package.json b/packages/solid/web/package.json index 1e9ce0a61..bf642cf0c 100644 --- a/packages/solid/web/package.json +++ b/packages/solid/web/package.json @@ -6,6 +6,7 @@ "./dist/server.cjs": "./dist/web.cjs", "./dist/server.js": "./dist/web.js" }, + "unpkg": "./dist/web.cjs", "types": "./types/index.d.ts", "type": "module", "sideEffects": false diff --git a/packages/solid/web/src/core.ts b/packages/solid/web/src/core.ts index e55204975..36d9453c0 100644 --- a/packages/solid/web/src/core.ts +++ b/packages/solid/web/src/core.ts @@ -10,6 +10,16 @@ import { } from "solid-js"; // reactive injection for dom-expressions -function memo(fn: () => any, equal: boolean) { return createMemo(fn, undefined, equal); } +function memo(fn: () => T, equals: boolean) { + return createMemo(fn, undefined, !equals ? { equals } : undefined); +} -export { getOwner, createComponent, createRoot as root, createRenderEffect as effect, memo, sharedConfig, awaitSuspense as asyncWrap } +export { + getOwner, + createComponent, + createRoot as root, + createRenderEffect as effect, + memo, + sharedConfig, + awaitSuspense as asyncWrap +}; diff --git a/packages/solid/web/src/index.ts b/packages/solid/web/src/index.ts index 8bc690acb..9d921f72a 100644 --- a/packages/solid/web/src/index.ts +++ b/packages/solid/web/src/index.ts @@ -8,7 +8,8 @@ import { Component, JSX, createRoot, - sharedConfig + sharedConfig, + Accessor } from "solid-js"; export * from "./client"; @@ -27,11 +28,10 @@ export { export * from "./server-mock"; export const isServer = false; -const SVG_NAMESPACE = 'http://www.w3.org/2000/svg'; +const SVG_NAMESPACE = "http://www.w3.org/2000/svg"; -function createElement(tagName: string, isSVG = false): HTMLElement|SVGElement { - return isSVG ? document.createElementNS(SVG_NAMESPACE, tagName) : - document.createElement(tagName); +function createElement(tagName: string, isSVG = false): HTMLElement | SVGElement { + return isSVG ? document.createElementNS(SVG_NAMESPACE, tagName) : document.createElement(tagName); } export function Portal(props: { @@ -79,15 +79,15 @@ export function Portal(props: { return marker; } -type DynamicProps = T&{ +type DynamicProps = T & { children?: any; - component?: Component|string|keyof JSX.IntrinsicElements; + component?: Component | string | keyof JSX.IntrinsicElements; }; -export function Dynamic(props: DynamicProps): () => JSX.Element { +export function Dynamic(props: DynamicProps): Accessor { const [p, others] = splitProps(props, ["component"]); return createMemo(() => { - const component = p.component as Function|string; + const component = p.component as Function | string; switch (typeof component) { case "function": return untrack(() => component(others)); diff --git a/packages/solid/web/test/element.spec.tsx b/packages/solid/web/test/element.spec.tsx index eadb91f12..40a02476d 100644 --- a/packages/solid/web/test/element.spec.tsx +++ b/packages/solid/web/test/element.spec.tsx @@ -3,10 +3,10 @@ import { createRoot, createSignal, JSX } from "../../src"; declare module "solid-js" { namespace JSX { - interface Actions { + interface Directives { getRef: boolean; } - } + } } describe("Basic element attributes", () => { @@ -60,11 +60,11 @@ describe("Basic element attributes", () => { }); }); - test("actions work properly", () => { + test("directives work properly", () => { let ref: HTMLDivElement, - el: HTMLDivElement, + el!: HTMLDivElement, getRef = (el: HTMLDivElement) => ref = el, d = (

    ) as HTMLDivElement; - expect(ref).toBe(el); + expect(ref!).toBe(el); }) }); diff --git a/packages/solid/web/test/errorboundary.spec.tsx b/packages/solid/web/test/errorboundary.spec.tsx index 9935998a8..7fbd3ad75 100644 --- a/packages/solid/web/test/errorboundary.spec.tsx +++ b/packages/solid/web/test/errorboundary.spec.tsx @@ -35,7 +35,7 @@ describe("Testing ErrorBoundary control flow", () => { }); test("Create an Error callback and reset", () => { - let r; + let r: () => void; createRoot(dispose => { disposer = dispose;
    { @@ -44,7 +44,7 @@ describe("Testing ErrorBoundary control flow", () => { }}>
    ; }); expect(div.innerHTML).toBe("Failure"); - r(); + r!(); }); test("dispose", () => disposer()); diff --git a/packages/solid/web/test/suspense.spec.tsx b/packages/solid/web/test/suspense.spec.tsx index c19536778..911278c9e 100644 --- a/packages/solid/web/test/suspense.spec.tsx +++ b/packages/solid/web/test/suspense.spec.tsx @@ -10,7 +10,7 @@ describe("Testing Suspense", () => { const LazyComponent = lazy(() => new Promise(r => resolvers.push(r))), ChildComponent = (props: { greeting: string }) => { let [value] = createResource( - () => triggered() ? "child" : null, + () => triggered() && "child", () => new Promise(r => setTimeout(() => r("Jo"), 300)), { initialValue: "" } ); diff --git a/packages/test-integration/package-lock.json b/packages/test-integration/package-lock.json index 8e98e6e1c..8cf0073b5 100644 --- a/packages/test-integration/package-lock.json +++ b/packages/test-integration/package-lock.json @@ -24,5 +24,5 @@ } } }, - "version": "0.26.5" + "version": "1.0.0-rc.1" } diff --git a/packages/test-integration/package.json b/packages/test-integration/package.json index f02858c69..1c04d7a70 100644 --- a/packages/test-integration/package.json +++ b/packages/test-integration/package.json @@ -7,8 +7,8 @@ "test:imports": "node test-imports.mjs" }, "dependencies": { - "babel-preset-solid": "^0.26.5", - "solid-js": "^0.26.5" + "babel-preset-solid": "^1.0.0-rc.1", + "solid-js": "^1.0.0-rc.1" }, - "version": "0.26.5" + "version": "1.0.0-rc.1" } diff --git a/packages/test-integration/test-imports.mjs b/packages/test-integration/test-imports.mjs index 64d4abd01..bb023bd82 100644 --- a/packages/test-integration/test-imports.mjs +++ b/packages/test-integration/test-imports.mjs @@ -25,8 +25,8 @@ function checkError(error) { } Promise.all([ - import("solid-js/dev").catch(checkError), - import("solid-js/dev/dist/dev.js").catch(checkError), + import("solid-js").catch(checkError), + import("solid-js/dist/solid.js").catch(checkError), import("solid-js/web").catch(checkError), import("solid-js/web/dist/web.js").catch(checkError), diff --git a/packages/test-integration/tsconfig.json b/packages/test-integration/tsconfig.json index 36aa9f9cc..6fa8f0a2b 100644 --- a/packages/test-integration/tsconfig.json +++ b/packages/test-integration/tsconfig.json @@ -1,6 +1,4 @@ { - "extends": "../../tsconfig.test.json", - "include": [ - "./test" - ] - } \ No newline at end of file + "extends": "../../tsconfig.test.json", + "include": ["./test"] +}