JSPM CDN Module Resolution
This document summarizes the resolution conventions used by @jspm/generator and the JSPM CLI.
These resolution rules enable:
- Comprehensive version resolution over arbitrary CDN installs.
- Universal resolution standard for URLs that supports package resolution features expected for modern workflows (conditional resolution, dependency version resolution, basic resolution rules).
- Backwards compatibility with current JS ecosystem conventions as an extension of the Node.js resolution rules to arbitrary URLs.
A package is a well-formed URL ending in a "/" that lies on a package boundary.
All URLs are contained within a package boundary. For any URL, the package boundary can be found based on the following rule.
A package boundary is a URL, u, ending in a "/", satisfying one of the following two properties:
- The URL corresponds to a well-known CDN package format which designates the package boundary. For example, https://deno.land/x/[pkgname]@vx.y.z/ is a package boundary due to the rules of the Deno CDN. These rules are hard-coded based on the unique CDN semantics.
new URL('./package.json', u)is an existing package.json file.
- The URL corresponds to a root URL of a host.
For (2), this involves hierarchically checking parent URLs for the existence of a package.json file, until we reach the root of the host.
(2) provides compatibility with the Node.js ecosystem and also provides a convention for determining network package boundaries such that packages copied from the local file system to be hosted on static URLs can still support package configuration-based resolution information.
(1) enables well-known CDNs to have their own custom boundary and configuration rules that avoid unnecessary GET requests to determine network package boundaries.
(1) is always checked before (2) above.
Every package boundary has a package configuration.
- If the URL corresponds to a well-known CDN then that CDN can provide any custom package configuration API which takes as input the package boundary and returns the package configuration.
- Otherwise, if the package has a package.json in its package boundary, then as per the previous section this provides the package configuration.
Package configuration is a JSON file with the following optional fields that are used by the resolver:
- name: The package name according to itself. A package may be aliased when imported by other importers, but this name is the name the package aliases itself. This enables package own name resolution (
import('name/export')) working from within the modules of the package itself, identical to the Node.js package resolution.
- imports: The internal package imports, as per Node.js package resolution.
- exports: The internal package exports, as per Node.js package resolution.
- dependencies: The internal package dependencies, as per npm.
- peerDependencies: The internal package peerDependencies, as per npm.
- optionalDependencies: The internal package optionalDependencies, as per npm.
A list of condition names under which resolution is being performed.
"imports" resolution, custom condition names can be used to branch resolutions. These define both properties of the environment (eg production versus development) as well as the environment itself, via well-known runtime names.
In order to select a specific branch for resolution, a list of applicable conditions must be provided to the resolution algorithm. For example:
A bare specifier as defined in the HTML specification is a string that does not start with
"/" and is not a valid URL.
Bare specifiers are explicitly handled by a package lookup system or import map, import maps should always take preference in bare specifier resolutions falling back on unmatched specifiers to an optional internal package lookup system.
The resolution rules for how to get from a bare specifier to an exact URL are explained in the next section as an extension of the Node.js resolution system to arbitrary URLs.
Resolution must be performed to a specific conditional environment.
The resolution rules for CDNs extend the Node.js resolution rules from file:/// URLs all URLs.
The rules follow the specification algorithm, with a very brief summary being:
- If the specifier starts with
"/"or is a valid URL, then resolve it to the parent and return the resolved URL.
- The specifier is now a bare specifier and follows the bare specifier resolution rules (which in turn can be supported via an import map or internal package import system):
- If the specifier starts with "#" then use the
"imports"resolution of the parent package boundary.
- If the specifier corresponds to the package's own
"name"field, perform package own name resolution.
- Finally, perform a package version resolution using the
"optionalDependencies"package configuration constraints, before applying the
"exports"resolution on that resolved package against the resolved package URL boundary configuration.
- For packages with no
"exports"configuration, legacy extension searching and automatic main file rules can apply. Including
"mod.ts"checks for the main (
import 'pkg'without a subpath) and
".ts"extensions for subpaths (