Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parcel 2's package.json#targets #1

Open
jamiebuilds opened this issue Jul 29, 2019 · 25 comments
Open

Parcel 2's package.json#targets #1

jamiebuilds opened this issue Jul 29, 2019 · 25 comments

Comments

@jamiebuilds
Copy link

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

@guybedford
Copy link
Owner

Thanks for clarifying @jamiebuilds that makes a lot of sense.... so maybe these are actually very compatible proposals.... by thinking of "entries" as simply a sort of "grouping field" for what would otherwise be the base-level mains. So your initial example could become something like -

(where "default" is what we use to mean the default entry instead of "main")

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

In this model, "targets" very much does seem to me like it could be seen as additive? Or am I missing something?

The convergence I am hoping to be able to see with exports is the following:

{
  "exports": {
    "./feature": "./dist/features/feature.js"
  }
}

allowing branching just like we do for mains via:

{
  "exports": {
    "./feature": {
      "module": "dist/module/feature.js",
      "browser": "dist/browser/feature.js",
      "default": "dist/main/feature.js"
    }
  }
}

which also seems like it could remain compatible with targets as well.

If this really does work out that would be very exciting, because it means we can have our cake and eat it, in that we have the simplicity of what users currently use, while also remaining fully compatible with the fine-grained definitions you've worked out!

There's still a bit to discuss though certainly.... I would be interested to hear your thoughts further.

@jamiebuilds
Copy link
Author

One thing I'm worried about is that if "entries" or "targets" didn't align with the existing ecosystem around top-level package.json fields, it will confuse people. In my research, I found a lot of tools that expect a single key name for the package.json#"entry", Webpack for example has the "mainFields": ["browser", "module", "main"] configuration.

Using your example before, I think it's more likely that people will write this (in order to remain compatible with the existing ecosystem):

{
  "name": "package-name",
  "main": "dist/main/index.js",
  "module": "dist/module/index.js",
  "browser": "dist/browser/index.js",
  "entries": {
    "module": "dist/module/index.js",
    "browser": "dist/browser/index.js",
    "default": "dist/main/index.js"
  },
  "targets": {
    "default": {...},
    "module": {...},
    "browser": {...}
  }
}

At which point it's kinda confusing that "default" and "main" effectively mean the same thing.

But if you do align the key names, then it also feels like a lot of extra work that users won't necessarily understand what they are getting from it:

{
  "name": "package-name",
  "main": "dist/main/index.js",
  "module": "dist/module/index.js",
  "browser": "dist/browser/index.js",
  "entries": {
    "module": "dist/module/index.js",
    "browser": "dist/browser/index.js",
    "default": "dist/main/index.js"
  },
  "targets": {
    "default": {...},
    "module": {...},
    "browser": {...}
  }
}

So while I think that these proposal can definitely be introduced separately without conflicting with one another (Yes they are both mutually additive imo). I think we should try for something more unified so that users 1. Don't feel like they are being forced to do a ton of extra work and 2. Understand everything that they are getting from any additional work they are putting in.

@guybedford
Copy link
Owner

That's a good point. The major win of the "entries" proposal is really having a way to define the meaning of these environment names as something that can be centrally managed.

I'm fine with users not using "entries" at all for years, until it's widely supported, and then only using it once it has compatibility as a way to do the grouping and avoid base-level package.json properties.

The major feature I want though really is to be able to share the meaning of these names, just like you have in targets, with both aliases and exports:

{
  "name": "package-name",
  "main": "dist/main/index.js",
  "module": "dist/module/index.js",
  "browser": "dist/browser/index.js",
  "targets": {
    "default": {...},
    "module": {...},
    "browser": {...}
  },
  "exports": {
    "./feature": {
      "module": "./dist/feature.module.js",
      "browser": "./dist/feature.browser.js",
      "default": "./dist/feature.default.js"
    }
  },
  "aliases": {
    "local": {
      "module": "./local/alias.module.js",
      "browser": "./local/alias.browser.js",
      "default": "./local/alias.default.js"
    }
  }
}

