-
Notifications
You must be signed in to change notification settings - Fork 877
Add package deduplication, --deduplicatePackages #2361
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
Changes from all commits
9b349c1
dc9841e
d81c18e
358bd71
ed7017c
f98b377
ac7cfc5
43f6e08
b99c0ff
e25f2bc
044eee2
d47b3a7
d7e8232
af6f313
03a3d8f
27d78af
cf0bc18
fb1e84f
614dafd
cae2fba
34155c9
68627e3
2bb7300
b966d60
9d0f1d2
a6956ef
15e0d64
8cbc494
2744db9
bb3e995
31b3a1e
a05a21e
ce80a21
013aa78
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -25,6 +25,7 @@ type parseTask struct { | |||||
| startedSubTasks bool | ||||||
| isForAutomaticTypeDirective bool | ||||||
| includeReason *FileIncludeReason | ||||||
| packageId module.PackageId | ||||||
|
|
||||||
| metadata ast.SourceFileMetaData | ||||||
| resolutionsInFile module.ModeAwareCache[*module.ResolvedModule] | ||||||
|
|
@@ -164,6 +165,7 @@ type resolvedRef struct { | |||||
| increaseDepth bool | ||||||
| elideOnDepth bool | ||||||
| includeReason *FileIncludeReason | ||||||
| packageId module.PackageId | ||||||
| } | ||||||
|
|
||||||
| func (t *parseTask) addSubTask(ref resolvedRef, libFile *LibFile) { | ||||||
|
|
@@ -174,6 +176,7 @@ func (t *parseTask) addSubTask(ref resolvedRef, libFile *LibFile) { | |||||
| increaseDepth: ref.increaseDepth, | ||||||
| elideOnDepth: ref.elideOnDepth, | ||||||
| includeReason: ref.includeReason, | ||||||
| packageId: ref.packageId, | ||||||
| } | ||||||
| t.subTasks = append(t.subTasks, subTask) | ||||||
| } | ||||||
|
|
@@ -190,6 +193,7 @@ type parseTaskData struct { | |||||
| mu sync.Mutex | ||||||
| lowestDepth int | ||||||
| startedSubTasks bool | ||||||
| packageId module.PackageId | ||||||
| } | ||||||
|
|
||||||
| func (w *filesParser) parse(loader *fileLoader, tasks []*parseTask) { | ||||||
|
|
@@ -220,6 +224,11 @@ func (w *filesParser) start(loader *fileLoader, tasks []*parseTask, depth int) { | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| // Propagate packageId to data if we have one and data doesn't yet | ||||||
| if data.packageId.Name == "" && task.packageId.Name != "" { | ||||||
| data.packageId = task.packageId | ||||||
| } | ||||||
|
|
||||||
| currentDepth := core.IfElse(task.increaseDepth, depth+1, depth) | ||||||
| if currentDepth < data.lowestDepth { | ||||||
| // If we're seeing this task at a lower depth than before, | ||||||
|
|
@@ -283,6 +292,14 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles { | |||||
| var sourceFilesFoundSearchingNodeModules collections.Set[tspath.Path] | ||||||
| libFilesMap := make(map[tspath.Path]*LibFile, libFileCount) | ||||||
|
|
||||||
| var redirectTargetsMap map[tspath.Path][]string | ||||||
| var deduplicatedPaths collections.Set[tspath.Path] | ||||||
| var packageIdToCanonicalPath map[module.PackageId]tspath.Path | ||||||
| if !loader.opts.Config.CompilerOptions().DeduplicatePackages.IsFalse() { | ||||||
|
Member
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.
Suggested change
(is this equivalent?)
Member
Author
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. It is, I waffled between the two and just chose the not false mode. Can followup PR this if you want (the PR automerged, oops) but we are generally inconsistent (the OrUnknown forms are "new")
Member
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. Not worth a followup IMO |
||||||
| redirectTargetsMap = make(map[tspath.Path][]string) | ||||||
| packageIdToCanonicalPath = make(map[module.PackageId]tspath.Path) | ||||||
| } | ||||||
|
|
||||||
| var collectFiles func(tasks []*parseTask, seen map[*parseTaskData]string) | ||||||
| collectFiles = func(tasks []*parseTask, seen map[*parseTaskData]string) { | ||||||
| for _, task := range tasks { | ||||||
|
|
@@ -331,8 +348,23 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles { | |||||
| for _, trace := range task.resolutionsTrace { | ||||||
| loader.opts.Host.Trace(trace.Message, trace.Args...) | ||||||
| } | ||||||
| if subTasks := task.subTasks; len(subTasks) > 0 { | ||||||
| collectFiles(subTasks, seen) | ||||||
|
|
||||||
| var existingCanonicalPath tspath.Path | ||||||
| if packageIdToCanonicalPath != nil && data.packageId.Name != "" { | ||||||
| if canonical, exists := packageIdToCanonicalPath[data.packageId]; exists { | ||||||
| redirectTargetsMap[canonical] = append(redirectTargetsMap[canonical], task.normalizedFilePath) | ||||||
| existingCanonicalPath = canonical | ||||||
| deduplicatedPaths.Add(task.path) | ||||||
| deduplicatedPaths.Add(canonical) | ||||||
| } else { | ||||||
| packageIdToCanonicalPath[data.packageId] = task.path | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| if existingCanonicalPath == "" { | ||||||
| if subTasks := task.subTasks; len(subTasks) > 0 { | ||||||
| collectFiles(subTasks, seen) | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| // Exclude automatic type directive tasks from include reason processing, | ||||||
|
|
@@ -350,6 +382,10 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles { | |||||
| continue | ||||||
| } | ||||||
| file := task.file | ||||||
| if existingCanonicalPath != "" { | ||||||
| file = filesByPath[existingCanonicalPath] | ||||||
| } | ||||||
|
|
||||||
| path := task.path | ||||||
|
|
||||||
| if len(task.processingDiagnostics) > 0 { | ||||||
|
|
@@ -365,7 +401,7 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles { | |||||
| if task.libFile != nil { | ||||||
| libFiles = append(libFiles, file) | ||||||
| libFilesMap[path] = task.libFile | ||||||
| } else { | ||||||
| } else if existingCanonicalPath == "" { | ||||||
| files = append(files, file) | ||||||
| } | ||||||
| filesByPath[path] = file | ||||||
|
|
@@ -424,6 +460,8 @@ func (w *filesParser) getProcessedFiles(loader *fileLoader) processedFiles { | |||||
| missingFiles: missingFiles, | ||||||
| includeProcessor: includeProcessor, | ||||||
| outputFileToProjectReferenceSource: outputFileToProjectReferenceSource, | ||||||
| redirectTargetsMap: redirectTargetsMap, | ||||||
| deduplicatedPaths: deduplicatedPaths, | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
|
|
||||||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| package fourslash_test | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| "github.com/microsoft/typescript-go/internal/fourslash" | ||
| "github.com/microsoft/typescript-go/internal/testutil" | ||
| ) | ||
|
|
||
| func TestDuplicatePackageServices_fileChanges(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| defer testutil.RecoverAndFail(t, "Panic on fourslash test") | ||
| const content = `// @noImplicitReferences: true | ||
| // @Filename: /node_modules/a/index.d.ts | ||
| import X from "x"; | ||
| export function a(x: X): void; | ||
| // @Filename: /node_modules/a/node_modules/x/index.d.ts | ||
| export default class /*defAX*/X { | ||
| private x: number; | ||
| } | ||
| // @Filename: /node_modules/a/node_modules/x/package.json | ||
| { "name": "x", "version": "1.2./*aVersionPatch*/3" } | ||
| // @Filename: /node_modules/b/index.d.ts | ||
| import X from "x"; | ||
| export const b: X; | ||
| // @Filename: /node_modules/b/node_modules/x/index.d.ts | ||
| export default class /*defBX*/X { | ||
| private x: number; | ||
| } | ||
| // @Filename: /node_modules/b/node_modules/x/package.json | ||
| { "name": "x", "version": "1.2./*bVersionPatch*/3" } | ||
| // @Filename: /src/a.ts | ||
| import { a } from "a"; | ||
| import { b } from "b"; | ||
| a(/*error*/b);` | ||
| f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) | ||
| defer done() | ||
|
|
||
| f.GoToFile(t, "/src/a.ts") | ||
| f.VerifyNumberOfErrorsInCurrentFile(t, 0) | ||
|
|
||
| testChangeAndChangeBack := func(versionPatch string, def string) { | ||
| // Insert "4" after the version patch marker, changing version from 1.2.3 to 1.2.43 | ||
| f.GoToMarker(t, versionPatch) | ||
| f.Insert(t, "4") | ||
|
|
||
| // Insert a space after the definition marker to trigger a recheck | ||
| f.GoToMarker(t, def) | ||
| f.Insert(t, " ") | ||
|
|
||
| // No longer have identical packageId, so we get errors. | ||
| f.VerifyErrorExistsAfterMarker(t, "error") | ||
|
|
||
| // Undo the changes | ||
| f.GoToMarker(t, versionPatch) | ||
| f.DeleteAtCaret(t, 1) | ||
| f.GoToMarker(t, def) | ||
| f.DeleteAtCaret(t, 1) | ||
|
|
||
| // Back to being identical. | ||
| f.GoToFile(t, "/src/a.ts") | ||
| f.VerifyNumberOfErrorsInCurrentFile(t, 0) | ||
| } | ||
|
|
||
| testChangeAndChangeBack("aVersionPatch", "defAX") | ||
| testChangeAndChangeBack("bVersionPatch", "defBX") | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In strada we didnt get the package ids to evaluate on already found file - but i think this is better behavior.. (that is if file was root file and then added through import (with package id - it wasnt added to "redirect" map) But just noting it for change in behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this was something I realized but didn't note, and I feel like it's a better behavior too.