Skip to content

Commit

Permalink
Use type guards for internal types
Browse files Browse the repository at this point in the history
  • Loading branch information
marvinhagemeister committed Dec 22, 2022
1 parent ca2bee9 commit 3debe02
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 38 deletions.
2 changes: 1 addition & 1 deletion src/create-context.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ let nextContextId = 0;

const providers = new Set();

/** @param {import('./internal').Internal} internal */
/** @param {import('./internal').ComponentInternal} internal */
export const unsubscribeFromContext = internal => {
// if this was a context provider, delete() returns true and we exit:
if (providers.delete(internal)) return;
Expand Down
6 changes: 3 additions & 3 deletions src/diff/catch-error.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {
DIRTY_BIT,
MODE_RERENDERING_ERROR,
MODE_PENDING_ERROR,
TYPE_COMPONENT
MODE_PENDING_ERROR
} from '../constants';
import { ENABLE_CLASSES } from '../component';
import { isComponentInternal } from '../helpers';

/**
* Find the closest error boundary to a thrown error and call it
Expand All @@ -16,7 +16,7 @@ import { ENABLE_CLASSES } from '../component';
export function _catchError(error, internal) {
while ((internal = internal._parent)) {
if (
internal.flags & TYPE_COMPONENT &&
isComponentInternal(internal) &&
~internal.flags & MODE_RERENDERING_ERROR
) {
try {
Expand Down
9 changes: 4 additions & 5 deletions src/diff/children.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { applyRef } from './refs';
import { normalizeToVNode } from '../create-element';
import {
TYPE_COMPONENT,
MODE_HYDRATE,
MODE_SUSPENDED,
EMPTY_ARR,
TYPE_DOM,
UNDEFINED
} from '../constants';
import { mount } from './mount';
import { patch } from './patch';
import { unmount } from './unmount';
import { createInternal, getDomSibling } from '../tree';
import { isComponentInternal, isDomInternal } from '../helpers';

/**
* Update an internal with new children.
Expand Down Expand Up @@ -98,7 +97,7 @@ export function patchChildren(internal, children, parentDom) {
}

// Perform insert of new dom
if (childInternal.flags & TYPE_DOM) {
if (isDomInternal(childInternal)) {
parentDom.insertBefore(
childInternal.data,
getDomSibling(internal, skewedIndex)
Expand Down Expand Up @@ -132,7 +131,7 @@ export function patchChildren(internal, children, parentDom) {
if (matchingIndex == i) break go;

let nextSibling = getDomSibling(internal, skewedIndex + 1);
if (childInternal.flags & TYPE_DOM) {
if (isDomInternal(childInternal)) {
parentDom.insertBefore(childInternal.data, nextSibling);
} else {
insertComponentDom(childInternal, nextSibling, parentDom);
Expand Down Expand Up @@ -241,7 +240,7 @@ export function insertComponentDom(internal, nextSibling, parentDom) {
if (childInternal) {
childInternal._parent = internal;

if (childInternal.flags & TYPE_COMPONENT) {
if (isComponentInternal(childInternal)) {
insertComponentDom(childInternal, nextSibling, parentDom);
} else if (childInternal.data != nextSibling) {
parentDom.insertBefore(childInternal.data, nextSibling);
Expand Down
17 changes: 7 additions & 10 deletions src/diff/mount.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { applyRef } from './refs';
import {
TYPE_COMPONENT,
TYPE_ELEMENT,
MODE_HYDRATE,
MODE_MUTATIVE_HYDRATE,
Expand All @@ -9,7 +8,6 @@ import {
TYPE_TEXT,
TYPE_CLASS,
MODE_ERRORED,
TYPE_ROOT,
MODE_SVG,
DIRTY_BIT
} from '../constants';
Expand All @@ -19,6 +17,7 @@ import { createInternal, getParentContext } from '../tree';
import options from '../options';
import { ENABLE_CLASSES } from '../component';
import { commitQueue } from './commit';
import { isComponentInternal, isRootInternal } from '../helpers';
/**
* Diff two virtual nodes and apply proper changes to the DOM
* @param {import('../internal').Internal} internal The Internal node to mount
Expand All @@ -34,14 +33,11 @@ export function mount(internal, newVNode, parentDom, startDom) {
let nextDomSibling, prevStartDom;

try {
if (internal.flags & TYPE_COMPONENT) {
if (isComponentInternal(internal)) {
// Root nodes signal that an attempt to render into a specific DOM node on
// the page. Root nodes can occur anywhere in the tree and not just at the
// top.
if (
internal.flags & TYPE_ROOT &&
newVNode.props._parentDom !== parentDom
) {
if (isRootInternal(internal) && newVNode.props._parentDom !== parentDom) {
parentDom = newVNode.props._parentDom;
prevStartDom = startDom;
startDom = null;
Expand All @@ -59,6 +55,7 @@ export function mount(internal, newVNode, parentDom, startDom) {
);
}

//
if (
internal.data._commitCallbacks &&
internal.data._commitCallbacks.length
Expand Down Expand Up @@ -249,7 +246,7 @@ export function mountChildren(internal, children, parentDom, startDom) {

newDom = childInternal.data;

if (childInternal.flags & TYPE_COMPONENT || newDom == startDom) {
if (isComponentInternal(childInternal) || newDom == startDom) {
// If the child is a Fragment-like or if it is DOM VNode and its _dom
// property matches the dom we are diffing (i.e. startDom), just
// continue with the mountedNextChild
Expand Down Expand Up @@ -290,14 +287,14 @@ export function mountChildren(internal, children, parentDom, startDom) {
}

/**
* @param {import('../internal').Internal} internal The component's backing Internal node
* @param {import('../internal').ComponentInternal} internal The component's backing Internal node
* @param {import('../internal').PreactNode} startDom the preceding node
* @returns {import('../internal').PreactNode} the component's children
*/
function mountComponent(internal, startDom) {
/** @type {import('../internal').Component} */
let c;
let type = /** @type {import('../internal').ComponentType} */ (internal.type);
let type = internal.type;
let newProps = internal.props;

// Necessary for createContext api. Setting this property will pass
Expand Down
2 changes: 1 addition & 1 deletion src/diff/patch.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ function patchElement(internal, vnode) {
}

/**
* @param {import('../internal').Internal} internal The component's backing Internal node
* @param {import('../internal').ComponentInternal} internal The component's backing Internal node
* @param {import('../internal').VNode} newVNode The new virtual node
* @returns {import('../internal').ComponentChildren} the component's children
*/
Expand Down
16 changes: 16 additions & 0 deletions src/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { TYPE_COMPONENT, TYPE_DOM, TYPE_ELEMENT, TYPE_ROOT } from './constants';

/** @type {import('./internal').isComponentInternal} */
export const isComponentInternal = internal =>
/** @type {*} */ (internal.flags & TYPE_COMPONENT);

/** @type {import('./internal').isDomInternal} */
export const isDomInternal = internal =>
/** @type {*} */ (internal.flags & TYPE_DOM);

/** @type {import('./internal').isRootInternal} */
export const isRootInternal = internal =>
/** @type {*} */ (internal.flags & TYPE_ROOT);

/** @type {(internal: import('./internal').Internal) => number} */
export const isElementInternal = internal => internal.flags & TYPE_ELEMENT;
53 changes: 42 additions & 11 deletions src/internal.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export interface Options extends preact.Options {
_internal?(internal: Internal, vnode: VNode | string): void;
}

export type CommitQueue = Internal[];
export type CommitQueue = ComponentInternal[];

// Redefine ComponentFactory using our new internal FunctionalComponent interface above
export type ComponentFactory<P> =
Expand Down Expand Up @@ -130,18 +130,13 @@ export interface VNode<P = {}> extends preact.VNode<P> {
* An Internal is a persistent backing node within Preact's virtual DOM tree.
* Think of an Internal like a long-lived VNode with stored data and tree linkages.
*/
export interface Internal<P = {}> {
type: string | ComponentType<P>;
/** The props object for Elements/Components, and the string contents for Text */
props: (P & { children: ComponentChildren }) | string | number;
export interface BaseInternal<P = {}> {
key: any;
ref: Ref<any> | null;
_prevRef: Ref<any> | null;

/** Bitfield containing information about the Internal or its component. */
flags: number;
/** Polymorphic property to store extensions like hooks on */
data: object | PreactNode;
/** The function that triggers in-place re-renders for an internal */
rerender: (internal: Internal) => void;

Expand All @@ -159,16 +154,52 @@ export interface Internal<P = {}> {
_component: Component | null;
/** This Internal's distance from the tree root */
_depth: number | null;
/** Callbacks to invoke when this internal commits */
_commitCallbacks: Array<() => void>;
_stateCallbacks: Array<() => void>; // Only class components
}

export interface ComponentInternal<P = {}> extends BaseInternal<P> {
type: ComponentType<P>;
props: P & { children: ComponentChildren };
/** Polymorphic property to store extensions like hooks on */
data: {
/** Callbacks to invoke when this internal commits */
_commitCallbacks: Array<() => void>;
_stateCallbacks: Array<() => void>; // Only class components
[key: string]: any;
};
}

export interface RootInternal<P = {}>
extends Exclude<ComponentInternal<P>, 'props'> {
props: P & { children: ComponentChildren; _parentDom: PreactNode };
}

export interface DomInternal<P = {}> extends BaseInternal<P> {
type: string;
/** The props object for Elements/Components, and the string contents for Text */
props: (P & { children: ComponentChildren }) | string | number;
data: PreactNode;
}

export type Internal<P = {}> =
| ComponentInternal<P>
| DomInternal<P>
| RootInternal<P>;

export type isDomInternal<P = {}> = (
internal: Internal<P>
) => internal is DomInternal<P>;
export type isComponentInternal<P = {}> = (
internal: Internal<P>
) => internal is ComponentInternal<P>;
export type isRootInternal<P = {}> = (
internal: Internal<P>
) => internal is RootInternal<P>;

export interface Component<P = {}, S = {}> extends preact.Component<P, S> {
// When component is functional component, this is reset to functional component
constructor: ComponentType<P>;
state: S; // Override Component["state"] to not be readonly for internal use, specifically Hooks
_internal?: Internal<P> | null;
_internal?: ComponentInternal<P> | null;
_nextState?: S | null; // Only class components
/** Only used in the devtools to later dirty check if state has changed */
_prevState?: S | null;
Expand Down
19 changes: 12 additions & 7 deletions src/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@ import {
TYPE_ROOT,
INHERITED_MODES,
TYPE_COMPONENT,
TYPE_DOM,
MODE_SVG,
UNDEFINED
} from './constants';
import { enqueueRender } from './component';
import {
isComponentInternal,
isDomInternal,
isElementInternal,
isRootInternal
} from './helpers';

/**
* Create an internal tree node
Expand Down Expand Up @@ -106,8 +111,8 @@ export function createInternal(vnode, parentInternal) {
}

const shouldSearchComponent = internal =>
internal.flags & TYPE_COMPONENT &&
(!(internal.flags & TYPE_ROOT) ||
isComponentInternal(internal) &&
(!isRootInternal(internal) ||
internal.props._parentDom == getParentDom(internal._parent));

/**
Expand Down Expand Up @@ -153,7 +158,7 @@ export function getChildDom(internal, offset) {
for (; offset < internal._children.length; offset++) {
let child = internal._children[offset];
if (child != null) {
if (child.flags & TYPE_DOM) {
if (isDomInternal(child)) {
return child.data;
}

Expand Down Expand Up @@ -190,15 +195,15 @@ export function getParentDom(internal) {
let parent = internal;

// if this is a Root internal, return its parent DOM:
if (parent.flags & TYPE_ROOT) {
if (isRootInternal(parent)) {
return parent.props._parentDom;
}

// walk up the tree to find the nearest DOM or Root Internal:
while ((parent = parent._parent)) {
if (parent.flags & TYPE_ROOT) {
if (isRootInternal(parent)) {
return parent.props._parentDom;
} else if (parent.flags & TYPE_ELEMENT) {
} else if (isElementInternal(parent)) {
return parent.data;
}
}
Expand Down

0 comments on commit 3debe02

Please sign in to comment.