@@ -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);
@@ -1204,33 +1206,18 @@ void ContextifyContext::CompileFunction(
1204
1206
data + cached_data_buf->ByteOffset (), cached_data_buf->ByteLength ());
1205
1207
}
1206
1208
1207
- // Set host_defined_options
1208
1209
Local<PrimitiveArray> host_defined_options =
1209
- PrimitiveArray::New (isolate, loader::HostDefinedOptions::kLength );
1210
- host_defined_options->Set (
1211
- isolate, loader::HostDefinedOptions::kID , id_symbol);
1212
-
1213
- ScriptOrigin origin (isolate,
1214
- filename,
1215
- line_offset, // line offset
1216
- column_offset, // column offset
1217
- true , // is cross origin
1218
- -1 , // script id
1219
- Local<Value>(), // source map URL
1220
- false , // is opaque (?)
1221
- false , // is WASM
1222
- false , // is ES Module
1223
- host_defined_options);
1224
-
1225
- ScriptCompiler::Source source (code, origin, cached_data);
1226
- ScriptCompiler::CompileOptions options;
1227
- if (source.GetCachedData () == nullptr ) {
1228
- options = ScriptCompiler::kNoCompileOptions ;
1229
- } else {
1230
- options = ScriptCompiler::kConsumeCodeCache ;
1231
- }
1210
+ GetHostDefinedOptions (isolate, id_symbol);
1211
+ ScriptCompiler::Source source =
1212
+ GetCommonJSSourceInstance (isolate,
1213
+ code,
1214
+ filename,
1215
+ line_offset,
1216
+ column_offset,
1217
+ host_defined_options,
1218
+ cached_data);
1219
+ ScriptCompiler::CompileOptions options = GetCompileOptions (source);
1232
1220
1233
- TryCatchScope try_catch (env);
1234
1221
Context::Scope scope (parsing_context);
1235
1222
1236
1223
// Read context extensions from buffer
@@ -1255,9 +1242,83 @@ void ContextifyContext::CompileFunction(
1255
1242
}
1256
1243
}
1257
1244
1245
+ TryCatchScope try_catch (env);
1246
+ Local<Object> result = CompileFunctionAndCacheResult (env,
1247
+ parsing_context,
1248
+ &source,
1249
+ params,
1250
+ context_extensions,
1251
+ options,
1252
+ produce_cached_data,
1253
+ id_symbol,
1254
+ try_catch);
1255
+
1256
+ if (try_catch.HasCaught () && !try_catch.HasTerminated ()) {
1257
+ try_catch.ReThrow ();
1258
+ return ;
1259
+ }
1260
+
1261
+ if (result.IsEmpty ()) {
1262
+ return ;
1263
+ }
1264
+ args.GetReturnValue ().Set (result);
1265
+ }
1266
+
1267
+ Local<PrimitiveArray> ContextifyContext::GetHostDefinedOptions (
1268
+ Isolate* isolate, Local<Symbol> id_symbol) {
1269
+ Local<PrimitiveArray> host_defined_options =
1270
+ PrimitiveArray::New (isolate, loader::HostDefinedOptions::kLength );
1271
+ host_defined_options->Set (
1272
+ isolate, loader::HostDefinedOptions::kID , id_symbol);
1273
+ return host_defined_options;
1274
+ }
1275
+
1276
+ ScriptCompiler::Source ContextifyContext::GetCommonJSSourceInstance (
1277
+ Isolate* isolate,
1278
+ Local<String> code,
1279
+ Local<String> filename,
1280
+ int line_offset,
1281
+ int column_offset,
1282
+ Local<PrimitiveArray> host_defined_options,
1283
+ ScriptCompiler::CachedData* cached_data) {
1284
+ ScriptOrigin origin (isolate,
1285
+ filename,
1286
+ line_offset, // line offset
1287
+ column_offset, // column offset
1288
+ true , // is cross origin
1289
+ -1 , // script id
1290
+ Local<Value>(), // source map URL
1291
+ false , // is opaque (?)
1292
+ false , // is WASM
1293
+ false , // is ES Module
1294
+ host_defined_options);
1295
+ return ScriptCompiler::Source (code, origin, cached_data);
1296
+ }
1297
+
1298
+ ScriptCompiler::CompileOptions ContextifyContext::GetCompileOptions (
1299
+ const ScriptCompiler::Source& source) {
1300
+ ScriptCompiler::CompileOptions options;
1301
+ if (source.GetCachedData () != nullptr ) {
1302
+ options = ScriptCompiler::kConsumeCodeCache ;
1303
+ } else {
1304
+ options = ScriptCompiler::kNoCompileOptions ;
1305
+ }
1306
+ return options;
1307
+ }
1308
+
1309
+ Local<Object> ContextifyContext::CompileFunctionAndCacheResult (
1310
+ Environment* env,
1311
+ Local<Context> parsing_context,
1312
+ ScriptCompiler::Source* source,
1313
+ std::vector<Local<String>> params,
1314
+ std::vector<Local<Object>> context_extensions,
1315
+ ScriptCompiler::CompileOptions options,
1316
+ bool produce_cached_data,
1317
+ Local<Symbol> id_symbol,
1318
+ const TryCatchScope& try_catch) {
1258
1319
MaybeLocal<Function> maybe_fn = ScriptCompiler::CompileFunction (
1259
1320
parsing_context,
1260
- & source,
1321
+ source,
1261
1322
params.size (),
1262
1323
params.data (),
1263
1324
context_extensions.size (),
@@ -1269,24 +1330,26 @@ void ContextifyContext::CompileFunction(
1269
1330
if (!maybe_fn.ToLocal (&fn)) {
1270
1331
if (try_catch.HasCaught () && !try_catch.HasTerminated ()) {
1271
1332
errors::DecorateErrorStack (env, try_catch);
1272
- try_catch. ReThrow ( );
1333
+ return Object::New (env-> isolate () );
1273
1334
}
1274
- return ;
1275
1335
}
1336
+
1337
+ Local<Context> context = env->context ();
1276
1338
if (fn->SetPrivate (context, env->host_defined_option_symbol (), id_symbol)
1277
1339
.IsNothing ()) {
1278
- return ;
1340
+ return Object::New (env-> isolate ()) ;
1279
1341
}
1280
1342
1343
+ Isolate* isolate = env->isolate ();
1281
1344
Local<Object> result = Object::New (isolate);
1282
1345
if (result->Set (parsing_context, env->function_string (), fn).IsNothing ())
1283
- return ;
1346
+ return Object::New (env-> isolate ()) ;
1284
1347
if (result
1285
1348
->Set (parsing_context,
1286
1349
env->source_map_url_string (),
1287
1350
fn->GetScriptOrigin ().SourceMapUrl ())
1288
1351
.IsNothing ())
1289
- return ;
1352
+ return Object::New (env-> isolate ()) ;
1290
1353
1291
1354
std::unique_ptr<ScriptCompiler::CachedData> new_cached_data;
1292
1355
if (produce_cached_data) {
@@ -1295,14 +1358,91 @@ void ContextifyContext::CompileFunction(
1295
1358
if (StoreCodeCacheResult (env,
1296
1359
result,
1297
1360
options,
1298
- source,
1361
+ * source,
1299
1362
produce_cached_data,
1300
1363
std::move (new_cached_data))
1301
1364
.IsNothing ()) {
1302
- return ;
1365
+ return Object::New (env-> isolate ()) ;
1303
1366
}
1304
1367
1305
- args.GetReturnValue ().Set (result);
1368
+ return result;
1369
+ }
1370
+
1371
+ // When compiling as CommonJS source code that contains ESM syntax, the
1372
+ // following error messages are returned:
1373
+ // - `import` statements: "Cannot use import statement outside a module"
1374
+ // - `export` statements: "Unexpected token 'export'"
1375
+ // - `import.meta` references: "Cannot use 'import.meta' outside a module"
1376
+ // Dynamic `import()` is permitted in CommonJS, so it does not error.
1377
+ // While top-level `await` is not permitted in CommonJS, it returns the same
1378
+ // error message as when `await` is used in a sync function, so we don't use it
1379
+ // as a disambiguation.
1380
+ constexpr std::array<std::string_view, 3 > esm_syntax_error_messages = {
1381
+ " Cannot use import statement outside a module" , // `import` statements
1382
+ " Unexpected token 'export'" , // `export` statements
1383
+ " Cannot use 'import.meta' outside a module" }; // `import.meta` references
1384
+
1385
+ void ContextifyContext::ContainsModuleSyntax (
1386
+ const FunctionCallbackInfo<Value>& args) {
1387
+ // Argument 1: source code
1388
+ CHECK (args[0 ]->IsString ());
1389
+ Local<String> code = args[0 ].As <String>();
1390
+
1391
+ // Argument 2: filename
1392
+ Local<String> filename = String::Empty (args.GetIsolate ());
1393
+ if (!args[1 ]->IsUndefined ()) {
1394
+ CHECK (args[1 ]->IsString ());
1395
+ filename = args[1 ].As <String>();
1396
+ }
1397
+
1398
+ Environment* env = Environment::GetCurrent (args);
1399
+ Isolate* isolate = env->isolate ();
1400
+ Local<Context> context = env->context ();
1401
+
1402
+ // TODO(geoffreybooth): Centralize this rather than matching the logic in
1403
+ // cjs/loader.js and translators.js
1404
+ Local<String> script_id = String::Concat (
1405
+ isolate, String::NewFromUtf8 (isolate, " cjs:" ).ToLocalChecked (), filename);
1406
+ Local<Symbol> id_symbol = Symbol::New (isolate, script_id);
1407
+
1408
+ Local<PrimitiveArray> host_defined_options =
1409
+ GetHostDefinedOptions (isolate, id_symbol);
1410
+ ScriptCompiler::Source source = GetCommonJSSourceInstance (
1411
+ isolate, code, filename, 0 , 0 , host_defined_options, nullptr );
1412
+ ScriptCompiler::CompileOptions options = GetCompileOptions (source);
1413
+
1414
+ std::vector<Local<String>> params = {
1415
+ String::NewFromUtf8 (isolate, " exports" ).ToLocalChecked (),
1416
+ String::NewFromUtf8 (isolate, " require" ).ToLocalChecked (),
1417
+ String::NewFromUtf8 (isolate, " module" ).ToLocalChecked (),
1418
+ String::NewFromUtf8 (isolate, " __filename" ).ToLocalChecked (),
1419
+ String::NewFromUtf8 (isolate, " __dirname" ).ToLocalChecked ()};
1420
+
1421
+ TryCatchScope try_catch (env);
1422
+
1423
+ ContextifyContext::CompileFunctionAndCacheResult (env,
1424
+ context,
1425
+ &source,
1426
+ params,
1427
+ std::vector<Local<Object>>(),
1428
+ options,
1429
+ true ,
1430
+ id_symbol,
1431
+ try_catch);
1432
+
1433
+ bool found_error_message_caused_by_module_syntax = false ;
1434
+ if (try_catch.HasCaught () && !try_catch.HasTerminated ()) {
1435
+ Utf8Value message_value (env->isolate (), try_catch.Message ()->Get ());
1436
+ auto message = message_value.ToStringView ();
1437
+
1438
+ for (const auto & error_message : esm_syntax_error_messages) {
1439
+ if (message.find (error_message) != std::string_view::npos) {
1440
+ found_error_message_caused_by_module_syntax = true ;
1441
+ break ;
1442
+ }
1443
+ }
1444
+ }
1445
+ args.GetReturnValue ().Set (found_error_message_caused_by_module_syntax);
1306
1446
}
1307
1447
1308
1448
static void StartSigintWatchdog (const FunctionCallbackInfo<Value>& args) {
0 commit comments