"entries" to me is, thus, not the primary proposal - rather it is the attempt to create a starting point to define the meaning of these names and allow them to be used in these new fields, by finding a useful way for bundlers to support them today, and then building off that success to try and get wider support like the above.

The overall goal is simply to get the same benefits we have with the browser field, but in a scalable way that scales with these names.

@jamiebuilds
Copy link
Author

The major feature I want though really is to be able to share the meaning of these names, just like you have in targets, with both aliases and exports

I absolutely agree with that. I much prefer the separate package.json#targets/exports/aliases fields such that you can have any combination of them without having all of them in the form of a deeply nested object (which would also be harder to memorize or teach).

In terms of having a "default" I think we need to consider carefully what we're implying. Because in terms of the "default" package.json#"entry" you can't really say there is one, because it entirely depends on the context you are requesting it from.

{
  "name": "package-name",
  "main": "dist/main/index.js", // "main" is the default if you are in a commonjs env (generally falling back to fs resolution, or another field if you've configured a bundler to do so)
  "module": "dist/module/index.js", // "module" is the default if you are in a esm env (generally falling back to "main")
  "browser": "dist/browser/index.js" // "browser" is the default if you are requesting for a browser bundle. (generally falling back to either "module" or "main")
}

However, you certainly could have a "defaults" in terms of the configuration for all of your entry fields.

{
  "name": "package-name",
  "main": "dist/main/index.js",
  "module": "dist/module/index.js",
  "browser": "dist/browser/index.js",
  "targets": {
    "default": {...}, // `package.json#main/module` uses this config (because they aren't configured separately)
    "browser": {...} // But `package.json#browser` uses this config
  }
}

However, in terms of this "default" configuration, I think we actually have a better option today: top-level fields

{
  "name": "package-name",
  "main": "dist/main/index.js",
  "module": "dist/module/index.js",
  "browser": "dist/browser/index.js",
  "engines": { "node": ">=4.0.0" }, // default targets config
  "browserslist": "...", // default targets config
  "targets": {
    "main": {
      // "node" -- defaults to `package.json#engines.node`
      "browsers": false // override to turn off default
    },
    "module": {
      "node": ">=12.0.0", // override to new value
      "browsers": "..." // override to new value
    },
    "browser": {
      "node": false // override to turn off default
      // "browsers" -- defaults to `package.json#browserslist
    }
  }
}

Counter Argument: People may be confused that they have to set "node": false on the "browser" field, thinking that the name implies more.

Alternative "defaults":

{
  "targets": {
    "defaults": {
      "node": ">4"
      "browsers": "..."
    },
    "main": {...},
    "module": {...},
    "browser": {...}
  }
}

@guybedford
Copy link
Owner

guybedford commented Jul 30, 2019

One of the underlying ideas of these "environment names" is that you could pass a list of truthy environment names to the bundler, eg - ['react-native', 'module', 'default']. Where the bundler also has its own internal defaults (eg default is always true). The targets definition seems to be a way for packages themselves to effectively define these conditions based on more fine-grained rules. Both seem compatible which seems to be the shared ground.

But the concept of "default" is that it is an environment that is always true.

Then when selecting which option to use, out of say:

{
  "exports": {
    "./feature": {
      "module": "./dist/feature.module.js",
      "browser": "./dist/feature.browser.js",
      "default": "./dist/feature.default.js"
    }
  }
}

the conditions get checked in object order from top to bottom. The first truthy environment is then selected. So if none match, we get the "default" as the always-truthy fallback.

So I can appreciate that "default" may not seem to make sense for the top-level main entry points, as a concept it is designed to deal with the selection of the non-main entry point handling for exports and aliases, such that we can provide the fallback / non-specific / general case. "main" as a concept doesn't extend to these otherwise.

