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

JEP Packageless

Gozala edited this page Apr 2, 2012 · 5 revisions

This page describes how package-less SDK and may look like & steps required to get there.

Rationale

  • Packages seem to be too heavy weight.
  • Packages require tools like package managers.
  • Packages require repositories for publishing them.
  • Package identifiers / names may conflict with each other.
  • Modules are light & encourage minimalism.
  • Modules don't require much tools since they are just URLs to actual code.
  • Modules don't need central repository, web is the a one.
  • Modules are similar to script tags and that's [what we'll have on the web] (http://wiki.ecmascript.org/doku.php?id=harmony:modules)

What is an add-on

Add-on is just a directory with a package descriptor and an entry point module in it. In a common case this will look as follows:

${pwd}/package.json
     ./main.js
     ./icon.png
     ./test/all.js

Module dependencies

Module may express it's dependencies via require statements. SDK will recognize following types of dependencies:

System

System modules IDs have a special require form that starts with @ character.

require("@[${name}/]${path}[.js][;${version}]")
  • ${name} is a unique module group identifier. As firefox features will be implemented using SDK, such groups can be created for feature specific APIs. For convenience ${name}/ can be omitted, in which case default group "sdk" will be used.
  • ${path} is a module file path with in a group.
  • ${version} is a string like 1.5. Versions suffix is just a convention that SDK MAY recognize in order to allow smoother transition from deprecated APIs.
require('@tabs')
require('@sdk/window/events')
require('@devtools/scratchpad')
require('@panel;1.5')

System modules intentionally have a visually distinct form and independent namespace:

  1. To signify the fact that such module are provided by a runtime & may not even have associated JS file.
  2. Having distinct namespace avoids conflicts with other dependencies which would have introduced room for surprises. With shared namespace either system or non-system module would have win the conflict, which is not problem free in both cases. System wins approach used by nodejs is not well suited for SDK, since deployment platform (Firefox) is very different, it may changes and introduce conflicting modules in a future releases causing add-on breakage. Non-system wins approach is also problematic, since it may be confusing for people coming from nodejs, in addition nested dependencies may shadow system modules without users knowledge or a way to load system modules.
  3. SDK has no way of knowing what will be available in the deployment runtime, while for system modules assumption that module is shipped with runtime is not-comforting sharing same namespace with other types of modules will SDK unable to catch missing dependencies as they will automatically be assumed to be shipped with a runtime.
  4. @ prefix intentionally replicates harmony's form:
module stdlib at '@std'; // => var stdlib = require('@std')

Local

Addons code may be authored in multiple modules located near each other. This type of modules, or more explicitly, modules that can be referenced via relative path are considered as local. All local module requirements IDs start with . character ('./foo', '../bar'):

require('./toolbar')
require('./toolbar/view')
require('../../outside/of/addon')

External

Modules are all about sharing reusable pieces of code. Most of the reusable code does not needs to be in core for add-on's to use it. Dependency on such third party developed modules / libraries are considered as external (as it's not part of system or add-on code). These modules are also visually distinct from other forms as they don't start with @ or . characters:

require('fs')
require('streamer')
require('backbone/model')

Special forms

Multiple different tools may be used in order to organize external dependencies. SDK will come with have built-in tool to will make dependency management seamless for external dependencies that will have a distinct forms:

URLs

