Skip to content

Commit

Permalink
feat: add support for ESM config files (#987)
Browse files Browse the repository at this point in the history
Co-authored-by: Tomer <tomer@corevo.io>
  • Loading branch information
ephys and corevo committed Jan 11, 2022
1 parent 36f41f1 commit 54b9ded
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 35 deletions.
14 changes: 13 additions & 1 deletion .babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,17 @@
],
"ignore": [
"src/assets"
]
],
"overrides": [{
"test": "./src/helpers/import-helper.js",
"presets": [
["@babel/env", {
"targets": {
"node": "10.0"
},
"shippedProposals": true,
"exclude": ["proposal-dynamic-import"],
}]
],
}]
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pids

# Editor files
.vscode
.idea

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
60 changes: 28 additions & 32 deletions src/helpers/config-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,41 @@ 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;

const api = {
config: undefined,
rawConfig: undefined,
error: undefined,
init() {
return Promise.resolve()
.then(() => {
let config;

if (args.url) {
config = api.parseDbUrl(args.url);
} else {
try {
config = require(api.getConfigFile());
} catch (e) {
api.error = e;
}
}
return config;
})
.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 = 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.
config = await config();
}
}

api.rawConfig = config;

return api;
},
getConfigFile() {
if (args.config) {
Expand Down
1 change: 1 addition & 0 deletions src/helpers/dummy-file.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// this file is imported by import-helper to detect whether dynamic imports are supported.
35 changes: 35 additions & 0 deletions src/helpers/import-helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +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<unknown>} 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 = {
supportsDynamicImport,
importModule,
};
69 changes: 67 additions & 2 deletions test/db/migrate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');

[
Expand All @@ -25,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';
Expand Down Expand Up @@ -70,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');
Expand Down Expand Up @@ -293,6 +298,58 @@ 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((e) => {
if (e) {
return done(e);
}

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) {
Expand Down Expand Up @@ -525,3 +582,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);
}
}

0 comments on commit 54b9ded

Please sign in to comment.