Re setting "node": false for the "browser" environment, the default model is that environments are false unless specifically "turned on" in the tool / js environment based on configuration passed to the tool, the native environment of the tool / js runtime itself, or something like "targets" which is able to control the analysis of these conditions on a per-package basis.

Composition of conditions is also supported in this model (as in this proposal) -

{
  "exports": {
    "./feature": {
      "module|browser": "./dist/feature.module.js",
      "default": "./dist/feature.default.js"
    }
  }
}

would only select feature.module.js for the module + browser environment.

@jamiebuilds
Copy link
Author

the conditions get checked in object order from top to bottom. The first truthy environment is then selected. So if none match, we get the "default" as the always-truthy fallback.

Okay, in that case I would question that "main" isn't a better name for the "always truthy" case. It's somewhat already understood to have that meaning, and I think it's more important to align with package.json#main than to use a more precise term (However much you can really argue that "default" is a precise term).

But going along with "default" I would still expect entries/targets/exports[*] to have a "main" in addition to "default" (or at least the ability to have both). In which case, I think I could make a more compelling argument to have a top-level package.json#defaultEntryField (or whatever name suits best):

{
  "main": "...", // is default
  "module": "...",
  "browser": "...",
  "defaultEntryField": "main",
  "targets": {
    "main": {...}, // is default
    "module": {...},
    "browser": {...},
  },
  "exports": {
    "./feature": {
      "main": "...", // is default
      "module": "...",
      "browser": "..."
    }
  }
}

@guybedford
Copy link
Owner

Now that you mention it I could actually get behind renaming "default" to just "main" and maintaining that consistency you mention.

Note though that "main" would need to be the last item in the list of "exports" because they are selected in object order, so otherwise the "main" would always be selected for "pkg/feature" in the example you have given there.

If we're in some vague agreement on the overall model, I'd be interested to hear how we might manage the balance between control over "targets", and having targets being package-defined. This feels a little like the Babel configuration problem for packages in that fine-grained rules for third-party packages might need some considerations from a debugging / usability point of view.

@guybedford
Copy link
Owner

(is the expectation that "targets" would be supported for installed packages in node_modules?)

@jamiebuilds
Copy link
Author

because they are selected in object order

