Giter VIP home page Giter VIP logo

proposal-pkg-entries's Introduction

package.json Entries Proposal

Background

Node.js has traditionally used the "main" field as a string to indicate the entry point to use for any given package when imported as a bare specifier (import 'pkg').

When support for browsers was needed the "browser" field was created to support different mains for the browser, allowing universal npm packages to coexist with standard Node.js packages.

With the introduction of ES modules in browsers, the "module" field has been adopted to mean "module" AND "browser".

In addition, many other environments exist with their own version of the main field in the same way, such as "react-native" and "electron" to just name a couple.

There has also been talk of introducing new environment targets to allow moving to no longer referencing transpiled code with special names such as a "syntax" field supporting "current" or "esmodule" main entry point targets.

Problem Statement

The above is just a short summary of some of the fields that have been used, but there are dozens more custom solutions in place.

The issue is that this proliferation of main entry point mechanisms does not scale, and it does not allow them to compose.

For example, it is impossible for Node.js to implement the "module" field, since it would break ecosystem packages in Node.js which assume "module" is a browser module field with certain tooling-defined semantics which it does not support. Does that mean Node.js would need to create a special "nodemodule" field? The potential combinations like this grow as we add any further differentiation of the main in future.

Proposal

The proposal is for a new field which captures multiple custom environment conditions under a single object, where the environment condition names can be extended and host-defined in both runtimes and tools such as bundlers.

{
  "entries": {
    "browser": "./main-browser.js",
    "node": "./main-node.js",
    "default": "./main-misc.js"
  }
}

The standard environment condition names implemented would include "node" (Node.js), "browser" (any browser runtime), "module" (any environment or runtime with support for ES modules) and "default" which would be true in any environment as a fallback main.

The "main" field effectively desugars to "entries": { "default": "..." }.

The entry point for a given resolve operation is taken based on the first match from the "entries" field, in object property order.

Unknown names are ignored, and names which are associated with the environment are matched as soon as they are seen.

Composition

In addition to direct environment conditional names, conditional names can also be composed using the | operator:

{
  "entries": {
    "module|browser": "./x.browser.module.js",
    "module|node": "./x.node.module.js",
    "default": "./x.cjs.js",
  }
}

In the above, we are able to provide different entry points for the ES module version in the browser and Node.js, while falling back to the CommonJS version.

Production / Development Environments

It could even be possible to define "production" and "development" environment names, so long as hosts or tools provide a way to set which environment the execution should be in. For example node --production x.js might default to using "production" paths in entry mappings, while node x.js would default to the "development" path. Or similarly for build tools. Again composibility of conditions here is key to enabling these different workflows.

Extending Environment Entry Names

Hosts can define custom conditional names for themselves and support them. For example, React Native can define "react-native" and Electron can define "electron" just fine.

If hosts want to register the name centrally, we could allow names to be reserved as well provided they have minimal chances of conflict with existing names in use with other hosts.

FAQ

Why not just allow conventions to continue as they are, with different types of main fields?

The problem is that as we get more and more fields, we have no ability to compose combinations or subsets of their conditions.

In addition, support for different fields may be limited between different tools and runtimes. By having a single field that can track the different types of conditions being detected, we can better ensure semantic-equivalence between resolver implementations as well.

Does this replace the "module" field?

The "module" field could continue to be supported just fine, and could even be desugared into this field as:

{
  "entries": {
    "module|browser": "..."
  }
}

since the "module" field has pretty much assumed the convention of meaning the modular browser environment.

If we were to specify such a backwards compatibility, this again would help different tools to be able to agree on the semantics at play here.

Why should this be implemented in Node.js?

It could be possible for this field not to be implemented in Node.js itself, and we could just disallow the "node" environment conditional and go along with the other types of conditionals in tools.

But we may well want to add conditionals into Node.js in future that would be useful to support, for example supporting non-transpiled entry points in Node.js would need new custom conditionals directly supported in Node.js, which could be a useful feature to add.

In addition in future with new types of entry points like binary AST support this could be a useful thing to select on as well.

There is no rush to implement this in Node.js though certainly.

Who will decide on what condition names are accepted as standard?

I have no idea. Let's discuss this!

License

MIT

proposal-pkg-entries's People

Contributors

guybedford avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

proposal-pkg-entries's Issues

Exports support

Here is a suggested path for supporting "exports" in Node.js (//cc @jamiebuilds, @devongovett):

  • Node.js supports the conditionals - ['node', 'esmodule', 'main'] in that order of precedence.
  • When "exports" is an object it will aim to perform the above selection in that precedence order for "exports".

My concern with the above is managing the list of conditionals requires some coordiation between environments.

Therefore I see the prerequisite for such a proposal as a central website / repo where we can coordinate on these names.

So I'd like to propose a new repo and website for managing this process and these proposals.

Does that seem a sensible approach moving forward?

Parcel 2's package.json#targets

Opening this issue after our discussion on how to align this proposal and the work that Parcel is doing to specify what Parcel 2 has proposed with package.json#targets.

At a high-level package.json#targets looks like this:

{
  "name": "package-name",
  "main": "dist/main/index.js",
  "module": "dist/module/index.js",
  "browser": "dist/browser/index.js",
  "targets": {
    "main": {
      "node": ["^4.0.0"]
    },
    "module": {
      "node": ["^8.0.0"]
    },
    "browser": {
      "browsers": ["> 1%", "not dead"]
    }
  }
}

For background, our motivations in this design were to solve for the following problems:

  • How do we decide which package.json fields are entry points to the package?
  • How do we know what a package.json entry field actually means (in terms of the environment it is supposed to be executed in)?
  • How do we align with the existing fields to describe the targeted environment for a package?

We also wanted to make sure that we designed something that:

  • Didn't change anything about the existing ecosystem
  • Didn't ask too much of the package author
  • Wouldn't conflict with existing toolchains

Basically what we ended up designing was a way of storing metadata about what existing entry fields existed and what they meant to the package author. We designed package.json#targets to not care when package authors added new package.json entry fields, and not to care when the ecosystem defined a new type of environment that an entry point could be targeting.

For starters, if you wanted to know what entry fields existed in package.json you could do keys(package.json#targets):

{
  // ...tons of other fields that may or may not be entries...
  "targets": {
    "main": {...},
    "module": {...},
    "browser": {...}
  }
}

This keeps us compatible with tools that really want the entry fields to be in the top-level of the package.json object, and we weren't replacing them at all, just describing them.

The package.json#targets values then start describing the environment they are intending to support. So from the context in which I am requesting the entry point for a package, I can say "I want X package for Y environment"

{
  "targets": {
    "entry-a": {
      "environment-x": true
    },
    "entry-b": {
      "environment-y": true
    }
  }
}

For the above package.json#targets, if I'm looking for an entry that can target "environment-y" I can easily figure out that entry-b is the only entry that supports that environment.

This is not intended to be a strictly defined system, it's meant to support whatever the ecosystem may come up with next-- Just tell us what it is and we'll do our best to make it work from the tooling side.

There is more information about this proposal in the Parcel 2 RFC

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.