Improve Esm interop (#3985)

refs #3978
This commit is contained in:
Mr D 2020-08-16 18:22:26 +02:00 committed by GitHub
parent 8a7db0c5a8
commit 369bbd6293
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 758 additions and 248 deletions

View File

@ -1,3 +1,4 @@
*.stub
#
lib/util/import-file.js
lib/util/import-file.js
test/jake-util/knexfile-imports

View File

@ -25,6 +25,9 @@ const { listMigrations } = require('./utils/migrationsLister');
async function openKnexfile(configPath) {
const importFile = require('../lib/util/import-file'); // require me late!
let config = await importFile(configPath);
if (config && config.default) {
config = config.default;
}
if (typeof config === 'function') {
config = await config();
}
@ -45,9 +48,22 @@ async function initKnex(env, opts) {
// enable esm interop via 'esm' module
require = require('esm')(module);
// https://github.com/standard-things/esm/issues/868
// complete the hack: enabling requiring esm from 'module' type package
require.extensions['.js'] = (m, fileName) =>
m._compile(require('fs').readFileSync(fileName, 'utf8'), fileName);
const ext = require.extensions['.js'];
require.extensions['.js'] = (m, fileName) => {
try {
// default to the original extension
// this fails if target file parent is of type='module'
return ext(m, fileName);
} catch (err) {
if (err && err.code === 'ERR_REQUIRE_ESM') {
return m._compile(
require('fs').readFileSync(fileName, 'utf8'),
fileName
);
}
throw err;
}
};
}
env.configuration = env.configPath

View File

@ -0,0 +1,403 @@
'use strict';
const assert = require('assert');
const path = require('path');
const fs = require('fs');
const { execCommand } = require('cli-testlab');
const sqlite3 = require('sqlite3');
const KNEX = path.normalize(__dirname + '/../../bin/cli.js');
const NODE_VERSION = Number((/v(\d+)/i.exec(process.version) || [])[1]);
const isNode10 = NODE_VERSION === 10;
const TEST_BASE = '../test/jake-util';
const fixture = [
/** MIGRATIONS */
{
title: 'migrates esm modules',
testCase: 'knexfile-esm',
knexArgs: ['migrate:latest', '--esm'],
dropDb: true,
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
},
{
title: 'migrates esm modules from a module',
testCase: 'knexfile-esm-module',
knexArgs: ['migrate:latest', '--esm'],
expectedOutput: 'Batch 1 run: 1 migrations',
dropDb: true,
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
},
{
title: 'migrate esm module without --esm flag from a module, throws error',
testCase: 'knexfile-esm-module',
knexArgs: ['migrate:latest'],
dropDb: true,
expectedOutput: 'Batch 1 run: 1 migrations',
expectedErrorMessage: isNode10
? 'Unexpected token export'
: 'Must use import to load ES Module',
expectedSchema: [],
},
{
title: 'migrates mjs modules',
testCase: 'knexfile-mjs',
knexfile: 'knexfile.mjs',
nodeArgs: [
// TODO: document this !
isNode10 && '--experimental-modules',
isNode10 && '--no-warnings',
],
knexArgs: [
'migrate:latest',
// TODO: document this !
isNode10 && `--esm`,
],
dropDb: true,
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
},
/** SEEDS */
{
title: 'seeds esm files',
testCase: 'knexfile-esm',
expectedOutput: 'Ran 1 seed files',
knexArgs: ['seed:run', '--esm'],
dropDb: true,
/** before assert */
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
},
{
title: 'seeds esm files from module',
testCase: 'knexfile-esm-module',
expectedOutput: 'Ran 1 seed files',
knexArgs: ['seed:run', '--esm'],
/** before assert */
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
dropDb: true,
},
{
title: 'seed throws when runs "esm" files from "module" without --esm flag',
testCase: 'knexfile-esm-module',
knexArgs: ['seed:run'],
expectedErrorMessage: isNode10
? 'Unexpected token export'
: 'Must use import to load ES Module',
dropDb: false,
},
{
title: 'seeds mjs files',
testCase: 'knexfile-mjs',
knexfile: 'knexfile.mjs',
nodeArgs: [
// TODO: document this !
isNode10 && '--experimental-modules',
isNode10 && '--no-warnings',
],
knexArgs: [
'seed:run',
// TODO: document this !
isNode10 && `--esm`,
],
expectedOutput: 'Ran 1 seed files',
dropDb: true,
/** before assert */
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
},
/** SPECIAL CASES */
{
title: 'mjs files with mjs top level imports',
testCase: 'knexfile-imports',
knexfile: 'knexfile.mjs',
nodeArgs: [
isNode10 && '--experimental-modules',
isNode10 && '--no-warnings',
],
knexArgs: ['migrate:latest', isNode10 && `--esm`],
dropDb: true,
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
},
{
title:
'Directory import is not supported, resolving ES modules imported from knexfile.mjs',
testCase: 'knexfile-imports',
knexfile: 'knexfile1.mjs',
nodeArgs: [
isNode10 && '--experimental-modules',
isNode10 && '--no-warnings',
],
knexArgs: ['migrate:latest', isNode10 && `--esm`],
expectedErrorMessage: isNode10
? 'Error: Cannot find module'
: `Error [ERR_UNSUPPORTED_DIR_IMPORT]`,
},
{
title: 'dynamic importing js file from NON module package is not supported',
testCase: 'knexfile-imports',
knexfile: 'knexfile2.mjs',
nodeArgs: [
isNode10 && '--experimental-modules',
isNode10 && '--no-warnings',
],
knexArgs: ['migrate:latest', isNode10 && `--esm`],
expectedErrorMessage: isNode10
? 'Error: Cannot load module from .mjs'
: "Unexpected token 'export'",
},
{
title: isNode10
? "NODE10 can't static impor js from .mjs"
: 'static importing js file from NON module package is not supported',
testCase: 'knexfile-imports',
knexfile: 'knexfile2.mjs',
nodeArgs: [
isNode10 && '--experimental-modules',
isNode10 && '--no-warnings',
],
knexArgs: ['migrate:latest', isNode10 && `--esm`],
expectedErrorMessage: isNode10
? 'Error: Cannot load module from .mjs'
: "Unexpected token 'export'",
},
{
//Example: external module.type='module' by url 'packane-name/index.js'
title: isNode10
? "NODE10 can't dynamically import external ESM module package by URL"
: 'can dynamically import external ESM module package by URL',
testCase: 'knexfile-imports',
knexfile: 'knexfile4.mjs',
nodeArgs: [
isNode10 && '--experimental-modules',
isNode10 && '--no-warnings',
],
knexArgs: ['migrate:latest', isNode10 && `--esm`],
expectedErrorMessage: isNode10 && 'Error: Cannot load module from .mjs',
expectedOutput: !isNode10 && 'Batch 1 run: 1 migrations',
expectedSchema: !isNode10 && [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
dropDb: true,
},
{
title: isNode10
? "NODE10 can't create require"
: 'Importing commonjs from a mjs module',
testCase: 'knexfile-imports',
knexfile: 'knexfile5.mjs',
nodeArgs: [
isNode10 && '--experimental-modules',
isNode10 && '--no-warnings',
],
knexArgs: ['migrate:latest', isNode10 && `--esm`],
expectedErrorMessage:
isNode10 && 'TypeError: module.createRequire is not a function',
expectedOutput: !isNode10 && 'Batch 1 run: 1 migrations',
expectedSchema: !isNode10 && [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
dropDb: true,
},
{
title: 'Importing commonjs from a js ESM module and --esm interop',
testCase: 'knexfile-imports',
knexfile: 'knexfile6.js',
nodeArgs: [
isNode10 && '--experimental-modules',
isNode10 && '--no-warnings',
],
knexArgs: ['migrate:latest', `--esm`],
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
dropDb: true,
},
{
title: 'Importing js ESM from js ESM with --esm interop',
testCase: 'knexfile-imports',
knexfile: 'knexfile7.js',
nodeArgs: [
isNode10 && '--experimental-modules',
isNode10 && '--no-warnings',
],
knexArgs: ['migrate:latest', `--esm`],
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
dropDb: true,
},
{
title: 'Dynamic importing js ESM from js ESM with --esm interop',
testCase: 'knexfile-imports',
knexfile: 'knexfile8.js',
nodeArgs: [
isNode10 && '--experimental-modules',
isNode10 && '--no-warnings',
],
knexArgs: ['migrate:latest', `--esm`],
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
dropDb: true,
},
{
title: 'Static top level cjs import from js ESM with --esm interop',
testCase: 'knexfile-imports',
knexfile: 'knexfile9.js',
nodeArgs: [
isNode10 && '--experimental-modules',
isNode10 && '--no-warnings',
],
knexArgs: ['migrate:latest', `--esm`],
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
dropDb: true,
},
];
describe('esm interop and mjs support', () => {
before(() => {
process.env.KNEX_PATH = '../knex.js';
});
for (const spec of fixture) {
it(spec.title, async function () {
const {
testCase,
knexfile = 'knexfile.js',
dbName = 'test.sqlite3',
nodeArgs = [],
knexArgs,
expectedOutput,
expectedSchema,
expectedErrorMessage,
before,
dropDb = false,
} = spec;
const cwd = path.resolve(path.dirname(KNEX), TEST_BASE, testCase);
const dbPath = path.resolve(cwd, dbName);
// ...
const cmd = [
`node`,
...nodeArgs,
KNEX,
...knexArgs,
`--cwd="${cwd}"`,
`--knexfile=${path.resolve(cwd, knexfile)}`,
`--knexpath=${KNEX}`,
]
.filter(Boolean)
.join(' ');
// ...
if (dropDb && fs.existsSync(dbPath)) fs.unlinkSync(dbPath);
// ...
if (before) {
await before({
...spec,
cmd,
cwd,
testCase,
knexfile,
dbName,
dbPath,
nodeArgs,
knexArgs,
expectedOutput,
expectedSchema,
expectedErrorMessage,
});
}
// ...
await execCommand(cmd, { expectedOutput, expectedErrorMessage });
// ...
if (expectedSchema) {
const db = new sqlite3.Database(dbPath);
const result = await getSchema(db);
await closeDB(db);
// migration performed
assert.deepEqual(
result.map(({ name }) => name),
expectedSchema
);
}
});
}
});
function createTable(db, ddl) {
return new Promise((resolve, reject) =>
db.exec(`create TABLE if not exists ${ddl};`, (err) => {
if (err) reject(err);
else resolve();
})
);
}
function getSchema(db) {
return new Promise((resolve, reject) => {
db.all('SELECT name from SQLITE_MASTER', (err, rows) => {
if (err) reject(err);
else resolve(rows);
});
});
}
function closeDB(db) {
return new Promise((resolve, reject) =>
db.close((err) => {
if (err) reject(err);
else resolve();
})
);
}

View File

@ -110,108 +110,4 @@ describe('seed:run', () => {
}
);
});
it('runs "esm" files', async () => {
const cwd = path.resolve(__dirname, '../jake-util/knexfile-esm');
const { Database } = new require('sqlite3');
const db = new Database(path.resolve(cwd, 'test.sqlite3'));
await new Promise((resolve, reject) =>
db.exec(`create TABLE if not exists xyz (name TEXT);`, (err) => {
if (err) reject(err);
else resolve();
})
);
await new Promise((resolve) => db.close(() => resolve()));
return execCommand(
[
`node ${KNEX}`,
'seed:run',
'--esm',
`--cwd=${cwd}`,
`--knexfile=./knexfile.js`,
].join(' '),
{
expectedOutput: 'Ran 1 seed files',
notExpectedOutput: ['first.js', 'second.js'],
}
);
});
it('runs "esm" files from "module"', async () => {
const cwd = path.resolve(__dirname, '../jake-util/knexfile-esm-module');
const { Database } = new require('sqlite3');
const db = new Database(path.resolve(cwd, 'test.sqlite3'));
await new Promise((resolve, reject) =>
db.exec(`create TABLE if not exists xyz (name TEXT);`, (err) => {
if (err) reject(err);
else resolve();
})
);
await new Promise((resolve) => db.close(() => resolve()));
return execCommand(
[
`node ${KNEX}`,
`--cwd=${cwd}`,
'--esm',
'seed:run',
'--knexfile=./knexfile.js',
].join(' '),
{
expectedOutput: 'Ran 1 seed files',
notExpectedOutput: ['first.js', 'second.js'],
}
);
});
it('throws when runs "esm" files from "module" without --esm flag', () => {
const cwd = path.resolve(__dirname, '../jake-util/knexfile-esm-module');
const version = Number((/v(\d+)/i.exec(process.version) || [])[1]);
return execCommand(
[
`node ${KNEX}`,
` --cwd=${cwd}`,
' seed:run',
'--knexfile=./knexfile.js',
].join(' '),
{
expectedErrorMessage:
version === 10
? 'Unexpected token export'
: 'Must use import to load ES Module',
}
);
});
});
it('runs mjs files', async () => {
const cwd = path.resolve(__dirname, '../jake-util/knexfile-mjs');
const { Database } = new require('sqlite3');
const db = new Database(path.resolve(cwd, 'test.sqlite3'));
await new Promise((resolve, reject) =>
db.exec(`create TABLE if not exists xyz (name TEXT);`, (err) => {
if (err) reject(err);
else resolve();
})
);
await new Promise((resolve) => db.close(() => resolve()));
const version = Number((/v(\d+)/i.exec(process.version) || [])[1]);
return execCommand(
[
`node`,
// TODO: document this !
version === 10 && '--experimental-modules',
version === 10 && '--no-warnings',
`${KNEX}`,
// TODO: document this !
version === 10 && `--esm`,
`--cwd=${cwd}`,
'seed:run',
'--knexfile=./knexfile.mjs',
]
.filter(Boolean)
.join(' '),
{
expectedOutput: 'Ran 1 seed files',
}
);
});

