Skip to content

Commit

Permalink
refine exports and browser field interaction
Browse files Browse the repository at this point in the history
  • Loading branch information
Jarred de Beer authored and Jarred de Beer committed Apr 22, 2021
1 parent 53776ee commit 1f60002
Show file tree
Hide file tree
Showing 16 changed files with 82 additions and 4 deletions.
2 changes: 1 addition & 1 deletion packages/node-resolve/README.md
Expand Up @@ -64,7 +64,7 @@ Setting this option will add extra conditions on top of the default conditions.
Type: `Boolean`<br>
Default: `false`

If `true`, instructs the plugin to use the `"browser"` property in `package.json` files to specify alternative files to load for bundling. This is useful when bundling for a browser environment. Alternatively, a value of `'browser'` can be added to the `mainFields` option. If `false`, any `"browser"` properties in package files will be ignored. This option takes precedence over `mainFields`.
If `true`, instructs the plugin to use the `"browser"` property in `package.json` files to specify alternative files to load for bundling. This is useful when bundling for a browser environment. Alternatively, a value of `'browser'` can be added to the `mainFields` option. If `false`, any `"browser"` properties in package files will be ignored. This option takes precedence over `mainFields`. In addition, if `"browser"` is not present in `exportConditions`, it will be appended so that browser conditionals in `"exports"` will also apply.

> This option does not work when a package is using [package entrypoints](https://nodejs.org/api/packages.html#packages_package_entry_points)
Expand Down
3 changes: 3 additions & 0 deletions packages/node-resolve/src/index.js
Expand Up @@ -189,6 +189,9 @@ export function nodeResolve(opts = {}) {
const isRequire =
opts && opts.custom && opts.custom['node-resolve'] && opts.custom['node-resolve'].isRequire;
const exportConditions = isRequire ? conditionsCjs : conditionsEsm;

if (useBrowserOverrides && !exportConditions.includes('browser'))
exportConditions.push('browser');

const resolvedWithoutBuiltins = await resolveImportSpecifiers({
importer,
Expand Down
24 changes: 21 additions & 3 deletions packages/node-resolve/src/package/resolvePackageTarget.js
Expand Up @@ -10,6 +10,24 @@ function includesInvalidSegments(pathSegments, moduleDirs) {
.some((t) => ['.', '..', ...moduleDirs].includes(t));
}

async function resolvePackageTargetBrowserMapped(context, { target, subpath, pattern, internal }) {
const resolvedTarget = await resolvePackageTarget(context, {
target,
subpath,
pattern,
internal
});
if (
resolvedTarget &&
resolvedTarget.startsWith('./') &&
context.packageBrowserField &&
context.packageBrowserField[resolvedTarget]
) {
return context.packageBrowserField[resolvedTarget];
}
return resolvedTarget;
}

async function resolvePackageTarget(context, { target, subpath, pattern, internal }) {
if (typeof target === 'string') {
if (!pattern && subpath.length > 0 && !target.endsWith('/')) {
Expand All @@ -24,11 +42,11 @@ async function resolvePackageTarget(context, { target, subpath, pattern, interna
target.replace(/\*/g, subpath),
context.pkgURL.href
);
return result ? pathToFileURL(result.location) : null;
return result ? pathToFileURL(result.location).href : null;
}

const result = await context.resolveId(`${target}${subpath}`, context.pkgURL.href);
return result ? pathToFileURL(result.location) : null;
return result ? pathToFileURL(result.location).href : null;
}
throw new InvalidPackageTargetError(context, `Invalid mapping: "${target}".`);
}
Expand Down Expand Up @@ -111,4 +129,4 @@ async function resolvePackageTarget(context, { target, subpath, pattern, interna
throw new InvalidPackageTargetError(context, `Invalid exports field.`);
}

export default resolvePackageTarget;
export default resolvePackageTargetBrowserMapped;
20 changes: 20 additions & 0 deletions packages/node-resolve/test/browser.js
Expand Up @@ -199,3 +199,23 @@ test('pkg.browser with mapping to prevent bundle by specifying a value of false'

t.is(module.exports, 'ok');
});

test('exports.browser can be mapped via pkg.browser', async (t) => {
const bundle = await rollup({
input: 'browser-exports-browser-browser.js',
plugins: [nodeResolve({ browser: true }), commonjs()]
});
const { module } = await testBundle(t, bundle);

t.is(module.exports, 'browser');
});

test('exports.browser does not take precedence over export map result, when browser:false', async (t) => {
const bundle = await rollup({
input: 'browser-exports-browser.js',
plugins: [nodeResolve(), commonjs()]
});
const { module } = await testBundle(t, bundle);

t.is(module.exports, 'require');
});
@@ -0,0 +1,3 @@
import b from 'exports-browser-browser';

module.exports = b;
@@ -0,0 +1,3 @@
const b = require('exports-browser');

module.exports = b;
@@ -0,0 +1,3 @@
const Nanoid = require('nanoid');

module.exports = Nanoid;

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 1f60002

Please sign in to comment.