From d37fba639f7b4243f9a5c4bc2a5976dc45ef8693 Mon Sep 17 00:00:00 2001 From: Nathan Date: Wed, 5 May 2021 15:52:26 -0400 Subject: [PATCH 1/4] ESM support for migrate-mongo-config.js --- lib/env/config.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/env/config.js b/lib/env/config.js index 084278f..2914ceb 100644 --- a/lib/env/config.js +++ b/lib/env/config.js @@ -1,5 +1,6 @@ const fs = require("fs-extra"); const path = require("path"); +const url = require("url"); const { get } = require("lodash"); const DEFAULT_CONFIG_FILE_NAME = "migrate-mongo-config.js"; @@ -60,6 +61,13 @@ module.exports = { return customConfigContent; } const configPath = getConfigPath(); - return Promise.resolve(require(configPath)); // eslint-disable-line + try { + return Promise.resolve(require(configPath)); // eslint-disable-line + } catch (e) { + if (e.code === 'ERR_REQUIRE_ESM') { + return Promise.resolve(import(url.pathToFileURL(configPath))); + } + throw e; + } } }; From 0f75ada5141e4c420672f85cd79b407221c33ea2 Mon Sep 17 00:00:00 2001 From: Nathan Date: Wed, 5 May 2021 16:07:29 -0400 Subject: [PATCH 2/4] Use default export for ESM config files --- lib/env/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/env/config.js b/lib/env/config.js index 2914ceb..840602c 100644 --- a/lib/env/config.js +++ b/lib/env/config.js @@ -65,7 +65,7 @@ module.exports = { return Promise.resolve(require(configPath)); // eslint-disable-line } catch (e) { if (e.code === 'ERR_REQUIRE_ESM') { - return Promise.resolve(import(url.pathToFileURL(configPath))); + return (await import(url.pathToFileURL(configPath))).default; } throw e; } From f1da7254718c77698c488d7447423318066a55b2 Mon Sep 17 00:00:00 2001 From: Nathan Date: Wed, 5 May 2021 17:00:48 -0400 Subject: [PATCH 3/4] ESM support for migrations --- lib/env/migrationsDir.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/env/migrationsDir.js b/lib/env/migrationsDir.js index 4e7c2f8..cc23a74 100644 --- a/lib/env/migrationsDir.js +++ b/lib/env/migrationsDir.js @@ -1,5 +1,6 @@ const fs = require("fs-extra"); const path = require("path"); +const url = require("url"); const crypto = require("crypto"); const config = require("./config"); @@ -94,11 +95,19 @@ module.exports = { async loadMigration(fileName) { const migrationsDir = await resolveMigrationsDirPath(); - return require(path.join(migrationsDir, fileName)); // eslint-disable-line + const migrationPath = path.join(migrationsDir, fileName); + try { + return require(migrationPath); // eslint-disable-line + } catch (e) { + if (e.code === 'ERR_REQUIRE_ESM') { + return import(url.pathToFileURL(migrationPath)); + } + throw e; + } }, async loadFileHash(fileName) { - const migrationsDir = await resolveMigrationsDirPath(); + const migrationsDir = await resolveMigrationsDirPath(); const filePath = path.join(migrationsDir, fileName) const hash = crypto.createHash('sha256'); const input = await fs.readFile(filePath); From 216d08665fad0e98ee6b289130ad15d910b9233e Mon Sep 17 00:00:00 2001 From: nathan-knight Date: Thu, 6 May 2021 12:44:23 -0400 Subject: [PATCH 4/4] Added module-loader to allow mocking import, added tests for ESM fallback --- lib/env/config.js | 5 +++-- lib/env/migrationsDir.js | 5 +++-- lib/utils/module-loader.js | 10 ++++++++++ test/env/config.test.js | 32 +++++++++++++++++++++++++------- test/env/migrationsDir.test.js | 21 +++++++++++++++++++-- 5 files changed, 60 insertions(+), 13 deletions(-) create mode 100644 lib/utils/module-loader.js diff --git a/lib/env/config.js b/lib/env/config.js index 840602c..507e67a 100644 --- a/lib/env/config.js +++ b/lib/env/config.js @@ -2,6 +2,7 @@ const fs = require("fs-extra"); const path = require("path"); const url = require("url"); const { get } = require("lodash"); +const moduleLoader = require('../utils/module-loader'); const DEFAULT_CONFIG_FILE_NAME = "migrate-mongo-config.js"; @@ -62,10 +63,10 @@ module.exports = { } const configPath = getConfigPath(); try { - return Promise.resolve(require(configPath)); // eslint-disable-line + return Promise.resolve(moduleLoader.require(configPath)); } catch (e) { if (e.code === 'ERR_REQUIRE_ESM') { - return (await import(url.pathToFileURL(configPath))).default; + return (await moduleLoader.import(url.pathToFileURL(configPath))).default; } throw e; } diff --git a/lib/env/migrationsDir.js b/lib/env/migrationsDir.js index cc23a74..08beab5 100644 --- a/lib/env/migrationsDir.js +++ b/lib/env/migrationsDir.js @@ -3,6 +3,7 @@ const path = require("path"); const url = require("url"); const crypto = require("crypto"); const config = require("./config"); +const moduleLoader = require('../utils/module-loader'); const DEFAULT_MIGRATIONS_DIR_NAME = "migrations"; const DEFAULT_MIGRATION_EXT = ".js"; @@ -97,10 +98,10 @@ module.exports = { const migrationsDir = await resolveMigrationsDirPath(); const migrationPath = path.join(migrationsDir, fileName); try { - return require(migrationPath); // eslint-disable-line + return moduleLoader.require(migrationPath); } catch (e) { if (e.code === 'ERR_REQUIRE_ESM') { - return import(url.pathToFileURL(migrationPath)); + return moduleLoader.import(url.pathToFileURL(migrationPath)); } throw e; } diff --git a/lib/utils/module-loader.js b/lib/utils/module-loader.js new file mode 100644 index 0000000..d4a2ed8 --- /dev/null +++ b/lib/utils/module-loader.js @@ -0,0 +1,10 @@ +module.exports = { + require(requirePath) { + return require(requirePath); // eslint-disable-line + }, + + /* istanbul ignore next */ + import(importPath) { + return import(importPath); // eslint-disable-line + }, +}; diff --git a/test/env/config.test.js b/test/env/config.test.js index 39957a8..bacd7dc 100644 --- a/test/env/config.test.js +++ b/test/env/config.test.js @@ -7,6 +7,7 @@ const path = require("path"); describe("config", () => { let config; // module under test let fs; // mocked dependencies + let moduleLoader; function mockFs() { return { @@ -14,9 +15,19 @@ describe("config", () => { }; } + function mockModuleLoader() { + return { + import: sinon.stub(), + }; + } + beforeEach(() => { fs = mockFs(); - config = proxyquire("../../lib/env/config", { "fs-extra": fs }); + moduleLoader = mockModuleLoader(); + config = proxyquire("../../lib/env/config", { + "fs-extra": fs, + "../utils/module-loader": moduleLoader + }); }); describe("shouldExist()", () => { @@ -98,19 +109,17 @@ describe("config", () => { await config.read(); expect.fail("Error was not thrown"); } catch (err) { - expect(err.message).to.match(new RegExp(`Cannot find module '${configPath}'`)); + expect(err.message).to.have.string(`Cannot find module '${configPath}'`); } }); it("should be possible to read a custom, absolute config file path", async () => { - global.options = { file: "/some/absoluete/path/to/a-config-file.js" }; + global.options = { file: "/some/absolute/path/to/a-config-file.js" }; try { await config.read(); expect.fail("Error was not thrown"); } catch (err) { - expect(err.message).to.match( - new RegExp(`Cannot find module '${global.options.file}'`) - ); + expect(err.message).to.have.string(`Cannot find module '${global.options.file}'`); } }); @@ -121,8 +130,17 @@ describe("config", () => { await config.read(); expect.fail("Error was not thrown"); } catch (err) { - expect(err.message).to.match(new RegExp(`Cannot find module '${configPath}'`)); + expect(err.message).to.have.string(`Cannot find module '${configPath}'`); } }); + + it("should fall back to using 'import' if Node requires the use of ESM", async () => { + const error = new Error('ESM required'); + error.code = 'ERR_REQUIRE_ESM'; + moduleLoader.require = sinon.stub().throws(error); + moduleLoader.import.returns({}); + await config.read(); + expect(moduleLoader.import.called).to.equal(true); + }); }); }); diff --git a/test/env/migrationsDir.test.js b/test/env/migrationsDir.test.js index 5ff5318..92935ba 100644 --- a/test/env/migrationsDir.test.js +++ b/test/env/migrationsDir.test.js @@ -8,6 +8,7 @@ describe("migrationsDir", () => { let migrationsDir; let fs; let config; + let moduleLoader; function mockFs() { return { @@ -26,12 +27,20 @@ describe("migrationsDir", () => { }; } + function mockModuleLoader() { + return { + import: sinon.stub(), + }; + } + beforeEach(() => { fs = mockFs(); config = mockConfig(); + moduleLoader = mockModuleLoader(); migrationsDir = proxyquire("../../lib/env/migrationsDir", { "fs-extra": fs, - "./config": config + "./config": config, + "../utils/module-loader": moduleLoader }); }); @@ -165,9 +174,17 @@ describe("migrationsDir", () => { await migrationsDir.loadMigration("someFile.js"); expect.fail("Error was not thrown"); } catch (err) { - expect(err.message).to.match(new RegExp(`Cannot find module '${pathToMigration}'`)); + expect(err.message).to.have.string(`Cannot find module '${pathToMigration}'`); } }); + + it("should fall back to using 'import' if Node requires the use of ESM", async () => { + const error = new Error('ESM required'); + error.code = 'ERR_REQUIRE_ESM'; + moduleLoader.require = sinon.stub().throws(error); + await migrationsDir.loadMigration("someFile.js"); + expect(moduleLoader.import.called).to.equal(true); + }); }); describe("resolveMigrationFileExtension()", () => {