View File

@ -54,4 +54,5 @@ describe('CLI tests', function () {
require('./cli/seed.spec');
require('./cli/seed-make.spec');
require('./cli/version.spec');
require('./cli/esm-interop.spec');
});

View File

@ -6,5 +6,6 @@ const config = {
},
useNullAsDefault: true,
};
export default config;
/** Named exports: or knex won't find them */
export const { client, connection, useNullAsDefault } = config;

View File

@ -2,5 +2,6 @@
"name": "knexfile-esm-module",
"version": "1.0.0",
"type": "module",
"module": "knexfile.js",
"license": "MIT"
}

View File

@ -0,0 +1,16 @@
{
"extends": "../../../.eslintrc.js",
"env": {
"browser": true,
"es6": true
},
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},
"rules": {}
}

View File

@ -0,0 +1,8 @@
/** @type {import("../../../").Config} */
export default {
client: 'sqlite3',
connection: {
filename: './test.sqlite3',
},
useNullAsDefault: true,
};

View File

@ -0,0 +1,14 @@
/**
* @param {import("../../../../")} knex
*/
export function up(knex) {
return knex.schema.createTable('xyz', (table) => {
table.string('name');
});
}
/**
* @param {import("../../../../")} knex
*/
export function down(knex) {
return knex.schema.dropTable('xyz');
}

