@@ -318,13 +318,15 @@ void ContextifyContext::CreatePerIsolateProperties(
318
318
SetMethod (isolate, target, " makeContext" , MakeContext);
319
319
SetMethod (isolate, target, " isContext" , IsContext);
320
320
SetMethod (isolate, target, " compileFunction" , CompileFunction);
321
+ SetMethod (isolate, target, " containsModuleSyntax" , ContainsModuleSyntax);
321
322
}
322
323
323
324
void ContextifyContext::RegisterExternalReferences (
324
325
ExternalReferenceRegistry* registry) {
325
326
registry->Register (MakeContext);
326
327
registry->Register (IsContext);
327
328
registry->Register (CompileFunction);
329
+ registry->Register (ContainsModuleSyntax);
328
330
registry->Register (PropertyGetterCallback);
329
331
registry->Register (PropertySetterCallback);
330
332
registry->Register (PropertyDescriptorCallback);
@@ -1339,6 +1341,104 @@ Local<Object> ContextifyContext::CompileFunctionAndCacheResult(
1339
1341
return result;
1340
1342
}
1341
1343
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
+
1342
1442
static void StartSigintWatchdog (const FunctionCallbackInfo<Value>& args) {
1343
1443
int ret = SigintWatchdogHelper::GetInstance ()->Start ();
1344
1444
args.GetReturnValue ().Set (ret == 0 );
0 commit comments