8000 [AssetMapper] Warn of missing or incompat dependencies by weaverryan · Pull Request #51849 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[AssetMapper] Warn of missing or incompat dependencies #51849

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 < 8000 a class="Link--inTextBlock" href="https://docs.github.com/terms" target="_blank">terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 19, 2023

Conversation

weaverryan
Copy link
Member
Q A
Branch? 6.4
Bug fix? no
New feature? yes
Deprecations? no
Tickets None
License MIT

Hi!

The aim of the importmap.php system is to be a simple way to manage your JS dependencies. But to make it robust enough for production use, it does need a few things - like the importmap:audit command in #51650. This PR adds a check, during importmap:require and importmap:update, that reports any missing dependencies or dependencies with invalid versions. This is necessary so that, if package A requires package B, their versions don't "drift" over time without you being aware (e.g. you update package A to v3 but keep package B at v1, even though v3 of A requires v2 of B).

Screenshot 2023-10-04 at 2 44 04 PM

Built on top of #51786.

Cheers!

@ahmedyakoubi
Copy link
ahmedyakoubi commented Oct 4, 2023

why not installing the missing dependency automatically instead of doing it manually?

@weaverryan
Copy link
Member Author

why not installing the missing dependency automatically instead of doing it manually?

They will be installed automatically when you first require the package - e.g. importmap:require bootstrap will also grab @popper/core. So, if you ever see the but it is not in the importmap.php message from this PR, something has gone wrong or you've removed something by hand :)

@weaverryan weaverryan added the ❄️ Feature Freeze Important Pull Requests to finish before the next Symfony "feature freeze" label Oct 6, 2023
@weaverryan weaverryan force-pushed the asset-mapper-warn-incompat branch 3 times, most recently from 7fefbbe to 0ad63e7 Compare October 6, 2023 18:10
@@ -35,7 +35,7 @@ public function resolvePackages(array $packagesToRequire): array;
*
* @param array<string, ImportMapEntry> $importMapEntries
*
* @return array<string, string>
* @return array<string, array{content: string, dependencies: string[]}>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not ideal... not sure if it merits a new object - as this is entirely internal, unless someone makes their own package resolver.

return substr($importName, 0, $i);
}

return $importName;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit of duplication with JsDelivrEsmResolver, but we have a plan to centralize this in a future PR.

@weaverryan weaverryan force-pushed the asset-mapper-warn-incompat branch 2 times, most recently from 588a4df to c7df5dc Compare October 7, 2023 17:59
$dependencyVersionConstraint = $packageDependencies[$dependencyName] ?? null;

if (null === $dependencyVersionConstraint) {
// odd that this would happen
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes me nervous 😂

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha, good point - that comment was a bit ominous. It would be an unexpected but not dangerous situation where we see that a vendor file has import 'foo', but then when we check the metadata for that package, it doesn't list foo as a dependency at all. I have no idea if this will ever happen under normal circumstances - I can't think of a way (some misconfigured package ?).

But, to be safe, I've changed this to also issue a warning (like the other warnings) so the user is aware of it.

@@ -17,6 +17,7 @@
],
"require": {
"php": ">=8.1",
"composer/semver": "^3.0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be careful about that. The constraints are not 100% compatible between the npm and composer ecosystems.

Copy link
Member Author
@weaverryan weaverryan Oct 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, is there an alternative? Checking into this quickly, differences I found:

  • npm has a 1.0.0 - 2.0.0 syntax.
  • npm seems to have a 5.4.x syntax which means 5.4.*.

I think this feature is pretty important, without it, it gets easy for your dependencies to "drift" out of sync (e.g. you install boostrap today and we also give you @popper/core, then you upgrade bootstrap but forget to upgrade popper and have no warning). So perhaps, IF we run into problems with odd, npm-specific dependency things we can handle those manually? Or maybe we can handle the above 2 items right now manually.

EDIT: I'll add a bit of pre-processing and tests to cover the edge-case differences.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, the ~ operator does not work the same either

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a simple conversion system using all of the differences I could find. There are some edge-case things that can't be converted - e.g. stable or https://example.com/file.js, but I think that's ok. This is just a system to warn users of potential problems (like the peer dependency warning in npm). And if we're missing some real-world cases, we can find out about them and patch them. The "conversion" code is fairly minimal, as the differences don't appear to be too many.

Thanks for catching this!

@weaverryan weaverryan force-pushed the asset-mapper-warn-incompat branch 2 times, most recently from d88507f to 00f269f Compare October 17, 2023 01:06
@weaverryan
Copy link
Member Author
weaverryan commented Oct 17, 2023

Failing tests coming from #50734

@weaverryan weaverryan force-pushed the asset-mapper-warn-incompat branch 2 times, most recently from e741d13 to 08b6059 Compare October 18, 2023 13:06
@weaverryan
Copy link
Member Author

@stof I'd love your thoughts on this PR now. A few notes:

A) I do think this PR is important, so that your dependency versions don't drift away from each other. We're not building a full-blown package manager, but this seems like a pragmatic precaution.

