-
Notifications
You must be signed in to change notification settings - Fork 104
/
match-path-sync.ts
146 lines (137 loc) · 4.58 KB
/
match-path-sync.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import * as path from "path";
import * as Filesystem from "./filesystem";
import * as MappingEntry from "./mapping-entry";
import * as TryPath from "./try-path";
/**
* Function that can match a path
*/
export interface MatchPath {
(
requestedModule: string,
readJson?: Filesystem.ReadJsonSync,
fileExists?: (name: string) => boolean,
extensions?: ReadonlyArray<string>
): string | undefined;
}
/**
* Creates a function that can resolve paths according to tsconfig paths property.
*
* @param absoluteBaseUrl Absolute version of baseUrl as specified in tsconfig.
* @param paths The paths as specified in tsconfig.
* @param mainFields A list of package.json field names to try when resolving module files. Select a nested field using an array of field names.
* @param addMatchAll Add a match-all "*" rule if none is present
* @returns a function that can resolve paths.
*/
export function createMatchPath(
absoluteBaseUrl: string,
paths: { [key: string]: Array<string> },
mainFields: (string | string[])[] = ["main"],
addMatchAll: boolean = true
): MatchPath {
const absolutePaths = MappingEntry.getAbsoluteMappingEntries(
absoluteBaseUrl,
paths,
addMatchAll
);
return (
requestedModule: string,
readJson?: Filesystem.ReadJsonSync,
fileExists?: Filesystem.FileExistsSync,
extensions?: Array<string>
) =>
matchFromAbsolutePaths(
absolutePaths,
requestedModule,
readJson,
fileExists,
extensions,
mainFields
);
}
/**
* Finds a path from tsconfig that matches a module load request.
*
* @param absolutePathMappings The paths to try as specified in tsconfig but resolved to absolute form.
* @param requestedModule The required module name.
* @param readJson Function that can read json from a path (useful for testing).
* @param fileExists Function that checks for existence of a file at a path (useful for testing).
* @param extensions File extensions to probe for (useful for testing).
* @param mainFields A list of package.json field names to try when resolving module files. Select a nested field using an array of field names.
* @returns the found path, or undefined if no path was found.
*/
export function matchFromAbsolutePaths(
absolutePathMappings: ReadonlyArray<MappingEntry.MappingEntry>,
requestedModule: string,
readJson: Filesystem.ReadJsonSync = Filesystem.readJsonFromDiskSync,
fileExists: Filesystem.FileExistsSync = Filesystem.fileExistsSync,
extensions: Array<string> = Object.keys(require.extensions),
mainFields: (string | string[])[] = ["main"]
): string | undefined {
const tryPaths = TryPath.getPathsToTry(
extensions,
absolutePathMappings,
requestedModule
);
if (!tryPaths) {
return undefined;
}
return findFirstExistingPath(tryPaths, readJson, fileExists, mainFields);
}
function findFirstExistingMainFieldMappedFile(
packageJson: Filesystem.PackageJson,
mainFields: (string | string[])[],
packageJsonPath: string,
fileExists: Filesystem.FileExistsSync
): string | undefined {
for (let index = 0; index < mainFields.length; index++) {
const mainFieldSelector = mainFields[index];
const candidateMapping =
typeof mainFieldSelector === "string"
? packageJson[mainFieldSelector]
: mainFieldSelector.reduce((obj, key) => obj[key], packageJson);
if (candidateMapping && typeof candidateMapping === "string") {
const candidateFilePath = path.join(
path.dirname(packageJsonPath),
candidateMapping
);
if (fileExists(candidateFilePath)) {
return candidateFilePath;
}
}
}
return undefined;
}
function findFirstExistingPath(
tryPaths: ReadonlyArray<TryPath.TryPath>,
readJson: Filesystem.ReadJsonSync = Filesystem.readJsonFromDiskSync,
fileExists: Filesystem.FileExistsSync,
mainFields: (string | string[])[] = ["main"]
): string | undefined {
for (const tryPath of tryPaths) {
if (
tryPath.type === "file" ||
tryPath.type === "extension" ||
tryPath.type === "index"
) {
if (fileExists(tryPath.path)) {
return TryPath.getStrippedPath(tryPath);
}
} else if (tryPath.type === "package") {
const packageJson: Filesystem.PackageJson = readJson(tryPath.path);
if (packageJson) {
const mainFieldMappedFile = findFirstExistingMainFieldMappedFile(
packageJson,
mainFields,
tryPath.path,
fileExists
);
if (mainFieldMappedFile) {
return mainFieldMappedFile;
}
}
} else {
TryPath.exhaustiveTypeException(tryPath.type);
}
}
return undefined;
}