Skip to content
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

Add support for nested main field selectors #218

Merged
merged 2 commits into from Aug 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 5 additions & 5 deletions README.md
Expand Up @@ -146,7 +146,7 @@ The public API consists of these functions:
export interface ExplicitParams {
baseUrl: string;
paths: { [key: string]: Array<string> };
mainFields?: Array<string>;
mainFields?: (string | string[])[];
addMatchAll?: boolean;
cwd?: string;
}
Expand Down Expand Up @@ -202,14 +202,14 @@ export interface MatchPath {
* 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.
* @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[] = ["main"],
mainFields: (string | string[])[] = ["main"],
addMatchAll: boolean = true
): MatchPath {
```
Expand All @@ -226,7 +226,7 @@ The `createMatchPath` function will create a function that can match paths. It a
* @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.
* @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(
Expand All @@ -235,7 +235,7 @@ export function matchFromAbsolutePaths(
readJson: Filesystem.ReadJsonSync = Filesystem.readJsonFromDiskSync,
fileExists: Filesystem.FileExistsSync = Filesystem.fileExistsSync,
extensions: Array<string> = Object.keys(require.extensions),
mainFields: string[] = ["main"]
mainFields: (string | string[])[] = ["main"]
): string | undefined {
```

Expand Down
13 changes: 12 additions & 1 deletion src/__tests__/data/match-path-data.ts
Expand Up @@ -7,7 +7,7 @@ export interface OneTest {
readonly skip?: boolean;
readonly absoluteBaseUrl: string;
readonly paths: { [key: string]: Array<string> };
readonly mainFields?: string[];
readonly mainFields?: (string | string[])[];
readonly addMatchAll?: boolean;
readonly existingFiles: ReadonlyArray<string>;
readonly requestedModule: string;
Expand Down Expand Up @@ -149,6 +149,17 @@ export const tests: ReadonlyArray<OneTest> = [
extensions: [".ts", ".js"],
expectedPath: join("/root", "location", "mylibjs", "kalle.js"),
},
{
name: "should resolve nested main fields",
absoluteBaseUrl: "/root/",
paths: { "lib/*": ["location/*"] },
mainFields: [["esnext", "main"]],
packageJson: { esnext: { main: "./main.js" } },
existingFiles: [join("/root", "location", "mylibjs", "main.js")],
extensions: [".ts", ".js"],
requestedModule: "lib/mylibjs",
expectedPath: join("/root", "location", "mylibjs", "main.js"),
},
{
name: "should ignore advanced field mappings in package.json",
absoluteBaseUrl: "/root/",
Expand Down
4 changes: 2 additions & 2 deletions src/config-loader.ts
Expand Up @@ -4,7 +4,7 @@ import * as path from "path";
export interface ExplicitParams {
baseUrl: string;
paths: { [key: string]: Array<string> };
mainFields?: Array<string>;
mainFields?: (string | string[])[];
addMatchAll?: boolean;
}

Expand All @@ -24,7 +24,7 @@ export interface ConfigLoaderSuccessResult {
baseUrl?: string;
absoluteBaseUrl: string;
paths: { [key: string]: Array<string> };
mainFields?: Array<string>;
mainFields?: (string | string[])[];
addMatchAll?: boolean;
}

Expand Down
2 changes: 1 addition & 1 deletion src/filesystem.ts
Expand Up @@ -4,7 +4,7 @@ import * as fs from "fs";
* Typing for the fields of package.json we care about
*/
export interface PackageJson {
[key: string]: string;
[key: string]: string | PackageJson;
}

/**
Expand Down
14 changes: 9 additions & 5 deletions src/match-path-async.ts
Expand Up @@ -27,7 +27,7 @@ export interface MatchPathAsyncCallback {
export function createMatchPathAsync(
absoluteBaseUrl: string,
paths: { [key: string]: Array<string> },
mainFields: string[] = ["main"],
mainFields: (string | string[])[] = ["main"],
addMatchAll: boolean = true
): MatchPathAsync {
const absolutePaths = MappingEntry.getAbsoluteMappingEntries(
Expand Down Expand Up @@ -64,7 +64,7 @@ export function matchFromAbsolutePathsAsync(
fileExists: Filesystem.FileExistsAsync = Filesystem.fileExistsAsync,
extensions: ReadonlyArray<string> = Object.keys(require.extensions),
callback: MatchPathAsyncCallback,
mainFields: string[] = ["main"]
mainFields: (string | string[])[] = ["main"]
): void {
const tryPaths = TryPath.getPathsToTry(
extensions,
Expand All @@ -88,7 +88,7 @@ export function matchFromAbsolutePathsAsync(

function findFirstExistingMainFieldMappedFile(
packageJson: Filesystem.PackageJson,
mainFields: string[],
mainFields: (string | string[])[],
packageJsonPath: string,
fileExistsAsync: Filesystem.FileExistsAsync,
doneCallback: (err?: Error, filepath?: string) => void,
Expand All @@ -108,7 +108,11 @@ function findFirstExistingMainFieldMappedFile(
index + 1
);

const mainFieldMapping = packageJson[mainFields[index]];
const mainFieldSelector = mainFields[index];
const mainFieldMapping =
typeof mainFieldSelector === "string"
? packageJson[mainFieldSelector]
: mainFieldSelector.reduce((obj, key) => obj[key], packageJson);
if (typeof mainFieldMapping !== "string") {
// Skip mappings that are not pointers to replacement files
return tryNext();
Expand Down Expand Up @@ -136,7 +140,7 @@ function findFirstExistingPath(
fileExists: Filesystem.FileExistsAsync,
doneCallback: MatchPathAsyncCallback,
index: number = 0,
mainFields: string[] = ["main"]
mainFields: (string | string[])[] = ["main"]
): void {
const tryPath = tryPaths[index];
if (
Expand Down
19 changes: 11 additions & 8 deletions src/match-path-sync.ts
Expand Up @@ -20,14 +20,14 @@ export interface MatchPath {
*
* @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.
* @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[] = ["main"],
mainFields: (string | string[])[] = ["main"],
addMatchAll: boolean = true
): MatchPath {
const absolutePaths = MappingEntry.getAbsoluteMappingEntries(
Expand Down Expand Up @@ -60,7 +60,7 @@ export function createMatchPath(
* @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.
* @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(
Expand All @@ -69,7 +69,7 @@ export function matchFromAbsolutePaths(
readJson: Filesystem.ReadJsonSync = Filesystem.readJsonFromDiskSync,
fileExists: Filesystem.FileExistsSync = Filesystem.fileExistsSync,
extensions: Array<string> = Object.keys(require.extensions),
mainFields: string[] = ["main"]
mainFields: (string | string[])[] = ["main"]
): string | undefined {
const tryPaths = TryPath.getPathsToTry(
extensions,
Expand All @@ -86,13 +86,16 @@ export function matchFromAbsolutePaths(

function findFirstExistingMainFieldMappedFile(
packageJson: Filesystem.PackageJson,
mainFields: string[],
mainFields: (string | string[])[],
packageJsonPath: string,
fileExists: Filesystem.FileExistsSync
): string | undefined {
for (let index = 0; index < mainFields.length; index++) {
const mainFieldName = mainFields[index];
const candidateMapping = packageJson[mainFieldName];
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),
Expand All @@ -111,7 +114,7 @@ function findFirstExistingPath(
tryPaths: ReadonlyArray<TryPath.TryPath>,
readJson: Filesystem.ReadJsonSync = Filesystem.readJsonFromDiskSync,
fileExists: Filesystem.FileExistsSync,
mainFields: string[] = ["main"]
mainFields: (string | string[])[] = ["main"]
): string | undefined {
for (const tryPath of tryPaths) {
if (
Expand Down