View File

@ -0,0 +1,8 @@
{
"name": "knexfile-esm-package",
"private": true,
"version": "0.1.0-1",
"main": "knexfile.js",
"module": "knexfile.js",
"license": "MIT"
}

View File

@ -0,0 +1,6 @@
/**
* @param {import("../../../../")} knex
*/
export function seed(knex) {
return knex('xyz').del();
}

View File

@ -0,0 +1,8 @@
/** @type {import("../../../").Config} */
module.exports = {
client: 'sqlite3',
connection: {
filename: './test.sqlite3',
},
useNullAsDefault: true,
};

View File

@ -0,0 +1,8 @@
/** @type {import("../../../").Config} */
export default {
client: 'sqlite3',
connection: {
filename: './test.sqlite3',
},
useNullAsDefault: true,
};

View File

@ -6,5 +6,5 @@ const config = {
},
useNullAsDefault: true,
};
/** Named exports: or knex won't find them */
/** Named export */
export const { client, connection, useNullAsDefault } = config;

View File

@ -0,0 +1,16 @@
{
"extends": "../../../.eslintrc.js",
"env": {
"browser": true,
"es6": true
},
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},
"rules": {}
}

View File

@ -0,0 +1,14 @@
/**
* @param {import("../../../../")} knex
*/
export function up(knex) {
return knex.schema.createTable('xyz', (table) => {
table.string('name');
});
}
/**
* @param {import("../../../../")} knex
*/
export function down(knex) {
return knex.schema.dropTable('xyz');
}

