From 4f64fa4ad695bd748fd487e1ae502f82683747b5 Mon Sep 17 00:00:00 2001
From: Andrew Bradley <abradley@brightcove.com>
Date: Sun, 26 Dec 2021 21:25:56 +0000
Subject: [PATCH] Addressing review feedback

---
 src/esm.ts                                | 11 ++++---
 src/test/esm-loader.spec.ts               | 38 +++++++++++++++++++++++
 tests/esm-loader-context/index.mjs        |  7 +++++
 tests/esm-loader-context/jsonModuleA.json |  1 +
 tests/esm-loader-context/jsonModuleB.json |  1 +
 tests/esm-loader-context/loader.mjs       |  8 +++++
 tests/esm-loader-context/moduleA.mjs      |  0
 tests/esm-loader-context/moduleB.mjs      |  0
 tests/esm-loader-context/moduleC.mjs      |  0
 tests/esm-loader-context/moduleD.mjs      |  0
 10 files changed, 61 insertions(+), 5 deletions(-)
 create mode 100644 tests/esm-loader-context/index.mjs
 create mode 100644 tests/esm-loader-context/jsonModuleA.json
 create mode 100644 tests/esm-loader-context/jsonModuleB.json
 create mode 100644 tests/esm-loader-context/loader.mjs
 create mode 100644 tests/esm-loader-context/moduleA.mjs
 create mode 100644 tests/esm-loader-context/moduleB.mjs
 create mode 100644 tests/esm-loader-context/moduleC.mjs
 create mode 100644 tests/esm-loader-context/moduleD.mjs

diff --git a/src/esm.ts b/src/esm.ts
index 37bab3856..4d88541ea 100644
--- a/src/esm.ts
+++ b/src/esm.ts
@@ -62,7 +62,7 @@ export interface NodeLoaderHooksAPI2 {
 export namespace NodeLoaderHooksAPI2 {
   export type ResolveHook = (
     specifier: string,
-    context: { parentURL: string },
+    context: { conditions?: NodeImportConditions, importAssertions?: NodeImportAssertions, parentURL: string },
     defaultResolve: ResolveHook
   ) => Promise<{ url: string }>;
   export type LoadHook = (
@@ -86,9 +86,10 @@ export type NodeLoaderHooksFormat =
   | 'module'
   | 'wasm';
 
-export type NodeImportAssertions = {
-  type: 'json' | 'wasm';
-};
+export type NodeImportConditions = unknown;
+export interface NodeImportAssertions {
+  type?: 'json';
+}
 
 /** @internal */
 export function registerAndCreateEsmHooks(opts?: RegisterOptions) {
@@ -187,8 +188,8 @@ export function createEsmHooks(tsNodeService: Service) {
       const { source: rawSource } = await defaultLoad(
         url,
         {
+          ...context,
           format,
-          importAssertions: context.importAssertions,
         },
         defaultLoad
       );
diff --git a/src/test/esm-loader.spec.ts b/src/test/esm-loader.spec.ts
index 6c3c1c51a..fe4110c58 100644
--- a/src/test/esm-loader.spec.ts
+++ b/src/test/esm-loader.spec.ts
@@ -16,6 +16,7 @@ import * as expect from 'expect';
 import type { NodeLoaderHooksAPI2 } from '../';
 
 const nodeUsesNewHooksApi = semver.gte(process.version, '16.12.0');
+const nodeSupportsImportAssertions = semver.gte(process.version, '17.1.0');
 
 const test = context(contextTsNodeUnderTest);
 
@@ -71,3 +72,40 @@ test.suite('hooks', (_test) => {
     });
   }
 });
+
+if (nodeSupportsImportAssertions) {
+  test.suite("Catch unexpected changes to node's loader context", (test) => {
+    /*
+     * This does not test ts-node.
+     * Rather, it is meant to alert us to potentially breaking changes in node's
+     * loader API.  If node starts returning more or less properties on `context`
+     * objects, we want to know, because it may indicate that our loader code
+     * should be updated to accomodate the new properties, either by proxying them,
+     * modifying them, or suppressing them.
+     */
+    test('Ensure context passed to loader by node has only expected properties', async (t) => {
+      const { stdout, stderr } = await exec(
+        `node --loader ./esm-loader-context/loader.mjs --experimental-json-modules ./esm-loader-context/index.mjs`
+      );
+      const rows = stdout.split('\n').filter((v) => v[0] === '{');
+      expect(rows.length).toBe(14);
+      rows.forEach((row) => {
+        const json = JSON.parse(row) as {
+          resolveContextKeys?: string[];
+          loadContextKeys?: string;
+        };
+        if (json.resolveContextKeys) {
+          expect(json.resolveContextKeys).toEqual([
+            'conditions',
+            'importAssertions',
+            'parentURL',
+          ]);
+        } else if (json.loadContextKeys) {
+          expect(json.loadContextKeys).toEqual(['format', 'importAssertions']);
+        } else {
+          throw new Error('Unexpected stdout in test.');
+        }
+      });
+    });
+  });
+}
diff --git a/tests/esm-loader-context/index.mjs b/tests/esm-loader-context/index.mjs
new file mode 100644
index 000000000..3def33246
--- /dev/null
+++ b/tests/esm-loader-context/index.mjs
@@ -0,0 +1,7 @@
+import * as moduleA from './moduleA.mjs';
+import * as moduleB from './moduleB.mjs' assert { foo: 'bar' };
+import * as jsonModule from './jsonModuleA.json' assert { type: 'json' };
+
+await import('./moduleC.mjs');
+await import('./moduleD.mjs', { foo: 'bar' });
+await import('./jsonModuleB.json', { assert: { type: 'json' } });
diff --git a/tests/esm-loader-context/jsonModuleA.json b/tests/esm-loader-context/jsonModuleA.json
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/tests/esm-loader-context/jsonModuleA.json
@@ -0,0 +1 @@
+{}
diff --git a/tests/esm-loader-context/jsonModuleB.json b/tests/esm-loader-context/jsonModuleB.json
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/tests/esm-loader-context/jsonModuleB.json
@@ -0,0 +1 @@
+{}
diff --git a/tests/esm-loader-context/loader.mjs b/tests/esm-loader-context/loader.mjs
new file mode 100644
index 000000000..d500b5a79
--- /dev/null
+++ b/tests/esm-loader-context/loader.mjs
@@ -0,0 +1,8 @@
+export function resolve(specifier, context, defaultResolve) {
+  console.log(JSON.stringify({ resolveContextKeys: Object.keys(context) }));
+  return defaultResolve(specifier, context);
+}
+export function load(url, context, defaultLoad) {
+  console.log(JSON.stringify({ loadContextKeys: Object.keys(context) }));
+  return defaultLoad(url, context);
+}
diff --git a/tests/esm-loader-context/moduleA.mjs b/tests/esm-loader-context/moduleA.mjs
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/esm-loader-context/moduleB.mjs b/tests/esm-loader-context/moduleB.mjs
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/esm-loader-context/moduleC.mjs b/tests/esm-loader-context/moduleC.mjs
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/esm-loader-context/moduleD.mjs b/tests/esm-loader-context/moduleD.mjs
new file mode 100644
index 000000000..e69de29bb