babel-plugin-transform-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)
const _li$statics = "key" "";const _li$wrapper = { _li$statics1 = _item$id; ; ; return ;}; const _header$wrapper = { return ;}; const _div$statics = "id" "container"; { var header = dataconditional ? : null; var collection = dataitems; ; ; ; ; ; ; ; ; ; ; return ;} // Helpers// ------- var { return __jsxDOMWrapper: true func: func args: args ;}; var { ;}; var { ;}; var _hasOwn = ObjectprototypehasOwnProperty; var { for var prop in object if _hasOwn ;}; var { var type = typeof child; if type === "number" || type === "string" || type === "object" && child instanceof String ; else if Array child; else if type === "object" if child__jsxDOMWrapper var func = childfunc args = childargs; if args func; else ; else if Stringchild === "[object Object]" ; };
Installation
$ npm install babel-plugin-transform-incremental-dom
Usage
.babelrc
(Recommended)
Via .babelrc
Any of the configuration options may also be passed.
Via CLI
$ babel --plugins transform-incremental-dom script.js
Via Node API
;
Any of the configuration options may also be passed.
Options
Require Statics Key
Incremental DOM recommends
only using static attribute arrays when a key
is specified. For that
reason this plugin will automatically generate a UUID key if there is
not one and there are static attributes.
Alternatively, you may disable the automatic generation. In this case, static attributes will be deoptimized into the dynamic attributes list.
// Disabled (default)var _statics = "key" "key" "href" "http://key/specified";var _statics2 = "href" "http://example.com";var _statics3 = "href" "http://other.com"; { ; if condition ; } else ; }
// Enabledvar _statics = "key" "key" "href" "http://key/specified"; { ; if condition ; } else ; }
To do this, simply add the requireStaticsKey
option to the Incremental DOM
plugin:
UUID Prefix
Together with automatic UUID generation, you can specify a UUID "prefix" that will allow for shorter automatic keys. You must ensure that this prefix will not conflict with any other value you use as a key.
When using the UUID prefix, a simple counter is used instead of creating a true UUID.
// Disabled (default)var _statics = "href" "http://example.com"; { ;}
// Enabled ("uuid-")var _statics = "href" "http://example.com"; { ;}
To do this, simply add the uuidPrefix
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:
Auto Importing Incremental DOM
By deafult, babel-plugin-transform-incremental-dom
expects any necessary
Incremental DOM methods to already imported and in-scope:
// Disabled (default) // Manually managed imports; { ; ; ;}
This is a hassle if you suddenly use a spread attribute and don't
remember to import elementOpenStart
, elementOpenEnd
, and attr
methods. And, attr
and text
are very generic names, leading to
issues where you might redefine the variable in the rendering function,
breaking it.
This can be fixed entirely by auto-importing everything necessary from Incremental DOM module:
// Enabled with `incremental-dom` // Auto generated importvar _incrementalDOM = ; { 0 _incrementalDOMelementOpen"div"; 0 _incrementalDOMtext"Hello World."; 0 _incrementalDOMelementClose"div";}
To do this, simply add the moduleSource
option to the Incremental DOM
plugin:
Additionally, the module source can be an absolute or relative path to
the module. If a relative path is used, it will be resolve relative to
the process.cwd()
of the babel process.
Skip Children
Some templates do not control all the child elements of certain nodes, like components. Incremental DOM supports such nodes using a "skip" child. To support this, we identify a special skip attribute, which will not contain any child nodes and will never clear the later-added children of the element.
{ // This node will neither create nor clear children. return <div __skip> </div>;}
To customize the attribute that signals a skip, simply add the
skipAttribute
option to the Incremental DOM plugin:
Runtime
By deafult, babel-plugin-transform-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:
-
jsxWrapper
To prevent iDOM's incremental nature from screwing up our beautiful JSX syntax, certain elements rendering functions must be wrapped evaluated at a later time. The element 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 rendering function, merely to mark the passed in function as a lazy evaluation. Here, we return a special__jsxDOMWrapper
struct with the needed information.runtime {return__jsxDOMWrapper: truefunc: elementFnargs: args;} -
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 lazy evaluation JSX functions, 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 lazy JSX functions by the
__jsxDOMWrapper
struct we created inside thejsxWrapper
runtime function.runtime {var type = typeof child;if type === "number" || type === string || type === 'object' && child instanceof StringiDOMtextchild;else if Arraychild;else if type === "object"if child__jsxDOMWrappervar func = childfunc args = childargs;if argsfunc;else;else if Stringchild === "[object Object]";} -
spreadAttribute
To set every attribute inside a SpreadAttribute, we'll need to iterate over every property and determine if it's an own property. If so, call
IncrementalDOM#attr
to set it.runtime {for var prop in spreadif ObjectprototypehasOwniDOM;}
To do this, simply add the runtimeModuleSource
option to the
Incremental DOM plugin:
Additionally, the runtime module source can be an absolute or relative
path to the module. If a relative path is used, it will be resolve
relative to the process.cwd()
of the babel process.