View File

@ -0,0 +1,6 @@
/**
* @param {import("../../../../")} knex
*/
export function seed(knex) {
return knex('xyz').del();
}

View File

@ -0,0 +1,19 @@
import config from '../knexfile-mjs/knexfile.mjs'
/** Named exports */
export const {
client,
connection,
useNullAsDefault,
migrations,
seeds
} = {
...config,
migrations: {
...config.migrations,
directory: './mjs/migrations',
},
seeds: {
...config.seeds,
directory: './mjs/seeds',
}
};

View File

@ -0,0 +1,19 @@
import config from '../knexfile-esm'
/** Named exports */
export const {
client,
connection,
useNullAsDefault,
migrations,
seeds
} = {
...config,
migrations: {
...config.migrations,
directory: './mjs/migrations',
},
seeds: {
...config.seeds,
directory: './mjs/seeds',
}
};

View File

@ -0,0 +1,20 @@
/**
* CASE: 'Unexpected token 'export'
* can't import ../knexfile-esm-package/knexfile.js as ESM Module
* because package.json is not type 'module'
*/
export default async () => {
const { default: config } = await import('../knexfile-esm-package/knexfile.js');
return {
...config,
migrations: {
...config.migrations,
directory: './mjs/migrations',
},
seeds: {
...config.seeds,
directory: './mjs/seeds',
}
};
}

