From dc40b4a5f0577880c7382e63368637ec8e7b720f Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 17 Dec 2019 14:00:21 -0500 Subject: [PATCH 1/6] module: logical conditional exports ordering --- doc/api/esm.md | 45 ++++++++------- lib/internal/modules/cjs/loader.js | 50 +++++++--------- src/env.h | 2 - src/module_wrap.cc | 57 +++++++++---------- .../node_modules/pkgexports/package.json | 15 ++++- 5 files changed, 86 insertions(+), 83 deletions(-) diff --git a/doc/api/esm.md b/doc/api/esm.md index 381bdc8a053443..02bc238848ab2c 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -343,25 +343,26 @@ Node.js and the browser can be written: When resolving the `"."` export, if no matching target is found, the `"main"` will be used as the final fallback. -The conditions supported in Node.js are matched in the following order: +The conditions supported in Node.js condition matching: -1. `"node"` - matched for any Node.js environment. Can be a CommonJS or ES +* `"default"` - the generic fallback that will always match. Can be a CommonJS + or ES module file. +* `"import"` - matched when the package is loaded via `import` or + `import()`. Can be any module format, this field does not set the type + interpretation. _This is currently only supported behind the + `--experimental-conditional-exports` flag._ +* `"node"` - matched for any Node.js environment. Can be a CommonJS or ES module file. _This is currently only supported behind the `--experimental-conditional-exports` flag._ -2. `"require"` - matched when the package is loaded via `require()`. +* `"require"` - matched when the package is loaded via `require()`. _This is currently only supported behind the `--experimental-conditional-exports` flag._ -3. `"import"` - matched when the package is loaded via `import` or - `import()`. Can be any module format, this field does not set the type - interpretation. _This is currently only supported behind the - `--experimental-conditional-exports` flag._ -4. `"default"` - the generic fallback that will always match if no other - more specific condition is matched first. Can be a CommonJS or ES module - file. -> Setting any of the above flagged conditions for a published package is not -> recommended until they are unflagged to avoid breaking changes to packages in -> future. +Condition matching is applied in object order from first to last within the +`"exports"` object. + +> Setting the above conditions for a published package is not recommended until +> conditional exports have been unflagged to avoid breaking changes to packages. Using the `"require"` condition it is possible to define a package that will have a different exported value for CommonJS and ES modules, which can be a @@ -369,7 +370,9 @@ hazard in that it can result in having two separate instances of the same package in use in an application, which can cause a number of bugs. Other conditions such as `"browser"`, `"electron"`, `"deno"`, `"react-native"`, -etc. could be defined in other runtimes or tools. +etc. could be defined in other runtimes or tools. Condition names must not start +with `"."` or be numbers. Further restrictions, definitions or guidance on +condition names may be provided in future. #### Exports Sugar @@ -1547,13 +1550,13 @@ _defaultEnv_ is the conditional environment name priority array, > 1. If _resolved_ is contained in _resolvedTarget_, then > 1. Return _resolved_. > 1. Otherwise, if _target_ is a non-null Object, then -> 1. If _target_ has an object key matching one of the names in _env_, then -> 1. Let _targetValue_ be the corresponding value of the first object key -> of _target_ in _env_. -> 1. Let _resolved_ be the result of **PACKAGE_EXPORTS_TARGET_RESOLVE** -> (_packageURL_, _targetValue_, _subpath_, _env_). -> 1. Assert: _resolved_ is a String. -> 1. Return _resolved_. +> 1. For each property _p_ of _target_, in object insertion order, +> 1. If _env_ contains an entry for _p_, then +> 1. Let _targetValue_ be the value of the _p_ property in _target_. +> 1. Let _resolved_ be the result of **PACKAGE_EXPORTS_TARGET_RESOLVE** +> (_packageURL_, _targetValue_, _subpath_, _env_). +> 1. Assert: _resolved_ is a String. +> 1. Return _resolved_. > 1. Otherwise, if _target_ is an Array, then > 1. For each item _targetValue_ in _target_, do > 1. If _targetValue_ is an Array, continue the loop. diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index cc888f14bb44ce..3ad980500a0169 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -587,34 +587,28 @@ function resolveExportsTarget(pkgPath, target, subpath, basePath, mappingKey) { } } } else if (typeof target === 'object' && target !== null) { - if (experimentalConditionalExports && - ObjectPrototypeHasOwnProperty(target, 'node')) { - try { - const result = resolveExportsTarget(pkgPath, target.node, subpath, - basePath, mappingKey); - emitExperimentalWarning('Conditional exports'); - return result; - } catch (e) { - if (e.code !== 'MODULE_NOT_FOUND') throw e; - } - } - if (experimentalConditionalExports && - ObjectPrototypeHasOwnProperty(target, 'require')) { - try { - const result = resolveExportsTarget(pkgPath, target.require, subpath, - basePath, mappingKey); - emitExperimentalWarning('Conditional exports'); - return result; - } catch (e) { - if (e.code !== 'MODULE_NOT_FOUND') throw e; - } - } - if (ObjectPrototypeHasOwnProperty(target, 'default')) { - try { - return resolveExportsTarget(pkgPath, target.default, subpath, - basePath, mappingKey); - } catch (e) { - if (e.code !== 'MODULE_NOT_FOUND') throw e; + for (const p of ObjectKeys(target)) { + switch (p) { + case 'node': + case 'require': + if (!experimentalConditionalExports) + continue; + try { + emitExperimentalWarning('Conditional exports'); + const result = resolveExportsTarget(pkgPath, target[p], subpath, + basePath, mappingKey); + return result; + } catch (e) { + if (e.code !== 'MODULE_NOT_FOUND') throw e; + } + break; + case 'default': + try { + return resolveExportsTarget(pkgPath, target.default, subpath, + basePath, mappingKey); + } catch (e) { + if (e.code !== 'MODULE_NOT_FOUND') throw e; + } } } } diff --git a/src/env.h b/src/env.h index 6d3522bcd5e197..db15788ffca7c0 100644 --- a/src/env.h +++ b/src/env.h @@ -202,7 +202,6 @@ constexpr size_t kFsStatsBufferLength = V(crypto_rsa_pss_string, "rsa-pss") \ V(cwd_string, "cwd") \ V(data_string, "data") \ - V(default_string, "default") \ V(dest_string, "dest") \ V(destroyed_string, "destroyed") \ V(detached_string, "detached") \ @@ -257,7 +256,6 @@ constexpr size_t kFsStatsBufferLength = V(http_1_1_string, "http/1.1") \ V(identity_string, "identity") \ V(ignore_string, "ignore") \ - V(import_string, "import") \ V(infoaccess_string, "infoAccess") \ V(inherit_string, "inherit") \ V(input_string, "input") \ diff --git a/src/module_wrap.cc b/src/module_wrap.cc index 74eacf198f7b84..f3b3a2969caa49 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc @@ -953,44 +953,39 @@ Maybe ResolveExportsTarget(Environment* env, return Nothing(); } else if (target->IsObject()) { Local target_obj = target.As(); - bool matched = false; + Local target_obj_keys = + target_obj->GetOwnPropertyNames(context).ToLocalChecked(); Local conditionalTarget; - if (env->options()->experimental_conditional_exports && - target_obj->HasOwnProperty(context, env->node_string()).FromJust()) { - matched = true; - conditionalTarget = - target_obj->Get(context, env->node_string()).ToLocalChecked(); - Maybe resolved = ResolveExportsTarget(env, pjson_url, - conditionalTarget, subpath, pkg_subpath, base, false); - if (!resolved.IsNothing()) { - ProcessEmitExperimentalWarning(env, "Conditional exports"); - return resolved; - } - } - if (env->options()->experimental_conditional_exports && - target_obj->HasOwnProperty(context, env->import_string()).FromJust()) { - matched = true; - conditionalTarget = - target_obj->Get(context, env->import_string()).ToLocalChecked(); - Maybe resolved = ResolveExportsTarget(env, pjson_url, + bool matched = false; + for (uint32_t i = 0; i < target_obj_keys->Length(); ++i) { + Local key = + target_obj_keys->Get(context, i).ToLocalChecked().As(); + Utf8Value key_utf8(env->isolate(), key); + std::string key_str(*key_utf8, key_utf8.length()); + if (key_str == "node" || key_str == "import") { + if (!env->options()->experimental_conditional_exports) continue; + matched = true; + conditionalTarget = target_obj->Get(context, key).ToLocalChecked(); + Maybe resolved = ResolveExportsTarget(env, pjson_url, conditionalTarget, subpath, pkg_subpath, base, false); - if (!resolved.IsNothing()) { - return resolved; - } - } - if (target_obj->HasOwnProperty(context, env->default_string()).FromJust()) { - matched = true; - conditionalTarget = - target_obj->Get(context, env->default_string()).ToLocalChecked(); - Maybe resolved = ResolveExportsTarget(env, pjson_url, + if (!resolved.IsNothing()) { + ProcessEmitExperimentalWarning(env, "Conditional exports"); + return resolved; + } + } else if (key_str == "default") { + matched = true; + conditionalTarget = target_obj->Get(context, key).ToLocalChecked(); + Maybe resolved = ResolveExportsTarget(env, pjson_url, conditionalTarget, subpath, pkg_subpath, base, false); - if (!resolved.IsNothing()) { - return resolved; + if (!resolved.IsNothing()) { + ProcessEmitExperimentalWarning(env, "Conditional exports"); + return resolved; + } } } if (matched && throw_invalid) { Maybe resolved = ResolveExportsTarget(env, pjson_url, - conditionalTarget, subpath, pkg_subpath, base, true); + conditionalTarget, subpath, pkg_subpath, base, true); CHECK(resolved.IsNothing()); return Nothing(); } diff --git a/test/fixtures/node_modules/pkgexports/package.json b/test/fixtures/node_modules/pkgexports/package.json index f7adc813ac81cc..02e06f0ebe5f3c 100644 --- a/test/fixtures/node_modules/pkgexports/package.json +++ b/test/fixtures/node_modules/pkgexports/package.json @@ -18,7 +18,20 @@ "./nofallback1": [], "./nofallback2": [null, {}, "builtin:x"], "./nodemodules": "./node_modules/internalpkg/x.js", - "./condition": [{ "require": "./sp ce.js" }, "./asdf.js"], + "./condition": [{ + "import": "///overridden", + "require": { + "require": { + "nomatch": "./nothing.js" + }, + "default": "./sp ce.js" + }, + "default": "./asdf.js", + "node": "./lib/hole.js", + "import": { + "nomatch": "./nothing.js" + } + }], "./resolve-self": { "require": "./resolve-self.js", "import": "./resolve-self.mjs" From f1fd65c0c52bc5e9654c0bc5233dc25e6f9ce641 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Sun, 29 Dec 2019 19:11:53 -0500 Subject: [PATCH 2/6] throw on numeric array index properties --- doc/api/esm.md | 2 ++ lib/internal/modules/cjs/loader.js | 22 +++++++++++++++- src/module_wrap.cc | 25 +++++++++++++++++++ test/es-module/test-esm-exports.mjs | 5 ++++ .../pkgexports-numeric/package.json | 6 +++++ 5 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/node_modules/pkgexports-numeric/package.json diff --git a/doc/api/esm.md b/doc/api/esm.md index 02bc238848ab2c..5bc6fce3ff4cd7 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -1517,6 +1517,8 @@ _defaultEnv_ is the conditional environment name priority array, > 1. If _exports_ is an Object with both a key starting with _"."_ and a key not > starting with _"."_, throw an "Invalid Package Configuration" error. > 1. If _exports_ is an Object and all keys of _exports_ start with _"."_, then +> 1. If _exports_ contains any index property keys, as defined in ECMA-262 +> 6.1.7 Array Index, throw an _Invalid Package Configuration_ error. > 1. Set _packagePath_ to _"./"_ concatenated with _packagePath_. > 1. If _packagePath_ is a key of _exports_, then > 1. Let _target_ be the value of _exports\[packagePath\]_. diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index 3ad980500a0169..a2d96442b9c76a 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -26,16 +26,19 @@ const { Error, JSONParse, Map, + Number, ObjectCreate, ObjectDefineProperty, ObjectFreeze, ObjectGetOwnPropertyDescriptor, ObjectGetPrototypeOf, + ObjectIs, ObjectKeys, ObjectPrototypeHasOwnProperty, ObjectSetPrototypeOf, ReflectSet, SafeMap, + String, StringPrototypeIndexOf, StringPrototypeMatch, StringPrototypeSlice, @@ -557,6 +560,18 @@ function resolveExports(nmPath, request, absoluteRequest) { return path.resolve(nmPath, request); } +function isArrayIndex(p) { + assert(typeof p === 'string'); + const n = Number(p); + if (String(n) !== p) + return false; + if (ObjectIs(n, +0)) + return true; + if (!Number.isInteger(n)) + return false; + return n >= 0 && n < (2 ** 32) - 1; +} + function resolveExportsTarget(pkgPath, target, subpath, basePath, mappingKey) { if (typeof target === 'string') { if (target.startsWith('./') && @@ -587,7 +602,12 @@ function resolveExportsTarget(pkgPath, target, subpath, basePath, mappingKey) { } } } else if (typeof target === 'object' && target !== null) { - for (const p of ObjectKeys(target)) { + const keys = ObjectKeys(target); + if (keys.some(isArrayIndex)) { + throw new ERR_INVALID_PACKAGE_CONFIG(basePath, '"exports" cannot ' + + 'contain numeric property keys.'); + } + for (const p of keys) { switch (p) { case 'node': case 'require': diff --git a/src/module_wrap.cc b/src/module_wrap.cc index f3b3a2969caa49..107d65b0d0677c 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc @@ -908,6 +908,20 @@ Maybe ResolveExportsTargetString(Environment* env, return Just(subpath_resolved); } +bool IsArrayIndex(Environment* env, Local p) { + Local context = env->context(); + double n_dbl = static_cast(p->NumberValue(context).FromJust()); + Local n = Number::New(env->isolate(), n_dbl); + Local cmp_str; + CHECK(n->ToString(context).ToLocal(&cmp_str)); + if (!p->Equals(context, cmp_str).FromJust()) + return false; + Local cmp_integer; + if (!n->ToInteger(context).ToLocal(&cmp_integer)) + return false; + return n_dbl >= 0 && n_dbl < (2 ^ 32) - 1; +} + Maybe ResolveExportsTarget(Environment* env, const URL& pjson_url, Local target, @@ -957,6 +971,17 @@ Maybe ResolveExportsTarget(Environment* env, target_obj->GetOwnPropertyNames(context).ToLocalChecked(); Local conditionalTarget; bool matched = false; + for (uint32_t i = 0; i < target_obj_keys->Length(); ++i) { + Local key = + target_obj_keys->Get(context, i).ToLocalChecked().As(); + if (IsArrayIndex(env, key)) { + const std::string msg = "Invalid package config for " + + pjson_url.ToFilePath() + ", \"exports\" cannot contain numeric " + + "property keys."; + node::THROW_ERR_INVALID_PACKAGE_CONFIG(env, msg.c_str()); + return Nothing(); + } + } for (uint32_t i = 0; i < target_obj_keys->Length(); ++i) { Local key = target_obj_keys->Get(context, i).ToLocalChecked().As(); diff --git a/test/es-module/test-esm-exports.mjs b/test/es-module/test-esm-exports.mjs index d55d8ffac47f8c..96f33978968612 100644 --- a/test/es-module/test-esm-exports.mjs +++ b/test/es-module/test-esm-exports.mjs @@ -124,6 +124,11 @@ import fromInside from '../fixtures/node_modules/pkgexports/lib/hole.js'; 'ERR_MODULE_NOT_FOUND'); })); + // Package export with numeric index properties must throw a validation error + loadFixture('pkgexports-numeric').catch(mustCall((err) => { + strictEqual(err.code, 'ERR_INVALID_PACKAGE_CONFIG'); + })); + // Sugar conditional exports main mixed failure case loadFixture('pkgexports-sugar-fail').catch(mustCall((err) => { strictEqual(err.code, 'ERR_INVALID_PACKAGE_CONFIG'); diff --git a/test/fixtures/node_modules/pkgexports-numeric/package.json b/test/fixtures/node_modules/pkgexports-numeric/package.json new file mode 100644 index 00000000000000..6d20abe1d59a63 --- /dev/null +++ b/test/fixtures/node_modules/pkgexports-numeric/package.json @@ -0,0 +1,6 @@ +{ + "main": "./main.cjs", + "exports": { + "0": "./should-throw" + } +} From 9dbc816aac18c5c05bd3a7ac7d4b31e0040a0fef Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Wed, 1 Jan 2020 04:26:47 +0200 Subject: [PATCH 3/6] cc impl fixup, docs feedback --- doc/api/esm.md | 7 ++++--- src/module_wrap.cc | 5 ++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/doc/api/esm.md b/doc/api/esm.md index 5bc6fce3ff4cd7..d48eb2ea9f7e19 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -1517,8 +1517,6 @@ _defaultEnv_ is the conditional environment name priority array, > 1. If _exports_ is an Object with both a key starting with _"."_ and a key not > starting with _"."_, throw an "Invalid Package Configuration" error. > 1. If _exports_ is an Object and all keys of _exports_ start with _"."_, then -> 1. If _exports_ contains any index property keys, as defined in ECMA-262 -> 6.1.7 Array Index, throw an _Invalid Package Configuration_ error. > 1. Set _packagePath_ to _"./"_ concatenated with _packagePath_. > 1. If _packagePath_ is a key of _exports_, then > 1. Let _target_ be the value of _exports\[packagePath\]_. @@ -1552,7 +1550,9 @@ _defaultEnv_ is the conditional environment name priority array, > 1. If _resolved_ is contained in _resolvedTarget_, then > 1. Return _resolved_. > 1. Otherwise, if _target_ is a non-null Object, then -> 1. For each property _p_ of _target_, in object insertion order, +> 1. If _exports_ contains any index property keys, as defined in ECMA-262 +> [6.1.7 Array Index][], throw an _Invalid Package Configuration_ error. +> 1. For each property _p_ of _target_, in object insertion order as, > 1. If _env_ contains an entry for _p_, then > 1. Let _targetValue_ be the value of the _p_ property in _target_. > 1. Let _resolved_ be the result of **PACKAGE_EXPORTS_TARGET_RESOLVE** @@ -1654,3 +1654,4 @@ success! [special scheme]: https://url.spec.whatwg.org/#special-scheme [the official standard format]: https://tc39.github.io/ecma262/#sec-modules [transpiler loader example]: #esm_transpiler_loader +[6.1.7 Array Index]: https://tc39.es/ecma262/#integer-index diff --git a/src/module_wrap.cc b/src/module_wrap.cc index 107d65b0d0677c..423a9981e4a161 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc @@ -9,10 +9,11 @@ #include "node_watchdog.h" #include "node_process.h" + #include // S_IFDIR #include -#include // PATH_MAX +#include // SIGNBIT namespace node { namespace loader { @@ -916,6 +917,8 @@ bool IsArrayIndex(Environment* env, Local p) { CHECK(n->ToString(context).ToLocal(&cmp_str)); if (!p->Equals(context, cmp_str).FromJust()) return false; + if (n_dbl == 0 && std::signbit(n_dbl) == false) + return true; Local cmp_integer; if (!n->ToInteger(context).ToLocal(&cmp_integer)) return false; From e00b4b7ce30be7acecab99dd741750c6c3da2137 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Thu, 2 Jan 2020 20:07:51 +0200 Subject: [PATCH 4/6] remove unncessary include --- src/module_wrap.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/module_wrap.cc b/src/module_wrap.cc index 423a9981e4a161..7aa71ed963b2e7 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc @@ -13,7 +13,6 @@ #include // S_IFDIR #include -#include // SIGNBIT namespace node { namespace loader { From d760e464b2d53ee2cd6030f1148b5f085ce90a90 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Fri, 3 Jan 2020 01:22:58 +0200 Subject: [PATCH 5/6] -0 correction --- src/module_wrap.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/module_wrap.cc b/src/module_wrap.cc index 7aa71ed963b2e7..05f83a00734822 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc @@ -921,7 +921,7 @@ bool IsArrayIndex(Environment* env, Local p) { Local cmp_integer; if (!n->ToInteger(context).ToLocal(&cmp_integer)) return false; - return n_dbl >= 0 && n_dbl < (2 ^ 32) - 1; + return n_dbl > 0 && n_dbl < (2 ^ 32) - 1; } Maybe ResolveExportsTarget(Environment* env, From 9bfdebf3676ba5e4e40ca2bd2d3a3d68d2fd4a86 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Mon, 6 Jan 2020 03:20:49 +0200 Subject: [PATCH 6/6] toLocalChecked --- src/module_wrap.cc | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/module_wrap.cc b/src/module_wrap.cc index 05f83a00734822..069d324b3327e1 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc @@ -1,3 +1,4 @@ +#define V8_ENABLE_CHECKS 1 #include "module_wrap.h" #include "env.h" @@ -9,7 +10,6 @@ #include "node_watchdog.h" #include "node_process.h" - #include // S_IFDIR #include @@ -908,19 +908,22 @@ Maybe ResolveExportsTargetString(Environment* env, return Just(subpath_resolved); } -bool IsArrayIndex(Environment* env, Local p) { +bool IsArrayIndex(Environment* env, Local p) { Local context = env->context(); - double n_dbl = static_cast(p->NumberValue(context).FromJust()); + Local p_str = p->ToString(context).ToLocalChecked(); + double n_dbl = static_cast(p_str->NumberValue(context).FromJust()); Local n = Number::New(env->isolate(), n_dbl); - Local cmp_str; - CHECK(n->ToString(context).ToLocal(&cmp_str)); - if (!p->Equals(context, cmp_str).FromJust()) + Local cmp_str = n->ToString(context).ToLocalChecked(); + if (!p_str->Equals(context, cmp_str).FromJust()) { return false; - if (n_dbl == 0 && std::signbit(n_dbl) == false) + } + if (n_dbl == 0 && std::signbit(n_dbl) == false) { return true; + } Local cmp_integer; - if (!n->ToInteger(context).ToLocal(&cmp_integer)) + if (!n->ToInteger(context).ToLocal(&cmp_integer)) { return false; + } return n_dbl > 0 && n_dbl < (2 ^ 32) - 1; } @@ -974,8 +977,8 @@ Maybe ResolveExportsTarget(Environment* env, Local conditionalTarget; bool matched = false; for (uint32_t i = 0; i < target_obj_keys->Length(); ++i) { - Local key = - target_obj_keys->Get(context, i).ToLocalChecked().As(); + Local key = + target_obj_keys->Get(context, i).ToLocalChecked(); if (IsArrayIndex(env, key)) { const std::string msg = "Invalid package config for " + pjson_url.ToFilePath() + ", \"exports\" cannot contain numeric " + @@ -985,9 +988,9 @@ Maybe ResolveExportsTarget(Environment* env, } } for (uint32_t i = 0; i < target_obj_keys->Length(); ++i) { - Local key = - target_obj_keys->Get(context, i).ToLocalChecked().As(); - Utf8Value key_utf8(env->isolate(), key); + Local key = target_obj_keys->Get(context, i).ToLocalChecked(); + Utf8Value key_utf8(env->isolate(), + key->ToString(context).ToLocalChecked()); std::string key_str(*key_utf8, key_utf8.length()); if (key_str == "node" || key_str == "import") { if (!env->options()->experimental_conditional_exports) continue; @@ -1035,8 +1038,8 @@ Maybe IsConditionalExportsMainSugar(Environment* env, exports_obj->GetOwnPropertyNames(context).ToLocalChecked(); bool isConditionalSugar = false; for (uint32_t i = 0; i < keys->Length(); ++i) { - Local key = keys->Get(context, i).ToLocalChecked().As(); - Utf8Value key_utf8(env->isolate(), key); + Local key = keys->Get(context, i).ToLocalChecked(); + Utf8Value key_utf8(env->isolate(), key->ToString(context).ToLocalChecked()); bool curIsConditionalSugar = key_utf8.length() == 0 || key_utf8[0] != '.'; if (i == 0) { isConditionalSugar = curIsConditionalSugar; @@ -1144,13 +1147,13 @@ Maybe PackageExportsResolve(Environment* env, Local keys = exports_obj->GetOwnPropertyNames(context).ToLocalChecked(); for (uint32_t i = 0; i < keys->Length(); ++i) { - Local key = keys->Get(context, i).ToLocalChecked().As(); - Utf8Value key_utf8(isolate, key); + Local key = keys->Get(context, i).ToLocalChecked(); + Utf8Value key_utf8(isolate, key->ToString(context).ToLocalChecked()); std::string key_str(*key_utf8, key_utf8.length()); if (key_str.back() != '/') continue; if (pkg_subpath.substr(0, key_str.length()) == key_str && key_str.length() > best_match_str.length()) { - best_match = key; + best_match = key->ToString(context).ToLocalChecked(); best_match_str = key_str; } }