-
Notifications
You must be signed in to change notification settings - Fork 122
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
How should the formatter handle missing package_config.json files? #1597
Comments
Importantly for why it was painful: at first I could not figure out why this was happening, because when I ran exactly the same command locally I got the old formatter. It was only when after a while of being confused when I ran it over my entire local repo instead of one package I hit—via pure luck—a package that I happened not to have done anything else with locally since the last time I cleaned my entire local repo. And from there I was able to trial-and-error my way into determining what the trigger was. Even then I remained pretty confused, because it's non-obvious to people who don't work on the internals of these tools that "You typically control the language version by setting a min SDK constraint in your package's pubspec." (from the WIP changelog) actually means "You typically control the language version by setting a min SDK constraint in your package's pubspec and then running |
If we don't want a complex solution, 2 would be dramatically better than 4 IMO. It is hard to overstate how much worse mysterious failures are than explicit ones. |
Here is an issue about running |
The pubspec is the better source of truth (if present), so if we are going to look there I would trust that over the package config (this will also handle the case of editing the SDK constraint but not running a pub get). I don't love the idea of the formatter having to look for both files, but it also feels wrong for a Afaik the other commands just always run a |
I don't know exactly which commands run |
It's worth noting that the reason that other tools run But And the formatter already does YAML parsing to read the page width configuration (alas, from a different file), so it's not taking on another dependency to read the pubspec. The formatter probably should still support the package_config file too, for rare cases where a user has a config but isn't using pub (like the Dart SDK itself). |
Drive-by comment: It sounds like option 3 (rely on
Could the implementation be shared?
OK, so there could be a reason to be able to use |
Either:
The error mode of using it in a Pub package directory with no A distant second would be erroring out and saying that you have to run Even then, looking only for
That is the canonical way to bring the package directory into a useful state where Dart tools can work on the Dart source (and correctly determine whether it's even valid Dart source at all.) There is no tool which depends on Dart language versions which can be trusted to work, correctly or at all, on code without an associated language version, which usually means a Or it can look for the answer itself, and make sure it still works for a file that has no configuration file at all. That means: Starting with the current directory of the Dart file it wants to find a version for:
That's something that can be computed once and cached for each directory. It prefers A Pub package (as defined by a Otherwise if Even if that package config doesn't actually assign a package to the current directory, still don't look further. (And if all files have a language version marker, you might never need to find the default language version, if you are willing to do a little pre-parsing to check for that. It has to occur before any Dart declaration in the file.) |
This is a very compelling argument imo for always looking for a pubspec.yaml as well and preferring those |
OK, we talked about this in the language meeting this morning. We decided that the formatter will do Lasse's second suggestion. It will walk surrounding directories looking for either a I'll also look into making this logic available as a library from the dart_style package so that tools using the formatter as a library have access to that behavior if they want it. |
OK, here is an annoying wrinkle. Let's say you have a pair of packages like:
So you've got a package Let's say you haven't run No. The formatter will be able to figure out the language version from the (In retrospect, if I knew the formatter might read the Since you will still need to run |
Obviously not. If your formatting configuration can depend on data from another package, and no file from that other package can be resolved without running This assumes that there is a correct formatting, and it's just not known yet. There isn't necessarily one correct formatting if the line length can depend on the version-solve. Changing one constraint can lead to including a different version of the imported I do feel that How is Consider a package structure of:
Here trying to format Maybe the only way to win is not to play :) If every configuration is inside the same Pub package root directory ( Otherwise you'll likely have to have run We're already stretching things by treating the file as an entry point (so its package configuration is located relative to the file), but that makes sense for Pub packages. Maybe we need to:
(Which can be interleaved into a single loop, which will most often end at the same point anyway.) That is, you only need to do a In that case, it'd probably also be OK to just say "import URI can't be found, please run If formatting a directory, find the configurations on the way down, rather then trying to find them for each |
I did think through that edge case when I first added support for page width configuration. As far as I can tell, it's not a problem. The page width used to format code inside a package is always based on that package's own dependency solve, not the solve of other packages that may be using it transitively. For example, say Now lets say that
If you then run
Unlike compiling and analyzing, the entrypoint package's dependency graph never affects how a file is formatted. It's always formatted according to its own surrounding package's version solve. We don't start at the files in So as far as I can tell, it all works out fine.
Right. That's why I no longer think it's a good idea to read the
Me too. In particular, since there is a lint that checks page width, it seems reasonable to configure the page width in the same place that lints are configured.
It walks the directory chain just looking for
It uses the original package configuration of the first
Yeah, you can definitely craft weird situations if you try, but I suspect most users simply don't do that. If a user has an
That is, uh, a lot of complexity. The only reason the formatter has been successful so far is because (for capacity reasons), I've been ruthless about restricting complexity. I'm trying to dial that up a little bit because users really do want project-wide page width configuration. But I don't have the capacity to add a lot of smarts in how to pokes around in the file system. That would just be an unending bug farm. I don't want to spend the rest of my days dealing with bugs like "I have an analysis_options.yaml file that is actually a symlink to one in another package's directory that is nested inside another directory that isn't a pub package but has an explicit
Currently, if it doesn't have a package config to resolve "package:" includes, or the package config doesn't specify the named package, it simply silently fails. I don't love silent failures, but it does keep things simpler.
There are likely some annoying failure modes or at least performance issues with that since the user can pass multiple potentially overlapping directory paths on the command line. I just have it find configurations for each file but then cache them on a per-directory basis. |
Don't have to try to be smart. The non-smart approach is:
You find an enclosing package configuration by finding the nearest enclosing The code for the latter is general. It can probably be written to also do Then cache line length and language version per directory. (We can always change what happens when the cache is empty for a directory.) |
Having
The only downside is that the formatter might have issues in places where We've contemplated introducing cc @sigurdm In general: We aim for |
@stuartmorgan ran into pain when the new formatter in the Dart SDK broke a Flutter CI. The problem was that the format CI check wasn't running
dart pub get
before runningdart format
. That meant the package didn't have a package config, so the formatter defaulted to the latest language version (and thus the new style) even though the code was actually on an older version and should have used the older style.When I made the formatter language-version aware, I had it look for a
.dart_tools/package_config.json
file. This is consistent with howdart analyze
,dart run
, etc. behave.For most users editing code on their local machine, that's probably fine. They will have already run
dart pub get
and have a valid package config. But the formatter is often run on CI and possibly other contexts where that has happened. Right now, in those places, the behavior you get is silently applying the new style (and then failing CI). What should we do instead?Some ideas:
Like other
dart
tools,dart format
could automatically rundart pub get
on the user's behalf if it thinks it's necessary. I'm not sure exactly what "thinks it's necessary" means or how the other tools detect that they need to rundart pub get
. Probably if it finds a pubspec but no corresponding package config, or the package config's mod time is older than the pubspec's?This does the right thing, but it means running the formatter might mutate the file system outside of the formatted files. I'm not sure how I feel about that.
If we don't find a package config, look for a pubspec. If found, print an error and tell the user to run
dart pub get
. This is more explicit than (1), but also probably more annoying. If the tool knows what the problem is, why does it yell at the user to solve it?Read the language version directly from the pubspec.yaml file. The formatter could parse the pubspec, look for the SDK constraint, and infer the language version from that the same way that pub does. No need to run pub or mutate the file system.
We'd have to decide how it behaves if there is both a package config and a pubspec. Do we only read the pubspec if there's no package config, or do we always prefer the pubspec?
This would mean that the formatter is on the hook to consistently and correctly implement the same logic for inferring a language version from the pubspec that pub uses. If that logic changes (for example, by allowing different constraint syntax in the SDK constraint), then the formatter has to be updated too.
Do nothing. It's an annoying UX if users hit it but we can mostly avoid it with good messaging. Once the world is on the new style, it won't affect many users.
@dart-lang/language-team, any thoughts on the right approach? I lean towards 3, but I'm on the fence.
The text was updated successfully, but these errors were encountered: