Skip to content

Commit

Permalink
Improve internal JSDoc types (#4173)
Browse files Browse the repository at this point in the history
* change ts-ignore to ts-expect-error

* Improve internal types to better type check src

* Move nextSibling to PreactElement

* Add createRootFragment TS test

* Reference property from dom types in PreactElement

* Use ContainerNode in compat types

* Add linting jsconfig.json

* Fix new type checking errors

* Put internal types into the global namespace so we don't have to import them

* Improving typing a bit more

* rename lint:tsc to just tsc

* Ensure nested node_modules and dist directories are excluded
  • Loading branch information
andrewiggins committed Oct 27, 2023
1 parent cff2df5 commit 180b3b1
Show file tree
Hide file tree
Showing 21 changed files with 397 additions and 291 deletions.
8 changes: 4 additions & 4 deletions compat/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,23 +84,23 @@ declare namespace React {

export function createPortal(
vnode: preact.VNode,
container: Element | DocumentFragment
container: preact.ContainerNode
): preact.VNode<any>;

export function render(
vnode: preact.VNode<any>,
parent: Element,
parent: preact.ContainerNode,
callback?: () => void
): Component | null;

export function hydrate(
vnode: preact.VNode<any>,
parent: Element,
parent: preact.ContainerNode,
callback?: () => void
): Component | null;

export function unmountComponentAtNode(
container: Element | Document | ShadowRoot | DocumentFragment
container: preact.ContainerNode
): boolean;

export function createFactory(
Expand Down
17 changes: 17 additions & 0 deletions compat/test/ts/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from '../../src';

React.render(<div />, document.createElement('div'));
React.render(<div />, document.createDocumentFragment());
React.render(<div />, document.body.shadowRoot!);

React.hydrate(<div />, document.createElement('div'));
React.hydrate(<div />, document.createDocumentFragment());
React.hydrate(<div />, document.body.shadowRoot!);

React.unmountComponentAtNode(document.createElement('div'));
React.unmountComponentAtNode(document.createDocumentFragment());
React.unmountComponentAtNode(document.body.shadowRoot!);

React.createPortal(<div />, document.createElement('div'));
React.createPortal(<div />, document.createDocumentFragment());
React.createPortal(<div />, document.body.shadowRoot!);
4 changes: 4 additions & 0 deletions jsconfig-lint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "./jsconfig.json",
"include": ["src/**/*"]
}
9 changes: 6 additions & 3 deletions jsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@
"baseUrl": ".",
"checkJs": true,
"jsx": "react",
"jsxFactory": "createElement",
"jsxFragmentFactory": "Fragment",
"lib": ["dom", "es5"],
"moduleResolution": "node",
"resolveJsonModule": true,
"paths": {
"preact": ["."],
"preact/*": ["./*"]
},
"reactNamespace": "createElement",
"target": "es5"
"target": "es5",
"noEmit": true
},
"exclude": ["node_modules", "dist", "demo"]
"exclude": ["**/node_modules/**", "**/dist/**", "coverage", "demo"]
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@
"test:karma:test-utils": "cross-env PERFORMANCE=false COVERAGE=false BABEL_NO_MODULES=true karma start karma.conf.js --grep=test-utils/test/shared/**.js --no-single-run",
"test:karma:bench": "cross-env PERFORMANCE=true COVERAGE=false BABEL_NO_MODULES=true karma start karma.conf.js --grep=test/benchmarks/**.js --single-run",
"benchmark": "npm run test:karma:bench -- no-single-run",
"lint": "run-s eslint",
"lint": "run-s eslint tsc",
"tsc": "tsc -p jsconfig-lint.json",
"eslint": "eslint src test debug compat hooks test-utils",
"format": "prettier --write \"**/*.{js,jsx,mjs,cjs,ts,tsx,yml,json,html,md,css,scss}\"",
"format:check": "prettier --check '**/*.{js,jsx,mjs,cjs,ts,tsx,yml,json,html,md,css,scss}'"
Expand Down
10 changes: 6 additions & 4 deletions src/clone-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import { assign, slice } from './util';
import { createVNode } from './create-element';

/**
* Clones the given VNode, optionally adding attributes/props and replacing its children.
* @param {import('./internal').VNode} vnode The virtual DOM element to clone
* Clones the given VNode, optionally adding attributes/props and replacing its
* children.
* @param {VNode} vnode The virtual DOM element to clone
* @param {object} props Attributes/props to add when cloning
* @param {Array<import('./internal').ComponentChildren>} rest Any additional arguments will be used as replacement children.
* @returns {import('./internal').VNode}
* @param {Array<ComponentChildren>} rest Any additional arguments will be used
* as replacement children.
* @returns {VNode}
*/
export function cloneElement(vnode, props, children) {
let normalizedProps = assign({}, vnode.props),
Expand Down
28 changes: 14 additions & 14 deletions src/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,21 @@ import { Fragment } from './create-element';
* @param {object} context The initial context from parent components'
* getChildContext
*/
export function Component(props, context) {
export function BaseComponent(props, context) {
this.props = props;
this.context = context;
}

/**
* Update component state and schedule a re-render.
* @this {import('./internal').Component}
* @this {Component}
* @param {object | ((s: object, p: object) => object)} update A hash of state
* properties to update with new values or a function that given the current
* state and props returns a new partial state
* @param {() => void} [callback] A function to be called once component state is
* updated
*/
Component.prototype.setState = function (update, callback) {
BaseComponent.prototype.setState = function (update, callback) {
// only clone state when copying to nextState the first time.
let s;
if (this._nextState != null && this._nextState !== this.state) {
Expand Down Expand Up @@ -56,11 +56,11 @@ Component.prototype.setState = function (update, callback) {

/**
* Immediately perform a synchronous re-render of the component
* @this {import('./internal').Component}
* @this {Component}
* @param {() => void} [callback] A function to be called after component is
* re-rendered
*/
Component.prototype.forceUpdate = function (callback) {
BaseComponent.prototype.forceUpdate = function (callback) {
if (this._vnode) {
// Set render mode so that we can differentiate where the render request
// is coming from. We need this because forceUpdate should never call
Expand All @@ -79,12 +79,12 @@ Component.prototype.forceUpdate = function (callback) {
* @param {object} state The component's current state
* @param {object} context Context object, as returned by the nearest
* ancestor's `getChildContext()`
* @returns {import('./index').ComponentChildren | void}
* @returns {ComponentChildren | void}
*/
Component.prototype.render = Fragment;
BaseComponent.prototype.render = Fragment;

/**
* @param {import('./internal').VNode} vnode
* @param {VNode} vnode
* @param {number | null} [childIndex]
*/
export function getDomSibling(vnode, childIndex) {
Expand Down Expand Up @@ -117,7 +117,7 @@ export function getDomSibling(vnode, childIndex) {

/**
* Trigger in-place re-rendering of a component.
* @param {import('./internal').Component} component The component to rerender
* @param {Component} component The component to rerender
*/
function renderComponent(component) {
let vnode = component._vnode,
Expand Down Expand Up @@ -158,7 +158,7 @@ function renderComponent(component) {
}

/**
* @param {import('./internal').VNode} vnode
* @param {VNode} vnode
*/
function updateParentDomPointers(vnode) {
if ((vnode = vnode._parent) != null && vnode._component != null) {
Expand All @@ -177,7 +177,7 @@ function updateParentDomPointers(vnode) {

/**
* The render queue
* @type {Array<import('./internal').Component>}
* @type {Array<Component>}
*/
let rerenderQueue = [];

Expand All @@ -199,7 +199,7 @@ const defer =

/**
* Enqueue a rerender of a component
* @param {import('./internal').Component} c The component to rerender
* @param {Component} c The component to rerender
*/
export function enqueueRender(c) {
if (
Expand All @@ -215,8 +215,8 @@ export function enqueueRender(c) {
}

/**
* @param {import('./internal').Component} a
* @param {import('./internal').Component} b
* @param {Component} a
* @param {Component} b
*/
const depthSort = (a, b) => a._vnode._depth - b._vnode._depth;

Expand Down
2 changes: 1 addition & 1 deletion src/constants.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const EMPTY_OBJ = {};
export const EMPTY_OBJ = /** @type {any} */ ({});
export const EMPTY_ARR = [];
export const IS_NON_DIMENSIONAL =
/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;
6 changes: 3 additions & 3 deletions src/create-context.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ export function createContext(defaultValue, contextId) {
const context = {
_id: contextId,
_defaultValue: defaultValue,
/** @type {import('./internal').FunctionComponent} */
/** @type {FunctionComponent} */
Consumer(props, contextValue) {
// return props.children(
// context[contextId] ? context[contextId].props.value : defaultValue
// );
return props.children(contextValue);
},
/** @type {import('./internal').FunctionComponent} */
/** @type {FunctionComponent} */
Provider(props) {
if (!this.getChildContext) {
/** @type {import('./internal').Component[]} */
/** @type {Component[]} */
let subs = [];
let ctx = {};
ctx[contextId] = this;
Expand Down
18 changes: 10 additions & 8 deletions src/create-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ let vnodeId = 0;

/**
* Create an virtual node (used for JSX)
* @param {import('./internal').VNode["type"]} type The node name or Component
* constructor for this virtual node
* @param {VNode["type"]} type The node name or Component constructor for this
* virtual node
* @param {object | null | undefined} [props] The properties of the virtual node
* @param {Array<import('.').ComponentChildren>} [children] The children of the virtual node
* @returns {import('./internal').VNode}
* @param {Array<import('.').ComponentChildren>} [children] The children of the
* virtual node
* @returns {VNode}
*/
export function createElement(type, props, children) {
let normalizedProps = {},
Expand Down Expand Up @@ -42,19 +43,20 @@ export function createElement(type, props, children) {

/**
* Create a VNode (used internally by Preact)
* @param {import('./internal').VNode["type"]} type The node name or Component
* @param {VNode["type"]} type The node name or Component
* Constructor for this virtual node
* @param {object | string | number | null} props The properties of this virtual node.
* If this virtual node represents a text node, this is the text of the node (string or number).
* @param {string | number | null} key The key for this virtual node, used when
* diffing it against its children
* @param {import('./internal').VNode["ref"]} ref The ref property that will
* @param {VNode["ref"]} ref The ref property that will
* receive a reference to its created child
* @returns {import('./internal').VNode}
* @returns {VNode}
*/
export function createVNode(type, props, key, ref, original) {
// V8 seems to be better at detecting type shapes if the object is allocated from the same call site
// Do not inline into createElement and coerceToVNode!
/** @type {VNode} */
const vnode = {
type,
props,
Expand Down Expand Up @@ -93,7 +95,7 @@ export function Fragment(props) {
/**
* Check if a the argument is a valid Preact VNode.
* @param {*} vnode
* @returns {vnode is import('./internal').VNode}
* @returns {vnode is VNode}
*/
export const isValidElement = vnode =>
vnode != null && vnode.constructor == undefined;
18 changes: 11 additions & 7 deletions src/diff/catch-error.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
/**
* Find the closest error boundary to a thrown error and call it
* @param {object} error The thrown value
* @param {import('../internal').VNode} vnode The vnode that threw
* the error that was caught (except for unmounting when this parameter
* is the highest parent that was being unmounted)
* @param {import('../internal').VNode} [oldVNode]
* @param {import('../internal').ErrorInfo} [errorInfo]
* @param {VNode} vnode The vnode that threw the error that was caught (except
* for unmounting when this parameter is the highest parent that was being
* unmounted)
* @param {VNode} [oldVNode]
* @param {ErrorInfo} [errorInfo]
*/
export function _catchError(error, vnode, oldVNode, errorInfo) {
/** @type {import('../internal').Component} */
let component, ctor, handled;
/** @type {Component} */
let component,
/** @type {ComponentType} */
ctor,
/** @type {boolean} */
handled;

for (; (vnode = vnode._parent); ) {
if ((component = vnode._component) && !component._processingException) {
Expand Down
Loading

0 comments on commit 180b3b1

Please sign in to comment.