diff --git a/CHANGELOG.md b/CHANGELOG.md index 38064c34..f53a21ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # changelog + * 2.2.2 _May.06.2023_ + * [detect async import.meta.resolve](https://github.com/iambumblehead/esmock/pull/201) and handle in a separate way + * remove un-necessary usage of await keyword in README example + * require node version less than 20.x * 2.2.1 _Apr.03.2023_ * [use Object.defineProperty](https://github.com/iambumblehead/esmock/pull/197) to write mock definitions protected on inherited prototype chain * 2.2.0 _Mar.23.2023_ diff --git a/README.md b/README.md index e8dcdbfa..74962a40 100644 --- a/README.md +++ b/README.md @@ -9,13 +9,13 @@ ``` ![npm](https://img.shields.io/npm/v/esmock) [![Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/iambumblehead/166d927bd0089d7bfdee4e98a537712c/raw/esmock__heads_master.json)][2] [![install size](https://packagephobia.now.sh/badge?p=esmock)](https://packagephobia.now.sh/result?p=esmock) [![downloads](https://badgen.now.sh/npm/dm/esmock)](https://npmjs.org/package/esmock) -**esmock provides native ESM import mocking for unit tests.** Use examples below as a quick-start guide, see the [descriptive and friendly esmock guide here,][10] or browse [esmock's test runner examples.][3] +**esmock provides native ESM import mocking for unit tests.** Use examples below as a quick-start guide, see the [descriptive and friendly esmock guide here,][4] or browse [esmock's test runner examples.][3] -[10]: https://github.com/iambumblehead/esmock/wiki -[0]: http://www.bumblehead.com "bumblehead" +[0]: https://www.bumblehead.com "bumblehead" [1]: https://github.com/iambumblehead/esmock/workflows/nodejs-ci/badge.svg "nodejs-ci pipeline" [2]: https://github.com/iambumblehead/esmock "esmock" [3]: https://github.com/iambumblehead/esmock/tree/master/tests "tests" +[4]: https://github.com/iambumblehead/esmock/wiki `esmock` is used with node's --loader ``` json @@ -52,7 +52,7 @@ import test from 'node:test' import assert from 'node:assert/strict' import esmock from 'esmock' -test('should mock packages and local files', async () => { +test('package, alias and local file mocks', async () => { const cookup = await esmock('../src/cookup.js', { addpkg: (a, b) => a + b, '#icon': { coffee: '☕', bacon: '🥓' }, @@ -65,7 +65,7 @@ test('should mock packages and local files', async () => { assert.strictEqual(cookup('breakfast'), '☕🥓🧂') }) -test('should do global instance mocks —third param', async () => { +test('global instance mocks —third param', async () => { const { getFile } = await esmock('../src/main.js', {}, { fs: { readFileSync: () => 'returns this 🌎 globally' } }) @@ -73,7 +73,7 @@ test('should do global instance mocks —third param', async () => { assert.strictEqual(getFile(), 'returns this 🌎 globally') }) -test('should mock "await import()" using esmock.p', async () => { +test('mocks "await import()" using esmock.p', async () => { // using esmock.p, mock definitions are kept in cache const doAwaitImport = await esmock.p('../awaitImportLint.js', { eslint: { ESLint: cfg => cfg } @@ -84,14 +84,14 @@ test('should mock "await import()" using esmock.p', async () => { // a bit more info are found in the wiki guide }) -test('should support "strict" mocking, at esmock.strict', async () => { +test('esmock.strict mocks', async () => { // replace original module definitions and do not merge them const pathWrapper = await esmock.strict('../src/pathWrapper.js', { path: { dirname: () => '/path/to/file' } }) // error, because "path" mock above does not define path.basename - await assert.rejects(async () => pathWrapper.basename('/dog.png'), { + assert.rejects(() => pathWrapper.basename('/dog.🐶.png'), { name: 'TypeError', message: 'path.basename is not a function' }) diff --git a/package.json b/package.json index 8ed2c32b..294e14d8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "esmock", "type": "module", - "version": "2.2.1", + "version": "2.2.2", "license": "ISC", "readmeFilename": "README.md", "description": "provides native ESM import mocking for unit tests", @@ -54,7 +54,7 @@ "rewire" ], "engines": { - "node": ">=14.16.0" + "node": ">=14.16.0 <=19.9.0" }, "dependencies": { "resolvewithplus": "^2.0.1" diff --git a/src/esmockModule.js b/src/esmockModule.js index 2d2f17e8..49e0504f 100644 --- a/src/esmockModule.js +++ b/src/esmockModule.js @@ -20,6 +20,17 @@ const asFileURL = p => p.startsWith('file://') ? p : url.pathToFileURL(p) const objProto = Object.getPrototypeOf({}) const isPlainObj = o => Object.getPrototypeOf(o) === objProto +// when import.meta.resolve fails to resolve windows paths, fallback resolvewith +const resolve = isMetaResolve ? + (import.meta.resolve.constructor.name === 'AsyncFunction' + ? async (id, p) => import.meta.resolve(id, asFileURL(p)) + .catch(() => resolvewith(id, p)) + : (id, p) => { + try { return import.meta.resolve(id, asFileURL(p)) } + catch { return resolvewith(id, p) } + }) + : resolvewith + // assigning the object to its own prototypal inheritor can error, eg // 'Cannot assign to read only property \'F_OK\' of object \'#\'' // @@ -116,9 +127,8 @@ const esmockModuleId = async (parent, treeid, defs, ids, opt, mocks, id) => { if (!id) return mocks - const fileURL = isMetaResolve - ? await import.meta.resolve(id, asFileURL(parent)).catch(() => null) - : resolvewith(id, parent) + const fileURL = resolve.constructor.name === 'AsyncFunction' + ? await resolve(id, parent) : resolve(id, parent) if (!fileURL && opt.isModuleNotFoundError !== false) throw esmockErr.errModuleIdNotFound(id, parent) @@ -128,7 +138,8 @@ const esmockModuleId = async (parent, treeid, defs, ids, opt, mocks, id) => { } const esmockModule = async (moduleId, parent, defs, gdefs, opt) => { - const moduleFileURL = resolvewith(moduleId, parent) + const moduleFileURL = resolve.constructor.name === 'AsyncFunction' + ? await resolve(moduleId, parent) : resolve(moduleId, parent) if (!moduleFileURL) throw esmockErr.errModuleIdNotFound(moduleId, parent) diff --git a/tests/tests-node/esmock.node.test.js b/tests/tests-node/esmock.node.test.js index 4b1a258c..496a7e73 100644 --- a/tests/tests-node/esmock.node.test.js +++ b/tests/tests-node/esmock.node.test.js @@ -39,7 +39,7 @@ test('should mock package, even when package is not installed', async () => { }) test('should mock a subpath', async () => { - const localpackagepath = path.resolve('../local/') + const localpackagepath = path.resolve('../local/') + path.sep const { subpathfunctionWrap } = await esmock( '../local/subpathimporter.js', localpackagepath, { '#sub': {