Skip to content
This repository was archived by the owner on Apr 16, 2020. It is now read-only.

Commit 4ebe563

Browse files
committed
esm: remove node specifier resolution algorithm
Refs: nodejs/modules#180
1 parent d2b38df commit 4ebe563

37 files changed

+120
-162
lines changed

doc/api/esm.md

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,14 @@ property:
5858

5959
ESM must have the `.mjs` extension.
6060

61+
### Mandatory file extensions
62+
63+
You must provide a file extension when using the `import` keyword.
64+
65+
### No importing directories
66+
67+
There is no support for importing directories.
68+
6169
### No NODE_PATH
6270

6371
`NODE_PATH` is not part of resolving `import` specifiers. Please use symlinks
@@ -82,21 +90,15 @@ Modules will be loaded multiple times if the `import` specifier used to resolve
8290
them have a different query or fragment.
8391

8492
```js
85-
import './foo?query=1'; // loads ./foo with query of "?query=1"
86-
import './foo?query=2'; // loads ./foo with query of "?query=2"
93+
import './foo.mjs?query=1'; // loads ./foo.mjs with query of "?query=1"
94+
import './foo.mjs?query=2'; // loads ./foo.mjs with query of "?query=2"
8795
```
8896

8997
For now, only modules using the `file:` protocol can be loaded.
9098

91-
## Interop with existing modules
99+
## CommonJS, JSON, and Native Modules
92100

93-
CommonJS and C++ modules can be used with `import`.
94-
95-
Modules loaded this way will only be loaded once, even if their query
96-
or fragment string differs between `import` statements.
97-
98-
When loaded via `import` these modules will provide a single `default` export
99-
representing the value of `module.exports` at the time they finished evaluating.
101+
CommonJS, JSON, and Native modules can be used with [`module.createRequireFromPath()`][`module.createRequireFromPath()`].
100102

101103
```js
102104
// cjs.js
@@ -112,6 +114,8 @@ const cjs = require('./cjs');
112114
cjs === 'cjs'; // true
113115
```
114116
117+
## Builtin modules
118+
115119
Builtin modules will provide named exports of their public API, as well as a
116120
default export which can be used for, among other things, modifying the named
117121
exports. Named exports of builtin modules are updated when the corresponding
@@ -261,3 +265,4 @@ in the import tree.
261265
262266
[Node.js EP for ES Modules]: https://github.com/nodejs/node-eps/blob/master/002-es-modules.md
263267
[dynamic instantiate hook]: #esm_dynamic_instantiate_hook
268+
[`module.createRequireFromPath()`]: modules.html#modules_module_createrequirefrompath_filename

