diff --git a/CHANGELOG.md b/CHANGELOG.md index 7de51df..65e472a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # changelog * 2.0.6 _Oct.10.2023_ + * [explicitly prioritize "browser"](https://github.com/iambumblehead/resolvewithplus/pull/54) then "import" then "default", when browser and import both true * [update README image link](https://github.com/iambumblehead/resolvewithplus/pull/52) to use "main" repo path * [replace reducer function](https://github.com/iambumblehead/resolvewithplus/pull/53) w/ simple recursion * 2.0.5 _Sep.13.2023_ diff --git a/resolvewithplus.js b/resolvewithplus.js index ba2713d..3aa2e62 100644 --- a/resolvewithplus.js +++ b/resolvewithplus.js @@ -29,6 +29,7 @@ const node_modules = 'node_modules' const packagejson = 'package.json' const specruntime = 'node' const specdefault = 'default' +const specbrowser = 'browser' const specimport = 'import' const specdot = '.' const isobj = o => o && typeof o === 'object' @@ -195,7 +196,7 @@ const esmparselist = (list, spec, specifier, key = list[0]) => { || esmparselist(list.slice(1), spec, specifier) } -const esmparse = (spec, specifier) => { +const esmparse = (spec, specifier, opts = {}) => { let indexval = false if (typeof spec === 'string') @@ -209,7 +210,7 @@ const esmparse = (spec, specifier) => { // }, "./index.cjs" ] // } indexval = spec - .reduce((p, elem) => p || esmparse(elem, specifier), null) + .reduce((p, elem) => p || esmparse(elem, specifier, opts), null) } if (!indexval && isobj(spec)) { @@ -226,12 +227,14 @@ const esmparse = (spec, specifier) => { // "require": "./feature-node.cjs" // } // } - if (!indexval && spec[specruntime]) - indexval = esmparse(spec[specruntime], specifier) - if (!indexval && spec[specdefault]) - indexval = esmparse(spec[specdefault], specifier) + if (!indexval) + indexval = (opts.specprioritylist || [ specruntime, specdefault ]) + .reduce((prev, specname) => ( + prev || esmparse(spec[specname], specifier, opts) + ), false) + if (!indexval && spec[specifier]) - indexval = esmparse(spec[specifier], specifier) + indexval = esmparse(spec[specifier], specifier, opts) // "exports": "./lib/index.js", // "exports": { "import": "./lib/index.js" }, @@ -239,8 +242,8 @@ const esmparse = (spec, specifier) => { // "exports": { ".": { "import": "./lib/index.js" } } if (!indexval && spec[specdot]) indexval = typeof spec[specdot] === 'string' - ? specifier === specimport && esmparse(spec[specdot], specifier) - : esmparse(spec[specdot], specifier) + ? specifier === specimport && esmparse(spec[specdot], specifier, opts) + : esmparse(spec[specdot], specifier, opts) // "exports": { // ".": "./lib/index.test.js", @@ -255,8 +258,8 @@ const esmparse = (spec, specifier) => { } const gettargetindex = (packagejson, opts) => { - let moduleobj = opts && opts.ismodule && packagejson.module, - browserobj = moduleobj || opts && opts.browser && packagejson.browser, + let moduleobj = opts && opts.isimport && packagejson.module, + browserobj = moduleobj || opts && opts.isbrowser && packagejson.browser, esmexportsobj = packagejson.exports, indexprop, indexval @@ -272,7 +275,7 @@ const gettargetindex = (packagejson, opts) => { } if (esmexportsobj) { - indexval = esmparse(esmexportsobj, specimport) + indexval = esmparse(esmexportsobj, specimport, opts) } return indexval @@ -345,9 +348,9 @@ const getasfileordir = (moduleId, parent, opts) => { // } // } // } -const esmparseimport = (targetpath, specifier, pjson) => { +const esmparseimport = (targetpath, specifier, pjson, opts) => { const pjsonimports = pjson && pjson.imports - const firstmatch = esmparse(pjsonimports, specifier) + const firstmatch = esmparse(pjsonimports, specifier, opts) return firstmatch && ( isRelPathRe.test(firstmatch) @@ -373,10 +376,11 @@ const esmparseimport = (targetpath, specifier, pjson) => { // 7. Let packageSubpath be "." concatenated with the substring of // packageSpecifier from the position at the length of packageName. // (removed steps 8-12 related to urls and error cases) -const esmparseexport = (targetpath, pname, pspecifier, pjson) => { +const esmparseexport = (targetpath, pname, pspecifier, pjson, opts) => { const firstmatch = esmparse( pjson && pjson.exports, - pspecifier ? './' + pspecifier : specimport) + pspecifier ? './' + pspecifier : specimport, + opts) return firstmatch && path.join(targetpath, pname, firstmatch) } @@ -459,9 +463,19 @@ const begin = (moduleId, parent, opts) => { } const createopts = (moduleId, parent, opts) => { + const boolOr = (v, def) => typeof v === 'boolean' ? v : def + opts = opts || {} - opts.isTypescript = typeof opts.isTypescript === 'boolean' - ? opts.isTypescript : isTsExtnRe.test(parent) + opts.isTypescript = boolOr(opts.isTypescript, isTsExtnRe.test(parent)) + opts.isbrowser = boolOr(opts.isbrowser, false) + opts.isimport = boolOr(opts.isimport, true) + + opts.specprioritylist = [] + + if (opts.isbrowser) opts.specprioritylist.push(specbrowser) + if (opts.isimport) opts.specprioritylist.push(specimport) + opts.specprioritylist.push(specruntime) + opts.specprioritylist.push(specdefault) return opts } diff --git a/tests/tests-basic/tests-basic.test.js b/tests/tests-basic/tests-basic.test.js index 860364c..3d2bda3 100644 --- a/tests/tests-basic/tests-basic.test.js +++ b/tests/tests-basic/tests-basic.test.js @@ -300,3 +300,79 @@ test('should handle mixed exports', () => { } }), './index.mjs') }) + +test('should return esm by default', () => { + // used by 'inferno@8.2.2' + assert.strictEqual(resolvewithplus.gettargetindex({ + name: 'test', + main: './index.js', + module: './index.esm.js' + }, { isimport: true }), './index.esm.js') + + // used by '@apollo/server@4.9.4' + assert.strictEqual(resolvewithplus.gettargetindex({ + name: 'test', + exports: { + '.': { + import: './dist/esm/index.js', + require: './dist/cjs/index.js' + } + } + }), './dist/esm/index.js') + + // similar patter used by 'react-dom@18.2.0' + assert.strictEqual(resolvewithplus.gettargetindex({ + name: 'test', + exports: { + '.': { + deno: './server.deno.js', + worker: './server.worker.js', + browser: './server.browser.js', + import: './server.import.js', + default: './server.default.js' + } + } + }), './server.import.js') + + assert.strictEqual(resolvewithplus.gettargetindex({ + name: 'test', + exports: { + '.': { + deno: './server.deno.js', + worker: './server.worker.js', + browser: './server.browser.js', + default: './server.node.default.js' + } + } + }), './server.node.default.js') +}) + +test('should return browser over import when both true', () => { + assert.strictEqual(resolvewithplus.gettargetindex({ + name: 'test', + exports: { + '.': { + deno: './server.deno.js', + worker: './server.worker.js', + browser: './server.browser.js', + default: './server.default.js' + } + } + }, { + specprioritylist: [ 'import', 'browser', 'default' ] + }), './server.browser.js') + + assert.strictEqual(resolvewithplus.gettargetindex({ + name: 'test', + exports: { + '.': { + deno: './server.deno.js', + worker: './server.worker.js', + browser: './server.browser.js', + default: './server.default.js' + } + } + }, { + specprioritylist: [ 'default' ] + }), './server.default.js') +})