@@ -318,13 +318,15 @@ void ContextifyContext::CreatePerIsolateProperties(
318318 SetMethod (isolate, target, " makeContext" , MakeContext);
319319 SetMethod (isolate, target, " isContext" , IsContext);
320320 SetMethod (isolate, target, " compileFunction" , CompileFunction);
321+ SetMethod (isolate, target, " containsModuleSyntax" , ContainsModuleSyntax);
321322}
322323
323324void ContextifyContext::RegisterExternalReferences (
324325 ExternalReferenceRegistry* registry) {
325326 registry->Register (MakeContext);
326327 registry->Register (IsContext);
327328 registry->Register (CompileFunction);
329+ registry->Register (ContainsModuleSyntax);
328330 registry->Register (PropertyGetterCallback);
329331 registry->Register (PropertySetterCallback);
330332 registry->Register (PropertyDescriptorCallback);
@@ -1339,6 +1341,104 @@ Local<Object> ContextifyContext::CompileFunctionAndCacheResult(
13391341 return result;
13401342}
13411343
1344+ // These are the error messages thrown due to ESM syntax in a CommonJS module.
1345+ constexpr std::array<std::string_view, 3 > esm_syntax_error_messages = {
1346+ // `import` statements return an error with the message:
1347+ " Cannot use import statement outside a module" ,
1348+ // `export` statements return an error with the message:
1349+ " Unexpected token 'export'" ,
1350+ // `import.meta` returns an error with the message:
1351+ " Cannot use 'import.meta' outside a module" };
1352+ // Top-level `await` currently returns the same error message as when `await` is
1353+ // used in a sync function, so we don't use it as a disambiguation. Dynamic
1354+ // `import()` is permitted in CommonJS, so we don't use it as a disambiguation.
1355+
1356+ void ContextifyContext::ContainsModuleSyntax (
1357+ const FunctionCallbackInfo<Value>& args) {
1358+ // Argument 1: source code
1359+ CHECK (args[0 ]->IsString ());
1360+ Local<String> code = args[0 ].As <String>();
1361+
1362+ // Argument 2: filename
1363+ CHECK (args[1 ]->IsString ());
1364+ Local<String> filename = args[1 ].As <String>();
1365+
1366+ Environment* env = Environment::GetCurrent (args);
1367+ Isolate* isolate = env->isolate ();
1368+ Local<Context> context = env->context ();
1369+
1370+ // TODO(geoffreybooth): Centralize this rather than matching the logic in
1371+ // cjs/loader.js and translators.js
1372+ Local<Symbol> id_symbol =
1373+ (String::Concat (isolate,
1374+ String::NewFromUtf8 (isolate, " cjs:" ).ToLocalChecked (),
1375+ filename))
1376+ .As <Symbol>();
1377+
1378+ // TODO: Abstract this into a separate function
1379+ // Set host_defined_options
1380+ Local<PrimitiveArray> host_defined_options =
1381+ PrimitiveArray::New (isolate, loader::HostDefinedOptions::kLength );
1382+ host_defined_options->Set (
1383+ isolate, loader::HostDefinedOptions::kID , id_symbol);
1384+
1385+ ScriptOrigin origin (isolate,
1386+ filename,
1387+ 0 , // line offset
1388+ 0 , // column offset
1389+ true , // is cross origin
1390+ -1 , // script id
1391+ Local<Value>(), // source map URL
1392+ false , // is opaque (?)
1393+ false , // is WASM
1394+ false , // is ES Module
1395+ host_defined_options);
1396+
1397+ ScriptCompiler::CachedData* cached_data = nullptr ;
1398+ ScriptCompiler::Source source =
1399+ ScriptCompiler::Source (code, origin, cached_data);
1400+ ScriptCompiler::CompileOptions options;
1401+ if (source.GetCachedData () == nullptr ) {
1402+ options = ScriptCompiler::kNoCompileOptions ;
1403+ } else {
1404+ options = ScriptCompiler::kConsumeCodeCache ;
1405+ }
1406+ // End TODO
1407+
1408+ std::vector<Local<String>> params = {
1409+ String::NewFromUtf8 (isolate, " exports" ).ToLocalChecked (),
1410+ String::NewFromUtf8 (isolate, " require" ).ToLocalChecked (),
1411+ String::NewFromUtf8 (isolate, " module" ).ToLocalChecked (),
1412+ String::NewFromUtf8 (isolate, " __filename" ).ToLocalChecked (),
1413+ String::NewFromUtf8 (isolate, " __dirname" ).ToLocalChecked ()};
1414+
1415+ TryCatchScope try_catch (env);
1416+
1417+ ContextifyContext::CompileFunctionAndCacheResult (env,
1418+ context,
1419+ source,
1420+ params,
1421+ std::vector<Local<Object>>(),
1422+ options,
1423+ true ,
1424+ id_symbol,
1425+ try_catch);
1426+
1427+ bool found_error_message_caused_by_module_syntax = false ;
1428+ if (try_catch.HasCaught () && !try_catch.HasTerminated ()) {
1429+ Utf8Value message_value (env->isolate (), try_catch.Message ()->Get ());
1430+ auto message = message_value.ToStringView ();
1431+
1432+ for (const auto & error_message : esm_syntax_error_messages) {
1433+ if (message.find (error_message) != std::string_view::npos) {
1434+ found_error_message_caused_by_module_syntax = true ;
1435+ break ;
1436+ }
1437+ }
1438+ }
1439+ args.GetReturnValue ().Set (found_error_message_caused_by_module_syntax);
1440+ }
1441+
13421442static void StartSigintWatchdog (const FunctionCallbackInfo<Value>& args) {
13431443 int ret = SigintWatchdogHelper::GetInstance ()->Start ();
13441444 args.GetReturnValue ().Set (ret == 0 );
0 commit comments