What's the reasoning for selecting them in that order. Not that I'm opposed to adding order to keys in JSON objects (That's a huge part of the design of .parcelrc). But it seems odd that:

  1. package.json#exports would behave differently from package.json#<mainFields...>
  2. package.json#exports wouldn't use the same information that is being provided in package.json#targets (assuming the functionality of package.json#targets was added)

(is the expectation that "targets" would be supported for installed packages in node_modules?)

Yes, it's also part of our strategy to know which installed packages need additional transpiling and which ones we're okay just using. If we can't find an entry point to a module that meets our requirements, we'll select one that is "closest" and compile it down further. (This is a big problem within existing tools today, I've seen a lot of not-enough-transpiled code end up in production that came from node_modules)

@devongovett
Copy link

Seems to me like the order shouldn't be configurable directly. The tool should choose the entry/target that satisfies the maximum number of constraints. First, it filters the set of entries to those that match the target environment (node, browser, electron, etc.). If multiple entries still match, sort based on target information (e.g. engines, syntax support, etc.). If none match, then fall back to the "default"/"main" entry.

Tools might also want to find the "closest" entry to what they need rather than an exact match. For example, Parcel will transpile modules futher if the desired target is "lower" than the resolved module.

@guybedford
Copy link
Owner

guybedford commented Jul 30, 2019

I guess I'm trying to define the behaviour here for when there is no targets present, and then to try to work out how targets could refine or additively apply. And then based on the concept that, in the general case, there can exist environments that fulfill multiple conditions.

If it isn't the case that an environment fulfills multiple conditions - say with the mains we have currently where react-native, electron, browser are all entirely disjoint, then sure that is the behaviour that we have.

But if we add a new environment condition like esmodules, where esmodules is strictly defined by the ability for the environment to support import/export syntax only (identical to module/nomodule in browsers, and a fully well-defined meaning that can be agreed on between tools), then esmodules is now an environment condition that overlaps / intersects with the browser, node, electron, react-native environment conditions etc. So it can't be used without defining precedence somehow. Because it is not clear how precedence applies. And this is also why the module field in the package.json isn't clear, because it simply isn't clear how to interacts with other environment conditions and what precedence would apply.

Maybe we won't define an "esmodule" environment name, maybe we won't have any intersections of conditions like this, in which case sure ordering doesn't need to apply. But I was just trying to handle generic environment definitions in this way.

Thinking about the simplest possible case:

{
  "exports": {
    "./x": {
      "node": "./index.js",
      "esmodule": "./index.esm.js"
    }
  }
}

How would Node.js know which one to pick if not through ordering?

Or do we need Node.js to understand "targets" in conjunction with the above?

I'd like to find a way that targets can act additively, as if there were a standard default "targets" field that would apply in the package.json if there were no "targets" present, which could be overridden by the custom targets.

Where the rules would be the sort of generic:

{
  "targets": {
    "main": {
      "node": ["*"],
      "browser": ["*"]
    },
    "node": { "node": ["*"] },
    "browser": { "browsers": ["*"] }
  }
}

does that make sense as an approach? Would you require targets to be present to be able to do differential aliases in Parcel, or handle this another way without precedence?

@guybedford
Copy link
Owner

guybedford commented Jul 30, 2019

For example, Parcel will transpile modules futher if the desired target is "lower" than the resolved module.

Very interesting. Out of interest, and to understand targets better, how would you define three main targets for main entry points in Node.js 4, one for Node.js 5 - 9 and another for Node.js 10+ in the package.json?

@jamiebuilds
Copy link
Author

I guess I'm trying to define the behaviour here for when there is no targets present, and then to try to work out how targets could refine or additively apply.

Right now with multiple entry points defined in a package.json priority is determined by the resolver rather than the package itself (i.e. the mainFields: ["browser", "module", "main"] config option in Webpack's resolver I mentioned before).

package.json#targets is in part trying to solve that hard-coded behavior so the resolver can make an informed decision on a package-by-package basis as described by the individual packages.

Since we were talking about having the names be mirrored in package.json#entries/targets/exports I think it makes sense to continue to use the "mainFields", ordered list of field names resolution, that exists in the ecosystem today.

Then we can come back later with package.json#targets and solve the problem without having to backwards support the ordered objects of exports.

resolvePackageEntry(pkg, { mainFields: ["browser", "module", "main"])
{
  "main": "...",
  "module": "...",
  "browser": "...", // selected because "browser" was first in array
  "exports": {
    "./feature": {
      "main": "...",
      "module": "..." // selected because "browser" doesn't exist and "module" was second in array
    }
  }
}

@guybedford
Copy link
Owner

guybedford commented Jul 31, 2019

Edit: Corrected the Electron rule.

Right now with multiple entry points defined in a package.json priority is determined by the resolver rather than the package itself (i.e. the mainFields: ["browser", "module", "main"] config option in Webpack's resolver I mentioned before).

Right, but in environments like electron, the rule is just ["electron", "main"] and that is explicit. I think it is important to consider runtimes, tools and bundlers equally in this standard.

Since we were talking about having the names be mirrored in package.json#entries/targets/exports I think it makes sense to continue to use the "mainFields", ordered list of field names resolution, that exists in the ecosystem today.

This certainly seems a sensible tooling choice to generalize the decision process.

If the resolver is free to handle the precedence decision itself, my concern with that approach is that the point of specifying something is to attempt to avoid ambiguity. A good spec should provide well-defined behaviours down to the inputs.

If precedence is not obviously defined that could be a concern for predictability.

This also brings up the question as to whether we should try and define these conditions. For consistency between tools I was hoping to maintain central definitions / meanings that try to accurately describe what the condition refers to. Eg "module" is well-defined as Node.js 12 with --experimental-modules unflagged or the <script type="module"> browser target, and similarly for other JS environments.

I was imagining a central website / form where these names could be registered along with their meanings to avoid conflicts, but tools would, like with MIME / content-type be free to do their own thing too. Parcel could redefine the definitions entirely through targets, but perhaps this would provide a useful default?

I was also hoping to define combining condition names with the | operator like "browser|module", where the combined precedence might not be obvious either.

I'm open to reconsidering the precedence question though certainly - perhaps each name could be associated with a precedence in the spec itself?

@guybedford
Copy link
Owner

If we allow names to have precedence associated with them in the resolver or spec, then combined precedence if we also introduce | might be defined by the specificity level (number of |), followed by the precedence ordering over any similar condition grouping. That seems like it could be well-defined actually.

Then the default precedence is probably something like (where each row is equal precedence, and environments / tools would only enable the ones that apply):

electron, react-native
parcel, jspm, webpack, rollup (do we want these?)
node, browser
worker, chrome-extension (do we want these?)
esmodule, wasm
main

@guybedford
Copy link
Owner

(And to clarify too, I'm not tied to | either)

@guybedford
Copy link
Owner

Another question - does Parcel support a "node" top-level field in the package.json as the "Node.js entry point"? I'm wondering if that would constitute the full generalization here if we don't have an explicit "entries" field?

@devongovett
Copy link

does Parcel support a "node" top-level field in the package.json

No, it treats "main" as this field, unless we infer that this is a browser package. If there is an engines.node field, no browserslist, and no browser entry, then we consider "main" as a node entry point, otherwise browser. It's a little biased toward the browser usecase, but that's more common for Parcel.

I wouldn't be against a "node" field though.

@guybedford
Copy link
Owner

This discussion has been really productive, thank you both again.

I've been thinking about how to consolidate this discussion into a new proposal that might work more flexibly than entries.

Would it be worth combining targets into their definitions with a new type of array fallback form:

{
  "entries": [{
    "target": "./main-browser-modern.js",
    "browsers": ["> 1%", "not dead"]
  }, {
    "target": "./main-browser-legacy.js"
    "browsers": ["> 1%", "not dead"]
  }, {
    "target": "./node-all.js",
    "engines": {
      "node": ">=4.x",
      "electron": ">=2.x"
    },
  }]
}

where just like array fallbacks in import maps, the first match is taken from left to right.

I've always steered clear of this type of a complex construct due to the difficulty of specifying the conditions, but perhaps we could find a way to sketch out the space simply in a spec between tools?

Again, the above would generalize to "exports" and "imports" in package.json too.

Would value your thoughts on that - and if it sounds sensible I can set up a new proposal to investigate this.

@guybedford
Copy link
Owner

(when I say this keeps coming up, both @jkrems and @bterlson have mentioned a desire for something like the above recently)

@guybedford
Copy link
Owner

I've gone ahead and created a new Package Targets Proposal based out of these discussions, and very similar to the Parcel 2 definition at https://github.com/guybedford/proposal-pkg-targets. It would be great to move further discussions there.

@guybedford
Copy link
Owner

Also, @devongovett @jamiebuilds @jkrems @bterlson would you be ok for me to list you as contributors? Since the ideas are mostly yours and from our discussions. I just didn't want to do so without consent though.

@jkrems
Copy link

jkrems commented Aug 12, 2019

Sure, happy to be involved (and be listed as a contributor)!

@jamiebuilds
Copy link
Author

would you be ok for me to list you as contributors?

Sure

If we allow names to have precedence associated with them in the resolver or spec, then combined precedence if we also introduce | might be defined by the specificity level (number of |), followed by the precedence ordering over any similar condition grouping. That seems like it could be well-defined actually.

Then the default precedence is probably something like (where each row is equal precedence, and environments / tools would only enable the ones that apply):

electron, react-native
parcel, jspm, webpack, rollup (do we want these?)
node, browser
worker, chrome-extension (do we want these?)
esmodule, wasm
main

I think this is the major divergence of what we're trying to do here.

Consider all the different things those field names are accounting for:

  1. JavaScript engine (browser, node)
  2. JavaScript global env/available apis (browser, node, electron, react-native, worker, chrome-extension)
  3. Tool resolving the package (parcel, jspm, webpack, rollup)
  4. JavaScript version/feature support (main, module, esmodule)

If we explode that out into a matrix of all the different combinations of "Browser/Node/Electron environment that supports/doesn't support modules and ES5/ES2015/ES2016"

Then you get into the problem of what do those mean over time. If we're using a very generic label like "browser" we could say that means Chrome 75, Safari 12, etc. But then 5 years down the line no one cares about those versions anymore, what do we do? Change the definition? Now every package using the old definition is out of date.

My opinion is that we should throw in the towel trying to make package.json entry fields make any sense, and we should focus on giving tools better metadata to make a more informed decision based on what the package was actually built for.

Then it doesn't matter what fields a package author defines. They could have a package.json#hissingbubblegumslide field and describe it in package.json#targets and it would work forever and no one but the package author would ever care.

Taking that into consideration, I would also like a central place for defining what different field names in package.json#targets means.

{
  "targets": {
    "hissingbubblegumslide": {
      "browsers": ["Chrome >= 75", ...], // https://package-json-target-types.org/spec#browsers
      "modules": "esm", // https://package-json-target-types.org/spec#modules
      "node": ">=4.0.0", // https://package-json-target-types.org/spec#node
      "electron": "..." // https://package-json-target-types.org/spec#electron
      ...
    }
  }
}

@guybedford
Copy link
Owner

If we explode that out into a matrix of all the different combinations of "Browser/Node/Electron environment that supports/doesn't support modules and ES5/ES2015/ES2016"

Note that the "targets" definition as you do in Parcel within the package.json are not excluded by this approach. It's designed to be a sane default where "browser" means all browser versions.

Further slicing on that doesn't look so bad either. There has been some discussion in guybedford/proposal-pkg-targets#2 of yearly target environments to capture this sort of thing.

Under such a model this might become something like -

{
  "main": "index-legacy.js",
  "browser": {
    "featureset2019": "./index-2019-browser.js",
    "main": "./index-legacy-browser.js"
  },
}

Because Electron is a browser it should match the above as well.

If we wanted to match both Node and browser, we wouldn't even need to specify the "browser" and could have gone straight to the featureset slice:

{
  "main": "index-legacy.js",
  "featureset2019": "./index-2019.js"
}

Then you get into the problem of what do those mean over time. If we're using a very generic label like "browser" we could say that means Chrome 75, Safari 12, etc. But then 5 years down the line no one cares about those versions anymore, what do we do? Change the definition? Now every package using the old definition is out of date.

The definitions are very carefully designed not to change over time. That is one of the major requirements of the default definitions.

My opinion is that we should throw in the towel trying to make package.json entry fields make any sense, and we should focus on giving tools better metadata to make a more informed decision based on what the package was actually built for.

I'm all for the Parcel targets proposal, but I think having sane defaults helps the major case. The browser field has been vitally important in the past to mean any browser. I just want to work out how to continue to use the "browser" field as we expand into "exports" and "imports" in the package.json, and think about other environments too.

I don't think anyone is complaining about the current meanings of these fields, but rather looking to extend them.

What I like about the Parcel approach is that exactly does provide an extension point. But having defaults that back it if you have no "targets" in the package.json seems very sensible to me for the average user use case.

Then it doesn't matter what fields a package author defines. They could have a package.json#hissingbubblegumslide field and describe it in package.json#targets and it would work forever and no one but the package author would ever care.

The proposal as it stands is exactly designed not to preclude that. I'm all for the freedom of the hissingbubblegumslide target!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants