defunctzombie / package-browser-field-spec

Spec document for the 'browser' field in package.json
437 stars 20 forks source link

Threat browser field as mandatory for non server-only packages #18

Open bitifet opened 8 months ago

bitifet commented 8 months ago

This spec currently seems to imply that the browser field is for "javascript bundlers or component tools" and only required if they need to do something differently at client side while, from NPM Docs explanation), I understand we can state the following:

Original text: If your module is meant to be used client-side the browser field should be used instead of the main field. This is helpful to hint users that it might rely on primitives that aren't available in Node.js modules. (e.g. window)

It may not seem a major issue since since most big applications use bundlers nowadays and those that don't are supposed to manually place required files in an assets directory.

However this is not always the case and, from my point of view, there are several reasons to reinforce its mandatory nature for packages that are expected to work client side:

  1. It's what NPM Docs says.
  2. It's a more general purpose approach (making the browser field even more useful).
  3. Clarifies when a package is expected to work client side (have "browser" field), server side (have "main") or both (have "both").
  4. Is (almost) 100% compatible with both basic and advanced approaches defined in this spec.
  5. Make it possible for non bundled apps to serve packages from node_modules without wasting resources in those that are unsuitable client side (See Motivation).

Compatibility

The current version of this spec is already mostly compatible with this approach:

In basic operation

For the basic operation, it only needs to declare the browser field as mandatory even if the entry point (and hence the whole code in this case) is the same. Encouraging package mantainers to add it if already haven't.

Bundlers can keep failing back to the main field for those packages that lacking the browser one since, if we are bundling for client side and that package is required from codebase, it's likely we expect it to work client side.

In avanced operation

For the advanced operation we already need the browser field so it is clear that it will be "marked" as a browser package.

The only flaw here is that it still relies in the main field as entry point and, even it can be remapped elsewhere, if the package is only suitable for client side operation, it should have no main field in its package.json.

This will not be a problem to discern packages suitable for client side from those which are not but, from my point of view, it does not conform to what NPM Docs says and will make impossible to mark a package as not expected to work outside a browser.

To solve this we only need to define an optional "main" (or, to be even more flexible, maybe "/") key to explicitly override the main field so that it will be no longer necessary.

For packages that can be used both client and server side, using the main field for both client and server side will still be Ok, so we are not breaking anything.

Motivation

I do use bundlers in several projects and I think they are a great tool but, since the arrival of http/2 and http/3, if the only reason is network performance, maybe they are not the way to go. Specially for small or medium projects. (I may be wrong though).

Anyway, if this is going to be the standard, the most use cases it embraces the better. And I wish to point out that not using a bundler does not imply not using a package manager: Just placing the files at an assets folder condemn them to become outdated soon...

Some people just place an additional package.json for the client code and then (if server is also in JS) "cd" forth and back to (double) execute npm install / upgrade / audit... But I prefer to have a single package.json for the whole project and then serve the needed files from _nodemodules.

Until now, to do so one needed to either serve the whole _nodemodules (ok: no secrets there –unless you use private packages– but not so much polite...) or "manually" hand-wire express (or whatever lib/framework you use) for every JS file you need (if there was a better way to do so, please let me know...). Both approaches also at the risk that future versions may change those files names and/or paths...

For this reason, as soon I discovered the browser field I was so excited so, after a little unsuccessful research of anything similar, I implemented ESMrouter (and even forked express-generator so that now, when I want to "get the hands dirty" in something, I just need to type a single command and npm install whatever packages I need. But that's another history...).

This way, whenever I install any NPM package which has the browser field defined (or that I specify in a include option to manually add packages that lack it), its browser entry point is transparently served at _/node_modules/_ and proper entry is added to the auto-generated importmap.

Of course, this is only my personal approach not widely used yet (ando possibly never will be). But, if not this, maybe other future approaches will be possible if only we leave the door open.

Conclusion

In my oppinion, the changes I propose do not affect the utility of this specification and give it a wider coverage with just small tweaks in its composition.

I could have just forked it and make a PR with that changes (and I still can) but I thought it would be better to first explain the underlaying reasons of that chantes.

If you agree with me, you can either do them to your taste or tell me and I'll be glad to prepare a PR with them.

References

ljharb commented 8 months ago

No, the browser field is not mandatory and must never be. By default, a working node module bundler polyfills or provides any node globals and builtins that are possible to provide in a browser, and the browser field is only used when there's a file that requires a more complex alternative for the browser.

bitifet commented 8 months ago

Can you elaborate why "it must never be"?

What problem could cause a package with the browser field defined to a bundler?

If both main and browser are defined with the same value, the the effect for a bundler will be the same as if only main were provided.

ljharb commented 8 months ago

Because the vast majority of packages work fine in the browser, and don't have a browser field (many of them never will, because maintainers are no longer alive). Requiring the field would immediately annihilate the usefulness of it.

bitifet commented 8 months ago

Ok.

I used the word "mandatory" wrongly.

But if you read carefully you'll see I said it's not necessary to "force" (I would never expect so!) all packages to add it.

It's just a matter of encouraging it's use whenever possible.

Existing packages will work anyway because, as I already said, bundlers will still fail back to the main field since, if client code requires a package it's obvious that that package is expected to work client side.

Encouraging the use of the browser field will only affect for non bundled apps since it will be easier to decide which packages are likely to be used client side so that we only need do manually specify those packages we need and are not automatically detected.

See https://www.npmjs.com/package/esmrouter to better understand what I mean…

ljharb commented 8 months ago

What would need to change to achieve that in your mind? To me, its use has been strongly encouraged for over a decade, and every bundler supports it.

bitifet commented 8 months ago

Maybe it's encouraged, but only when the module is expected to work both client and server side and there are parts that need to be different from one side to another.

What would "need to be changed in my mind" are mostly slight details here and there, but, specially:

ljharb commented 8 months ago

the browser field still helps to emphasize that it is also suitable for client side

that's false, and things that work automatically should not use the browser field. It's simply not an indicator that something works in the browser.

Reinforce what NPM Docs already says

the npm docs are wrong, and should be fixed. file an issue there.

bitifet commented 8 months ago

that's false, and things that work automatically should not use the browser field. It's simply not an indicator that something works in the browser.

Are you kidding? Of course it's not a test. But I cannot imagine anyone specifying the browser field in a package not intended to be used browser side...

the npm docs are wrong, and should be fixed. file an issue there.

Ah! Ok... You own the holy truth...

Nice to know. Checkmate.

ljharb commented 8 months ago

Oh, to be sure, the presence of the browser field indicates that, but the absence indicates nothing.

I maintain nearly 500 npm packages, and the vast majority work in a browser. Only a handful - single digits, probably - need the browser field.