-
Notifications
You must be signed in to change notification settings - Fork 29.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
esm: Implement esm mode flag #18392
esm: Implement esm mode flag #18392
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -492,6 +492,7 @@ Maybe<uv_file> CheckFile(const std::string& path, | |
using Exists = PackageConfig::Exists; | ||
using IsValid = PackageConfig::IsValid; | ||
using HasMain = PackageConfig::HasMain; | ||
using PackageMode = PackageConfig::PackageMode; | ||
|
||
const PackageConfig& GetPackageConfig(Environment* env, | ||
const std::string& path) { | ||
|
@@ -502,7 +503,8 @@ const PackageConfig& GetPackageConfig(Environment* env, | |
Maybe<uv_file> check = CheckFile(path, LEAVE_OPEN_AFTER_CHECK); | ||
if (check.IsNothing()) { | ||
auto entry = env->package_json_cache.emplace(path, | ||
PackageConfig { Exists::No, IsValid::Yes, HasMain::No, "" }); | ||
PackageConfig { Exists::No, IsValid::Yes, HasMain::No, "", | ||
PackageMode::NONE }); | ||
return entry.first->second; | ||
} | ||
|
||
|
@@ -520,7 +522,8 @@ const PackageConfig& GetPackageConfig(Environment* env, | |
v8::NewStringType::kNormal, | ||
pkg_src.length()).ToLocal(&src)) { | ||
auto entry = env->package_json_cache.emplace(path, | ||
PackageConfig { Exists::No, IsValid::Yes, HasMain::No, "" }); | ||
PackageConfig { Exists::No, IsValid::Yes, HasMain::No, "", | ||
PackageMode::NONE }); | ||
return entry.first->second; | ||
} | ||
|
||
|
@@ -530,7 +533,8 @@ const PackageConfig& GetPackageConfig(Environment* env, | |
if (!JSON::Parse(env->context(), src).ToLocal(&pkg_json_v) || | ||
!pkg_json_v->ToObject(env->context()).ToLocal(&pkg_json)) { | ||
auto entry = env->package_json_cache.emplace(path, | ||
PackageConfig { Exists::Yes, IsValid::No, HasMain::No, "" }); | ||
PackageConfig { Exists::Yes, IsValid::No, HasMain::No, "", | ||
PackageMode::NONE }); | ||
return entry.first->second; | ||
} | ||
|
||
|
@@ -543,11 +547,49 @@ const PackageConfig& GetPackageConfig(Environment* env, | |
main_std.assign(std::string(*main_utf8, main_utf8.length())); | ||
} | ||
|
||
Local<Value> pkg_mode_v; | ||
PackageMode pkg_mode = PackageMode::CJS; | ||
if (pkg_json->Get(env->context(), env->mode_string()).ToLocal(&pkg_mode_v) && | ||
pkg_mode_v->StrictEquals(env->esm_string())) { | ||
pkg_mode = PackageMode::ESM; | ||
} | ||
|
||
auto entry = env->package_json_cache.emplace(path, | ||
PackageConfig { Exists::Yes, IsValid::Yes, has_main, main_std }); | ||
PackageConfig { Exists::Yes, IsValid::Yes, has_main, | ||
main_std, pkg_mode }); | ||
return entry.first->second; | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this C++ json reader/parser is faster than JS would it be possible in a follow up PR to just use it for reading of package.json's or at least cached the parsed data here to avoid a subsequent load/parse? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes the goal is to unify on a single cache with further work. |
||
PackageMode GetPackageMode(Environment* env, const URL& search) { | ||
URL pjson_path("package.json", &search); | ||
while (true) { | ||
const PackageConfig& pkg_json = | ||
GetPackageConfig(env, pjson_path.ToFilePath()); | ||
if (pkg_json.exists == Exists::Yes) { | ||
return pkg_json.mode; | ||
} | ||
URL last_pjson_path = pjson_path; | ||
pjson_path = URL("../package.json", pjson_path); | ||
// Terminates at root where ../package.json equals ../../package.json | ||
// (can't just check "/package.json" for Windows support). | ||
if (pjson_path.path().length() == last_pjson_path.path().length()) { | ||
return PackageMode::CJS; | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What guarantees that this loop terminates? It's not evident to me. A comment or a check is probably in order. |
||
} | ||
|
||
void SetPackageMode(Environment* env, const URL& search, | ||
PackageMode pkg_mode) { | ||
std::string pjson_path_str = URL("package.json", &search).ToFilePath(); | ||
const PackageConfig& pkg_json = GetPackageConfig(env, pjson_path_str); | ||
if (pkg_json.mode != pkg_mode) { | ||
env->package_json_cache.erase(env->package_json_cache.find(pjson_path_str)); | ||
env->package_json_cache.emplace(pjson_path_str, | ||
PackageConfig { pkg_json.exists, pkg_json.is_valid, | ||
pkg_json.has_main, pkg_json.main, pkg_mode }); | ||
} | ||
} | ||
|
||
enum ResolveExtensionsOptions { | ||
TRY_EXACT_NAME, | ||
ONLY_VIA_EXTENSIONS | ||
|
@@ -556,8 +598,8 @@ enum ResolveExtensionsOptions { | |
template <ResolveExtensionsOptions options> | ||
Maybe<URL> ResolveExtensions(const URL& search) { | ||
if (options == TRY_EXACT_NAME) { | ||
std::string filePath = search.ToFilePath(); | ||
Maybe<uv_file> check = CheckFile(filePath); | ||
std::string file_path = search.ToFilePath(); | ||
Maybe<uv_file> check = CheckFile(file_path); | ||
if (!check.IsNothing()) { | ||
return Just(search); | ||
} | ||
|
@@ -670,8 +712,8 @@ Maybe<URL> Resolve(Environment* env, | |
void ModuleWrap::Resolve(const FunctionCallbackInfo<Value>& args) { | ||
Environment* env = Environment::GetCurrent(args); | ||
|
||
// module.resolve(specifier, url) | ||
CHECK_EQ(args.Length(), 2); | ||
// module.resolve(specifier, url, setPackageEsmMode) | ||
CHECK_EQ(args.Length(), 3); | ||
|
||
CHECK(args[0]->IsString()); | ||
Utf8Value specifier_utf8(env->isolate(), args[0]); | ||
|
@@ -686,13 +728,50 @@ void ModuleWrap::Resolve(const FunctionCallbackInfo<Value>& args) { | |
env, "second argument is not a URL string"); | ||
} | ||
|
||
if (!args[2]->IsBoolean()) { | ||
env->ThrowError("third argument is not a boolean"); | ||
return; | ||
} | ||
|
||
Maybe<URL> result = node::loader::Resolve(env, specifier_std, url); | ||
if (result.IsNothing() || (result.FromJust().flags() & URL_FLAGS_FAILED)) { | ||
std::string msg = "Cannot find module " + specifier_std; | ||
return node::THROW_ERR_MISSING_MODULE(env, msg.c_str()); | ||
} | ||
|
||
args.GetReturnValue().Set(result.FromJust().ToObject(env)); | ||
bool esm_package = false; | ||
bool set_package_esm_mode = args[2]->IsTrue(); | ||
if (set_package_esm_mode) { | ||
esm_package = true; | ||
SetPackageMode(env, result.FromJust(), PackageMode::ESM); | ||
} else { | ||
// Check the package esm mode for ambiguous extensions. | ||
std::string file_path = result.FromJust().ToFilePath(); | ||
std::string ext; | ||
const size_t pos = file_path.rfind('.'); | ||
if (pos != 0 && pos != std::string::npos) ext = file_path.substr(pos); | ||
if (ext != ".mjs" && ext != ".json" && ext != ".node") { | ||
if (GetPackageMode(env, result.FromJust()) == PackageMode::ESM) { | ||
esm_package = true; | ||
} | ||
} | ||
} | ||
|
||
Local<Object> resolved = Object::New(env->isolate()); | ||
|
||
resolved->DefineOwnProperty( | ||
env->context(), | ||
env->esm_string(), | ||
v8::Boolean::New(env->isolate(), esm_package), | ||
v8::ReadOnly).FromJust(); | ||
|
||
resolved->DefineOwnProperty( | ||
env->context(), | ||
env->url_string(), | ||
result.FromJust().ToObject(env), | ||
v8::ReadOnly).FromJust(); | ||
|
||
args.GetReturnValue().Set(resolved); | ||
} | ||
|
||
static MaybeLocal<Promise> ImportModuleDynamically( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// Flags: --experimental-modules | ||
import '../common'; | ||
import assert from 'assert'; | ||
import { x } from '../fixtures/es-modules/node_lookups/module.mjs'; | ||
|
||
assert.deepStrictEqual(x, 42); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// Flags: --experimental-modules | ||
/* eslint-disable node-core/required-modules */ | ||
import m from '../fixtures/es-modules/esm-cjs-nested/module'; | ||
import assert from 'assert'; | ||
|
||
assert.strictEqual(m, 'cjs'); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// Flags: --experimental-modules | ||
/* eslint-disable node-core/required-modules */ | ||
import m from '../fixtures/es-modules/esm-invalid/module'; | ||
import assert from 'assert'; | ||
|
||
assert.strictEqual(m, 'cjs'); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// Flags: --experimental-modules | ||
/* eslint-disable node-core/required-modules */ | ||
import m from '../fixtures/es-modules/esm/sub/dir/module'; | ||
import assert from 'assert'; | ||
|
||
assert.strictEqual(m, 'esm submodule'); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// Flags: --experimental-modules | ||
/* eslint-disable node-core/required-modules */ | ||
import m from '../fixtures/es-modules/esm/module'; | ||
import assert from 'assert'; | ||
|
||
assert.strictEqual(m, 'esm'); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = 'cjs'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default } from './cjs-nested/module.js'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"mode": "esm" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = 'cjs'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"mode": 1 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export default 'esm'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"mode": "esm" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export default 'esm submodule'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default as x } from 'cjs'; |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Simpler: