Skip to content
This repository has been archived by the owner on Sep 3, 2022. It is now read-only.

Commit

Permalink
Updated JSDoc Types.
Browse files Browse the repository at this point in the history
1. Imported types as typedef instead of importing inline.
2. Reduced the number of type casts by changing types from `Node` to `Element` where possible.
3. Add `checkjs` to package to run  tsc to verify TypeScript parsing passes.
4. Build script now automatically runs type checker.
Wobbabits committed Jul 12, 2018
1 parent e0824f9 commit 4ee632e
Showing 22 changed files with 121 additions and 42 deletions.
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -161,6 +161,34 @@ Because Composi uses JSX, there are many similarities to React patterns. Please

Composi is small, just 3KB for the gzipped core. It loads quickly. Its small and focused API means you can learn everything in half a day and be productive. If you're already familiar with JSX, then you only need to learn the Component API. You can easily do that in an hour.

Type Safety
-----------

Composi is written in standard ES6 with JSDoc comments to document type usage. This exposes Composi's type system to TypeScript during build time to verify that the source code is correctly typed. This also provides enhanced features when using Composi with [Visual Studio Code](https://code.visualstudio.com). Open `Settings` from the `Preferences` menu in Visual Studio Code and add the following setting:

```javascript
"javascript.implicitProjectConfig.checkJs": true
```
This tells Visual Studio Code to use TypeScript to check the JavaScript in a project. By default it uses inference to understand JavaScript types, which is very lax. But with JSDoc comments specifying types, it uses those instead for a more strict check of the code structure.

This also gives intellisense when hovering over terms, intelligent code completion, symbol definition peek, symbol renaming across files, typo detection, flagging of unused variables, and wrong use of types as parameters, wrong number of parameters, etc. All of this is happening live, as you are coding. Not build step necessary.

You can run a type check on Composi with TypeScript using this package script:

```javascript
npm run checkjs
```
This test gets run automatically when performiing a build:

```shell
npm run build
> npm run format && npm run lint && npm run checkjs && npm run bundle && gulp gzip
> prettier --no-semi --single-quote --write ./lib/*.js ./lib/utils/*.js
> eslint --config ./.eslintrc.json lib
> tsc --allowJs --checkJs --noEmit --target ES6 lib/*.js lib/**/*.js
> rollup -c
```

Running Tests
-------------

2 changes: 1 addition & 1 deletion dist/composi.js

Large diffs are not rendered by default.

Binary file modified dist/composi.js.gzip
Binary file not shown.
2 changes: 1 addition & 1 deletion dist/composi.js.map

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions lib/fragment.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/**
* @typedef {import('./h').VNode} VNode
*/
/**
* A tag to enable returning sibling elements. This is useful for returning list items to render in a list or table cells to render in a table row.
* @example
@@ -13,7 +16,7 @@
* h('li', {}, 'C')
* ])
* @param {Object} [props] When using Fragment as a function, props is the first argument. Provide either null or {} as the value for props.
* @param {import('./h').VNode[]} [children] The siblings to return with the Fragment. This will be an array of sibling elements.
* @return {import('./h').VNode[]} An array of virtual nodes.
* @param {VNode[]} [children] The siblings to return with the Fragment. This will be an array of sibling elements.
* @return {VNode[]} An array of virtual nodes.
*/
export const Fragment = (props, children) => children
12 changes: 10 additions & 2 deletions lib/patch.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
import { patchElement } from './utils/patchElement'
/**
* @typedef {import('./h').VNode} VNode
*/