B) I think i've covered the npm-specific version constraint differences nicely

C) In practice, it seems that when some packages are published, their dependencies in package.json change to exact versions. An example is vue - notice @vue/runtime-dom in https://registry.npmjs.org/vue/3.3.0 is listed at exactly 3.3.0. This isn't a problem, it just looks a bit odd and I wanted to mention it (and thought you might have even more insight).

But, I do still think this PR is ready to go

@stof
Copy link
Member
stof commented Oct 18, 2023

For C, it is a choice of each package maintainer.
Some JS ED4F maintainers prefer to require exact versions of their dependencies. Unlike in composer, this does not create a dependency hell as the npm ecosystem does not require a flat dependency list in the installed dependencies with a single version of each package (Composer requires that for PHP package because PHP defines all classes in the same global scope so you cannot have 2 different version of the same package loaded. But JS works with a concept of modules exporting things, not with a shared naming scope).
There are mostly 2 cases there:

  • several related packages from the same maintainer having a shared release cycle (often in a mono-repo): exact version constraint avoids the need to deal with the case of having an updated version of one package while others are not updated. Especially useful when the public API of one package actually relies on the other package
  • dependencies on unrelated packages. For those, I don't have an argument in favor of it (as I'm actually in the other side there). The big drawback is that this requires actively monitoring releases of the dependencies to update them even when they release patch versions instead of relying on semver)

Note that the fact that the JS ecosystem does not require flat dependencies means that it might be impossible to resolve dependencies in a flat way (and doing that requires a composer-like solver anyway and would fail in cases of a dependency hell). And this means that your installer should be able to support the case of having several versions of the same package to respect constraints otherwise it might cause issues.

@weaverryan
Copy link
Member Author
weaverryan commented Oct 18, 2023

That's exactly the insight I was hoping for! 🙏

Note that the fact that the JS ecosystem does not require flat dependencies means that it might be impossible to resolve dependencies in a flat way. And this means that your installer should be able to support the case of having several versions of the same package to respect constraints otherwise it might cause issues

I think this is possible, via the importmap scopes. But I'd like to see if we actually need this. The consequence is that, right now, the system will work in the simple, "flat dependencies" kind of way. And I think that's fine. This PR would at least help users discover this potential problem more readily, then we can chat in the future about solving it.

@@ -17,6 +17,7 @@
],
"require": {
"php": ">=8.1",
"composer/semver": "^3.0",
Copy link
Member
@nicolas-grekas nicolas-grekas Oct 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about making this an optional dep and throw a useful LogicException in the command when used?

nvm, it's not an optional feat

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about that... but we don't use it in a specific command, we run this in the background when using importmap:require or importmap:update. So in practice, it WILL be needed. And if we just didn't show the warning if this wasn't installed, then nobody would have it installed and it wouldn't do anything. I couldn't find a nice way to avoid it :)

@nicolas-grekas nicolas-grekas force-pushed the asset-mapper-warn-incompat branch from 08b6059 to 42dfb9a Compare October 19, 2023 15:21
@nicolas-grekas
Copy link
Member

Thank you @weaverryan.

@nicolas-grekas nicolas-grekas merged commit 071a971 into symfony:6.4 Oct 19, 2023
@weaverryan weaverryan deleted the asset-mapper-warn-incompat branch October 19, 2023 18:00
This was referenced Oct 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
AssetMapper Feature ❄️ Feature Freeze Important Pull Requests to finish before the next Symfony "feature freeze" Status: Needs Review
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants
0