JEP Linker

Gozala edited this page May 3, 2012 · 11 revisions

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:

1. Extraction of requirements

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'
}
/>

2. Requirement type detection

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/).

3. Locating requirements

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

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

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

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
}
/>

4. Backwards compatibility

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:

system ?

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
}

local ?

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
}

test magic ?

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.

5. Graph analyzes

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.

6. Traverse tree

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.

7. Normalization

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

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.