-
Notifications
You must be signed in to change notification settings - Fork 973
Fix module resolver bug exposed by malformed package.json import patterns #2680
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
Merged
+90
−2
Merged
Changes from 7 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
df286f8
Initial plan
Copilot a4781f7
Fix crash with malformed package.json imports mapping
Copilot 7dee5c1
Address code review comments
Copilot 0edcafc
Add fix summary documentation
Copilot 2b730dc
Remove investigation docs per code review
Copilot 90aa27c
Fix root cause: resolvedUsingTsExtension should check module specifier
Copilot ab2810c
Restore panics in checker.go per code review
Copilot 77a5486
Fix resolvedUsingTsExtension to check both conditions
Copilot 934d7c5
Simplify resolvedUsingTsExtension logic per code review
Copilot eb80f5f
Add test baselines for packageJsonImportsMalformedPattern
Copilot c3031cc
Add test for wildcard matching TS extension in module specifier
Copilot 91ff619
Add TryExtractTSExtensionLoose for best-effort extraction
Copilot 60d385b
Move tryExtractTSExtensionLoose to checker.go per code review
Copilot 85d2d5e
Address code review: inline helper, add directives, rename tests
Copilot ef63974
Merge branch 'main' into copilot/fix-package-json-imports
andrewbranch 1c72bd8
Delete unused baseline
andrewbranch 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
30 changes: 30 additions & 0 deletions
30
testdata/tests/cases/compiler/packageJsonImportsMalformedPattern.ts
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 |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| // @module: nodenext | ||
| // @moduleResolution: nodenext | ||
| // @filename: src/a.ts | ||
| import * as b from "#/b."; | ||
|
|
||
| b.foo(); | ||
|
|
||
| // @filename: src/b.ts | ||
| export function foo() {} | ||
|
|
||
| // @filename: package.json | ||
| { | ||
| "imports": { | ||
| "#/*": { | ||
| "types": "./src/*ts", | ||
| "default": "./dist/*js" | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // @filename: tsconfig.json | ||
| { | ||
| "compilerOptions": { | ||
| "module": "nodenext", | ||
| "moduleResolution": "nodenext", | ||
| "rootDir": "src", | ||
| "outDir": "dist" | ||
| }, | ||
| "include": ["src"] | ||
| } |
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.
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.
resolvedUsingTsExtensionmeans the combination of two things:.tsor similar.tsending was used to match a.tsfile on disk, as opposed to matching a package name, directory name,"exports"key or pattern, or whatever. For patterns, it’s tricky, becauseimport "pkg/foo.ts"qualifies under"./*": "./*"but not under"./*.ts": "./*.ts".This change is wrong because it only checks (1). (There would be no need to implement this logic in the module resolver if that were the only criteria, since it can be trivially checked in the checker.)
The original logic here was trying to say "if we're trying to look up a
.tsfile but the package.json field we’re looking up didn’t explicitly mention a.tsextension, it must have come from the module specifier mapping onto a pattern in this field." From the failing test, we know that can be only partially true, where part of the extension comes from the module specifier and part comes from the field.Off the top of my head, it seems like
resolvedUsingTsExtensionshould only betrueif both (a)r.namehas a TS extension and (b)packageJSONValueends in*. But you’ll have to verify by running the full test suite.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.
Fixed in 77a5486. Now checking both conditions:
r.namehas a TS extension (module specifier literally ends in.ts)packageJSONValueends with*(extension was used to match, not from the pattern itself)This correctly distinguishes
import "pkg/foo.ts"under pattern./*from./*.ts.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.
@copilot slight correction, consider mapping
import "pkg/foo.ts.omg"through"./*.omg": "./*": this, tragically, should result inresolvedUsingTsExtension: trueeven though the module specifier does not end in.ts. I think maybe you don’t need to checkr.nameat all; it might be enough to say that if the field ended in*and the candidate file ended in.ts, that star must have matched a TS extension... if that still leaves some holes, there will need to be a more complicated plumbing through of which part of the pattern matched the module specifier, which we can tackle separately from the crash fix.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.
Fixed in 934d7c5. Simplified to only check:
*(packageJSONValue)This correctly handles
import "pkg/foo.ts.omg"with pattern"./*.omg": "./*"(resolves to true, since * matched .ts) without needing to check r.name.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.
@copilot add a new test case for this
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.
@copilot No, I think we should make an ad-hoc utility function that uses
strings.Containsto look for thepath.SupportedTSExtensionsFlatin the module specifier to do a best-effort extraction. It's possible that function will misidentify which extension matched the star in an insane case like"foo.ts.mts.cts.oops"but the truth is nobody is ever going to hit this case in real life so it doesn't matter. Also the error message will still say "An import path cannot end with ..." which mislabels the problem in this case, but again, it's not worth carving out a new error message for this. Not crashing + issuing an error is close enough. But we do need an error here.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.
Implemented in 91ff619. Created
TryExtractTSExtensionLoosethat usesstrings.Containsto do best-effort extraction. The checker now reports an error for cases like"#/foo.ts.omg"instead of skipping or panicking. The error message says "can only end with" which is slightly misleading, but acceptable for these rare edge cases.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.
I don't like the name, and I don't want it exported from tspath. Just put it in checker.go near where it's used
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.
@copilot
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.
Fixed in 60d385b. Moved the function to
checker.goas an unexported helpertryExtractTSExtensionLooseand placed it near where it's used.