lib/internal/modules/cjs/loader.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const {
4343
const preserveSymlinks = !!process.binding('config').preserveSymlinks;
4444
const preserveSymlinksMain = !!process.binding('config').preserveSymlinksMain;
4545
const experimentalModules = !!process.binding('config').experimentalModules;
46+
const hasLoader = !!process.binding('config').userLoader;
4647

4748
const {
4849
ERR_INVALID_ARG_TYPE,
@@ -742,7 +743,11 @@ if (experimentalModules) {
742743
// bootstrap main module.
743744
Module.runMain = function() {
744745
// Load the main module--the command line argument.
745-
if (experimentalModules) {
746+
const base = path.basename(process.argv[1]);
747+
const ext = path.extname(base);
748+
const isESM = ext === '.mjs';
749+
750+
if (experimentalModules && (isESM || hasLoader)) {
746751
if (asyncESM === undefined) lazyLoadESM();
747752
asyncESM.loaderPromise.then((loader) => {
748753
return loader.import(pathToFileURL(process.argv[1]).pathname);

src/module_wrap.cc

Lines changed: 11 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ using v8::HandleScope;
2626
using v8::Integer;
2727
using v8::IntegrityLevel;
2828
using v8::Isolate;
29-
using v8::JSON;
3029
using v8::Just;
3130
using v8::Local;
3231
using v8::Maybe;
@@ -489,70 +488,17 @@ Maybe<uv_file> CheckFile(const std::string& path,
489488
return Just(fd);
490489
}
491490

492-
using Exists = PackageConfig::Exists;
493-
using IsValid = PackageConfig::IsValid;
494-
using HasMain = PackageConfig::HasMain;
495-
496-
const PackageConfig& GetPackageConfig(Environment* env,
497-
const std::string& path) {
498-
auto existing = env->package_json_cache.find(path);
499-
if (existing != env->package_json_cache.end()) {
500-
return existing->second;
501-
}
502-
Maybe<uv_file> check = CheckFile(path, LEAVE_OPEN_AFTER_CHECK);
503-
if (check.IsNothing()) {
504-
auto entry = env->package_json_cache.emplace(path,
505-
PackageConfig { Exists::No, IsValid::Yes, HasMain::No, "" });
506-
return entry.first->second;
507-
}
508-
509-
Isolate* isolate = env->isolate();
510-
v8::HandleScope handle_scope(isolate);
511-
512-
std::string pkg_src = ReadFile(check.FromJust());
513-
uv_fs_t fs_req;
514-
CHECK_EQ(0, uv_fs_close(nullptr, &fs_req, check.FromJust(), nullptr));
515-
uv_fs_req_cleanup(&fs_req);
516-
517-
Local<String> src;
518-
if (!String::NewFromUtf8(isolate,
519-
pkg_src.c_str(),
520-
v8::NewStringType::kNormal,
521-
pkg_src.length()).ToLocal(&src)) {
522-
auto entry = env->package_json_cache.emplace(path,
523-
PackageConfig { Exists::No, IsValid::Yes, HasMain::No, "" });
524-
return entry.first->second;
525-
}
526-
527-
Local<Value> pkg_json_v;
528-
Local<Object> pkg_json;
529-
530-
if (!JSON::Parse(env->context(), src).ToLocal(&pkg_json_v) ||
531-
!pkg_json_v->ToObject(env->context()).ToLocal(&pkg_json)) {
532-
auto entry = env->package_json_cache.emplace(path,
533-
PackageConfig { Exists::Yes, IsValid::No, HasMain::No, "" });
534-
return entry.first->second;
535-
}
536-
537-
Local<Value> pkg_main;
538-
HasMain has_main = HasMain::No;
539-
std::string main_std;
540-
if (pkg_json->Get(env->context(), env->main_string()).ToLocal(&pkg_main)) {
541-
has_main = HasMain::Yes;
542-
Utf8Value main_utf8(isolate, pkg_main);
543-
main_std.assign(std::string(*main_utf8, main_utf8.length()));
544-
}
545-
546-
auto entry = env->package_json_cache.emplace(path,
547-
PackageConfig { Exists::Yes, IsValid::Yes, has_main, main_std });
548-
return entry.first->second;
549-
}
550-
551491
enum ResolveExtensionsOptions {
552492
TRY_EXACT_NAME,
553493
ONLY_VIA_EXTENSIONS
554494
};
555495

496+
inline bool ResolvesToFile(const URL& search) {
497+
std::string filePath = search.ToFilePath();
498+
Maybe<uv_file> check = CheckFile(filePath);
499+
return !check.IsNothing();
500+
}
501+
556502
template <ResolveExtensionsOptions options>
557503
Maybe<URL> ResolveExtensions(const URL& search) {
558504
if (options == TRY_EXACT_NAME) {
@@ -578,24 +524,6 @@ inline Maybe<URL> ResolveIndex(const URL& search) {
578524
return ResolveExtensions<ONLY_VIA_EXTENSIONS>(URL("index", search));
579525
}
580526

581-
Maybe<URL> ResolveMain(Environment* env, const URL& search) {
582-
URL pkg("package.json", &search);
583-
584-
const PackageConfig& pjson =
585-
GetPackageConfig(env, pkg.ToFilePath());
586-
// Note invalid package.json should throw in resolver
587-
// currently we silently ignore which is incorrect
588-
if (pjson.exists == Exists::No ||
589-
pjson.is_valid == IsValid::No ||
590-
pjson.has_main == HasMain::No) {
591-
return Nothing<URL>();
592-
}
593-
if (!ShouldBeTreatedAsRelativeOrAbsolutePath(pjson.main)) {
594-
return Resolve(env, "./" + pjson.main, search, IgnoreMain);
595-
}
596-
return Resolve(env, pjson.main, search, IgnoreMain);
597-
}
598-
599527
Maybe<URL> ResolveModule(Environment* env,
600528
const std::string& specifier,
601529
const URL& base) {
@@ -604,7 +532,7 @@ Maybe<URL> ResolveModule(Environment* env,
604532
do {
605533
dir = parent;
606534
Maybe<URL> check =
607-
Resolve(env, "./node_modules/" + specifier, dir, CheckMain);
535+
Resolve(env, "./node_modules/" + specifier, dir);
608536
if (!check.IsNothing()) {
609537
const size_t limit = specifier.find('/');
610538
const size_t spec_len =
@@ -624,23 +552,11 @@ Maybe<URL> ResolveModule(Environment* env,
624552
return Nothing<URL>();
625553
}
626554

627-
Maybe<URL> ResolveDirectory(Environment* env,
628-
const URL& search,
629-
PackageMainCheck check_pjson_main) {
630-
if (check_pjson_main) {
631-
Maybe<URL> main = ResolveMain(env, search);
632-
if (!main.IsNothing())
633-
return main;
634-
}
635-
return ResolveIndex(search);
636-
}
637-
638555
} // anonymous namespace
639556

640557
Maybe<URL> Resolve(Environment* env,
641558
const std::string& specifier,
642-
const URL& base,
643-
PackageMainCheck check_pjson_main) {
559+
const URL& base) {
644560
URL pure_url(specifier);
645561
if (!(pure_url.flags() & URL_FLAGS_FAILED)) {
646562
// just check existence, without altering
@@ -655,13 +571,9 @@ Maybe<URL> Resolve(Environment* env,
655571
}
656572
if (ShouldBeTreatedAsRelativeOrAbsolutePath(specifier)) {
657573
URL resolved(specifier, base);
658-
Maybe<URL> file = ResolveExtensions<TRY_EXACT_NAME>(resolved);
659-
if (!file.IsNothing())
660-
return file;
661-
if (specifier.back() != '/') {
662-
resolved = URL(specifier + "/", base);
663-
}
664-
return ResolveDirectory(env, resolved, check_pjson_main);
574+
if (ResolvesToFile(resolved))
575+
return Just(resolved);
576+
return Nothing<URL>();
665577
} else {
666578
return ResolveModule(env, specifier, base);
667579
}

src/module_wrap.h

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,9 @@
1212
namespace node {
1313
namespace loader {
1414

15-
enum PackageMainCheck : bool {
16-
CheckMain = true,
17-
IgnoreMain = false
18-
};
19-
2015
v8::Maybe<url::URL> Resolve(Environment* env,
2116
const std::string& specifier,
22-
const url::URL& base,
23-
PackageMainCheck read_pkg_json = CheckMain);
17+
const url::URL& base);
2418

2519
class ModuleWrap : public BaseObject {
2620
public:

test/es-module/test-esm-basic-imports.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Flags: --experimental-modules
2-
import '../common';
2+
/* eslint-disable node-core/required-modules */
3+
import '../common/index.mjs';
34
import assert from 'assert';
45
import ok from '../fixtures/es-modules/test-esm-ok.mjs';
56
import okShebang from './test-esm-shebang.mjs';
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
// Flags: --experimental-modules
2-
import '../common';
3-
import('./test-esm-cyclic-dynamic-import');
2+
/* eslint-disable node-core/required-modules */
3+
import '../common/index.mjs';
4+
import('./test-esm-cyclic-dynamic-import.mjs');

test/es-module/test-esm-double-encoding.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Flags: --experimental-modules
2-
import '../common';
2+
/* eslint-disable node-core/required-modules */
3+
import '../common/index.mjs';
34

45
// Assert we can import files with `%` in their pathname.
56

test/es-module/test-esm-encoded-path.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Flags: --experimental-modules
2-
import '../common';
2+
/* eslint-disable node-core/required-modules */
3+
import '../common/index.mjs';
34
import assert from 'assert';
45
// ./test-esm-ok.mjs
56
import ok from '../fixtures/es-modules/test-%65%73%6d-ok.mjs';

test/es-module/test-esm-forbidden-globals.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Flags: --experimental-modules
2-
import '../common';
2+
/* eslint-disable node-core/required-modules */
3+
import '../common/index.mjs';
34

45
// eslint-disable-next-line no-undef
56
if (typeof arguments !== 'undefined') {

test/es-module/test-esm-import-meta.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Flags: --experimental-modules
2+
/* eslint-disable node-core/required-modules */
23

3-
import '../common';
4+
import '../common/index.mjs';
45
import assert from 'assert';
56

67
assert.strictEqual(Object.getPrototypeOf(import.meta), null);

0 commit comments

Comments
 (0)