From e5cdab53e111cb795ad5472358666565fddf40e0 Mon Sep 17 00:00:00 2001 From: Tomer Date: Mon, 15 Nov 2021 02:17:38 +0200 Subject: [PATCH 1/4] feat: added support for ESM config files --- .babelrc | 10 ++++++- package.json | 1 + src/helpers/config-helper.js | 20 +++++++------ src/helpers/import-helper.js | 5 ++++ test/db/migrate.test.js | 57 ++++++++++++++++++++++++++++++++++++ 5 files changed, 83 insertions(+), 10 deletions(-) create mode 100644 src/helpers/import-helper.js diff --git a/.babelrc b/.babelrc index 9aa643c0..b8844242 100644 --- a/.babelrc +++ b/.babelrc @@ -10,5 +10,13 @@ ], "ignore": [ "src/assets" - ] + ], + "overrides": [{ + "test": "./src/helpers/import-helper.js", + "presets": [ + ["@babel/env", { + "exclude": ["proposal-dynamic-import"], + }] + ], + }] } diff --git a/package.json b/package.json index db99c888..487c17a2 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "pg": "latest", "pg-hstore": "latest", "prettier": "^2.4.1", + "semver": "^7.3.5", "sequelize": "^6.9.0", "sqlite3": "latest", "through2": "^4.0.2" diff --git a/src/helpers/config-helper.js b/src/helpers/config-helper.js index 17b9ac6e..17296c56 100644 --- a/src/helpers/config-helper.js +++ b/src/helpers/config-helper.js @@ -5,6 +5,7 @@ import _ from 'lodash'; import { promisify } from 'util'; import helpers from './index'; import getYArgs from '../core/yargs'; +import importHelper from './import-helper'; const args = getYArgs().argv; @@ -15,18 +16,19 @@ const api = { init() { return Promise.resolve() .then(() => { - let config; - if (args.url) { - config = api.parseDbUrl(args.url); + return api.parseDbUrl(args.url); } else { - try { - config = require(api.getConfigFile()); - } catch (e) { - api.error = e; - } + return importHelper.importModule(api.getConfigFile()); + } + }) + .then((module) => module.default) + .catch(() => { + try { + return require(api.getConfigFile()); + } catch (e) { + api.error = e; } - return config; }) .then((config) => { if (typeof config === 'object' || config === undefined) { diff --git a/src/helpers/import-helper.js b/src/helpers/import-helper.js new file mode 100644 index 00000000..3ab0d4dd --- /dev/null +++ b/src/helpers/import-helper.js @@ -0,0 +1,5 @@ +module.exports = { + importModule: function (module) { + return import(module); + }, +}; diff --git a/test/db/migrate.test.js b/test/db/migrate.test.js index 38479662..755d7219 100644 --- a/test/db/migrate.test.js +++ b/test/db/migrate.test.js @@ -3,6 +3,7 @@ const expect = require('expect.js'); const Support = require(__dirname + '/../support'); const helpers = require(__dirname + '/../support/helpers'); const gulp = require('gulp'); +const semver = require('semver'); const _ = require('lodash'); [ @@ -293,6 +294,54 @@ describe(Support.getTestDialectTeaser('db:migrate'), () => { }); }); +describeOnlyForESM(Support.getTestDialectTeaser('db:migrate'), () => { + describe('with config.mjs', () => { + const prepare = function (callback) { + const config = helpers.getTestConfig(); + const configContent = 'export default ' + JSON.stringify(config); + let result = ''; + + return gulp + .src(Support.resolveSupportPath('tmp')) + .pipe(helpers.clearDirectory()) + .pipe(helpers.runCli('init')) + .pipe(helpers.removeFile('config/config.json')) + .pipe(helpers.copyMigration('createPerson.js')) + .pipe(helpers.overwriteFile(configContent, 'config/config.mjs')) + .pipe(helpers.runCli('db:migrate --config config/config.mjs')) + .on('error', (e) => { + callback(e); + }) + .on('data', (data) => { + result += data.toString(); + }) + .on('end', () => { + callback(null, result); + }); + }; + + it('creates a SequelizeMeta table', function (done) { + prepare(() => { + helpers.readTables(this.sequelize, (tables) => { + expect(tables).to.have.length(2); + expect(tables).to.contain('SequelizeMeta'); + done(); + }); + }); + }); + + it('creates the respective table', function (done) { + prepare(() => { + helpers.readTables(this.sequelize, (tables) => { + expect(tables).to.have.length(2); + expect(tables).to.contain('Person'); + done(); + }); + }); + }); + }); +}); + describe(Support.getTestDialectTeaser('db:migrate'), () => { describe('with config.json and url option', () => { const prepare = function (callback) { @@ -525,3 +574,11 @@ describe(Support.getTestDialectTeaser('db:migrate'), () => { }); }); }); + +function describeOnlyForESM(title, fn) { + if (semver.satisfies(process.version, '^12.20.0 || ^14.13.1 || >=16.0.0')) { + describe(title, fn); + } else { + describe.skip(title, fn); + } +} From 164d26434963502208db44d2c2e7bdeef2701e7d Mon Sep 17 00:00:00 2001 From: Guylian Cox Date: Sat, 8 Jan 2022 11:36:18 +0100 Subject: [PATCH 2/4] feat: feature detect dynamic import for config --- .babelrc | 4 +++ src/helpers/config-helper.js | 60 ++++++++++++++++-------------------- src/helpers/dummy-file.js | 1 + src/helpers/import-helper.js | 36 ++++++++++++++++++++-- test/db/migrate.test.js | 14 +++++++-- 5 files changed, 76 insertions(+), 39 deletions(-) create mode 100644 src/helpers/dummy-file.js diff --git a/.babelrc b/.babelrc index b8844242..26bf4050 100644 --- a/.babelrc +++ b/.babelrc @@ -15,6 +15,10 @@ "test": "./src/helpers/import-helper.js", "presets": [ ["@babel/env", { + "targets": { + "node": "10.0" + }, + "shippedProposals": true, "exclude": ["proposal-dynamic-import"], }] ], diff --git a/src/helpers/config-helper.js b/src/helpers/config-helper.js index 17296c56..f44547b6 100644 --- a/src/helpers/config-helper.js +++ b/src/helpers/config-helper.js @@ -13,39 +13,33 @@ const api = { config: undefined, rawConfig: undefined, error: undefined, - init() { - return Promise.resolve() - .then(() => { - if (args.url) { - return api.parseDbUrl(args.url); - } else { - return importHelper.importModule(api.getConfigFile()); - } - }) - .then((module) => module.default) - .catch(() => { - try { - return require(api.getConfigFile()); - } catch (e) { - api.error = e; - } - }) - .then((config) => { - if (typeof config === 'object' || config === undefined) { - return config; - } else if (config.length === 1) { - return promisify(config)(); - } else { - return config(); - } - }) - .then((config) => { - api.rawConfig = config; - }) - .then(() => { - // Always return the full config api - return api; - }); + async init() { + let config; + + try { + if (args.url) { + config = await api.parseDbUrl(args.url); + } else { + const module = await importHelper.importModule(api.getConfigFile()); + config = await module.default; + } + } catch (e) { + api.error = e; + } + + if (typeof config === 'function') { + // accepts callback parameter + if (config.length === 1) { + config = await promisify(config)(); + } else { + // returns a promise. + await config(); + } + } + + api.rawConfig = config; + + return api; }, getConfigFile() { if (args.config) { diff --git a/src/helpers/dummy-file.js b/src/helpers/dummy-file.js new file mode 100644 index 00000000..24388397 --- /dev/null +++ b/src/helpers/dummy-file.js @@ -0,0 +1 @@ +// this file is imported by import-helper to detect whether dynamic imports are supported. diff --git a/src/helpers/import-helper.js b/src/helpers/import-helper.js index 3ab0d4dd..516ce9a2 100644 --- a/src/helpers/import-helper.js +++ b/src/helpers/import-helper.js @@ -1,5 +1,35 @@ +async function supportsDynamicImport() { + try { + // imports are cached. + // no need to worry about perf here. + // Don't remove .js: extension must be included for ESM imports! + await import('./dummy-file.js'); + return true; + } catch (e) { + return false; + } +} + +/** + * Imports a JSON, CommonJS or ESM module + * based on feature detection. + * + * @param modulePath path to the module to import + * @returns {Promise} the imported module. + */ +async function importModule(modulePath) { + // JSON modules are still behind a flag. Fallback to require for now. + // https://nodejs.org/api/esm.html#json-modules + if (!modulePath.endsWith('.json') && (await supportsDynamicImport())) { + return import(modulePath); + } + + // mimics what `import()` would return for + // cjs modules. + return { default: require(modulePath) }; +} + module.exports = { - importModule: function (module) { - return import(module); - }, + supportsDynamicImport, + importModule, }; diff --git a/test/db/migrate.test.js b/test/db/migrate.test.js index 755d7219..9690d95c 100644 --- a/test/db/migrate.test.js +++ b/test/db/migrate.test.js @@ -26,7 +26,7 @@ const _ = require('lodash'); let configPath = 'config/'; let migrationFile = options.migrationFile || 'createPerson'; const config = _.assign({}, helpers.getTestConfig(), options.config); - let configContent = JSON.stringify(config); + let configContent = JSON.stringify(config, null, 2); if (!migrationFile.match(/\.(cjs|ts)$/)) { migrationFile = migrationFile + '.js'; @@ -71,7 +71,11 @@ const _ = require('lodash'); it('creates a SequelizeMeta table', function (done) { const self = this; - prepare(() => { + prepare((e) => { + if (e) { + return done(e); + } + helpers.readTables(self.sequelize, (tables) => { expect(tables).to.have.length(2); expect(tables).to.contain('SequelizeMeta'); @@ -321,7 +325,11 @@ describeOnlyForESM(Support.getTestDialectTeaser('db:migrate'), () => { }; it('creates a SequelizeMeta table', function (done) { - prepare(() => { + prepare((e) => { + if (e) { + return done(e); + } + helpers.readTables(this.sequelize, (tables) => { expect(tables).to.have.length(2); expect(tables).to.contain('SequelizeMeta'); From 0b26b6d559ecb435e07dacc67b0e9c2e2f864482 Mon Sep 17 00:00:00 2001 From: ephys Date: Sat, 8 Jan 2022 14:08:18 +0100 Subject: [PATCH 3/4] build: ignore .idea files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b03a1ad5..1118f206 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ pids # Editor files .vscode +.idea # Directory for instrumented libs generated by jscoverage/JSCover lib-cov From e2008ce23530e321e62d53a2fd826127506a5c77 Mon Sep 17 00:00:00 2001 From: ephys Date: Sat, 8 Jan 2022 14:08:26 +0100 Subject: [PATCH 4/4] fix: add missing await --- src/helpers/config-helper.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helpers/config-helper.js b/src/helpers/config-helper.js index f44547b6..dd37da9d 100644 --- a/src/helpers/config-helper.js +++ b/src/helpers/config-helper.js @@ -18,7 +18,7 @@ const api = { try { if (args.url) { - config = await api.parseDbUrl(args.url); + config = api.parseDbUrl(args.url); } else { const module = await importHelper.importModule(api.getConfigFile()); config = await module.default; @@ -33,7 +33,7 @@ const api = { config = await promisify(config)(); } else { // returns a promise. - await config(); + config = await config(); } }