External dependencies that have form of http / https URLs (for convenience http:// and https:// may be omitted) will be recognized by an SDK and instilled at build time so that they will be requireable at runtime:

require('http://git.io/streamer.js') // => require('git.io/streamer')
require('raw.github.com/gist/1733262/d98dd3f854eed5deb129122e2459de10a5e609a0/recovery')

Not that modules are not fetched from the URLs at runtime, those modules are bundled with add-on during build time and are somewhat equivalent of JS files in HTML app-cache manifest. Also this form intentionally resembles on from Harmony.

Assets

Remote modules written for third party consumption may depend on other assets. Such dependencies may be declared via special require form that SDK will recognize and bundle with an add-on.

var iconURI = require('asset!./icon.png')

Mechanics

Add-on may be build by running cfx xpi command from an add-on directory. When XPI is build all .js files in ${pwd} and it's nested directories (except ${pwd}/test/ and ${pwd}/@modules/) will be included into XPI in exact same layout as presented on filesystem. In addition ${pwd}/data/ ${pwd}/icon.png and ${pwd}/index.html will be included

System modules

System modules in a future will be shipped with a firefox and will directly map to an associated resource URIs (this URI mapping is hypothetical and may end up slightly different in practice):

@${name}/${path/to}[;${version}] => resource:///commonjs/${name}[;${version}]/${path/to}.js
require('@tabs')                 // require('resource:///commonjs/sdk/tabs.js')
require('@sdk/window/events')    // require('resource:///commonjs/sdk/window/events.js')
require('@devtools/scratchpad')  // require('resource:///commonjs/devtools/scratchpad.js')
require('@panel;1.5')            // require('resource:///commonjs/sdk;1.5/panel.js')

Until then standard library modules will be included into XPI and will be available under special URIs:

require('@tabs')                 // require('resource://jid/@modules/@sdk/tabs.js')
require('@sdk/window/events')    // require('resource://jid/@modules/@sdk/window/events.js')

Note: System modules are copied to a special @modules folder along with other dependencies. Also, they won't conflict with other dependencies since they have distinct @ prefix.

Local modules

All the local modules (any file with .js extension in the add-on directory and it's nested directories except ${pwd}/test/ and ${pwd}/@modules/) are included in a xpi with a same layout as presented in the source directory. These files will be mapped to an associated resource URIs:

require('./foo')                    // => require('resource://${jid}/foo.js')
require('./foo/bar')                // => require('resource://${jid}/foo/bar.js')
require('../../bla')                // => require('resource://${jid}/@modules/__/__/bla.js')

Local modules that are out of the add-on folder will be copied into special @modules directory. And path will be transformed as shown in example above.

External dependencies

External dependencies are also included into XPI as long as they are located under the special @modules folder:

require('fs')                                      // => require('resource://${jid}/@modules/fs.js')
require('underscorejs.org/underscore')             // => require('resource://${jid}/@modules/underscorejs.org/underscore.js')

External dependencies that have special forms supported by SDK will be automatically downloaded and correctly organized in special @modules folder:

// Special
require('http://underscorejs.org/underscore.js')   // => require('underscorejs.org/underscore')

Alternatively users may choose to use npm like package managers for more complicated cases to organizing dependencies in a special @modules directory.

TODO:

  • Move all SDK modules into ${SDK}/lib/
  • Make all SDK library modules require-able as follows:
require("@${name}/${path}[.js][;${version}]")
require('@addon/panel;1.4')
require('@addon/system')

Backwards compatibility

  • Make it possible to omit @ in which case cfx will perform search:

    1. ${SDK}/lib/ (normalization panel -> @panel)
    2. If require term starts with addon-kit/ or api-utils/ it will be stripped out and searched again in ${SDK}/lib/ (normalization api-utils/system - @system)
    3. In ${PWD} (normalization foo -> ./foo)
    4. In folders one level up to current one (to support case of packages folder ?)

If module is found print out deprecation warning explaining the normalization and encouraging use of new standard.

  • Support third party module dependencies by locating it in a @modules folder. Requirement in form of require(!host.com/module/path') will have to be located under the following location: ${ADDON}/@modules/!host.com/path/to/module.js

If requirement is not found it will be downloaded to: ${ADDON}/@modules/!host.com/path/to/module.js from one of the following locations:

  1. https://git.io/streamer
  2. http://git.io/streamer
  3. https://git.io/streamer.js
  4. http://git.io/streamer.js

Note: That that if require term contains protocol scheme http!git.io/streamer then discovery will be limited to given protocol only.

  • Support third party assets by locating them in a special @modules folder or by downloading them there in a same exact way as in case of modules.