11cb0ef41Sopenharmony_ciimport { mustCall } from '../common/index.mjs'; 21cb0ef41Sopenharmony_ciimport { ok, deepStrictEqual, strictEqual } from 'assert'; 31cb0ef41Sopenharmony_ciimport { sep } from 'path'; 41cb0ef41Sopenharmony_ci 51cb0ef41Sopenharmony_ciimport { requireFixture, importFixture } from '../fixtures/pkgexports.mjs'; 61cb0ef41Sopenharmony_ciimport fromInside from '../fixtures/node_modules/pkgexports/lib/hole.js'; 71cb0ef41Sopenharmony_ci 81cb0ef41Sopenharmony_ci[requireFixture, importFixture].forEach((loadFixture) => { 91cb0ef41Sopenharmony_ci const isRequire = loadFixture === requireFixture; 101cb0ef41Sopenharmony_ci 111cb0ef41Sopenharmony_ci const validSpecifiers = new Map([ 121cb0ef41Sopenharmony_ci // A simple mapping of a path. 131cb0ef41Sopenharmony_ci ['pkgexports/valid-cjs', { default: 'asdf' }], 141cb0ef41Sopenharmony_ci // A mapping pointing to a file that needs special encoding (%20) in URLs. 151cb0ef41Sopenharmony_ci ['pkgexports/space', { default: 'encoded path' }], 161cb0ef41Sopenharmony_ci // Verifying that normal packages still work with exports turned on. 171cb0ef41Sopenharmony_ci isRequire ? ['baz/index', { default: 'eye catcher' }] : [null], 181cb0ef41Sopenharmony_ci // Fallbacks 191cb0ef41Sopenharmony_ci ['pkgexports/fallbackdir/asdf.js', { default: 'asdf' }], 201cb0ef41Sopenharmony_ci ['pkgexports/fallbackfile', { default: 'asdf' }], 211cb0ef41Sopenharmony_ci // Conditional split for require 221cb0ef41Sopenharmony_ci ['pkgexports/condition', isRequire ? { default: 'encoded path' } : 231cb0ef41Sopenharmony_ci { default: 'asdf' }], 241cb0ef41Sopenharmony_ci // String exports sugar 251cb0ef41Sopenharmony_ci ['pkgexports-sugar', { default: 'main' }], 261cb0ef41Sopenharmony_ci // Conditional object exports sugar 271cb0ef41Sopenharmony_ci ['pkgexports-sugar2', isRequire ? { default: 'not-exported' } : 281cb0ef41Sopenharmony_ci { default: 'main' }], 291cb0ef41Sopenharmony_ci // Resolve self 301cb0ef41Sopenharmony_ci ['pkgexports/resolve-self', isRequire ? 311cb0ef41Sopenharmony_ci { default: 'self-cjs' } : { default: 'self-mjs' }], 321cb0ef41Sopenharmony_ci // Resolve self sugar 331cb0ef41Sopenharmony_ci ['pkgexports-sugar', { default: 'main' }], 341cb0ef41Sopenharmony_ci // Path patterns 351cb0ef41Sopenharmony_ci ['pkgexports/subpath/sub-dir1', { default: 'main' }], 361cb0ef41Sopenharmony_ci ['pkgexports/subpath/sub-dir1.js', { default: 'main' }], 371cb0ef41Sopenharmony_ci ['pkgexports/features/dir1', { default: 'main' }], 381cb0ef41Sopenharmony_ci ['pkgexports/dir1/dir1/trailer', { default: 'main' }], 391cb0ef41Sopenharmony_ci ['pkgexports/dir2/dir2/trailer', { default: 'index' }], 401cb0ef41Sopenharmony_ci ['pkgexports/a/dir1/dir1', { default: 'main' }], 411cb0ef41Sopenharmony_ci ['pkgexports/a/b/dir1/dir1', { default: 'main' }], 421cb0ef41Sopenharmony_ci 431cb0ef41Sopenharmony_ci // Deprecated: 441cb0ef41Sopenharmony_ci // Double slashes: 451cb0ef41Sopenharmony_ci ['pkgexports/a//dir1/dir1', { default: 'main' }], 461cb0ef41Sopenharmony_ci // double slash target 471cb0ef41Sopenharmony_ci ['pkgexports/doubleslash', { default: 'asdf' }], 481cb0ef41Sopenharmony_ci // Null target with several slashes 491cb0ef41Sopenharmony_ci ['pkgexports/sub//internal/test.js', { default: 'internal only' }], 501cb0ef41Sopenharmony_ci ['pkgexports/sub//internal//test.js', { default: 'internal only' }], 511cb0ef41Sopenharmony_ci ['pkgexports/sub/////internal/////test.js', { default: 'internal only' }], 521cb0ef41Sopenharmony_ci // trailing slash 531cb0ef41Sopenharmony_ci ['pkgexports/trailing-pattern-slash/', 541cb0ef41Sopenharmony_ci { default: 'trailing-pattern-slash' }], 551cb0ef41Sopenharmony_ci ]); 561cb0ef41Sopenharmony_ci 571cb0ef41Sopenharmony_ci if (!isRequire) { 581cb0ef41Sopenharmony_ci // No exports or main field 591cb0ef41Sopenharmony_ci validSpecifiers.set('no_exports', { default: 'index' }); 601cb0ef41Sopenharmony_ci // Main field without extension 611cb0ef41Sopenharmony_ci validSpecifiers.set('default_index', { default: 'main' }); 621cb0ef41Sopenharmony_ci } 631cb0ef41Sopenharmony_ci 641cb0ef41Sopenharmony_ci for (const [validSpecifier, expected] of validSpecifiers) { 651cb0ef41Sopenharmony_ci if (validSpecifier === null) continue; 661cb0ef41Sopenharmony_ci 671cb0ef41Sopenharmony_ci loadFixture(validSpecifier) 681cb0ef41Sopenharmony_ci .then(mustCall((actual) => { 691cb0ef41Sopenharmony_ci deepStrictEqual({ ...actual }, expected); 701cb0ef41Sopenharmony_ci })); 711cb0ef41Sopenharmony_ci } 721cb0ef41Sopenharmony_ci 731cb0ef41Sopenharmony_ci const undefinedExports = new Map([ 741cb0ef41Sopenharmony_ci // There's no such export - so there's nothing to do. 751cb0ef41Sopenharmony_ci ['pkgexports/missing', './missing'], 761cb0ef41Sopenharmony_ci // The file exists but isn't exported. The exports is a number which counts 771cb0ef41Sopenharmony_ci // as a non-null value without any properties, just like `{}`. 781cb0ef41Sopenharmony_ci ['pkgexports-number/hidden.js', './hidden.js'], 791cb0ef41Sopenharmony_ci // Sugar cases still encapsulate 801cb0ef41Sopenharmony_ci ['pkgexports-sugar/not-exported.js', './not-exported.js'], 811cb0ef41Sopenharmony_ci ['pkgexports-sugar2/not-exported.js', './not-exported.js'], 821cb0ef41Sopenharmony_ci // Conditional exports with no match are "not exported" errors 831cb0ef41Sopenharmony_ci ['pkgexports/invalid1', './invalid1'], 841cb0ef41Sopenharmony_ci ['pkgexports/invalid4', './invalid4'], 851cb0ef41Sopenharmony_ci // Null mapping 861cb0ef41Sopenharmony_ci ['pkgexports/sub/internal/test.js', './sub/internal/test.js'], 871cb0ef41Sopenharmony_ci ['pkgexports/sub/internal//test.js', './sub/internal//test.js'], 881cb0ef41Sopenharmony_ci ['pkgexports/null', './null'], 891cb0ef41Sopenharmony_ci ['pkgexports//null', './/null'], 901cb0ef41Sopenharmony_ci ['pkgexports/////null', './////null'], 911cb0ef41Sopenharmony_ci ['pkgexports/null/subpath', './null/subpath'], 921cb0ef41Sopenharmony_ci // Empty fallback 931cb0ef41Sopenharmony_ci ['pkgexports/nofallback1', './nofallback1'], 941cb0ef41Sopenharmony_ci // Non pattern matches 951cb0ef41Sopenharmony_ci ['pkgexports/trailer', './trailer'], 961cb0ef41Sopenharmony_ci ]); 971cb0ef41Sopenharmony_ci 981cb0ef41Sopenharmony_ci const invalidExports = new Map([ 991cb0ef41Sopenharmony_ci // This path steps back inside the package but goes through an exports 1001cb0ef41Sopenharmony_ci // target that escapes the package, so we still catch that as invalid 1011cb0ef41Sopenharmony_ci ['pkgexports/belowdir/pkgexports/asdf.js', './belowdir/'], 1021cb0ef41Sopenharmony_ci // This target file steps below the package 1031cb0ef41Sopenharmony_ci ['pkgexports/belowfile', './belowfile'], 1041cb0ef41Sopenharmony_ci // Invalid targets 1051cb0ef41Sopenharmony_ci ['pkgexports/invalid2', './invalid2'], 1061cb0ef41Sopenharmony_ci ['pkgexports/invalid3', './invalid3'], 1071cb0ef41Sopenharmony_ci ['pkgexports/invalid5', 'invalid5'], 1081cb0ef41Sopenharmony_ci // Missing / invalid fallbacks 1091cb0ef41Sopenharmony_ci ['pkgexports/nofallback2', './nofallback2'], 1101cb0ef41Sopenharmony_ci // Reaching into nested node_modules 1111cb0ef41Sopenharmony_ci ['pkgexports/nodemodules', './nodemodules'], 1121cb0ef41Sopenharmony_ci // Self resolve invalid 1131cb0ef41Sopenharmony_ci ['pkgexports/resolve-self-invalid', './invalid2'], 1141cb0ef41Sopenharmony_ci ]); 1151cb0ef41Sopenharmony_ci 1161cb0ef41Sopenharmony_ci const invalidSpecifiers = new Map([ 1171cb0ef41Sopenharmony_ci // Even though 'pkgexports/sub/asdf.js' works, alternate "path-like" 1181cb0ef41Sopenharmony_ci // variants do not to prevent confusion and accidental loopholes. 1191cb0ef41Sopenharmony_ci ['pkgexports/sub/./../asdf.js', './sub/./../asdf.js'], 1201cb0ef41Sopenharmony_ci // Cannot reach into node_modules, even percent encoded 1211cb0ef41Sopenharmony_ci ['pkgexports/sub/no%64e_modules', './sub/no%64e_modules'], 1221cb0ef41Sopenharmony_ci // Cannot backtrack below exposed path, even with percent encoded "." 1231cb0ef41Sopenharmony_ci ['pkgexports/sub/%2e./asdf', './asdf'], 1241cb0ef41Sopenharmony_ci ]); 1251cb0ef41Sopenharmony_ci 1261cb0ef41Sopenharmony_ci for (const [specifier, subpath] of undefinedExports) { 1271cb0ef41Sopenharmony_ci loadFixture(specifier).catch(mustCall((err) => { 1281cb0ef41Sopenharmony_ci strictEqual(err.code, 'ERR_PACKAGE_PATH_NOT_EXPORTED'); 1291cb0ef41Sopenharmony_ci assertStartsWith(err.message, 'Package subpath '); 1301cb0ef41Sopenharmony_ci assertIncludes(err.message, subpath); 1311cb0ef41Sopenharmony_ci })); 1321cb0ef41Sopenharmony_ci } 1331cb0ef41Sopenharmony_ci 1341cb0ef41Sopenharmony_ci for (const [specifier, subpath] of invalidExports) { 1351cb0ef41Sopenharmony_ci loadFixture(specifier).catch(mustCall((err) => { 1361cb0ef41Sopenharmony_ci strictEqual(err.code, 'ERR_INVALID_PACKAGE_TARGET'); 1371cb0ef41Sopenharmony_ci assertStartsWith(err.message, 'Invalid "exports"'); 1381cb0ef41Sopenharmony_ci assertIncludes(err.message, subpath); 1391cb0ef41Sopenharmony_ci if (!subpath.startsWith('./')) { 1401cb0ef41Sopenharmony_ci assertIncludes(err.message, 'targets must start with'); 1411cb0ef41Sopenharmony_ci } 1421cb0ef41Sopenharmony_ci })); 1431cb0ef41Sopenharmony_ci } 1441cb0ef41Sopenharmony_ci 1451cb0ef41Sopenharmony_ci for (const [specifier, subpath] of invalidSpecifiers) { 1461cb0ef41Sopenharmony_ci loadFixture(specifier).catch(mustCall((err) => { 1471cb0ef41Sopenharmony_ci strictEqual(err.code, 'ERR_INVALID_MODULE_SPECIFIER'); 1481cb0ef41Sopenharmony_ci assertStartsWith(err.message, 'Invalid module '); 1491cb0ef41Sopenharmony_ci assertIncludes(err.message, 'is not a valid match in pattern'); 1501cb0ef41Sopenharmony_ci assertIncludes(err.message, subpath); 1511cb0ef41Sopenharmony_ci })); 1521cb0ef41Sopenharmony_ci } 1531cb0ef41Sopenharmony_ci 1541cb0ef41Sopenharmony_ci // Conditional export, even with no match, should still be used instead 1551cb0ef41Sopenharmony_ci // of falling back to main 1561cb0ef41Sopenharmony_ci if (isRequire) { 1571cb0ef41Sopenharmony_ci loadFixture('pkgexports-main').catch(mustCall((err) => { 1581cb0ef41Sopenharmony_ci strictEqual(err.code, 'ERR_PACKAGE_PATH_NOT_EXPORTED'); 1591cb0ef41Sopenharmony_ci assertStartsWith(err.message, 'No "exports" main '); 1601cb0ef41Sopenharmony_ci })); 1611cb0ef41Sopenharmony_ci } 1621cb0ef41Sopenharmony_ci 1631cb0ef41Sopenharmony_ci const notFoundExports = new Map([ 1641cb0ef41Sopenharmony_ci // Non-existing file 1651cb0ef41Sopenharmony_ci ['pkgexports/sub/not-a-file.js', `pkgexports${sep}not-a-file.js`], 1661cb0ef41Sopenharmony_ci // No extension lookups 1671cb0ef41Sopenharmony_ci ['pkgexports/no-ext', `pkgexports${sep}asdf`], 1681cb0ef41Sopenharmony_ci // Pattern specificity 1691cb0ef41Sopenharmony_ci ['pkgexports/dir2/trailer', `subpath${sep}dir2.js`], 1701cb0ef41Sopenharmony_ci // Pattern double $$ escaping! 1711cb0ef41Sopenharmony_ci ['pkgexports/a/$$', `subpath${sep}$$.js`], 1721cb0ef41Sopenharmony_ci ]); 1731cb0ef41Sopenharmony_ci 1741cb0ef41Sopenharmony_ci if (!isRequire) { 1751cb0ef41Sopenharmony_ci const onDirectoryImport = (err) => { 1761cb0ef41Sopenharmony_ci strictEqual(err.code, 'ERR_UNSUPPORTED_DIR_IMPORT'); 1771cb0ef41Sopenharmony_ci assertStartsWith(err.message, 'Directory import'); 1781cb0ef41Sopenharmony_ci }; 1791cb0ef41Sopenharmony_ci loadFixture('pkgexports/subpath/dir1').catch(mustCall(onDirectoryImport)); 1801cb0ef41Sopenharmony_ci loadFixture('pkgexports/subpath/dir2').catch(mustCall(onDirectoryImport)); 1811cb0ef41Sopenharmony_ci } 1821cb0ef41Sopenharmony_ci 1831cb0ef41Sopenharmony_ci for (const [specifier, request] of notFoundExports) { 1841cb0ef41Sopenharmony_ci loadFixture(specifier).catch(mustCall((err) => { 1851cb0ef41Sopenharmony_ci strictEqual(err.code, (isRequire ? '' : 'ERR_') + 'MODULE_NOT_FOUND'); 1861cb0ef41Sopenharmony_ci assertIncludes(err.message, request); 1871cb0ef41Sopenharmony_ci assertStartsWith(err.message, 'Cannot find module'); 1881cb0ef41Sopenharmony_ci })); 1891cb0ef41Sopenharmony_ci } 1901cb0ef41Sopenharmony_ci 1911cb0ef41Sopenharmony_ci // The use of %2F and %5C escapes in paths fails loading 1921cb0ef41Sopenharmony_ci loadFixture('pkgexports/sub/..%2F..%2Fbar.js').catch(mustCall((err) => { 1931cb0ef41Sopenharmony_ci strictEqual(err.code, 'ERR_INVALID_MODULE_SPECIFIER'); 1941cb0ef41Sopenharmony_ci })); 1951cb0ef41Sopenharmony_ci loadFixture('pkgexports/sub/..%5C..%5Cbar.js').catch(mustCall((err) => { 1961cb0ef41Sopenharmony_ci strictEqual(err.code, 'ERR_INVALID_MODULE_SPECIFIER'); 1971cb0ef41Sopenharmony_ci })); 1981cb0ef41Sopenharmony_ci 1991cb0ef41Sopenharmony_ci // Package export with numeric index properties must throw a validation error 2001cb0ef41Sopenharmony_ci loadFixture('pkgexports-numeric').catch(mustCall((err) => { 2011cb0ef41Sopenharmony_ci strictEqual(err.code, 'ERR_INVALID_PACKAGE_CONFIG'); 2021cb0ef41Sopenharmony_ci })); 2031cb0ef41Sopenharmony_ci 2041cb0ef41Sopenharmony_ci // Sugar conditional exports main mixed failure case 2051cb0ef41Sopenharmony_ci loadFixture('pkgexports-sugar-fail').catch(mustCall((err) => { 2061cb0ef41Sopenharmony_ci strictEqual(err.code, 'ERR_INVALID_PACKAGE_CONFIG'); 2071cb0ef41Sopenharmony_ci assertStartsWith(err.message, 'Invalid package'); 2081cb0ef41Sopenharmony_ci assertIncludes(err.message, '"exports" cannot contain some keys starting ' + 2091cb0ef41Sopenharmony_ci 'with \'.\' and some not. The exports object must either be an object of ' + 2101cb0ef41Sopenharmony_ci 'package subpath keys or an object of main entry condition name keys ' + 2111cb0ef41Sopenharmony_ci 'only.'); 2121cb0ef41Sopenharmony_ci })); 2131cb0ef41Sopenharmony_ci}); 2141cb0ef41Sopenharmony_ci 2151cb0ef41Sopenharmony_ciconst { requireFromInside, importFromInside } = fromInside; 2161cb0ef41Sopenharmony_ci[importFromInside, requireFromInside].forEach((loadFromInside) => { 2171cb0ef41Sopenharmony_ci const validSpecifiers = new Map([ 2181cb0ef41Sopenharmony_ci // A file not visible from outside of the package 2191cb0ef41Sopenharmony_ci ['../not-exported.js', { default: 'not-exported' }], 2201cb0ef41Sopenharmony_ci // Part of the public interface 2211cb0ef41Sopenharmony_ci ['pkgexports/valid-cjs', { default: 'asdf' }], 2221cb0ef41Sopenharmony_ci ]); 2231cb0ef41Sopenharmony_ci for (const [validSpecifier, expected] of validSpecifiers) { 2241cb0ef41Sopenharmony_ci if (validSpecifier === null) continue; 2251cb0ef41Sopenharmony_ci 2261cb0ef41Sopenharmony_ci loadFromInside(validSpecifier) 2271cb0ef41Sopenharmony_ci .then(mustCall((actual) => { 2281cb0ef41Sopenharmony_ci deepStrictEqual({ ...actual }, expected); 2291cb0ef41Sopenharmony_ci })); 2301cb0ef41Sopenharmony_ci } 2311cb0ef41Sopenharmony_ci}); 2321cb0ef41Sopenharmony_ci 2331cb0ef41Sopenharmony_cifunction assertStartsWith(actual, expected) { 2341cb0ef41Sopenharmony_ci const start = actual.toString().substr(0, expected.length); 2351cb0ef41Sopenharmony_ci strictEqual(start, expected); 2361cb0ef41Sopenharmony_ci} 2371cb0ef41Sopenharmony_ci 2381cb0ef41Sopenharmony_cifunction assertIncludes(actual, expected) { 2391cb0ef41Sopenharmony_ci ok(actual.toString().indexOf(expected) !== -1, 2401cb0ef41Sopenharmony_ci `${JSON.stringify(actual)} includes ${JSON.stringify(expected)}`); 2411cb0ef41Sopenharmony_ci} 242