Skip to content

Commit 40b8dcb

Browse files
committed
Addressing review feedback
1 parent 03c91ad commit 40b8dcb

File tree

11 files changed

+95
-6
lines changed

11 files changed

+95
-6
lines changed

src/esm.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,11 @@ export interface NodeLoaderHooksAPI2 {
6262
export namespace NodeLoaderHooksAPI2 {
6363
export type ResolveHook = (
6464
specifier: string,
65-
context: { parentURL: string },
65+
context: {
66+
conditions?: NodeImportConditions;
67+
importAssertions?: NodeImportAssertions;
68+
parentURL: string;
69+
},
6670
defaultResolve: ResolveHook
6771
) => Promise<{ url: string }>;
6872
export type LoadHook = (
@@ -86,9 +90,10 @@ export type NodeLoaderHooksFormat =
8690
| 'module'
8791
| 'wasm';
8892

89-
export type NodeImportAssertions = {
90-
type: 'json' | 'wasm';
91-
};
93+
export type NodeImportConditions = unknown;
94+
export interface NodeImportAssertions {
95+
type?: 'json';
96+
}
9297

9398
/** @internal */
9499
export function registerAndCreateEsmHooks(opts?: RegisterOptions) {
@@ -187,8 +192,8 @@ export function createEsmHooks(tsNodeService: Service) {
187192
const { source: rawSource } = await defaultLoad(
188193
url,
189194
{
195+
...context,
190196
format,
191-
importAssertions: context.importAssertions,
192197
},
193198
defaultLoad
194199
);

src/test/esm-loader.spec.ts

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,19 @@
55
import { context } from './testlib';
66
import semver = require('semver');
77
import {
8+
CMD_ESM_LOADER_WITHOUT_PROJECT,
89
contextTsNodeUnderTest,
910
EXPERIMENTAL_MODULES_FLAG,
1011
resetNodeEnvironment,
1112
TEST_DIR,
1213
} from './helpers';
1314
import { createExec } from './exec-helpers';
14-
import { join } from 'path';
15+
import { join, resolve } from 'path';
1516
import * as expect from 'expect';
1617
import type { NodeLoaderHooksAPI2 } from '../';
1718

1819
const nodeUsesNewHooksApi = semver.gte(process.version, '16.12.0');
20+
const nodeSupportsImportAssertions = semver.gte(process.version, '17.1.0');
1921

2022
const test = context(contextTsNodeUnderTest);
2123

@@ -71,3 +73,55 @@ test.suite('hooks', (_test) => {
7173
});
7274
}
7375
});
76+
77+
if (nodeSupportsImportAssertions) {
78+
test.suite('Supports import assertions', (test) => {
79+
test('Can import JSON using the appropriate flag and assertion', async (t) => {
80+
const { err, stdout } = await exec(
81+
`${CMD_ESM_LOADER_WITHOUT_PROJECT} ./importJson.ts`,
82+
{
83+
cwd: resolve(TEST_DIR, 'esm-import-assertions'),
84+
}
85+
);
86+
expect(err).toBe(null);
87+
expect(stdout).toMatch(
88+
'A fuchsia car has 2 seats and the doors are open.`\nDone!'
89+
);
90+
});
91+
});
92+
93+
test.suite("Catch unexpected changes to node's loader context", (test) => {
94+
/*
95+
* This does not test ts-node.
96+
* Rather, it is meant to alert us to potentially breaking changes in node's
97+
* loader API. If node starts returning more or less properties on `context`
98+
* objects, we want to know, because it may indicate that our loader code
99+
* should be updated to accomodate the new properties, either by proxying them,
100+
* modifying them, or suppressing them.
101+
*/
102+
test('Ensure context passed to loader by node has only expected properties', async (t) => {
103+
const { stdout, stderr } = await exec(
104+
`node --loader ./esm-loader-context/loader.mjs --experimental-json-modules ./esm-loader-context/index.mjs`
105+
);
106+
const rows = stdout.split('\n').filter((v) => v[0] === '{');
107+
expect(rows.length).toBe(14);
108+
rows.forEach((row) => {
109+
const json = JSON.parse(row) as {
110+
resolveContextKeys?: string[];
111+
loadContextKeys?: string;
112+
};
113+
if (json.resolveContextKeys) {
114+
expect(json.resolveContextKeys).toEqual([
115+
'conditions',
116+
'importAssertions',
117+
'parentURL',
118+
]);
119+
} else if (json.loadContextKeys) {
120+
expect(json.loadContextKeys).toEqual(['format', 'importAssertions']);
121+
} else {
122+
throw new Error('Unexpected stdout in test.');
123+
}
124+
});
125+
});
126+
});
127+
}

tests/esm-import-assertions/importJson.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,16 @@ if (dynamicCarData.doors !== 'open')
1212
console.log(
1313
`A ${carData.color} car has ${carData.seats} seats and the doors are ${dynamicCarData.doors}.`
1414
);
15+
16+
// Test that omitting the assertion causes node to throw an error
17+
await import('./car.json')
18+
.then(() => {
19+
throw new Error('should have thrown');
20+
},
21+
(error: Error) => {
22+
if(!error.message.includes('foo bar')) {
23+
throw error;
24+
}
25+
/* error is expected */
26+
});
27+
console.log('Done!');

tests/esm-loader-context/index.mjs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import * as moduleA from './moduleA.mjs';
2+
import * as moduleB from './moduleB.mjs' assert { foo: 'bar' };
3+
import * as jsonModule from './jsonModuleA.json' assert { type: 'json' };
4+
5+
await import('./moduleC.mjs');
6+
await import('./moduleD.mjs', { foo: 'bar' });
7+
await import('./jsonModuleB.json', { assert: { type: 'json' } });
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

tests/esm-loader-context/loader.mjs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export function resolve(specifier, context, defaultResolve) {
2+
console.log(JSON.stringify({ resolveContextKeys: Object.keys(context) }));
3+
return defaultResolve(specifier, context);
4+
}
5+
export function load(url, context, defaultLoad) {
6+
console.log(JSON.stringify({ loadContextKeys: Object.keys(context) }));
7+
return defaultLoad(url, context);
8+
}

tests/esm-loader-context/moduleA.mjs

Whitespace-only changes.

tests/esm-loader-context/moduleB.mjs

Whitespace-only changes.

tests/esm-loader-context/moduleC.mjs

Whitespace-only changes.

tests/esm-loader-context/moduleD.mjs

Whitespace-only changes.

0 commit comments

Comments
 (0)