| 1 | import * as path from "path";
|
|---|
| 2 | import * as TryPath from "./try-path";
|
|---|
| 3 | import * as MappingEntry from "./mapping-entry";
|
|---|
| 4 | import * as Filesystem from "./filesystem";
|
|---|
| 5 |
|
|---|
| 6 | /**
|
|---|
| 7 | * Function that can match a path async
|
|---|
| 8 | */
|
|---|
| 9 | export interface MatchPathAsync {
|
|---|
| 10 | (
|
|---|
| 11 | requestedModule: string,
|
|---|
| 12 | readJson: Filesystem.ReadJsonAsync | undefined,
|
|---|
| 13 | fileExists: Filesystem.FileExistsAsync | undefined,
|
|---|
| 14 | extensions: ReadonlyArray<string> | undefined,
|
|---|
| 15 | callback: MatchPathAsyncCallback
|
|---|
| 16 | ): void;
|
|---|
| 17 | }
|
|---|
| 18 |
|
|---|
| 19 | export interface MatchPathAsyncCallback {
|
|---|
| 20 | (err?: Error, path?: string): void;
|
|---|
| 21 | }
|
|---|
| 22 |
|
|---|
| 23 | /**
|
|---|
| 24 | * See the sync version for docs.
|
|---|
| 25 | */
|
|---|
| 26 | export function createMatchPathAsync(
|
|---|
| 27 | absoluteBaseUrl: string,
|
|---|
| 28 | paths: { [key: string]: Array<string> },
|
|---|
| 29 | mainFields: string[] = ["main"],
|
|---|
| 30 | addMatchAll: boolean = true
|
|---|
| 31 | ): MatchPathAsync {
|
|---|
| 32 | const absolutePaths = MappingEntry.getAbsoluteMappingEntries(
|
|---|
| 33 | absoluteBaseUrl,
|
|---|
| 34 | paths,
|
|---|
| 35 | addMatchAll
|
|---|
| 36 | );
|
|---|
| 37 |
|
|---|
| 38 | return (
|
|---|
| 39 | requestedModule: string,
|
|---|
| 40 | readJson: Filesystem.ReadJsonAsync | undefined,
|
|---|
| 41 | fileExists: Filesystem.FileExistsAsync | undefined,
|
|---|
| 42 | extensions: ReadonlyArray<string> | undefined,
|
|---|
| 43 | callback: MatchPathAsyncCallback
|
|---|
| 44 | ) =>
|
|---|
| 45 | matchFromAbsolutePathsAsync(
|
|---|
| 46 | absolutePaths,
|
|---|
| 47 | requestedModule,
|
|---|
| 48 | readJson,
|
|---|
| 49 | fileExists,
|
|---|
| 50 | extensions,
|
|---|
| 51 | callback,
|
|---|
| 52 | mainFields
|
|---|
| 53 | );
|
|---|
| 54 | }
|
|---|
| 55 |
|
|---|
| 56 | /**
|
|---|
| 57 | * See the sync version for docs.
|
|---|
| 58 | */
|
|---|
| 59 | export function matchFromAbsolutePathsAsync(
|
|---|
| 60 | absolutePathMappings: ReadonlyArray<MappingEntry.MappingEntry>,
|
|---|
| 61 | requestedModule: string,
|
|---|
| 62 | readJson: Filesystem.ReadJsonAsync = Filesystem.readJsonFromDiskAsync,
|
|---|
| 63 | fileExists: Filesystem.FileExistsAsync = Filesystem.fileExistsAsync,
|
|---|
| 64 | extensions: ReadonlyArray<string> = Object.keys(require.extensions),
|
|---|
| 65 | callback: MatchPathAsyncCallback,
|
|---|
| 66 | mainFields: string[] = ["main"]
|
|---|
| 67 | ): void {
|
|---|
| 68 | const tryPaths = TryPath.getPathsToTry(
|
|---|
| 69 | extensions,
|
|---|
| 70 | absolutePathMappings,
|
|---|
| 71 | requestedModule
|
|---|
| 72 | );
|
|---|
| 73 |
|
|---|
| 74 | if (!tryPaths) {
|
|---|
| 75 | return callback();
|
|---|
| 76 | }
|
|---|
| 77 |
|
|---|
| 78 | findFirstExistingPath(
|
|---|
| 79 | tryPaths,
|
|---|
| 80 | readJson,
|
|---|
| 81 | fileExists,
|
|---|
| 82 | callback,
|
|---|
| 83 | 0,
|
|---|
| 84 | mainFields
|
|---|
| 85 | );
|
|---|
| 86 | }
|
|---|
| 87 |
|
|---|
| 88 | function findFirstExistingMainFieldMappedFile(
|
|---|
| 89 | packageJson: Filesystem.PackageJson,
|
|---|
| 90 | mainFields: string[],
|
|---|
| 91 | packageJsonPath: string,
|
|---|
| 92 | fileExistsAsync: Filesystem.FileExistsAsync,
|
|---|
| 93 | doneCallback: (err?: Error, filepath?: string) => void,
|
|---|
| 94 | index: number = 0
|
|---|
| 95 | ): void {
|
|---|
| 96 | if (index >= mainFields.length) {
|
|---|
| 97 | return doneCallback(undefined, undefined);
|
|---|
| 98 | }
|
|---|
| 99 |
|
|---|
| 100 | const tryNext = () =>
|
|---|
| 101 | findFirstExistingMainFieldMappedFile(
|
|---|
| 102 | packageJson,
|
|---|
| 103 | mainFields,
|
|---|
| 104 | packageJsonPath,
|
|---|
| 105 | fileExistsAsync,
|
|---|
| 106 | doneCallback,
|
|---|
| 107 | index + 1
|
|---|
| 108 | );
|
|---|
| 109 |
|
|---|
| 110 | const mainFieldMapping = packageJson[mainFields[index]];
|
|---|
| 111 | if (typeof mainFieldMapping !== "string") {
|
|---|
| 112 | // Skip mappings that are not pointers to replacement files
|
|---|
| 113 | return tryNext();
|
|---|
| 114 | }
|
|---|
| 115 |
|
|---|
| 116 | const mappedFilePath = path.join(
|
|---|
| 117 | path.dirname(packageJsonPath),
|
|---|
| 118 | mainFieldMapping
|
|---|
| 119 | );
|
|---|
| 120 | fileExistsAsync(mappedFilePath, (err?: Error, exists?: boolean) => {
|
|---|
| 121 | if (err) {
|
|---|
| 122 | return doneCallback(err);
|
|---|
| 123 | }
|
|---|
| 124 | if (exists) {
|
|---|
| 125 | return doneCallback(undefined, mappedFilePath);
|
|---|
| 126 | }
|
|---|
| 127 | return tryNext();
|
|---|
| 128 | });
|
|---|
| 129 | }
|
|---|
| 130 |
|
|---|
| 131 | // Recursive loop to probe for physical files
|
|---|
| 132 | function findFirstExistingPath(
|
|---|
| 133 | tryPaths: ReadonlyArray<TryPath.TryPath>,
|
|---|
| 134 | readJson: Filesystem.ReadJsonAsync,
|
|---|
| 135 | fileExists: Filesystem.FileExistsAsync,
|
|---|
| 136 | doneCallback: MatchPathAsyncCallback,
|
|---|
| 137 | index: number = 0,
|
|---|
| 138 | mainFields: string[] = ["main"]
|
|---|
| 139 | ): void {
|
|---|
| 140 | const tryPath = tryPaths[index];
|
|---|
| 141 | if (
|
|---|
| 142 | tryPath.type === "file" ||
|
|---|
| 143 | tryPath.type === "extension" ||
|
|---|
| 144 | tryPath.type === "index"
|
|---|
| 145 | ) {
|
|---|
| 146 | fileExists(tryPath.path, (err: Error, exists: boolean) => {
|
|---|
| 147 | if (err) {
|
|---|
| 148 | return doneCallback(err);
|
|---|
| 149 | }
|
|---|
| 150 | if (exists) {
|
|---|
| 151 | return doneCallback(undefined, TryPath.getStrippedPath(tryPath));
|
|---|
| 152 | }
|
|---|
| 153 | if (index === tryPaths.length - 1) {
|
|---|
| 154 | return doneCallback();
|
|---|
| 155 | }
|
|---|
| 156 | // Continue with the next path
|
|---|
| 157 | return findFirstExistingPath(
|
|---|
| 158 | tryPaths,
|
|---|
| 159 | readJson,
|
|---|
| 160 | fileExists,
|
|---|
| 161 | doneCallback,
|
|---|
| 162 | index + 1,
|
|---|
| 163 | mainFields
|
|---|
| 164 | );
|
|---|
| 165 | });
|
|---|
| 166 | } else if (tryPath.type === "package") {
|
|---|
| 167 | readJson(tryPath.path, (err, packageJson) => {
|
|---|
| 168 | if (err) {
|
|---|
| 169 | return doneCallback(err);
|
|---|
| 170 | }
|
|---|
| 171 | if (packageJson) {
|
|---|
| 172 | return findFirstExistingMainFieldMappedFile(
|
|---|
| 173 | packageJson,
|
|---|
| 174 | mainFields,
|
|---|
| 175 | tryPath.path,
|
|---|
| 176 | fileExists,
|
|---|
| 177 | (mainFieldErr?: Error, mainFieldMappedFile?: string) => {
|
|---|
| 178 | if (mainFieldErr) {
|
|---|
| 179 | return doneCallback(mainFieldErr);
|
|---|
| 180 | }
|
|---|
| 181 | if (mainFieldMappedFile) {
|
|---|
| 182 | return doneCallback(undefined, mainFieldMappedFile);
|
|---|
| 183 | }
|
|---|
| 184 |
|
|---|
| 185 | // No field in package json was a valid option. Continue with the next path.
|
|---|
| 186 | return findFirstExistingPath(
|
|---|
| 187 | tryPaths,
|
|---|
| 188 | readJson,
|
|---|
| 189 | fileExists,
|
|---|
| 190 | doneCallback,
|
|---|
| 191 | index + 1,
|
|---|
| 192 | mainFields
|
|---|
| 193 | );
|
|---|
| 194 | }
|
|---|
| 195 | );
|
|---|
| 196 | }
|
|---|
| 197 |
|
|---|
| 198 | // This is async code, we need to return unconditionally, otherwise the code still falls
|
|---|
| 199 | // through and keeps recursing. While this might work in general, libraries that use neo-async
|
|---|
| 200 | // like Webpack will actually not allow you to call the same callback twice.
|
|---|
| 201 | //
|
|---|
| 202 | // An example of where this caused issues:
|
|---|
| 203 | // https://github.com/dividab/tsconfig-paths-webpack-plugin/issues/11
|
|---|
| 204 | //
|
|---|
| 205 | // Continue with the next path
|
|---|
| 206 | return findFirstExistingPath(
|
|---|
| 207 | tryPaths,
|
|---|
| 208 | readJson,
|
|---|
| 209 | fileExists,
|
|---|
| 210 | doneCallback,
|
|---|
| 211 | index + 1,
|
|---|
| 212 | mainFields
|
|---|
| 213 | );
|
|---|
| 214 | });
|
|---|
| 215 | } else {
|
|---|
| 216 | TryPath.exhaustiveTypeException(tryPath.type);
|
|---|
| 217 | }
|
|---|
| 218 | }
|
|---|