View File

@ -0,0 +1,18 @@
/**
* CASE: 'Unexpected token 'export'
* can't import ../knexfile-esm-package/knexfile.js as ESM Module
* because package.json is not type 'module'
*/
import config from '../knexfile-esm-package/knexfile.js'
/** */
export default {
...config,
migrations: {
...config.migrations,
directory: './mjs/migrations',
},
seeds: {
...config.seeds,
directory: './mjs/seeds',
}
}

View File

@ -0,0 +1,23 @@
const NODE_VERSION = Number((/v(\d+)/i.exec(process.version) || [])[1]);
const isNode10 = NODE_VERSION === 10;
/** @returns {Promise<import("../../../").Config>}*/
export default async () => {
const { default: config } = await import(
isNode10
? '../knexfile-esm-module/knexfile'
: '../knexfile-esm-module/knexfile.js'
);
return ({
...config,
migrations: {
directory: './mjs/migrations',
extension: "mjs",
loadExtensions: ['.mjs']
},
seeds: {
directory: './mjs/seeds',
extension: "mjs",
loadExtensions: ['.mjs']
}
})
}

View File

@ -0,0 +1,24 @@
import module from "module";
/**
* Importing commonjs from a mjs module
* @returns {Promise<import("../../../").Config>}
* */
export default async () => {
const require = module.createRequire(
import.meta.url
)
const config = require('../knexfile-esm/knexfile.cjs');
return ({
...config,
migrations: {
directory: './mjs/migrations',
extension: "mjs",
loadExtensions: ['.mjs']
},
seeds: {
directory: './mjs/seeds',
extension: "mjs",
loadExtensions: ['.mjs']
}
})
}

View File

@ -0,0 +1,34 @@
import module from 'module';
const NODE_VERSION = Number((/v(\d+)/i.exec(process.version) || [])[1]);
const isNode10 = NODE_VERSION === 10;
/**
* Importing commonjs from a js ESM module
* @returns {Promise<import("../../../").Config>}
* */
export default async () => {
let config;
if (typeof require === 'undefined') {
// This shouldn't happen
// you could NOT load this knexfile
// if you didn't pass the '--esm' flag
// unless you are transpiling it
if (isNode10) {
require = (await import('esm')).default(
new module.Module(import.meta.url)
);
} else {
// Node 12 & 14
require = module.createRequire(import.meta.url);
}
}
config = require('../knexfile-esm/knexfile.cjs');
return {
...config,
migrations: {
directory: './esm/migrations',
},
seeds: {
directory: './esm/seeds',
},
};
};

View File

@ -0,0 +1,13 @@
import config from '../knexfile-esm/knexfile.default.js';
/**
* @returns {Promise<import("../../../").Config>}
* */
export default {
...config,
migrations: {
directory: './esm/migrations',
},
seeds: {
directory: './esm/seeds',
},
};

View File

@ -0,0 +1,17 @@
/**
* @returns {Promise<import("../../../").Config>}
* */
export default async () => {
const { default: config } = await import(
'../knexfile-esm/knexfile.default.js'
);
return {
...config,
migrations: {
directory: './esm/migrations',
},
seeds: {
directory: './esm/seeds',
},
};
};

View File

@ -0,0 +1,15 @@
import config from '../knexfile-esm/knexfile.cjs';
/**
* Static 'cjs' import from js ESM with --esm interop
* @returns {import("../../../").Config}
* NOTE: this is NOT supported by NODE
* */
export default {
...config,
migrations: {
directory: './esm/migrations',
},
seeds: {
directory: './esm/seeds',
},
};

View File

@ -0,0 +1,14 @@
/**
* @param {import("../../../../")} knex
*/
export function up(knex) {
return knex.schema.createTable('xyz', (table) => {
table.string('name');
});
}
/**
* @param {import("../../../../")} knex
*/
export function down(knex) {
return knex.schema.dropTable('xyz');
}

View File

@ -0,0 +1,7 @@
/**
* @param {import("../../../../")} knex
*/
export function seed(knex) {
return knex('xyz').del();
}

View File

