Skip to content

Commit 097bb29

Browse files
authored
Fixed support for importing namespaced modules e.g. @serenity-js/jasmine
* Fixes #199 * Merges #209 from @jan-molak
1 parent 2689181 commit 097bb29

File tree

2 files changed

+53
-13
lines changed

2 files changed

+53
-13
lines changed

lib/loader.js

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const path = require('path');
2+
const url = require('url');
23

34
class Loader {
45
constructor(options) {
@@ -11,16 +12,7 @@ class Loader {
1112

1213
load(modulePath) {
1314
if ((this.alwaysImport && !modulePath.endsWith('.json')) || modulePath.endsWith('.mjs')) {
14-
let importSpecifier;
15-
16-
if (modulePath.indexOf(path.sep) === -1 && modulePath.indexOf('/') === -1) {
17-
importSpecifier = modulePath;
18-
} else {
19-
// The ES module spec requires import paths to be valid URLs. As of v14,
20-
// Node enforces this on Windows but not on other OSes. On OS X, import
21-
// paths that are URLs must not contain parent directory references.
22-
importSpecifier = `file://${this.resolvePath_(modulePath)}`;
23-
}
15+
const importSpecifier = this.resolveImportSpecifier_(modulePath);
2416

2517
return this.import_(importSpecifier)
2618
.then(
@@ -47,6 +39,25 @@ class Loader {
4739
});
4840
}
4941
}
42+
43+
resolveImportSpecifier_(modulePath) {
44+
const isNamespaced = modulePath.startsWith('@');
45+
const isRelative = modulePath.startsWith('.');
46+
const hasExtension = /\.[A-Za-z]+/.test(modulePath);
47+
48+
const resolvedModulePath = hasExtension || isRelative
49+
? this.resolvePath_(modulePath)
50+
: modulePath;
51+
52+
if (isNamespaced || ! hasExtension) {
53+
return resolvedModulePath;
54+
}
55+
56+
// The ES module spec requires import paths to be valid URLs. As of v14,
57+
// Node enforces this on Windows but not on other OSes. On OS X, import
58+
// paths that are URLs must not contain parent directory references.
59+
return url.pathToFileURL(resolvedModulePath).toString();
60+
}
5061
}
5162

5263
function requireShim(modulePath) {

spec/loader_spec.js

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const path = require('path');
2+
const url = require('url');
23
const Loader = require('../lib/loader');
34

45
describe('loader', function() {
@@ -55,6 +56,21 @@ describe('loader', function() {
5556
expect(importShim).toHaveBeenCalledWith('some-module');
5657
});
5758

59+
it('imports namespaced modules', async function() {
60+
const payload = {default: {}};
61+
const requireShim = jasmine.createSpy('requireShim');
62+
const importShim = jasmine.createSpy('importShim')
63+
.and.returnValue(Promise.resolve(payload));
64+
const loader = new Loader({requireShim, importShim});
65+
loader.alwaysImport = true;
66+
67+
const result = await loader.load('@namespace/some-module');
68+
69+
expect(result).toBe(payload.default);
70+
expect(requireShim).not.toHaveBeenCalled();
71+
expect(importShim).toHaveBeenCalledWith('@namespace/some-module');
72+
});
73+
5874
it('uses require to load JSON files', async function() {
5975
const requireShim = jasmine.createSpy('requireShim')
6076
.and.returnValue(Promise.resolve());
@@ -101,6 +117,19 @@ describe('loader', function() {
101117
expect(importShim).not.toHaveBeenCalled();
102118
});
103119

120+
it('loads namespaced commonjs module', async function () {
121+
const requireShim = jasmine.createSpy('requireShim')
122+
.and.returnValue(Promise.resolve());
123+
const importShim = jasmine.createSpy('importShim');
124+
const loader = new Loader({requireShim, importShim});
125+
loader.alwaysImport = false;
126+
127+
await expectAsync(loader.load('@namespace/some-module')).toBeResolved();
128+
129+
expect(requireShim).toHaveBeenCalledWith('@namespace/some-module');
130+
expect(importShim).not.toHaveBeenCalled();
131+
});
132+
104133
it('propagates the error when import fails', async function () {
105134
const underlyingError = new Error('nope');
106135
const requireShim = jasmine.createSpy('requireShim')
@@ -135,18 +164,18 @@ function esModuleSharedExamples(extension, alwaysImport) {
135164

136165
expect(requireShim).not.toHaveBeenCalled();
137166
expect(resolvePath).toHaveBeenCalledWith(requestedPath);
138-
expect(importShim).toHaveBeenCalledWith('file:///the/path/to/the/module');
167+
expect(importShim).toHaveBeenCalledWith(url.pathToFileURL('/the/path/to/the/module').toString());
139168
await expectAsync(loaderPromise).toBePending();
140169

141170
resolve({});
142171

143172
await expectAsync(loaderPromise).toBeResolved();
144173
}
145-
174+
146175
it('loads the file as an es module', async function () {
147176
await testBasicEsModuleLoading(path.sep);
148177
});
149-
178+
150179
it('supports /-separated paths', async function() {
151180
await testBasicEsModuleLoading('/');
152181
});

0 commit comments

Comments
 (0)