/**
* A function to patch a virtual node against a DOM element, updating it in the most efficient manner possible.
* @param {() => import('./h').VNode} node A function that returns a virtual node. This may be a JSX tag, which gets converted into a function, or a hyperscript function.
* @param {() => VNode} node A function that returns a virtual node. This may be a JSX tag, which gets converted into a function, or a hyperscript function.
* @param {Node} [element] The element to patch.
* @return {Node} The updated element.
*/
export function patch(node, element) {
if (element) {
patchElement(element.parentNode, element, element && element['vnode'], node)
patchElement(
element.parentNode,
/** @type{Element} */ (element),
element && element['vnode'],
node
)
} else {
element = patchElement(null, null, null, node)
}
5 changes: 4 additions & 1 deletion lib/utils/componentHelpers/handleSetState.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { mixin } from '../mixin'
import { isObject } from '../componentHelpers/isObject'

/**
* @typedef {import('../../component').Component} Component
*/
/**
* A helper function for the Component class. This sets state on the class component provided.
* @param {*} data Data to use as state.
* @param {import('../../component').Component} component A reference to the component to use.
* @param {Component} component A reference to the component to use.
* @return {void} undefined
*/
export function handleSetState(data, component) {
10 changes: 8 additions & 2 deletions lib/utils/componentHelpers/isSameNode.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
/**
* @typedef {import('../../h').VNode} VNode
*/
/**
* @typedef {import('../../component').Component} Component
*/
/**
* A function to test whether the data provided for updating a component creates a new virtual node or not.
* @param {import('../../h').VNode} oldNode The previous virtual node of a component.
* @param {VNode} oldNode The previous virtual node of a component.
* @param {*} data Data to be used when rendering a new virtual node for a component.
* @param {import('../../component').Component} component A reference to the component being used.
* @param {Component} component A reference to the component being used.
* @return {boolean} boolean
*/
export function isSameNode(oldNode, data, component) {
5 changes: 4 additions & 1 deletion lib/utils/componentHelpers/unmountComponent.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { eventWhitelist } from './eventWhitelist'

/**
* @typedef {import('../../component').Component} Component
*/
/**
* This function will unmount the provided component. Doing so it unregisters a whitelist of events, deletes the base element of the component from the DOM, and sets the component instance properties to null.
* @param {import('../../component').Component} component
* @param {Component} component
* @return {void} undefined
*/
export function unmountComponent(component) {
6 changes: 4 additions & 2 deletions lib/utils/componentHelpers/updateComponent.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { isSameNode } from './isSameNode'
import { patch } from '../../patch'

/**
* @typedef {import('../../component').Component} Component
*/
/**
* This function updates an already rendered component. In doing so it checks to see if user provided data as an argument to this function. If data was provided, it uses that to render the component. Otherwise it checks if the component has state. If true, the function uses that to render the component. If no data was provided and the component is stateless, nothing will happen.
* @param {boolean | number | string | Object.<string, any> | any[]} data
* @param {import('../../component').Component} component
* @param {Component} component
* @return {void} undefined
*/
export function updateComponent(data, component) {
10 changes: 8 additions & 2 deletions lib/utils/patchElement.js
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ import { removeOldKeyedElements } from './patchElementHelpers/removeOldKeyedElem
/**
* A function to diff and patch a DOM node with a virtual node.
* @param {Node} parent The parent node of the elment being patched.
* @param {Node} element The element being patched.
* @param {Element} element The element being patched.
* @param {Object} oldNode A virtual dom node from the previous patch.
* @param {Object} newNode The current virtual dom node.
* @param {boolean} [isSVG] Whether we are dealing with an SVG element or not.
@@ -20,7 +20,13 @@ export function patchElement(parent, element, oldNode, newNode, isSVG) {
if (newNode === oldNode) {
return
} else if (oldNode == null || oldNode.type !== newNode.type) {
element = createNewElement(newNode, isSVG, parent, element, oldNode)
element = /** @type {Element} */ (createNewElement(
newNode,
isSVG,
parent,
element,
oldNode
))
} else if (oldNode.type == null) {
element.nodeValue = newNode
} else {
6 changes: 3 additions & 3 deletions lib/utils/patchElementHelpers/createNewElement.js
Original file line number Diff line number Diff line change
@@ -6,8 +6,8 @@ import { removeElement} from '../patchElementHelpers/removeElement'
* @param {Node} node
* @param {boolean} isSVG
* @param {Node} parent
* @param {Node} element
* @param {Node} oldNode
* @param {Element} element
* @param {Element} oldNode
* @return {Node} Node
*/
export function createNewElement(node, isSVG, parent, element, oldNode) {
@@ -18,6 +18,6 @@ export function createNewElement(node, isSVG, parent, element, oldNode) {
removeElement(parent, element, oldNode)
}
}
element = newElement
element = /** @type {Element} */(newElement)
return element
}
5 changes: 4 additions & 1 deletion lib/utils/patchElementHelpers/getKey.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
/**
* @typedef {import('../../h').VNode} VNode
*/
/**
* Function to get a node's key.
* @param {import('../../h').VNode} node A virtual node.
* @param {VNode} node A virtual node.
* @return {string | number | null} key.
*/
export const getKey = node => (node ? node.key : null)
6 changes: 3 additions & 3 deletions lib/utils/patchElementHelpers/removeChildren.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
/**
* A function to remove the children of a node.
* @param {Node} element The parent of the node whose children will be removed.
* @param {Node} node The node whose children will be removed.
* @param {Element} node The node whose children will be removed.
* @return {Node} element The parent of the removed nodes.
*/
export function removeChildren(element, node) {
const props = node['props']
if (props) {
for (let i = 0; i < /** @type {Element} */ (node).children.length; i++) {
removeChildren(element.childNodes[i], /** @type {Element} */ (node).children[i])
for (let i = 0; i < node.children.length; i++) {
removeChildren(element.childNodes[i], node.children[i])
}
}
return element
12 changes: 8 additions & 4 deletions lib/utils/patchElementHelpers/removeElement.js
Original file line number Diff line number Diff line change
@@ -4,14 +4,18 @@ import { removeChildren } from './removeChildren'
* Function to remove element from DOM.
* @param {Node} parent The containing element in which the component resides.
* @param {Node} element The parent of the element to remove.
* @param {Node} node The element to remove.
* @param {Element} node The element to remove.
* @return {void} undefined
*/
export const removeElement = (parent, element, node) => {
parent.removeChild(removeChildren(element, node))
if (node && /** @type {Object.<string, any>}*/(node).props && /** @type {Object.<string, any>}*/(node).props.onComponentDidUnmount) {
/** @type {Object.<string, any>}*/(node).props.onComponentDidUnmount.call(
/** @type {Object.<string, any>}*/(node).props.onComponentDidUnmount,
if (
node &&
node['props'] &&
node['props'].onComponentDidUnmount
) {
node['props'].onComponentDidUnmount.call(
node['props'].onComponentDidUnmount,
parent
)
}
8 changes: 4 additions & 4 deletions lib/utils/patchElementHelpers/setProp.js
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ import { handleXlinkHref } from './setPropHelpers/handleXlinkHref'

/**
* Function to set properties and attributes on element.
* @param {Node} element The element to set props on.
* @param {Element} element The element to set props on.
* @param {string} prop The property/attribute.
* @param {*} value The value of the prop.
* @param {string | number | boolean} oldValue The original value of the prop.
@@ -21,7 +21,7 @@ export function setProp(element, prop, value, oldValue, isSVG) {
prop === 'onComponentWillUnmount'
) {
return
} else if (prop === 'style' && typeof value !== 'string') {
} else if (prop === 'style' && typeof value === 'object' && !Array.isArray(value)) {
handleStyles(element, prop, value, oldValue)
} else {
// Convert camel case props to lower case:
@@ -49,7 +49,7 @@ export function setProp(element, prop, value, oldValue, isSVG) {
if (value === 'true') value = ''
// Set prop as attribute, except dangerouslySetInnerHTML:
if (prop !== 'dangerouslysetinnerhtml') {
/** @type {Element} */ (element).setAttribute(prop, value)
element.setAttribute(prop, value)
}
}
}
@@ -62,7 +62,7 @@ export function setProp(element, prop, value, oldValue, isSVG) {
value === 'no' ||
value === 'off'
) {
/** @type {Element} */ (element).removeAttribute(prop)
element.removeAttribute(prop)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/**
* Enable setting innerHTML as a prop.
* @param {Node} element
* @param {Element} element
* @param {string} prop
* @param {*} value
* @return {void} undefined
*/
export function handleDangerouslySetInnerHTML(element, prop, value) {
if (prop === 'dangerouslysetinnerhtml') {
/** @type {Element} */ (element).innerHTML = value
element.innerHTML = value
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/**
* Enable setting xlink href value for browser that only support SVG 1.0.
* @param {Node} element
* @param {Element} element
* @param {string} prop
* @param {*} value
* @return {void} undefined
*/
export function handleXlinkHref(element, prop, value) {
/** @type {Element} */ (element).setAttributeNS('http://www.w3.org/1999/xlink', 'href', value);
/** @type {Element} */(element).setAttribute('href', value)
element.setAttributeNS('http://www.w3.org/1999/xlink', 'href', value);
element.setAttribute('href', value)
}
6 changes: 4 additions & 2 deletions lib/utils/patchElementHelpers/trackOldElements.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { getKey } from '../patchElementHelpers/getKey'

/**
* @typedef {import('../../h').VNode} VNode
*/
/**
* Update values for old element and key.
* @param {Node} element
* @param {Node[]} oldElements
* @param {import('../../h').VNode[]} oldChildren
* @param {VNode[]} oldChildren
* @param {Object.<string, any>} oldKeyed
* @return {void} undefined
*/
7 changes: 5 additions & 2 deletions lib/utils/patchElementHelpers/updateElement.js
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ import { setProp } from '../patchElementHelpers/setProp'

/**
* @description A function to update an element based on a virtual dom node.
* @param {Node} element
* @param {Element} element
* @param {Object.<string, any>} oldProps The original props used to create the element.
* @param {Object.<string, any>} props New props generated by the virtual dom.
* @param {boolean} isSVG Whether we are dealing with SVG or not.
@@ -21,7 +21,10 @@ export function updateElement(element, oldProps, props, isSVG) {
}

// Handle lifecycle hook:
if (/** @type {Object.<string, any>}*/(element).mounted && props && props.onComponentDidUpdate) {
if (
element['mounted'] &&
props &&
props.onComponentDidUpdate) {
props.onComponentDidUpdate.call(
props.onComponentDidUpdate,
oldProps,
8 changes: 7 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{
"name": "composi",
"version": "2.5.0",
"version": "2.5.1",
"description": "A JavaScript library for creating websites, PWAs and hybrid apps.",
"main": "index.js",
"scripts": {
"bundle": "rollup -c",
"build": "npm run format && npm run lint && npm run bundle && gulp gzip",
"build": "npm run format && npm run lint && npm run checkjs && npm run bundle && gulp gzip",
"checkjs": "tsc --allowJs --checkJs --noEmit --target ES6 lib/*.js lib/**/*.js",
"test": "gulp test",
"prepare": "npm run build",
"format": "prettier --no-semi --single-quote --write ./lib/*.js ./lib/utils/*.js",
@@ -61,6 +62,7 @@
"rollup-plugin-commonjs": "^8.2.0",
"rollup-plugin-node-resolve": "^3.0.0",
"rollup-plugin-resolve": "0.0.1-predev.1",
"rollup-plugin-uglify": "^2.0.1"
"rollup-plugin-uglify": "^2.0.1",
"typescript": "^2.9.2"
}
}

0 comments on commit 4ee632e

Please sign in to comment.