-
Notifications
You must be signed in to change notification settings - Fork 263
JEP Linker
This page describes linker process that will be used by cfx-js to build an add-on manifest. Linker will perform multiple iterations of graph refinements before reaching a final manifest format. Following sections describe iterations and expected results:
Linker takes add-on's root
path and entry
point module
path (relative path to an add-on root
) as an arguments
and starts building a dependency graph for the given module.
Linker reads module source, and extracts requirements to
spit out stream of requirement nodes:
link('./main.js', './addon/')
<stream
{
requirement: './utils',
requirer: './main.js',
root: './addon/'
}
{
requirement: 'panel',
requirer: './main.js',
root: './addon/'
}
{
requirement: 'devtools/scratchpad',
requirer: './main.js',
root: './addon/'
}
{
requirement: 'fs',
requirer: './main.js',
root: './addon'
}
{
requirement: 'tabs',
requirer: './main.js',
root: './addon',
}
{
requirement: 'foo/bar',
requirer: './main.js',
root: './addon'
}
/>
In this iteration node of the generate graph are expanded to include information about requirement type ('local', 'system', 'external') and expected paths:
<stream
{
requirement: './utils',
requirer: './main.js',
root: './addon/',
type: 'local',
path: './utils.js'
}
{
requirement: 'panel',
requirer: './main.js',
root: './addon/'
type: 'system',
path: './std/panel.js'
}
{
requirement: 'devtools/scratchpad',
requirer: './main.js',
root: './addon/,
type: 'system',
path: './devtools/scratchpad.js'
}
{
requirement: 'fs',
requirer: './main.js',
root: './addon',
type: 'external',
path: './fs.js'
}
{
requirement: 'tabs',
requirer: './main.js',
root: './addon',
type: 'system',
path: './tabs.js'
}
{
requirement: 'foo/bar',
requirer: './main.js',
root: './addon',
type: 'external',
path: './foo/bar.js'
}
/>
*Note: path
on 'local' and 'external' nodes are relative
to add-on root
. For 'system' nodes it is relative to a
loader baseURI
(In case of SDK add-on's it's
resource:///modules/
).
In this iteration linker locates module requirements from graph in the filesystem. This process is type specific, so requirements grpah is fork-joined by a node types.
Local requirements are located under the node path
relative to the add-on root. Nodes are expanded with an
information of file existence:
<stream
{
requirement: './utils',
requirer: './main.js',
root: './addon/',
type: 'local',
path: './utils.js',
located: true
}
/>
System requirements are located under the node path
relative to the add-on SDK lib
. Nodes are expanded
with an information of file existence:
<stream
{
requirement: '@panel',
requirer: './main.js',
root: './addon/'
type: 'system',
path: './sdk/panel.js',
located: true
}
{
requirement: '@devtools/scratchpad',
requirer: './main.js',
root: './addon/,
type: 'system',
path: './devtools/scratchpad.js',
located: false
}
/>
External dependencies are located under under the node
path
relative to @modules
directory in an add-on root
.
Nodes are expanded with an information of file existence:
<stream
{
requirement: 'fs',
requirer: './main.js',
root: './addon',
type: 'external',
path: './fs.js',
located: true
}
{
requirement: 'tabs',
requirer: './main.js',
root: './addon',
type: 'external',
path: './tabs.js',
located: false
}
{
requirement: 'foo/bar',
requirer: './main.js',
root: './addon',
type: 'external',
path: './foo/bar.js',
located: false
}
/>
In this iteration graph is fork-joined for external
type
nodes that were not located (node located
is false
). We
do this several times, once per fallback algorithm:
Assuming that user meant system module we fork-join
sub-graph to refine nodes that can be discovered in
add-on SDK lib
:
{
requirement: 'tabs',
requirer: './main.js',
root: './addon',
type: 'deprecated',
correction: { type: 'system', requirement: '@tabs' },
path: './sdk/tabs.js',
located: true
}
Assuming that user meant local module (located relative to a
requirer) we fork-join sub-graph to refine nodes that can be
found under the node path
resolved to it's requirer
:
{
requirement: 'foo/bar',
requirer: './main.js',
root: './addon',
type: 'deprecated',
correction: {
type: 'local',
requirement: './foo/bar'
},
path: './foo/bar.js',
located: true
}
If we run linker in test mode we fork-join sub-graph to
refine nodes that could have being discovered if we were
running cfx test
in older versions of cfx. If node path
relative to add-on root
exists we enhance node
accordingly.
In this iteration we filter graph by nodes of deprecated
type and log deprecation warnings with instructions of
correct requirement forms. Then we filter graph by all
non-system type nodes that could not be located report
errors and quit.
In this iteration we traverse nested requirements by processing each node through linker process described by this document. Once we're done traversing a tree we flatten in down.
In this iteration we normalize manifest to standard compilation ready form:
{
"./main": {
"type": "local",
"requirements": {
"./utils/model": "./utils/model"
}
},
"./utils/model": {
"type": "local",
"requirements": {
"panel": "sdk/panel",
"api-utils/functional": "api-utils/functional",
"devtools/scratchpad": "devtools/scratchpad",
"./controller": "./utils/controller",
"backbone/events": "backbone/events"
}
},
"./utils/controller": {
"type": "local",
"requirements": {
// ...
}
},
"api-utils/functional": {
"type": "deprecated",
"correction": "sdk/functional"
},
"sdk/functional": {
"type": "std"
},
"panel": {
"type": "std"
},
"devtools/scratchpad": {
"type": "system"
},
"backbone/events": {
"type": "external"
}
}
This manifest format can be later visualized using a tool to help reviewers. Example of how this might look like http://bl.ocks.org/2582184