@@ -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,100 @@ 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+ // TODO: Centralize this rather than matching the logic in cjs/loader.js and translators.js
1370+ Local<Symbol> id_symbol = (String::Concat (isolate,
1371+ String::NewFromUtf8 (isolate, " cjs:" ).ToLocalChecked (), filename)).As <Symbol>();
1372+
1373+ // TODO: Abstract this into a separate function
1374+ // Set host_defined_options
1375+ Local<PrimitiveArray> host_defined_options =
1376+ PrimitiveArray::New (isolate, loader::HostDefinedOptions::kLength );
1377+ host_defined_options->Set (
1378+ isolate, loader::HostDefinedOptions::kID , id_symbol);
1379+
1380+ ScriptOrigin origin (isolate,
1381+ filename,
1382+ 0 , // line offset
1383+ 0 , // column offset
1384+ true , // is cross origin
1385+ -1 , // script id
1386+ Local<Value>(), // source map URL
1387+ false , // is opaque (?)
1388+ false , // is WASM
1389+ false , // is ES Module
1390+ host_defined_options);
1391+
1392+ ScriptCompiler::CachedData* cached_data = nullptr ;
1393+ ScriptCompiler::Source source = ScriptCompiler::Source (code, origin, cached_data);
1394+ ScriptCompiler::CompileOptions options;
1395+ if (source.GetCachedData () == nullptr ) {
1396+ options = ScriptCompiler::kNoCompileOptions ;
1397+ } else {
1398+ options = ScriptCompiler::kConsumeCodeCache ;
1399+ }
1400+ // End TODO
1401+
1402+ std::vector<Local<String>> params = {
1403+ String::NewFromUtf8 (isolate, " exports" ).ToLocalChecked (),
1404+ String::NewFromUtf8 (isolate, " require" ).ToLocalChecked (),
1405+ String::NewFromUtf8 (isolate, " module" ).ToLocalChecked (),
1406+ String::NewFromUtf8 (isolate, " __filename" ).ToLocalChecked (),
1407+ String::NewFromUtf8 (isolate, " __dirname" ).ToLocalChecked ()
1408+ };
1409+
1410+ TryCatchScope try_catch (env);
1411+
1412+ ContextifyContext::CompileFunctionAndCacheResult (
1413+ env,
1414+ context,
1415+ source,
1416+ params,
1417+ std::vector<Local<Object>>(),
1418+ options,
1419+ true ,
1420+ id_symbol,
1421+ try_catch);
1422+
1423+ bool found_error_message_caused_by_module_syntax = false ;
1424+ if (try_catch.HasCaught () && !try_catch.HasTerminated ()) {
1425+ Utf8Value message_value (env->isolate (), try_catch.Message ()->Get ());
1426+ auto message = message_value.ToStringView ();
1427+
1428+ for (const auto & error_message : esm_syntax_error_messages) {
1429+ if (message.find (error_message) != std::string_view::npos) {
1430+ found_error_message_caused_by_module_syntax = true ;
1431+ break ;
1432+ }
1433+ }
1434+ }
1435+ args.GetReturnValue ().Set (found_error_message_caused_by_module_syntax);
1436+ }
1437+
13421438static void StartSigintWatchdog (const FunctionCallbackInfo<Value>& args) {
13431439 int ret = SigintWatchdogHelper::GetInstance ()->Start ();
13441440 args.GetReturnValue ().Set (ret == 0 );
0 commit comments