0
0
mirror of https://github.com/nodejs/node.git synced 2024-11-29 23:16:30 +01:00
nodejs/test/es-module/test-esm-exports.mjs
Guy Bedford 3f3ad38c87 module: reintroduce package exports dot main
This reintroduces the dot main in exports as discussed in the previous
Node.js modules meeting.

The implementation includes both CommonJS and ES module resolution with
the associated documentation and resolver specification changes.

In addition to the dot main, "exports" as a string or direct fallback
array is supported as well.

Co-Authored-By: Geoffrey Booth <GeoffreyBooth@users.noreply.github.com>
PR-URL: https://github.com/nodejs/node/pull/29494
Reviewed-By: Jan Krems <jan.krems@gmail.com>
Reviewed-By: Myles Borins <myles.borins@gmail.com>
2019-09-17 18:54:44 -07:00

111 lines
4.4 KiB
JavaScript

// Flags: --experimental-modules
import { mustCall } from '../common/index.mjs';
import { ok, deepStrictEqual, strictEqual } from 'assert';
import { requireFixture, importFixture } from '../fixtures/pkgexports.mjs';
[requireFixture, importFixture].forEach((loadFixture) => {
const isRequire = loadFixture === requireFixture;
const validSpecifiers = new Map([
// A simple mapping of a path.
['pkgexports/valid-cjs', { default: 'asdf' }],
// A directory mapping, pointing to the package root.
['pkgexports/sub/asdf.js', { default: 'asdf' }],
// A mapping pointing to a file that needs special encoding (%20) in URLs.
['pkgexports/space', { default: 'encoded path' }],
// Verifying that normal packages still work with exports turned on.
isRequire ? ['baz/index', { default: 'eye catcher' }] : [null],
// Fallbacks
['pkgexports/fallbackdir/asdf.js', { default: 'asdf' }],
['pkgexports/fallbackfile', { default: 'asdf' }],
// Dot main
['pkgexports', { default: 'asdf' }],
]);
for (const [validSpecifier, expected] of validSpecifiers) {
if (validSpecifier === null) continue;
loadFixture(validSpecifier)
.then(mustCall((actual) => {
deepStrictEqual({ ...actual }, expected);
}));
}
const undefinedExports = new Map([
// There's no such export - so there's nothing to do.
['pkgexports/missing', './missing'],
// The file exists but isn't exported. The exports is a number which counts
// as a non-null value without any properties, just like `{}`.
['pkgexports-number/hidden.js', './hidden.js'],
]);
const invalidExports = new Map([
// Even though 'pkgexports/sub/asdf.js' works, alternate "path-like"
// variants do not to prevent confusion and accidental loopholes.
['pkgexports/sub/./../asdf.js', './sub/./../asdf.js'],
// This path steps back inside the package but goes through an exports
// target that escapes the package, so we still catch that as invalid
['pkgexports/belowdir/pkgexports/asdf.js', './belowdir/pkgexports/asdf.js'],
// This target file steps below the package
['pkgexports/belowfile', './belowfile'],
// Directory mappings require a trailing / to work
['pkgexports/missingtrailer/x', './missingtrailer/x'],
// Invalid target handling
['pkgexports/null', './null'],
['pkgexports/invalid1', './invalid1'],
['pkgexports/invalid2', './invalid2'],
['pkgexports/invalid3', './invalid3'],
['pkgexports/invalid4', './invalid4'],
// Missing / invalid fallbacks
['pkgexports/nofallback1', './nofallback1'],
['pkgexports/nofallback2', './nofallback2'],
// Reaching into nested node_modules
['pkgexports/nodemodules', './nodemodules'],
]);
for (const [specifier, subpath] of undefinedExports) {
loadFixture(specifier).catch(mustCall((err) => {
strictEqual(err.code, (isRequire ? '' : 'ERR_') + 'MODULE_NOT_FOUND');
assertStartsWith(err.message, 'Package exports');
assertIncludes(err.message, `do not define a '${subpath}' subpath`);
}));
}
for (const [specifier, subpath] of invalidExports) {
loadFixture(specifier).catch(mustCall((err) => {
strictEqual(err.code, (isRequire ? '' : 'ERR_') + 'MODULE_NOT_FOUND');
assertStartsWith(err.message, (isRequire ? 'Package exports' :
'Cannot resolve'));
assertIncludes(err.message, isRequire ?
`do not define a valid '${subpath}' subpath` :
`matched for '${subpath}'`);
}));
}
// Covering out bases - not a file is still not a file after dir mapping.
loadFixture('pkgexports/sub/not-a-file.js').catch(mustCall((err) => {
strictEqual(err.code, (isRequire ? '' : 'ERR_') + 'MODULE_NOT_FOUND');
// ESM returns a full file path
assertStartsWith(err.message, isRequire ?
'Cannot find module \'pkgexports/sub/not-a-file.js\'' :
'Cannot find module');
}));
// THe use of %2F escapes in paths fails loading
loadFixture('pkgexports/sub/..%2F..%2Fbar.js').catch(mustCall((err) => {
strictEqual(err.code, isRequire ? 'ERR_INVALID_FILE_URL_PATH' :
'ERR_MODULE_NOT_FOUND');
}));
});
function assertStartsWith(actual, expected) {
const start = actual.toString().substr(0, expected.length);
strictEqual(start, expected);
}
function assertIncludes(actual, expected) {
ok(actual.toString().indexOf(expected),
`${JSON.stringify(actual)} includes ${JSON.stringify(expected)}`);
}