123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- import * as path from "path";
- import * as TryPath from "./try-path";
- import * as MappingEntry from "./mapping-entry";
- import * as Filesystem from "./filesystem";
- /**
- * Function that can match a path async
- */
- export interface MatchPathAsync {
- (
- requestedModule: string,
- readJson: Filesystem.ReadJsonAsync | undefined,
- fileExists: Filesystem.FileExistsAsync | undefined,
- extensions: ReadonlyArray<string> | undefined,
- callback: MatchPathAsyncCallback
- ): void;
- }
- export interface MatchPathAsyncCallback {
- (err?: Error, path?: string): void;
- }
- /**
- * See the sync version for docs.
- */
- export function createMatchPathAsync(
- absoluteBaseUrl: string,
- paths: { [key: string]: Array<string> },
- mainFields: string[] = ["main"],
- addMatchAll: boolean = true
- ): MatchPathAsync {
- const absolutePaths = MappingEntry.getAbsoluteMappingEntries(
- absoluteBaseUrl,
- paths,
- addMatchAll
- );
- return (
- requestedModule: string,
- readJson: Filesystem.ReadJsonAsync | undefined,
- fileExists: Filesystem.FileExistsAsync | undefined,
- extensions: ReadonlyArray<string> | undefined,
- callback: MatchPathAsyncCallback
- ) =>
- matchFromAbsolutePathsAsync(
- absolutePaths,
- requestedModule,
- readJson,
- fileExists,
- extensions,
- callback,
- mainFields
- );
- }
- /**
- * See the sync version for docs.
- */
- export function matchFromAbsolutePathsAsync(
- absolutePathMappings: ReadonlyArray<MappingEntry.MappingEntry>,
- requestedModule: string,
- readJson: Filesystem.ReadJsonAsync = Filesystem.readJsonFromDiskAsync,
- fileExists: Filesystem.FileExistsAsync = Filesystem.fileExistsAsync,
- extensions: ReadonlyArray<string> = Object.keys(require.extensions),
- callback: MatchPathAsyncCallback,
- mainFields: string[] = ["main"]
- ): void {
- const tryPaths = TryPath.getPathsToTry(
- extensions,
- absolutePathMappings,
- requestedModule
- );
- if (!tryPaths) {
- return callback();
- }
- findFirstExistingPath(
- tryPaths,
- readJson,
- fileExists,
- callback,
- 0,
- mainFields
- );
- }
- 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>,
- readJson: Filesystem.ReadJsonAsync,
- fileExists: Filesystem.FileExistsAsync,
- doneCallback: MatchPathAsyncCallback,
- index: number = 0,
- mainFields: string[] = ["main"]
- ): void {
- const tryPath = tryPaths[index];
- if (
- tryPath.type === "file" ||
- tryPath.type === "extension" ||
- tryPath.type === "index"
- ) {
- fileExists(tryPath.path, (err: Error, exists: boolean) => {
- if (err) {
- return doneCallback(err);
- }
- if (exists) {
- return doneCallback(undefined, TryPath.getStrippedPath(tryPath));
- }
- if (index === tryPaths.length - 1) {
- return doneCallback();
- }
- // Continue with the next path
- return findFirstExistingPath(
- tryPaths,
- readJson,
- fileExists,
- doneCallback,
- index + 1,
- mainFields
- );
- });
- } else if (tryPath.type === "package") {
- readJson(tryPath.path, (err, packageJson) => {
- if (err) {
- return doneCallback(err);
- }
- if (packageJson) {
- return findFirstExistingMainFieldMappedFile(
- packageJson,
- mainFields,
- tryPath.path,
- fileExists,
- (mainFieldErr?: Error, mainFieldMappedFile?: string) => {
- if (mainFieldErr) {
- return doneCallback(mainFieldErr);
- }
- if (mainFieldMappedFile) {
- return doneCallback(undefined, mainFieldMappedFile);
- }
- // 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
- // 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);
- }
- }
|