-
Notifications
You must be signed in to change notification settings - Fork 955
fix(deps): exclude @types/* packages from component peer dependencies #10186
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
Open
davidfirst
wants to merge
10
commits into
master
Choose a base branch
from
fix/exclude-types-from-component-peers
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+192
−95
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
1ea9786
fix(deps): exclude @types/* packages from component peer dependencies
davidfirst c4637a1
refactor(deps): extract helper methods to reduce cognitive complexity
davidfirst a62dafc
Merge branch 'master' into fix/exclude-types-from-component-peers
davidfirst 7cb0f29
Merge branch 'master' into fix/exclude-types-from-component-peers
davidfirst 2016587
Merge branch 'master' into fix/exclude-types-from-component-peers
davidfirst ef69ec4
test(deps): add e2e test for excluding @types/* from component peers
davidfirst ec32978
Merge branch 'master' into fix/exclude-types-from-component-peers
davidfirst f67480f
Merge branch 'master' into fix/exclude-types-from-component-peers
davidfirst f9964c2
Merge branch 'master' into fix/exclude-types-from-component-peers
davidfirst 0176b9a
Merge branch 'master' into fix/exclude-types-from-component-peers
davidfirst File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -570,107 +570,158 @@ export class ApplyOverrides { | |
| } | ||
|
|
||
| const originallyExists: string[] = []; | ||
| let missingPackages: string[] = []; | ||
| // We want to also add missing packages to the peer list as we know to resolve the version from the env anyway | ||
| const missingPackages = this.getMissingPackagesFromIssues(); | ||
|
|
||
| ['dependencies', 'devDependencies', 'peerDependencies'].forEach((field) => { | ||
| forEach(autoDetectOverrides[field], (pkgVal, pkgName) => { | ||
| this.applyAutoDetectedOverrideForPackage(field, pkgName, pkgVal, originallyExists, missingPackages); | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| private getMissingPackagesFromIssues(): string[] { | ||
| const missingData = this.issues.getIssueByName('MissingPackagesDependenciesOnFs')?.data as | ||
| | MissingPackagesData[] | ||
| | undefined; | ||
| if (missingData) { | ||
| missingPackages = uniq(missingData.map((d) => d.missingPackages).flat()); | ||
| if (!missingData) return []; | ||
| return uniq(missingData.map((d) => d.missingPackages).flat()); | ||
| } | ||
|
|
||
| private isPackageAutoDetected(pkgName: string, originallyExists: string[], missingPackages: string[]): boolean { | ||
| // Check component dependencies | ||
| const existsInAnyCompDeps = | ||
| this.allDependencies.dependencies.some((dep) => dep.packageName === pkgName) || | ||
| this.allDependencies.devDependencies.some((dep) => dep.packageName === pkgName) || | ||
| this.allDependencies.peerDependencies.some((dep) => dep.packageName === pkgName); | ||
|
|
||
| // Check originAllPackagesDependencies instead of allPackagesDependencies | ||
| // as it might be already removed at this point if it was set with "-" in runtime/dev | ||
| const existsInOriginalPkgDeps = | ||
| this.originAllPackagesDependencies.packageDependencies[pkgName] || | ||
| this.originAllPackagesDependencies.devPackageDependencies[pkgName] || | ||
| this.originAllPackagesDependencies.peerPackageDependencies[pkgName]; | ||
|
|
||
| // Check if it was originally exists in the component | ||
| // This handles policies like: dependencies: {"my-dep": "-"}, devDependencies: {"my-dep": "1.0.0"} | ||
| // where we might remove it before getting to devDeps | ||
| return ( | ||
| existsInAnyCompDeps || | ||
| !!existsInOriginalPkgDeps || | ||
| originallyExists.includes(pkgName) || | ||
| missingPackages.includes(pkgName) | ||
| ); | ||
| } | ||
|
|
||
| private applyAutoDetectedOverrideForPackage( | ||
| field: string, | ||
| pkgName: string, | ||
| pkgVal: string, | ||
| originallyExists: string[], | ||
| missingPackages: string[] | ||
| ): void { | ||
| if (this.overridesDependencies.shouldIgnorePeerPackage(pkgName)) { | ||
| return; | ||
| } | ||
| ['dependencies', 'devDependencies', 'peerDependencies'].forEach((field) => { | ||
| forEach(autoDetectOverrides[field], (pkgVal, pkgName) => { | ||
| if (this.overridesDependencies.shouldIgnorePeerPackage(pkgName)) { | ||
| return; | ||
| } | ||
|
|
||
| const existsInCompsDeps = this.allDependencies.dependencies.find((dep) => dep.packageName === pkgName); | ||
| const existsInCompsDevDeps = this.allDependencies.devDependencies.find((dep) => dep.packageName === pkgName); | ||
| const existsInCompsPeerDeps = this.allDependencies.peerDependencies.find((dep) => dep.packageName === pkgName); | ||
|
|
||
| // Validate it was auto detected, we only affect stuff that were detected | ||
| const isAutoDetected = | ||
| existsInCompsDeps || | ||
| existsInCompsDevDeps || | ||
| existsInCompsPeerDeps || | ||
| // We are checking originAllPackagesDependencies instead of allPackagesDependencies | ||
| // as it might be already removed from allPackagesDependencies at this point if it was set with | ||
| // "-" in runtime/dev | ||
| // in such case we still want to apply it here | ||
| this.originAllPackagesDependencies.packageDependencies[pkgName] || | ||
| this.originAllPackagesDependencies.devPackageDependencies[pkgName] || | ||
| this.originAllPackagesDependencies.peerPackageDependencies[pkgName] || | ||
| // Check if it was originally exists in the component | ||
| // as we might have a policy which looks like this: | ||
| // "components": { | ||
| // "dependencies": { | ||
| // "my-dep": "-" | ||
| // }, | ||
| // "devDependencies": { | ||
| // "my-dep": "1.0.0" | ||
| // }, | ||
| // } | ||
| // in that case we might remove it before getting to the devDeps then we will think that it wasn't required in the component | ||
| // which is incorrect | ||
| originallyExists.includes(pkgName) || | ||
| missingPackages.includes(pkgName); | ||
|
|
||
| if (!isAutoDetected) { | ||
| return; | ||
| } | ||
| originallyExists.push(pkgName); | ||
| const key = DepsKeysToAllPackagesDepsKeys[field]; | ||
| delete this.allPackagesDependencies[key][pkgName]; | ||
| // When changing peer dependency we want it to be stronger than the other types | ||
| if (field === 'peerDependencies') { | ||
| delete this.allPackagesDependencies.devPackageDependencies[pkgName]; | ||
| delete this.allPackagesDependencies.packageDependencies[pkgName]; | ||
| if (existsInCompsDeps) { | ||
| this.allDependencies.dependencies = this.allDependencies.dependencies.filter( | ||
| (dep) => dep.packageName !== pkgName | ||
| ); | ||
| } | ||
| if (existsInCompsDevDeps) { | ||
| this.allDependencies.devDependencies = this.allDependencies.devDependencies.filter( | ||
| (dep) => dep.packageName !== pkgName | ||
| ); | ||
| } | ||
| } | ||
| if (!this.isPackageAutoDetected(pkgName, originallyExists, missingPackages)) { | ||
| return; | ||
| } | ||
|
|
||
| // This was restored to fix an issue with a case where | ||
| // You have a package dep in env.jsonc under peers (like @testing-library/react) | ||
| // Then you change the env.jsonc and move it from peer to devDependencies | ||
| // the deps resolver data will be correct, but in the legacy data | ||
| // it will still be in the peerPackageDependencies, so we need to remove it from there | ||
| // to avoid having it in package.json as a peer dependency | ||
| // which then will affect the installation of the component | ||
| delete this.allPackagesDependencies.packageDependencies[pkgName]; | ||
| delete this.allPackagesDependencies.devPackageDependencies[pkgName]; | ||
| delete this.allPackagesDependencies.peerPackageDependencies[pkgName]; | ||
|
|
||
| // If it exists in comps deps / comp dev deps, we don't want to add it to the allPackagesDependencies | ||
| // as it will make the same dep both a dev and runtime dep | ||
| // since we are here only for auto detected deps, it means we already resolved the version correctly | ||
| // so we don't need to really modify the version | ||
| // also the version here might have a range (^ or ~ for example) so we can't | ||
| // just put it as is, as it is not valid for component deps to have range | ||
| if ( | ||
| pkgVal !== MANUALLY_REMOVE_DEPENDENCY && | ||
| ((!existsInCompsDeps && !existsInCompsDevDeps) || field === 'peerDependencies') | ||
| ) { | ||
| if ((existsInCompsDeps || existsInCompsDevDeps) && field === 'peerDependencies') { | ||
| const comp = (existsInCompsDeps ?? existsInCompsDevDeps) as Dependency; | ||
| comp.versionRange = pkgVal; | ||
| this.allDependencies.peerDependencies.push(comp); | ||
| } else { | ||
| this.allPackagesDependencies[key][pkgName] = pkgVal; | ||
| } | ||
| if (existsInCompsPeerDeps) { | ||
| existsInCompsPeerDeps.versionRange = pkgVal; | ||
| } | ||
| } | ||
| }); | ||
| }); | ||
| originallyExists.push(pkgName); | ||
|
|
||
| const existsInCompsDeps = this.allDependencies.dependencies.find((dep) => dep.packageName === pkgName); | ||
| const existsInCompsDevDeps = this.allDependencies.devDependencies.find((dep) => dep.packageName === pkgName); | ||
| const existsInCompsPeerDeps = this.allDependencies.peerDependencies.find((dep) => dep.packageName === pkgName); | ||
|
|
||
| // When changing peer dependency we want it to be stronger than the other types | ||
| if (field === 'peerDependencies') { | ||
| const shouldSkip = this.handlePeerDependencyOverride(pkgName, existsInCompsDeps, existsInCompsDevDeps); | ||
| if (shouldSkip) return; | ||
| } | ||
|
|
||
| // Clear package from all dependency types to avoid duplicates | ||
| // This fixes cases where a package moves between dep types in env.jsonc | ||
| this.removePackageFromAllDependencyTypes(pkgName); | ||
|
|
||
| this.applyPackageVersion(field, pkgName, pkgVal, existsInCompsDeps, existsInCompsDevDeps, existsInCompsPeerDeps); | ||
| } | ||
|
|
||
| /** | ||
| * Handle peer dependency specific logic. | ||
| * @returns true if the package should be skipped (not added to dependencies) | ||
| */ | ||
| private handlePeerDependencyOverride( | ||
| pkgName: string, | ||
| existsInCompsDeps: Dependency | undefined, | ||
| existsInCompsDevDeps: Dependency | undefined | ||
| ): boolean { | ||
| delete this.allPackagesDependencies.devPackageDependencies[pkgName]; | ||
| delete this.allPackagesDependencies.packageDependencies[pkgName]; | ||
|
|
||
| // @types/* packages should not be added to components at all when in env peers. | ||
| // When a component is installed in a workspace, its env is installed as well. | ||
| // Packages listed as peers in env.jsonc become runtime dependencies of the env itself, | ||
| // so they're always installed alongside the env. Since @types/* packages are only needed | ||
| // for TypeScript compilation (which the env handles), there's no need for components | ||
| // to have them in their own dependencies. | ||
| if (pkgName.startsWith('@types/')) { | ||
| delete this.allPackagesDependencies.peerPackageDependencies[pkgName]; | ||
| return true; | ||
| } | ||
|
Comment on lines
+661
to
+670
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1. Stale missing @types issue handlePeerDependencyOverride() removes @types/* packages from the component dependency maps and returns early, but does not remove the same package from an already-populated MissingPackagesDependenciesOnFs issue. This can incorrectly keep the component in a tag-blocking “missing packages” state even though @types/* was intentionally excluded from the component dependencies. Agent Prompt
|
||
|
|
||
| if (existsInCompsDeps) { | ||
| this.allDependencies.dependencies = this.allDependencies.dependencies.filter( | ||
| (dep) => dep.packageName !== pkgName | ||
| ); | ||
| } | ||
| if (existsInCompsDevDeps) { | ||
| this.allDependencies.devDependencies = this.allDependencies.devDependencies.filter( | ||
| (dep) => dep.packageName !== pkgName | ||
| ); | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| private removePackageFromAllDependencyTypes(pkgName: string): void { | ||
| delete this.allPackagesDependencies.packageDependencies[pkgName]; | ||
| delete this.allPackagesDependencies.devPackageDependencies[pkgName]; | ||
| delete this.allPackagesDependencies.peerPackageDependencies[pkgName]; | ||
| } | ||
|
|
||
| private applyPackageVersion( | ||
| field: string, | ||
| pkgName: string, | ||
| pkgVal: string, | ||
| existsInCompsDeps: Dependency | undefined, | ||
| existsInCompsDevDeps: Dependency | undefined, | ||
| existsInCompsPeerDeps: Dependency | undefined | ||
| ): void { | ||
| // If it exists in comps deps / comp dev deps, we don't want to add it to the allPackagesDependencies | ||
| // as it will make the same dep both a dev and runtime dep | ||
| // since we are here only for auto detected deps, it means we already resolved the version correctly | ||
| // so we don't need to really modify the version | ||
| // also the version here might have a range (^ or ~ for example) so we can't | ||
| // just put it as is, as it is not valid for component deps to have range | ||
| const shouldAddPackage = | ||
| pkgVal !== MANUALLY_REMOVE_DEPENDENCY && | ||
| ((!existsInCompsDeps && !existsInCompsDevDeps) || field === 'peerDependencies'); | ||
|
|
||
| if (!shouldAddPackage) return; | ||
|
|
||
| const key = DepsKeysToAllPackagesDepsKeys[field]; | ||
|
|
||
| if ((existsInCompsDeps || existsInCompsDevDeps) && field === 'peerDependencies') { | ||
| const comp = (existsInCompsDeps ?? existsInCompsDevDeps) as Dependency; | ||
| comp.versionRange = pkgVal; | ||
| this.allDependencies.peerDependencies.push(comp); | ||
| } else { | ||
| this.allPackagesDependencies[key][pkgName] = pkgVal; | ||
| } | ||
|
|
||
| if (existsInCompsPeerDeps) { | ||
| existsInCompsPeerDeps.versionRange = pkgVal; | ||
| } | ||
| } | ||
|
|
||
| private async applyAutoDetectedPeersFromEnvOnEnvItSelf(): Promise<void> { | ||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.