-
Notifications
You must be signed in to change notification settings - Fork 102
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
Ignore field name mappings in package.json files that are not paths of existing files #46
Changes from all commits
c75868d
5ecdd96
5745558
8946b8f
ceed9be
3101688
6792de2
a83e0dd
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 |
---|---|---|
|
@@ -2,7 +2,6 @@ import * as path from "path"; | |
import * as TryPath from "./try-path"; | ||
import * as MappingEntry from "./mapping-entry"; | ||
import * as Filesystem from "./filesystem"; | ||
import { getPrioritizedMainFieldName } from "./match-path-sync"; | ||
|
||
/** | ||
* Function that can match a path async | ||
|
@@ -84,6 +83,49 @@ export function matchFromAbsolutePathsAsync( | |
); | ||
} | ||
|
||
function findFirstExistingMainFieldMappedFile( | ||
packageJson: Filesystem.PackageJson, | ||
mainFields: string[], | ||
packageJsonPath: string, | ||
fileExistsAsync: Filesystem.FileExistsAsync, | ||
doneCallback: (err?: Error, filepath?: string) => void, | ||
index: number = 0 | ||
): void { | ||
if (index >= mainFields.length) { | ||
return doneCallback(undefined, undefined); | ||
} | ||
|
||
const tryNext = () => | ||
findFirstExistingMainFieldMappedFile( | ||
packageJson, | ||
mainFields, | ||
packageJsonPath, | ||
fileExistsAsync, | ||
doneCallback, | ||
index + 1 | ||
); | ||
|
||
const mainFieldMapping = packageJson[mainFields[index]]; | ||
if (typeof mainFieldMapping !== "string") { | ||
// Skip mappings that are not pointers to replacement files | ||
return tryNext(); | ||
} | ||
|
||
const mappedFilePath = path.join( | ||
path.dirname(packageJsonPath), | ||
mainFieldMapping | ||
); | ||
fileExistsAsync(mappedFilePath, (err?: Error, exists?: boolean) => { | ||
if (err) { | ||
return doneCallback(err); | ||
} | ||
if (exists) { | ||
return doneCallback(undefined, mappedFilePath); | ||
} | ||
return tryNext(); | ||
}); | ||
} | ||
|
||
// Recursive loop to probe for physical files | ||
function findFirstExistingPath( | ||
tryPaths: ReadonlyArray<TryPath.TryPath>, | ||
|
@@ -125,48 +167,53 @@ function findFirstExistingPath( | |
if (err) { | ||
return doneCallback(err); | ||
} | ||
const mainFieldName = getPrioritizedMainFieldName( | ||
packageJson, | ||
mainFields | ||
); | ||
if (mainFieldName) { | ||
const file = path.join( | ||
path.dirname(tryPath.path), | ||
packageJson[mainFieldName] | ||
); | ||
fileExists(file, (err2, exists) => { | ||
if (err2) { | ||
return doneCallback(err2); | ||
} | ||
if (exists) { | ||
// Not sure why we don't just return the full path? Why strip it? | ||
return doneCallback(undefined, Filesystem.removeExtension(file)); | ||
} | ||
// Continue with the next path | ||
return findFirstExistingPath( | ||
tryPaths, | ||
readJson, | ||
fileExists, | ||
doneCallback, | ||
index + 1, | ||
mainFields | ||
); | ||
}); | ||
} else { | ||
// This is async code, we need to return in an else-branch otherwise the code still falls through and keeps recursing. | ||
// While this might work in general, libraries that use neo-async like Webpack will actually not allow you to call the same callback twice. | ||
// An example of where this caused issues: https://github.com/dividab/tsconfig-paths-webpack-plugin/issues/11 | ||
|
||
// Continue with the next path | ||
return findFirstExistingPath( | ||
tryPaths, | ||
readJson, | ||
if (packageJson) { | ||
return findFirstExistingMainFieldMappedFile( | ||
packageJson, | ||
mainFields, | ||
tryPath.path, | ||
fileExists, | ||
doneCallback, | ||
index + 1, | ||
mainFields | ||
(mainFieldErr?: Error, mainFieldMappedFile?: string) => { | ||
if (mainFieldErr) { | ||
return doneCallback(mainFieldErr); | ||
} | ||
if (mainFieldMappedFile) { | ||
// Not sure why we don't just return the full path? Why strip it? | ||
return doneCallback( | ||
undefined, | ||
Filesystem.removeExtension(mainFieldMappedFile) | ||
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. As the comment above asks, why are we actually removing the extension? 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. IIRC I added this comment some time ago when doing the initial work on the async version. @Jontem do you remember why the stripping is done? Or do you also think we can just return the raw filename? 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. The code has been rewritten many times since i looked at it and i don't recall the reason. 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. @christoffer I think you can go ahead and remove the stripping of the extension. 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. @jonaskello Hi Jonas. Sorry for the delay, PTOs and what-not came in the way. Would you be OK if we remove the extension in a separate diff? I feel that it's unrelated to the change I'm introducing here. And since it might have side effects, it would be nice to keep that change separate. 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. My original comment was probably unclear. I was only asking out of curiosity, not as a way to ask if I should remove it now :) 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. @christoffer Sure, doing it in a separate diff makes sense. |
||
); | ||
} | ||
|
||
// No field in package json was a valid option. Continue with the next path. | ||
return findFirstExistingPath( | ||
tryPaths, | ||
readJson, | ||
fileExists, | ||
doneCallback, | ||
index + 1, | ||
mainFields | ||
); | ||
} | ||
); | ||
} | ||
|
||
// This is async code, we need to return unconditionally, otherwise the code still falls | ||
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. @jonaskello Moved it here, let me know what you thing of the formulation. Changed "return in an else-branch otherwise" -> "return unconditionally, otherwise" |
||
// through and keeps recursing. While this might work in general, libraries that use neo-async | ||
// like Webpack will actually not allow you to call the same callback twice. | ||
// | ||
// An example of where this caused issues: | ||
// https://github.com/dividab/tsconfig-paths-webpack-plugin/issues/11 | ||
// | ||
// Continue with the next path | ||
return findFirstExistingPath( | ||
tryPaths, | ||
readJson, | ||
fileExists, | ||
doneCallback, | ||
index + 1, | ||
mainFields | ||
); | ||
}); | ||
} else { | ||
TryPath.exhaustiveTypeException(tryPath.type); | ||
|
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.
Should we keep this comment somewhere or is it no longer relevant?
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.
Good point, I'll include it at the bottom return statement. Thanks for pointing it out!