Skip to content

Commit

Permalink
support importing node core modules
Browse files Browse the repository at this point in the history
  • Loading branch information
SimenB committed Apr 12, 2020
1 parent 56d872b commit a9c1bfc
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 3 deletions.
2 changes: 1 addition & 1 deletion e2e/__tests__/__snapshots__/nativeEsm.test.ts.snap
Expand Up @@ -2,7 +2,7 @@

exports[`on node ^12.16.0 || >=13.0.0 runs test with native ESM 1`] = `
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: <<REPLACED>>
Ran all test suites.
Expand Down
17 changes: 17 additions & 0 deletions e2e/native-esm/__tests__/native-esm.test.js
Expand Up @@ -5,9 +5,13 @@
* LICENSE file in the root directory of this source tree.
*/

import {readFileSync} from 'fs';
import {dirname, resolve} from 'path';
import {fileURLToPath} from 'url';
import {double} from '../index';

test('should have correct import.meta', () => {
expect(typeof require).toBe('undefined');
expect(typeof jest).toBe('undefined');
expect(import.meta).toEqual({
jest: expect.anything(),
Expand All @@ -21,3 +25,16 @@ test('should have correct import.meta', () => {
test('should double stuff', () => {
expect(double(1)).toBe(2);
});

test('should support importing node core modules', () => {
const dir = dirname(fileURLToPath(import.meta.url));
const packageJsonPath = resolve(dir, '../package.json');

expect(JSON.parse(readFileSync(packageJsonPath, 'utf8'))).toEqual({
jest: {
testEnvironment: 'node',
transform: {},
},
type: 'module',
});
});
38 changes: 36 additions & 2 deletions packages/jest-runtime/src/index.ts
Expand Up @@ -12,6 +12,9 @@ import {
// @ts-ignore: experimental, not added to the types
SourceTextModule,
// @ts-ignore: experimental, not added to the types
SyntheticModule,
Context as VMContext,
// @ts-ignore: experimental, not added to the types
Module as VMModule,
compileFunction,
} from 'vm';
Expand Down Expand Up @@ -331,6 +334,12 @@ class Runtime {

invariant(context);

if (this._resolver.isCoreModule(modulePath)) {
const core = await this._importCoreModule(modulePath, context);
this._esmoduleRegistry.set(cacheKey, core);
return core;
}

const transformedFile = this.transformFile(modulePath, {
isInternalModule: false,
supportsDynamicImport: true,
Expand Down Expand Up @@ -1024,6 +1033,24 @@ class Runtime {
return require(moduleName);
}

private _importCoreModule(moduleName: string, context: VMContext) {
const required = this._requireCoreModule(moduleName);

return new SyntheticModule(
['default', ...Object.keys(required)],
function () {
// @ts-ignore: TS doesn't know what `this` is
this.setExport('default', required);
Object.entries(required).forEach(([key, value]) => {
// @ts-ignore: TS doesn't know what `this` is
this.setExport(key, value);
});
},
// should identifier be `node://${moduleName}`?
{context, identifier: moduleName},
);
}

private _getMockedNativeModule(): typeof nativeModule.Module {
if (this._moduleImplementation) {
return this._moduleImplementation;
Expand Down Expand Up @@ -1058,13 +1085,20 @@ class Runtime {
// should we implement the class ourselves?
class Module extends nativeModule.Module {}

Object.entries(nativeModule.Module).forEach(([key, value]) => {
// @ts-ignore
Module[key] = value;
});

Module.Module = Module;

if ('createRequire' in nativeModule) {
Module.createRequire = createRequire;
}
if ('createRequireFromPath' in nativeModule) {
Module.createRequireFromPath = (filename: string | URL) => {
Module.createRequireFromPath = function createRequireFromPath(
filename: string | URL,
) {
if (typeof filename !== 'string') {
const error = new TypeError(
`The argument 'filename' must be string. Received '${filename}'.${
Expand All @@ -1081,7 +1115,7 @@ class Runtime {
};
}
if ('syncBuiltinESMExports' in nativeModule) {
Module.syncBuiltinESMExports = () => {};
Module.syncBuiltinESMExports = function syncBuiltinESMExports() {};
}

this._moduleImplementation = Module;
Expand Down

0 comments on commit a9c1bfc

Please sign in to comment.