@ -14,5 +14,7 @@ const config = {
loadExtensions: ['.mjs']
}
};
/** ignored by knex */
export default config;
/** Named exports: or knex won't find them */
export const { client, connection, useNullAsDefault, migrations , seeds } = config;
export const { client, connection, useNullAsDefault, migrations , seeds } = config;

View File

@ -703,144 +703,6 @@ test('migrate:list prints migrations both completed and pending', async (temp) =
);
});
test('migrate runs "esm" modules', async (temp) => {
const cwd = path.resolve(
path.dirname(KNEX),
'../test/jake-util/knexfile-esm'
);
const dbPath = path.resolve(cwd, 'test.sqlite3');
if (fs.existsSync(dbPath)) fs.unlinkSync(dbPath);
const { stdout } = await assertExec(
[
`node ${KNEX}`,
'migrate:latest',
'--esm',
`--cwd="${cwd}"`,
`--knexfile=${path.resolve(cwd, 'knexfile.js')}`,
`--knexpath=${KNEX}`,
].join(' ')
);
assert.include(stdout, 'Batch 1 run: 1 migrations');
const db = new sqlite3.Database(dbPath);
const result = await new Promise((resolve, reject) => {
db.all('SELECT name from SQLITE_MASTER', (err, rows) => {
if (err) reject(err);
else resolve(rows.map(({ name }) => name));
});
});
await new Promise((resolve) => db.close(resolve));
// migration performed
assert.deepEqual(result, [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
]);
});
test('migrate runs "esm" modules from a "module"', async (temp) => {
const cwd = path.resolve(
path.dirname(KNEX),
'../test/jake-util/knexfile-esm-module'
);
const dbPath = path.resolve(cwd, 'test.sqlite3');
if (fs.existsSync(dbPath)) fs.unlinkSync(dbPath);
const { stdout } = await assertExec(
[
`node ${KNEX}`,
'--esm',
`--cwd=${cwd}`,
`--knexfile=${path.resolve(cwd, 'knexfile.js')}`,
`--knexpath=${KNEX}`,
'migrate:latest',
].join(' ')
);
assert.include(stdout, 'Batch 1 run: 1 migrations');
const db = new sqlite3.Database(dbPath);
const result = await new Promise((resolve, reject) => {
db.all('SELECT name from SQLITE_MASTER', (err, rows) => {
if (err) reject(err);
else resolve(rows.map(({ name }) => name));
});
});
await new Promise((resolve) => db.close(resolve));
// migration performed
assert.deepEqual(result, [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
]);
});
test('migrate "esm" module without --esm flag from a "module", throws error', async (temp) => {
const cwd = path.resolve(
path.dirname(KNEX),
'../test/jake-util/knexfile-esm-module'
);
const stderr = await assertExecError(
[
`node ${KNEX}`,
`--cwd=${cwd}`,
`--knexfile=${path.resolve(cwd, 'knexfile.js')}`,
`--knexpath=${KNEX}`,
'migrate:latest',
].join(' ')
);
const version = Number((/v(\d+)/i.exec(process.version) || [])[1]);
assert.include(
stderr,
version === 10
? 'Unexpected token export'
: 'Must use import to load ES Module'
);
});
test('migrate runs "mjs" modules', async (temp) => {
const cwd = path.resolve(
path.dirname(KNEX),
'../test/jake-util/knexfile-mjs'
);
const dbPath = path.resolve(cwd, 'test.sqlite3');
if (fs.existsSync(dbPath)) fs.unlinkSync(dbPath);
const version = Number((/v(\d+)/i.exec(process.version) || [])[1]);
const { stdout } = await assertExec(
[
`node`,
// TODO: document this !
version === 10 && '--experimental-modules',
version === 10 && '--no-warnings',
`${KNEX}`,
// TODO: document this !
version === 10 && `--esm`,
`--cwd=${cwd}`,
`--knexfile=${path.resolve(cwd, 'knexfile.mjs')}`,
`--knexpath=${KNEX}`,
'migrate:latest',
]
.filter(Boolean)
.join(' ')
);
assert.include(stdout, 'Batch 1 run: 1 migrations');
const db = new sqlite3.Database(dbPath);
const result = await new Promise((resolve, reject) => {
db.all('SELECT name from SQLITE_MASTER', (err, rows) => {
if (err) reject(err);
else resolve(rows.map(({ name }) => name));
});
});
await new Promise((resolve) => db.close(resolve));
// migration performed
assert.deepEqual(result, [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
]);
});
module.exports = {
taskList,
};