Skip to content

Commit

Permalink
Fixed support for importing namespaced modules e.g. @serenity-js/jasmine
Browse files Browse the repository at this point in the history
* Fixes #199
* Merges #209 from @jan-molak
  • Loading branch information
jan-molak authored Jun 22, 2023
1 parent 2689181 commit 097bb29
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 13 deletions.
31 changes: 21 additions & 10 deletions lib/loader.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const path = require('path');
const url = require('url');

class Loader {
constructor(options) {
Expand All @@ -11,16 +12,7 @@ class Loader {

load(modulePath) {
if ((this.alwaysImport && !modulePath.endsWith('.json')) || modulePath.endsWith('.mjs')) {
let importSpecifier;

if (modulePath.indexOf(path.sep) === -1 && modulePath.indexOf('/') === -1) {
importSpecifier = modulePath;
} else {
// The ES module spec requires import paths to be valid URLs. As of v14,
// Node enforces this on Windows but not on other OSes. On OS X, import
// paths that are URLs must not contain parent directory references.
importSpecifier = `file://${this.resolvePath_(modulePath)}`;
}
const importSpecifier = this.resolveImportSpecifier_(modulePath);

return this.import_(importSpecifier)
.then(
Expand All @@ -47,6 +39,25 @@ class Loader {
});
}
}

resolveImportSpecifier_(modulePath) {
const isNamespaced = modulePath.startsWith('@');
const isRelative = modulePath.startsWith('.');
const hasExtension = /\.[A-Za-z]+/.test(modulePath);

const resolvedModulePath = hasExtension || isRelative
? this.resolvePath_(modulePath)
: modulePath;

if (isNamespaced || ! hasExtension) {
return resolvedModulePath;
}

// The ES module spec requires import paths to be valid URLs. As of v14,
// Node enforces this on Windows but not on other OSes. On OS X, import
// paths that are URLs must not contain parent directory references.
return url.pathToFileURL(resolvedModulePath).toString();
}
}

function requireShim(modulePath) {
Expand Down
35 changes: 32 additions & 3 deletions spec/loader_spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const path = require('path');
const url = require('url');
const Loader = require('../lib/loader');

describe('loader', function() {
Expand Down Expand Up @@ -55,6 +56,21 @@ describe('loader', function() {
expect(importShim).toHaveBeenCalledWith('some-module');
});

it('imports namespaced modules', async function() {
const payload = {default: {}};
const requireShim = jasmine.createSpy('requireShim');
const importShim = jasmine.createSpy('importShim')
.and.returnValue(Promise.resolve(payload));
const loader = new Loader({requireShim, importShim});
loader.alwaysImport = true;

const result = await loader.load('@namespace/some-module');

expect(result).toBe(payload.default);
expect(requireShim).not.toHaveBeenCalled();
expect(importShim).toHaveBeenCalledWith('@namespace/some-module');
});

it('uses require to load JSON files', async function() {
const requireShim = jasmine.createSpy('requireShim')
.and.returnValue(Promise.resolve());
Expand Down Expand Up @@ -101,6 +117,19 @@ describe('loader', function() {
expect(importShim).not.toHaveBeenCalled();
});

it('loads namespaced commonjs module', async function () {
const requireShim = jasmine.createSpy('requireShim')
.and.returnValue(Promise.resolve());
const importShim = jasmine.createSpy('importShim');
const loader = new Loader({requireShim, importShim});
loader.alwaysImport = false;

await expectAsync(loader.load('@namespace/some-module')).toBeResolved();

expect(requireShim).toHaveBeenCalledWith('@namespace/some-module');
expect(importShim).not.toHaveBeenCalled();
});

it('propagates the error when import fails', async function () {
const underlyingError = new Error('nope');
const requireShim = jasmine.createSpy('requireShim')
Expand Down Expand Up @@ -135,18 +164,18 @@ function esModuleSharedExamples(extension, alwaysImport) {

expect(requireShim).not.toHaveBeenCalled();
expect(resolvePath).toHaveBeenCalledWith(requestedPath);
expect(importShim).toHaveBeenCalledWith('file:///the/path/to/the/module');
expect(importShim).toHaveBeenCalledWith(url.pathToFileURL('/the/path/to/the/module').toString());
await expectAsync(loaderPromise).toBePending();

resolve({});

await expectAsync(loaderPromise).toBeResolved();
}

it('loads the file as an es module', async function () {
await testBasicEsModuleLoading(path.sep);
});

it('supports /-separated paths', async function() {
await testBasicEsModuleLoading('/');
});
Expand Down

0 comments on commit 097bb29

Please sign in to comment.