From b1ff90afeedfb9eb6bfb4bc9f99166a1a7fb9c0e Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Wed, 24 Oct 2018 16:24:00 +0100 Subject: [PATCH 1/3] draft esm resolver specification --- doc/esm-resolver-specification.md | 68 +++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 doc/esm-resolver-specification.md diff --git a/doc/esm-resolver-specification.md b/doc/esm-resolver-specification.md new file mode 100644 index 0000000..a229b6d --- /dev/null +++ b/doc/esm-resolver-specification.md @@ -0,0 +1,68 @@ +## ESM Resolver Algorithm Specification + +As with the CommonJS resolver, one goal of the modules implementation is to provide a straightforward resolver specifiation that can be followed by tools and bundlers to properly adhere to the Node.js resolution semantics. + +The resolver algorithm here covers the current plans for the [Minimal Kernel Modules implementation](https://github.com/nodejs/modules/blob/master/doc/plan-for-new-modules-implementation.md) and will change as that work progresses. + +### Features + +Currently, as per the minimal kernel, the resolver has the following properties: + +* FileURL-based resolution as is used by ES modules +* Relative and absolute URL resolution +* No default extensions +* No directory lookups +* Bare specifier package resolution lookup through node_modules + +### Package main + +This implementation differs from the CommonJS resolver in detecting a package main request based on the package name format itself being either `package` or `@scoped/package` and then applying the main lookup only for those cases. + +The main lookup is then provided by _CHECK_PACKAGE_MAIN_ which is still an experimental approach under discussion. + +Currently the main lookup will only check for _index.mjs_. + +### Resolver Algorithm + +The algorithm to resolve an ES module specifier is provided through _ESM_RESOLVE_: + +ESM_RESOLVE(specifier, parentURL) +> 1. If _specifier_ starts with _"/", _"./"_ or _"../"_ then, +> 1. Return the URL resolution of _specifier_ to _parentURL_. +> 1. If _specifier_ is a valid URL then, +> 1. Return the result of parsing and reserializing _specifier_ as a URL. +> 1. Note: _name_ is now a bare specifier. +> 1. Let _packageName_ be _undefined_. +> 1. Let _packagePath_ be _undefined_. +> 1. If _name_ does not start with _"@"_ then, +> 1. Set _packageName_ to the substring of _specifier_ until the first _"/"_ separator or the end of the string. +> 1. If _name_ starts with _"@"_ then, +> 1. If _name_ does not contain a _"/"_ separator then, +> 1. Throw a _Invalid Package Name_ error. +> 1. Set _packageName_ to the substring of _specifier_ until the second _"/"_ separator or the end of the string. +> 1. Let _packagePath_ be the substring of _specifier_ from the position at the length of _packageName_ plus one, if any. +> 1. Return the result of _PACKAGE_RESOLVE(specifier, parentURL)_. + +PACKAGE_RESOLVE(packageName, packagePath, parentURL) +> 1. Assert: _packagePath_ contains no leading separator and can be empty. +> 1. Assert: _packageName_ is a valid package name or scoped package name. +> 1. Note: Further package name encoding validations can be implemented here. +> 1. Let _parentURL_ be the parent folder URL of _parentURL_. +> 1. While _parentURL_ contains a non-empty _pathname_, +> 1. Let _packageURL_ be equal to _"${parentPath}/node_modules/${packageName}_. +> 1. If _packagePath_ is not empty then, +> 1. Let _url_ be equal to _"${packageURL}/${packagePath}"_. +> 1. If the file at _url_ exists then, +> 1. Return _url_. +> 1. Continue the next iteration. +> 1. Otherwise, +> 1. Let _packageMain_ be the result of _CHECK_PACKAGE_MAIN(packageURL)_. +> 1. If _packageMain_ is not _undefined_ then, +> 1. Return _packageMain_. +> 1. Set _parentURL_ to the parent URL path of _parentURL_. +> 1. Throw a _Module Not Found_ error. + +CHECK_PACKAGE_MAIN(packageURL) +> 1. If the file at _"${packageURL}/index.mjs"_ exists then, +> 1. Return _"${packageURL}/index.mjs"_. +> 1. Return _undefined_. From ceb48064601669e9ee3da3f5526cd420ab3a3fea Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Wed, 24 Oct 2018 16:27:16 +0100 Subject: [PATCH 2/3] tweaks --- doc/esm-resolver-specification.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/esm-resolver-specification.md b/doc/esm-resolver-specification.md index a229b6d..35945ad 100644 --- a/doc/esm-resolver-specification.md +++ b/doc/esm-resolver-specification.md @@ -49,12 +49,11 @@ PACKAGE_RESOLVE(packageName, packagePath, parentURL) > 1. Note: Further package name encoding validations can be implemented here. > 1. Let _parentURL_ be the parent folder URL of _parentURL_. > 1. While _parentURL_ contains a non-empty _pathname_, -> 1. Let _packageURL_ be equal to _"${parentPath}/node_modules/${packageName}_. +> 1. Let _packageURL_ be equal to _"${parentPath}/node_modules/${packageName}"_. > 1. If _packagePath_ is not empty then, > 1. Let _url_ be equal to _"${packageURL}/${packagePath}"_. > 1. If the file at _url_ exists then, > 1. Return _url_. -> 1. Continue the next iteration. > 1. Otherwise, > 1. Let _packageMain_ be the result of _CHECK_PACKAGE_MAIN(packageURL)_. > 1. If _packageMain_ is not _undefined_ then, From 8085e5a89309ced69559cb5e82926bcd83e2a514 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Wed, 24 Oct 2018 16:48:14 +0100 Subject: [PATCH 3/3] tweaks and examples --- doc/esm-resolver-specification.md | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/doc/esm-resolver-specification.md b/doc/esm-resolver-specification.md index 35945ad..b865ae2 100644 --- a/doc/esm-resolver-specification.md +++ b/doc/esm-resolver-specification.md @@ -22,6 +22,35 @@ The main lookup is then provided by _CHECK_PACKAGE_MAIN_ which is still an exper Currently the main lookup will only check for _index.mjs_. +### Examples + +Relative and absolute resolution without directory or extension resolution (URL resolution): + +* _ESM_RESOLVE(./a.asdf, file:///parent/path)_ -> _file:///parent/a.asdf_ +* _ESM_RESOLVE(/a, file:///parent/path)_ -> _file:///a_ +* _ESM_RESOLVE(file:///a, file:///parent/path)_ -> _file:///a_ + +Package resolution: + +1. _ESM_RESOLVE(pkg/x, file:///path/to/project)_ -> _file:///path/to/node_modules/pkg/x_ (if it exists) +1. _ESM_RESOLVE(pkg/x, file:///path/to/project)_ -> _file:///path/node_modules/pkg/x_ (otherwise, if it exists) +1. _ESM_RESOLVE(pkg/x, file:///path/to/project)_ -> _file:///node_modules/pkg/x_ (otherwise, if it exists) +1. _ESM_RESOLVE(pkg/x, file:///path/to/project)_ -> _Not Found_ (otherwise) + +Main resolution: + +1. _ESM_RESOLVE(pkg, file:///path/to/project)_ -> _file:///path/to/node_modules/pkg/index.mjs_ (if it exists) +1. _ESM_RESOLVE(pkg, file:///path/to/project)_ -> _file:///path/node_modules/pkg/index.mjs_ (otherwise, if it exists) +1. _ESM_RESOLVE(pkg, file:///path/to/project)_ -> _file:///node_modules/pkg/index.mjs_ (otherwise, if it exists) +1. _ESM_RESOLVE(pkg, file:///path/to/project)_ -> _Not Found_ (otherwise) + +Scoped package main resolution: + +1. _ESM_RESOLVE(@pkg/name, file:///path/to/project)_ -> _file:///path/to/node_modules/@pkg/name/index.mjs_ (if it exists) +1. _ESM_RESOLVE(@pkg/name, file:///path/to/project)_ -> _file:///path/node_modules/@pkg/name/index.mjs_ (otherwise, if it exists) +1. _ESM_RESOLVE(@pkg/name, file:///path/to/project)_ -> _file:///node_modules/@pkg/name/index.mjs_ (otherwise, if it exists) +1. _ESM_RESOLVE(@pkg/name, file:///path/to/project)_ -> _Not Found_ (otherwise) + ### Resolver Algorithm The algorithm to resolve an ES module specifier is provided through _ESM_RESOLVE_: @@ -47,7 +76,7 @@ PACKAGE_RESOLVE(packageName, packagePath, parentURL) > 1. Assert: _packagePath_ contains no leading separator and can be empty. > 1. Assert: _packageName_ is a valid package name or scoped package name. > 1. Note: Further package name encoding validations can be implemented here. -> 1. Let _parentURL_ be the parent folder URL of _parentURL_. +> 1. Set _parentURL_ to the parent folder URL of _parentURL_. > 1. While _parentURL_ contains a non-empty _pathname_, > 1. Let _packageURL_ be equal to _"${parentPath}/node_modules/${packageName}"_. > 1. If _packagePath_ is not empty then,