Skip to content

Commit

Permalink
[New] Use import() on esm files in supported node versions
Browse files Browse the repository at this point in the history
  • Loading branch information
lohfu authored and ljharb committed Feb 22, 2021
1 parent 9bbf270 commit 28d6e51
Show file tree
Hide file tree
Showing 19 changed files with 257 additions and 11 deletions.
10 changes: 10 additions & 0 deletions .eslintrc
Expand Up @@ -29,6 +29,16 @@
"strict": "error",
},
"overrides": [
{
"files": ["*.mjs", "test/import/package_type/*.js"],
"extends": "@ljharb/eslint-config/esm",
},
{
"files": ["bin/import-or-require.js"],
"parserOptions": {
"ecmaVersion": 2020,
},
},
{
"files": ["test/async-await/*"],
"parserOptions": {
Expand Down
1 change: 1 addition & 0 deletions .nycrc
Expand Up @@ -3,6 +3,7 @@
"check-coverage": false,
"reporter": ["text-summary", "text", "html", "json"],
"exclude": [
"bin/import-or-require.js",
"coverage",
"example",
"test"
Expand Down
13 changes: 13 additions & 0 deletions bin/import-or-require.js
@@ -0,0 +1,13 @@
'use strict';

const { extname: extnamePath } = require('path');
const getPackageType = require('get-package-type');

module.exports = function (file) {
const ext = extnamePath(file);

if (ext === '.mjs' || (ext === '.js' && getPackageType.sync(file) === 'module')) {
return import(file);
}
require(file);
};
29 changes: 27 additions & 2 deletions bin/tape
Expand Up @@ -8,6 +8,9 @@ var readFileSync = require('fs').readFileSync;
var parseOpts = require('minimist');
var glob = require('glob');
var ignore = require('dotignore');
var hasImport = require('has-dynamic-import');

var tape = require('../');

var opts = parseOpts(process.argv.slice(2), {
alias: { r: 'require', i: 'ignore' },
Expand Down Expand Up @@ -47,7 +50,7 @@ var files = opts._.reduce(function (result, arg) {
var files = glob.sync(arg);

if (!Array.isArray(files)) {
throw new TypeError('unknown error: glob.sync did not return an array or throw. Please report this.');
throw new TypeError('unknown error: glob.sync("' + arg + '") did not return an array or throw. Please report this.');
}

return result.concat(files);
Expand All @@ -57,6 +60,28 @@ var files = opts._.reduce(function (result, arg) {
return resolvePath(cwd, file);
});

files.forEach(function (x) { require(x); });
hasImport().then(function (hasSupport) {
// the nextTick callback gets called outside the promise chain, avoiding
// promises and unhandled rejections when only loading commonjs files
process.nextTick(importFiles, hasSupport);
});

function importFiles(hasSupport) {
if (!hasSupport) {
return files.forEach(function (x) { require(x); });
}

var importOrRequire = require('./import-or-require');

tape.wait();

var promise = files.reduce(function (promise, file) {
return promise ? promise.then(function () {
return importOrRequire(file);
}) : importOrRequire(file);
}, null);

return promise ? promise.then(function () { tape.run(); }) : tape.run();
}

// vim: ft=javascript
39 changes: 30 additions & 9 deletions index.js
Expand Up @@ -14,11 +14,22 @@ var canExit = typeof process !== 'undefined' && process
;

exports = module.exports = (function () {
var wait = false;
var harness;
var lazyLoad = function () {
return getHarness().apply(this, arguments);
};

lazyLoad.wait = function () {
wait = true;
};

lazyLoad.run = function () {
var run = getHarness().run;

if (run) run();
};

lazyLoad.only = function () {
return getHarness().only.apply(this, arguments);
};
Expand Down Expand Up @@ -48,26 +59,25 @@ exports = module.exports = (function () {
function getHarness(opts) {
if (!opts) opts = {};
opts.autoclose = !canEmitExit;
if (!harness) harness = createExitHarness(opts);
if (!harness) harness = createExitHarness(opts, wait);
return harness;
}
})();

function createExitHarness(conf) {
function createExitHarness(conf, wait) {
if (!conf) conf = {};
var harness = createHarness({
autoclose: defined(conf.autoclose, false)
});
var running = false;
var ended = false;

var stream = harness.createStream({ objectMode: conf.objectMode });
var es = stream.pipe(conf.stream || createDefaultStream());
if (canEmitExit) {
es.on('error', function (err) { harness._exitCode = 1; });
if (wait) {
harness.run = run;
} else {
run();
}

var ended = false;
stream.on('end', function () { ended = true; });

if (conf.exit === false) return harness;
if (!canEmitExit || !canExit) return harness;

Expand All @@ -92,6 +102,17 @@ function createExitHarness(conf) {
});

return harness;

function run() {
if (running) return;
running = true;
var stream = harness.createStream({ objectMode: conf.objectMode });
var es = stream.pipe(conf.stream || createDefaultStream());
if (canEmitExit) {
es.on('error', function (err) { harness._exitCode = 1; });
}
stream.on('end', function () { ended = true; });
};
}

exports.createHarness = createHarness;
Expand Down
3 changes: 3 additions & 0 deletions package.json
Expand Up @@ -27,8 +27,10 @@
"defined": "^1.0.0",
"dotignore": "^0.1.2",
"for-each": "^0.3.3",
"get-package-type": "^0.1.0",
"glob": "^7.1.7",
"has": "^1.0.3",
"has-dynamic-import": "^2.0.0",
"inherits": "^2.0.4",
"is-regex": "^1.1.3",
"minimist": "^1.2.5",
Expand All @@ -41,6 +43,7 @@
"through": "^2.3.8"
},
"devDependencies": {
"@ljharb/eslint-config": "^17.6.0",
"array.prototype.flatmap": "^1.2.4",
"aud": "^1.1.5",
"concat-stream": "^1.6.2",
Expand Down
93 changes: 93 additions & 0 deletions test/import.js
@@ -0,0 +1,93 @@
'use strict';

var tap = require('tap');
var spawn = require('child_process').spawn;
var concat = require('concat-stream');
var hasDynamicImport = require('has-dynamic-import');

tap.test('importing mjs files', function (t) {
hasDynamicImport().then(function (hasSupport) {
if (hasSupport) {
var tc = function (rows) {
t.same(rows.toString('utf8'), [
'TAP version 13',
'# mjs-a',
'ok 1 test ran',
'# mjs-b',
'ok 2 test ran after mjs-a',
'# mjs-c',
'ok 3 test ran after mjs-b',
'# mjs-d',
'ok 4 test ran after mjs-c',
'# mjs-e',
'ok 5 test ran after mjs-d',
'# mjs-f',
'ok 6 test ran after mjs-e',
'# mjs-g',
'ok 7 test ran after mjs-f',
'# mjs-h',
'ok 8 test ran after mjs-g',
'',
'1..8',
'# tests 8',
'# pass 8',
'',
'# ok'
].join('\n') + '\n\n');
};

var ps = tape('import/*.mjs');
ps.stdout.pipe(concat(tc));
ps.stderr.pipe(process.stderr);
ps.on('exit', function (code) {
t.equal(code, 0);
t.end();
});
} else {
t.pass('does not support dynamic import');
t.end();
}
});
});

tap.test('importing type: "module" files', function (t) {
hasDynamicImport().then(function (hasSupport) {
if (hasSupport) {
var tc = function (rows) {
t.same(rows.toString('utf8'), [
'TAP version 13',
'# package-type-a',
'ok 1 test ran',
'# package-type-b',
'ok 2 test ran after package-type-a',
'# package-type-c',
'ok 3 test ran after package-type-b',
'',
'1..3',
'# tests 3',
'# pass 3',
'',
'# ok'
].join('\n') + '\n\n');
};

var ps = tape('import/package_type/*.js');
ps.stdout.pipe(concat(tc));
ps.stderr.pipe(process.stderr);
ps.on('exit', function (code) {
t.equal(code, 0);
t.end();
});
} else {
t.pass('does not support dynamic import');
t.end();
}
});
});

function tape(args) {
var proc = require('child_process');
var bin = __dirname + '/../bin/tape';

return proc.spawn('node', [bin].concat(args.split(' ')), { cwd: __dirname });
}
8 changes: 8 additions & 0 deletions test/import/mjs-a.mjs
@@ -0,0 +1,8 @@
import tape from '../../index.js';

tape.test('mjs-a', function (t) {
t.pass('test ran');
t.end();
global.mjs_a = true;
});

7 changes: 7 additions & 0 deletions test/import/mjs-b.mjs
@@ -0,0 +1,7 @@
import tape from '../../index.js';

tape.test('mjs-b', function (t) {
t.ok(global.mjs_a, 'test ran after mjs-a');
t.end();
global.mjs_b = true;
});
7 changes: 7 additions & 0 deletions test/import/mjs-c.mjs
@@ -0,0 +1,7 @@
import tape from '../../index.js';

tape.test('mjs-c', function (t) {
t.ok(global.mjs_b, 'test ran after mjs-b');
t.end();
global.mjs_c = true;
});
7 changes: 7 additions & 0 deletions test/import/mjs-d.mjs
@@ -0,0 +1,7 @@
import tape from '../../index.js';

tape.test('mjs-d', function (t) {
t.ok(global.mjs_c, 'test ran after mjs-c');
t.end();
global.mjs_d = true;
});
7 changes: 7 additions & 0 deletions test/import/mjs-e.mjs
@@ -0,0 +1,7 @@
import tape from '../../index.js';

tape.test('mjs-e', function (t) {
t.ok(global.mjs_d, 'test ran after mjs-d');
t.end();
global.mjs_e = true;
});
7 changes: 7 additions & 0 deletions test/import/mjs-f.mjs
@@ -0,0 +1,7 @@
import tape from '../../index.js';

tape.test('mjs-f', function (t) {
t.ok(global.mjs_e, 'test ran after mjs-e');
t.end();
global.mjs_f = true;
});
7 changes: 7 additions & 0 deletions test/import/mjs-g.mjs
@@ -0,0 +1,7 @@
import tape from '../../index.js';

tape.test('mjs-g', function (t) {
t.ok(global.mjs_f, 'test ran after mjs-f');
t.end();
global.mjs_g = true;
});
6 changes: 6 additions & 0 deletions test/import/mjs-h.mjs
@@ -0,0 +1,6 @@
import tape from '../../index.js';

tape.test('mjs-h', function (t) {
t.ok(global.mjs_g, 'test ran after mjs-g');
t.end();
});
8 changes: 8 additions & 0 deletions test/import/package_type/package-a.js
@@ -0,0 +1,8 @@
import tape from '../../../index.js';

tape.test('package-type-a', function (t) {
t.pass('test ran');
t.end();
global.package_type_a = true;
});

7 changes: 7 additions & 0 deletions test/import/package_type/package-b.js
@@ -0,0 +1,7 @@
import tape from '../../../index.js';

tape.test('package-type-b', function (t) {
t.ok(global.package_type_a, 'test ran after package-type-a');
t.end();
global.package_type_b = true;
});
6 changes: 6 additions & 0 deletions test/import/package_type/package-c.js
@@ -0,0 +1,6 @@
import tape from '../../../index.js';

tape.test('package-type-c', function (t) {
t.ok(global.package_type_b, 'test ran after package-type-b');
t.end();
});
3 changes: 3 additions & 0 deletions test/import/package_type/package.json
@@ -0,0 +1,3 @@
{
"type": "module"
}

0 comments on commit 28d6e51

Please sign in to comment.