babel-plugin-incremental-dom
Turn JSX into Incremental DOM.
Example
In
{ var header = dataconditional ? <div /> : null; var collection = dataitems; return <div id="container"> header <ul>collection</ul> <p ...dataprops>Some features</p> </div>;}
Out (default, unoptimized options)
{ var header = dataconditional ? : null; var collection = dataitems; ; ; ; ; ; ; ; ; ; ; return ;} var { var wrapper = args ? { return func; } : func; wrapper__jsxDOMWrapper = true; return wrapper;}; var { ;}; var _hasOwn = ObjectprototypehasOwnProperty; var { for var prop in object if _hasOwn ; }; var { var type = typeof child; if type === "number" || type === "string" || child && child instanceof String ; else if type === "function" && child__jsxDOMWrapper ; else if Array child; else ; };
Installation
$ npm install babel-plugin-incremental-dom
Usage
.babelrc
(Recommended)
Via .babelrc
An optional function prefix, runtime, and hoist boolean may be passed.
Via CLI
$ babel --plugins incremental-dom script.js
Via Node API
;
An optional function prefix, runtime, and hoist boolean may be passed.
Options
Hoist
You may enable the experimental hoist
option to hoist static attribute
arrays and element wrappers to the highest available scope. This avoids
expensive instance allocations when running the render function multiple
times.
// Disabled (default) { return ;}
// Enabledvar _statics = "id" "container"; { return ;}
To do this, simply add the hoist
option to the Incremental DOM plugin:
Force Statics Key
Incremental DOM recommends
only using static attribute arrays when a key
is specified. For that
reason this plugin will deoptimize static attributes into dynamic unless
there is a key.
Another option is to generate a cryptographically secure v4 UUID key
,
since the chance of collisions is so infinitesimal.
// Disabled (default) { ; if condition ; } else ; }
// Enabled { ; if condition ; } else ; }
To do this, simply add the forceStatics
option to the Incremental DOM
plugin:
Namspaced Attributes
Incremental DOM supports a few Attribute Namespaces, but those are
foreign to JSX. You can enabled them with the namespaceAttributes
option. Note that this does not enable Namespaced Elements.
// Enabled { return ;}
To do this, simply add the namespaceAttributes
option to the
Incremental DOM plugin:
Inline JSX Expressions
You may enable the experimental inlineExpressions
option to attempt to
inline any variables declared outside the root JSX element. This can
save you from allocating needless closure wrappers around elements that
are only referenced inside the root element.
// Disabled (default) { var header = ; ; ; return ;}
// Enabled { ; ; return ;}
To do this, simply add the inlineExpressions
option to the Incremental DOM
plugin:
Fast Root
You may enable the experimental fastRoot
option so that JSX tags
inside the root element are never wrapped inside a closure. For code
with array maps, this should significantly decrease memory usage and
increase speed.
// Disabled (default) { ; ; return ;}
// Enabled { ; items; return ;}
To do this, simply add the fastRoot
option to the Incremental DOM
plugin:
Alternatively, you may enable and disable this with inline comments:
{ /** * Enable for this tree * @incremental-dom enable-fastRoot */ return <div> items </div>;} /** * Enable for everything under this function * @incremental-dom enable-fastRoot */ { /** * Disable fastroot for this tree * @incremental-dom disable-fastRoot */ return <div> items </div>;}
Components
You may enable the experimental components
option so that JSX tags
that start with an upper case letter are passed as a reference to
incremental DOM calls, instead of as a string. This can be useful when
your code implements components through these kind of calls, though
that's not done by incremental DOM automatically. Note that this will
break unless you have code to handle it.
// Disabled (default) { ;}
// Enabled { ;}
To do this, simply add the components
option to the Incremental DOM
plugin:
Function Prefix
By deafult, babel-plugin-incremental-dom
directly calls Incremental
DOM functions:
// Disabled (default) { ; ;}
If you are instead including Incremental DOM via a browser script, it
may be easier to reference the functions from the IncrementalDOM
object:
// Enabled with `IncrementalDOM` { IncrementalDOM; IncrementalDOM;}
To do this, simply add the prefix
option to the Incremental DOM
plugin:
Runtime
By deafult, babel-plugin-incremental-dom
injects several helpers into
each file as needed. When transforming multiple files with JSX, you can
avoid this helper duplication by specifying a runtime library to use
instead.
The runtime's required functions are:
-
attr
Not to be confused with IncrementalDOM's own
#attr
function, the runtime'sattr
must take in avalue
andattrName
and call IncrementalDOM's#attr
. Basically, it flip flops its parameters so thatIncrementalDOM#attr
can be used in aArray#forEach
like method signature.runtime {IncrementalDOM;}; -
forOwn
No surprises here, this iterates over every enumerable-own property of
object
, callingiterator
with the property's value and name.runtime {var hasOwn = ObjectprototypehasOwnProperty;for var prop in objectif hasOwn;}; -
jsxWrapper
To prevent iDOM's incremental nature from screwing up our beautiful JSX syntax, certain elements must be wrapped in a function closure that will be later evaluated. That closure will be passed into
jsxWrapper
, along with an array of any (if any) arguments needed to render the contained JSX element.Note it is not
jsxWrapper
's responsibility to create the JSX closure, merely to help identify the passed in closure later. Here, we set the__jsxDOMWrapper
property of the returned closure.runtime {var wrapper = args ? {return elementClosure;} : jsxClosure;wrapper__jsxDOMWrapper = true;return wrapper;} -
renderArbitrary
To render child elements correctly, we'll need to be able to identify them.
renderArbitrary
receives achild
, and must call the appropriate action. For string and numbers, that's to callIncrementalDOM#text
. For wrapped JSX Closures, that's to invoke the closure. For arrays, that's to render every element. And for objects, that's to render every property.Note that we identify JSX Closures by the
__jsxDOMWrapper
property we set inside thejsxWrapper
runtime function.runtime {var type = typeof child;if type === "number" || type === string || type === 'object' && child instanceof String;else if type === "function" && child__jsxDOMWrapper;else if Arraychild;else if type === 'object' && Stringchild === '[object Object]'runtime;}
To do this, simply add the runtime
option to the Incremental DOM
plugin: