Skip to content

Commit

Permalink
docs: annotate the utilities for plugin creation
Browse files Browse the repository at this point in the history
  • Loading branch information
petyosi committed Aug 13, 2023
1 parent e023b0f commit e979078
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 7 deletions.
19 changes: 19 additions & 0 deletions src/exportMarkdownFromLexical.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,15 @@ export interface LexicalExportVisitor<LN extends LexicalNode, UN extends Mdast.C
join?<T extends Mdast.Content>(prevNode: T, currentNode: T): T
}

/**
* The "any" type for LexicalExportVisitor.
* @internal
*/
export type LexicalVisitor = LexicalExportVisitor<LexicalNode, Mdast.Content>

/**
* @internal
*/
export interface ExportLexicalTreeOptions {
root: LexicalRootNode
visitors: LexicalVisitor[]
Expand All @@ -95,6 +102,9 @@ function isParent(node: unknown): node is Mdast.Parent {
return (node as { children?: any[] }).children instanceof Array
}

/**
* @internal
*/
export function exportLexicalTreeToMdast({
root,
visitors,
Expand Down Expand Up @@ -290,8 +300,14 @@ function fixWrappingWhitespace(node: Mdast.Parent | Mdast.Content, parentChain:
}
}

/**
* @internal
*/
export type ToMarkdownExtension = NonNullable<ToMarkdownOptions['extensions']>[number]

/**
* @internal
*/
export interface ExportMarkdownFromLexicalOptions extends ExportLexicalTreeOptions {
visitors: LexicalVisitor[]

Expand Down Expand Up @@ -324,6 +340,9 @@ export interface LexicalConvertOptions {
toMarkdownOptions?: ToMarkdownOptions
}

/**
* @internal
*/
export function exportMarkdownFromLexical({
root,
toMarkdownOptions,
Expand Down
30 changes: 29 additions & 1 deletion src/gurx/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,10 @@ export type MethodsFromPropMap<Sys extends System, Map extends SystemPropsMap<Sy
}

/**
* Used to correctly specify type refs for system components
* @internal
* Used to correctly specify type refs for system components.
*
* @example
* ```tsx
* const s = system(() => { return { a: statefulStream(0) } })
* const { Component } = systemToComponent(s)
Expand Down Expand Up @@ -227,6 +229,7 @@ export function realmFactoryToComponent<
}
}

/** @internal */
export function sysHooks<Sys extends System>() {
// this enables HMR in vite. Unless context is persistent, HMR breaks.
const Context = GurxContext as unknown as React.Context<TypedRealm<Sys> | undefined>
Expand Down Expand Up @@ -293,18 +296,40 @@ type SystemAndDependencies<Spec extends AnySystemSpec> = SystemOfSpecs<[Spec]> &

const UsedPluginsContext = React.createContext<Set<string>>(new Set())

/**
* The parameters of a plugin declaration. THe best way to understand what each one does is to examine the source code of the existing plugins.
*/
export interface RealmPluginParams<Spec extends AnySystemSpec, Params extends object> {
/**
* The id of the plugin. Used to declare conditional features that are activated only if the plugin is present.
*/
id: string
/**
* The ids of the plugins that this plugin depends on. The plugin will not be activated if any of the dependencies is not present.
*/
dependencies?: string[]
/**
* The system spec of the plugin. Construct one using {@link system}.
*/
systemSpec: Spec
/**
* The callback is executed every time the react component is re-rendered.
*/
applyParamsToSystem?: (realm: TypedRealm<SystemAndDependencies<Spec>>, props: Params) => void
/**
* Executed when the component mounts. Use this to register import/export visitors, add lexical nodes to the editor, etc.
*/
init?: (realm: TypedRealm<SystemAndDependencies<Spec>>, props: Params) => void
}

/** @internal */
export interface PluginConstructor<Spec extends AnySystemSpec, Params extends object> {
(params?: Params): { pluginParams?: Params } & RealmPluginParams<Spec, Params>
}

/**
* Declares a new MDXEditor plugin.
*/
export function realmPlugin<Spec extends AnySystemSpec, Params extends object>(params: RealmPluginParams<Spec, Params>) {
const plugin: PluginConstructor<Spec, Params> = (pluginParams?: Params) => {
return {
Expand All @@ -320,6 +345,7 @@ export function realmPlugin<Spec extends AnySystemSpec, Params extends object>(p
return [plugin, sysHooks<SystemAndDependencies<Spec>>()] as const
}

/** @internal */
export const RealmPluginInitializer = function <P extends Array<ReturnType<PluginConstructor<AnySystemSpec, any>>>>({
plugins,
children
Expand Down Expand Up @@ -369,11 +395,13 @@ export const RealmPluginInitializer = function <P extends Array<ReturnType<Plugi
)
}

/** @internal */
export function useHasPlugin(id: string) {
const usedPlugins = React.useContext(UsedPluginsContext)
return usedPlugins.has(id)
}

/** @internal */
export const RequirePlugin: React.FC<{ id: string; children: React.ReactNode }> = ({ id, children }) => {
return useHasPlugin(id) ? React.createElement(React.Fragment, {}, children) : null
}
23 changes: 19 additions & 4 deletions src/gurx/realm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,35 @@ import { tap } from '../utils/fp'
import { uuidv4 } from '../utils/uuid4'
import { LongTuple } from './realmFactory'

/** @internal */
export type NodeKey = string

// eslint-disable-next-line @typescript-eslint/no-unused-vars
export interface RealmNode<T = unknown> {
/** @internal */
export interface RealmNode<_T = unknown> {
key: NodeKey
toString(): string
}

/** @internal */
type RN<T = unknown> = RealmNode<T>

// eslint-disable-next-line @typescript-eslint/no-explicit-any
/** @internal **/
export type Subscription<T = any> = (value: T) => unknown

/** @internal */
export type UnsubscribeHandle = () => void

type ProjectionFunc<T extends unknown[] = unknown[]> = (done: (...values: unknown[]) => void) => (...args: T) => void

/** @internal */
export interface RealmProjection<T extends unknown[] = unknown[]> {
sources: Set<NodeKey>
pulls: Set<NodeKey>
sink: NodeKey
map: ProjectionFunc<T>
}

/** @internal */
export interface RealmProjectionSpec<T extends unknown[] = unknown[]> {
sources: RealmNode[]
pulls?: RealmNode[]
Expand All @@ -35,7 +40,7 @@ export interface RealmProjectionSpec<T extends unknown[] = unknown[]> {

/**
* A function which determines if two values are equal.
* Implement custom comparators when [[distinctUntilChanged]] needs to work on non-primitive objects.
* Implement custom comparators when the distinctUntilChanged operator needs to work on non-primitive objects.
* @returns true if values should be considered equal.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand All @@ -47,8 +52,12 @@ type NodesFromValuesRec<T extends unknown[], Acc extends unknown[]> = T extends
? NodesFromValuesRec<Tail, [...Acc, Head extends unknown ? RealmNode<Head> : never]>
: Acc

/** @internal */
export type NodesFromValues<T extends unknown[]> = T extends unknown[] ? NodesFromValuesRec<T, []> : never

/**
* A comparator function to determine if two values are equal. Used by distinctUntilChanged operator.
*/
export function defaultComparator<T>(current: T, next: T) {
return current === next
}
Expand Down Expand Up @@ -81,6 +90,9 @@ class SetMap<T> {
}
}

/**
* @internal
*/
export class RefCount {
map: Map<NodeKey, number>

Expand Down Expand Up @@ -109,9 +121,11 @@ export class RefCount {
}
}

/** @internal */
export type RealmGraph = SetMap<RealmProjection>
const NO_VALUE = Symbol('NO_VALUE')

/** @internal */
export function realm() {
const subscriptions = new SetMap<Subscription>()
const singletonSubscriptions = new Map<NodeKey, Subscription>()
Expand Down Expand Up @@ -662,5 +676,6 @@ export function realm() {
}
}

/** @internal */
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface Realm extends ReturnType<typeof realm> {}
25 changes: 23 additions & 2 deletions src/gurx/realmFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import { uuidv4 } from '../utils/uuid4'
import { Realm, realm, RealmNode, Subscription, UnsubscribeHandle } from './realm'

// eslint-disable-next-line
/**
* The system interface is the runtime representation of a state management module, a a record of nodes.
*/
export interface System {
[key: string]: RealmNode<any>
}
Expand All @@ -21,13 +23,16 @@ export interface SystemSpec<Dependencies extends AnySystemSpecs, /** @internal *
dependencies: Dependencies
}

/** @internal **/
export interface AnySystemSpec extends SystemSpec<AnySystemSpecs, any> {
id: string
}

// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface AnySystemSpecs extends Array<AnySystemSpec> {}
/** @internal **/
export type AnySystemSpecs = Array<AnySystemSpec>

/** @internal **/
export type SystemOfSpec<Spec extends AnySystemSpec> = ReturnType<Spec['constructor']>

/** @internal **/
Expand All @@ -47,6 +52,11 @@ export interface SystemConstructor<D extends AnySystemSpecs> {
(r: Realm, dependencies: SystemsFromSpecs<D>): System
}

/**
* Declare a new state management module (system). The constructor receives the realm and the dependencies as arguments.
* @param constructor - The system constructor.
* @param dependencies - The dependencies of the system - other systems.
*/
export function system<Dependencies extends LongTuple<AnySystemSpec>, Constructor extends SystemConstructor<Dependencies>>(
constructor: Constructor,
dependencies: Dependencies = [] as unknown as Dependencies
Expand All @@ -60,19 +70,22 @@ export function system<Dependencies extends LongTuple<AnySystemSpec>, Constructo

type SystemKey<S extends System> = Extract<keyof S, string>

/** @internal */
export type ValuesForKeys<S extends System, K extends SystemKey<S>[]> = K extends unknown[] ? ValuesForKeysRec<S, K, []> : never

type ValuesForKeysRec<S extends Record<any, RealmNode>, K extends unknown[], Acc extends unknown[]> = K extends [infer Head, ...infer Tail]
? ValuesForKeysRec<S, Tail, [...Acc, S[Head] extends RealmNode<infer R> ? R : never]>
: Acc

/** @internal **/
// eslint-disable-next-line @typescript-eslint/ban-types
export type SystemOfSpecs<Specs extends AnySystemSpec[]> = Specs extends unknown[] ? SystemOfSpecsRec<Specs, {}> : Specs
// eslint-disable-next-line @typescript-eslint/ban-types
type SystemOfSpecsRec<Specs extends unknown[], Acc extends {}> = Specs extends [infer Head, ...infer Tail]
? SystemOfSpecsRec<Tail, Head extends AnySystemSpec ? Acc & SystemOfSpec<Head> : never>
: Acc

/** @internal */
export type LongTuple<K> =
| []
| [K]
Expand All @@ -99,13 +112,18 @@ export type LongTuple<K> =
| [K, K, K, K, K, K, K, K, K, K, K, K, K, K, K, K, K, K, K, K, K, K]
| [K, K, K, K, K, K, K, K, K, K, K, K, K, K, K, K, K, K, K, K, K, K, K]

/** @internal */
export type SystemKeys<S extends System, K extends Extract<keyof S, string> = Extract<keyof S, string>> = LongTuple<K>

/** @internal */
export type ValueForKey<S extends System, K extends keyof S> = S[K] extends RealmNode<infer V> ? V : never

/** @internal */
export type SystemDict<S extends System> = { [K in keyof S]?: ValueForKey<S, K> }
/** @internal */
export type SystemKeysArray<S extends System> = Array<keyof S>

/** @internal */
export interface TypedRealm<S extends System>
extends Pick<Realm, 'sub' | 'pub' | 'spread' | 'derive' | 'singletonSubKey' | 'resetSingletonSubs'> {
getKeyValues<K extends SystemKeys<S>>(keys: K): ValuesForKeys<S, K>
Expand All @@ -117,6 +135,7 @@ export interface TypedRealm<S extends System>
subKeys<K extends SystemKeys<S>>(keys: K, subscription: Subscription<ValuesForKeys<S, K>>): UnsubscribeHandle
}

/** @internal */
export function realmFactory<Specs extends LongTuple<AnySystemSpec>>(...specs: Specs): TypedRealm<SystemOfSpecs<Specs>> {
const singletons = new Map<string, System>()
const r = realm()
Expand All @@ -135,8 +154,10 @@ export function realmFactory<Specs extends LongTuple<AnySystemSpec>>(...specs: S
return r as unknown as TypedRealm<SystemOfSpecs<Specs>>
}

/** @internal */
export type RealmFactory<Sys extends System> = () => TypedRealm<Sys>

/** @internal */
export function getRealmFactory<Specs extends LongTuple<AnySystemSpec>>(...specs: Specs) {
return () => realmFactory(...specs)
}
7 changes: 7 additions & 0 deletions src/importMarkdownToLexical.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { fromMarkdown } from 'mdast-util-from-markdown'
import { ParseOptions } from 'micromark-util-types'
import { IS_BOLD, IS_ITALIC, IS_UNDERLINE } from './FormatConstants'

/** @internal */
export type MdastExtensions = NonNullable<Parameters<typeof fromMarkdown>[1]>['mdastExtensions']
/**
* A set of actions that can be used to modify the lexical tree while visiting the mdast tree.
Expand Down Expand Up @@ -73,13 +74,15 @@ function isParent(node: unknown): node is Mdast.Parent {

/**
* The options of the tree import utility. Not meant to be used directly.
* @internal
*/
export interface MdastTreeImportOptions {
root: LexicalRootNode
visitors: MdastImportVisitor<Mdast.Content>[]
mdastRoot: Mdast.Root
}

/** @internal */
export interface MarkdownParseOptions extends Omit<MdastTreeImportOptions, 'mdastRoot'> {
markdown: string
syntaxExtensions: NonNullable<ParseOptions['extensions']>
Expand All @@ -88,14 +91,17 @@ export interface MarkdownParseOptions extends Omit<MdastTreeImportOptions, 'mdas

/**
* An extension for the `fromMarkdown` utility tree construction.
* @internal
*/
export type MdastExtension = NonNullable<MdastExtensions>[number]

/**
* An extension for the `fromMarkdown` utility markdown parse.
* @internal
*/
export type SyntaxExtension = MarkdownParseOptions['syntaxExtensions'][number]

/** @internal */
export function importMarkdownToLexical({ root, markdown, visitors, syntaxExtensions, mdastExtensions }: MarkdownParseOptions): void {
const mdastRoot = fromMarkdown(markdown, {
extensions: syntaxExtensions,
Expand All @@ -109,6 +115,7 @@ export function importMarkdownToLexical({ root, markdown, visitors, syntaxExtens
importMdastTreeToLexical({ root, mdastRoot, visitors })
}

/** @internal */
export function importMdastTreeToLexical({ root, mdastRoot, visitors }: MdastTreeImportOptions): void {
const formattingMap = new WeakMap<object, number>()

Expand Down
Loading

0 comments on commit e979078

Please sign in to comment.