-
Notifications
You must be signed in to change notification settings - Fork 229
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
Allow adding custom data to package_config.json through pubspec.yaml config #3917
Comments
cc @natebosch |
Should we have a convention around using a single top-level key to namespace a chunk of config? Should we be worried about potential conflicts? I worry a bit that this becomes another option amongst many for this type of config. |
We should have a convention that packages accept configuration under their package name. Packages that don't do that might run into conflicts, but it should be fairly rare even then.
Obligatory https://xkcd.com/927/ It is a valid concern but I think this is also significantly better than the typical solutions which require each tool to read the pubspec itself (and potentially transitive pubspecs). |
Not sure I understand the proposal completely - but I think I like the idea :) In the formatter example, is the Are these present only in the root-package? Another use-case for having a way to add stuff from pubspec.yaml to package_config.json would be a way of specifying a "generated package" (like |
@lrhn might have things to say. |
Well, I was being a bit intentionally vague here as to leave it open ended to the details, I don't love pubspec: name: my_package
environment:
sdk: ">=3.0.0 <4.0.0"
package_config:
sweet: ["config"] package config: {
"configVersion": 2,
"packages": [
{
"name": "my_package",
"rootUri": "../",
"packageUri": "lib/",
"languageVersion": "3.0"
"sweet": ["config"]
}
]
}
These would be supported for all transitive packages (that is actually an important part of the value). This means only one tool actually has to crawl through and read files from all transitive packages (pubspecs, which they are already reading anyways), and then all other tools just read the one file which has everything merged in already.
I could see that yes, not sure what the configuration would exactly look like though. |
Aside from not having to deal with YAML, what's easier about reading the |
I suspect it primarily gives a single place to read, instead of having to hunt for transitive Do we need to consider the impact on parse time for this file? If we starting allowing arbitrarily large content could it have an impact on compile times or VM startup time? |
It would still be bounded by the size of transitive pubspec.yaml files, which should remain reasonably small, although its certainly possible to abuse it to the point that it causes a problem. |
It did just occur to me that In the case where it is formatting multiple packages, it likely doesn't even do the right thing:
That combined with the fact that it won't work if you haven't ran a |
I don't think that would benefit the formatter much since it would end up looking for a config file for each file being formatted anyway. The formatter doesn't know what packages are, and it's entirely reasonable to run it on arbitrary collections of directories and files that may be within a package, span multiple packages, not be inside a package at all, be in a package that hasn't had |
I could imagine something like: # in pubspec.yaml
config:
<package>:
<key>: <value>
# Example configuration for package:retry
retry:
default_retries: 5 Then in {
"configVersion": 2,
"packages": [
{
"name": "retry",
"rootUri": "$PUB_CACHE/...",
"packageUri": "lib/",
"languageVersion": "3.0"
"config": {
"default_retries": 5,
}
}
]
} Then we could allow code to read configuration either by parsing This would allow for conditional imports, which I'm sure could be a footgun. On the other hand it would enable some nice features. Maybe it could configure which backend to use for Or it could just generally be used for writing configuration values without having yet-another Not sure how serious I am about this :D |
Cross-linking from the ideas in dart-lang/native#39: We should consider a script instead of a yaml config as it should support conditional configuration. (E.g. only including something in debug mode, or when some flag is set.) For localization, we need something that combines values. # pubspec.yaml package:foo
config:
localization:
messages:
- data/messages.json # pubspec.yaml package:bar
config:
localization:
messages:
- data/messages.json {
"configVersion": 2,
"packages": [
{
"name": "localization",
"rootUri": "$PUB_CACHE/...",
"packageUri": "lib/",
"languageVersion": "3.0"
"config": {
"messages": [
"${packageRootPackageBar}/data/messages.json",
"${packageRootPackageFoo}/data/messages.json",
]
}
}
]
} Having a script (such as The two above constraints lead me to think that a script for config would be better than yaml entries. If there is a more general want/need for config than only in the context of cc @mosuem |
The custom config would live under the package config for the package in which it was written, so you would have the package root information, something more like this in your example: {
"configVersion": 2,
"packages": [
{
"name": "bar",
"rootUri": "$PUB_CACHE/...",
"packageUri": "lib/",
"languageVersion": "3.0"
"config": {
"messages": [
"data/messages.json",
]
}
}, {
"name": "foo",
"rootUri": "$PUB_CACHE/...",
"packageUri": "lib/",
"languageVersion": "3.0"
"config": {
"messages": [
"data/messages.json",
]
}
}
]
} I think that would work fine (but of course you would have to iterate all packages in the config looking for the messages). In general I think it is better design though as you have all the context about the surrounding package with each config entry. |
Right, but then still you'd probably want to nest under the "target" package: {
"configVersion": 2,
"packages": [
{
"name": "bar",
"rootUri": "$PUB_CACHE/...",
"packageUri": "lib/",
"languageVersion": "3.0"
"config": {
"localization" : {
"messages": [
"data/messages.json",
]
}
}
},
{
"name": "foo",
"rootUri": "$PUB_CACHE/...",
"packageUri": "lib/",
"languageVersion": "3.0"
"config": {
"localization" : {
"messages": [
"data/messages.json",
]
}
}
}
]
} In the context of native assets, one might want to branch on target OS or build mode (debug/release) for certain config. So in that scenario, the config needs to be computed on a per
Regarding caching/invalidation, in This makes me think there are multiple different type of configurations we are talking about here:
With the design we had in mind for the build config, we would provide an API in the CLI we already have, so that @jakemac53 reading from your initial proposal, you were not suggesting option 1. right? Only option 2. Still 2 and 3 seem quite closely related. If we'd drop the conditional config (which would probably mean people start writing some kind of conditionals in the config themselves: One argument for not unifying 2 and 3 is caching. If the solution proposed in this issue (a) contains config for |
Yes, the package config should be usable across configurations. I would do what you alluded to later, encode in the config what to do for each mode/configuration. This is what people do in build.yaml files for instance. Whatever tools are consuming this configuration would handle selecting the correct config for the current invocation.
Yeah I think that -D defines are typically invocation specific config, and also global, so I am not sure the pubspec is a good place for it. I also don't think all tools should have to start reading pubspecs (and definitely not transitive ones) in order to find defines.
I agree this isn't a viable option for shipped apps, but it is viable for build-time (or general development) configuration.
Yes this is likely accurate, as the config could change the behavior. I wouldn't expect the config to change often. |
I was thinking that for |
Yeah I think it makes sense to have both 👍 |
Conditional imports would continue to be supported only for |
Oh, that makes me sad. We could supposedly change that some day, but I understand of there is a lot of arguments for not giving away that footgun. |
@dcharkes I was surprised that you wanted configuration from other sources than the root-package. For replacing top-level config files like We could do something like: # pubspec.yaml package:foo
name: foo
config:
# config for package:localization
localization:
messages:
- data/messages.json # pubspec.yaml package:bar
name: bar
config:
# config for package:localization
localization:
messages:
- data/messages.json {
"configVersion": 2,
"packages": [
{
"name": "localization",
"rootUri": "$PUB_CACHE/...",
"packageUri": "lib/",
"languageVersion": "3.0"
"configFromPackages": [
{
"package": "foo",
"config": {
// So long as the reader knows that this is config from "package:foo"
// a relative path can be interpreted relative to "rootUri" for "package:foo".
// Isolate.resolvePackageUri already provides this logic.
"messages": ["data/messages.json"]
}
},
{
"package": "bar",
"config": {"messages": ["data/messages.json"]}
},
],
...
}
]
} This way it's only necessary to rebuild Then we leave for the build scripts in This would probably make it hard to use |
In a discussion with @mkustermann today we discussed we might actually want dynamic input in some cases (based on the build in build.dart) for tree shaking. So maybe we should not unify this type of config with input for tree shaking. If we start using the config proposed in this issue now, it could bite us later when we try to support more use cases with |
There is very little here about who would consume this config, how and when. There seem to at least two related ideas:
The former could just be stored in The latter could be put in, say, I guess that's my idea: Recommend that metadata is stored in Any tool which knows what to look for can find the Anything you can do by reading The data doesn't even have to be YAML- and JSON-embedable, or limited in size. If putting it in (I can write you a package to help find the |
I definitely agree that this doesn't unlock any new feature - but what it would do is provide a single canonical way of doing configuration. It also wouldn't require each package that wants to support said configuration to do redundant searching of all transitive packages for said configuration. So that is the value this feature would provide - a consistent convention and de-duplication of work. |
This is actually a very solid approach, indeed this is what package:extension_discovery does 🤣 The only limitation to this that a build process wouldn't be able to know if the configuration changed, and thus, whether to rebuild. One can also argue that it's easier (less duplication of work). But if the native builds are taking a different direction with respect to user-defines, not sure I fully grasp it -- then maybe this is less important. |
Proposal
The package_config.json file permits storing extra data in both package entries as well as top level fields, but there is no general way to utilize this today, because pub owns the generation of the config and doesn't support adding in extra data.
I would like to propose that we add a way to configure extra data for a package entry into the
pubspec.yaml
file, something like:This would just be added directly to the extra data for the package config entry for the current package when it is generated. We could bike shed on the actual name of the key later, if we can agree this is a good idea :).
This would allow for a generalized approach to package configuration, and it would both unify and simplify how various packages/tools accept configuration on a per-package level.
Use cases
One example use case would be the formatter, cc @munificent . People could configure their formatter settings via
pubspec.yaml
, and then the formatter only has to read a single configuration file (package_config.json). While this is some work to do, it would also enable the formatter to be more future proof (and generally correct) regarding language versions. It also allows it to maintain minimal coupling to tools likepub
- internally for instance we could also support generating package configs with this extra information.Another use case could be package:build - instead of having a new file (build.yaml), which we have to transitively look for in all packages, we could have just supported configuration via this option.
Concerns
While this configuration is mostly a black box to pub, it will have to make a decision regarding invalidation. At a minimum, the
package_config.json
file will have to be regenerated whenever this config changes for any transitive dep (most of these will never have a change because they will be pub deps). If any compilers start accepting configuration in this way, then we would also have to make sure we invalidated compilation of pub binaries (which might require deeper changes in frontend_server etc).I think pub can mostly ignore this, and rely on frontend_server to do the heavy lifting. It should re-invoke it if there is any change to the config, and assume frontend_server will handle the invalidation appropriately.
The text was updated successfully, but these errors were encountered: