- Sponsor
-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(paths): createPathsMatcher (#5)
1 parent
48c2df8
commit b27dd16
Showing
12 changed files
with
424 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import path from 'path'; | ||
import type { TsConfigResult } from '../types'; | ||
import { | ||
assertStarCount, | ||
isRelativePathPattern, | ||
parsePattern, | ||
isPatternMatch, | ||
} from './utils'; | ||
import type { StarPattern, PathEntry } from './types'; | ||
|
||
function parsePaths( | ||
paths: Partial<Record<string, string[]>>, | ||
baseUrl: string | undefined, | ||
absoluteBaseUrl: string, | ||
) { | ||
return Object.entries(paths).map(([pattern, substitutions]) => { | ||
assertStarCount(pattern, `Pattern '${pattern}' can have at most one '*' character.`); | ||
|
||
return { | ||
pattern: parsePattern(pattern), | ||
substitutions: substitutions!.map((substitution) => { | ||
assertStarCount( | ||
substitution, | ||
`Substitution '${substitution}' in pattern '${pattern}' can have at most one '*' character.`, | ||
); | ||
|
||
if (!substitution.startsWith('./') && !baseUrl) { | ||
throw new Error('Non-relative paths are not allowed when \'baseUrl\' is not set. Did you forget a leading \'./\'?'); | ||
} | ||
|
||
return path.join(absoluteBaseUrl, substitution); | ||
}), | ||
} as PathEntry<string | StarPattern>; | ||
}); | ||
} | ||
|
||
/** | ||
* Reference: | ||
* https://github.com/microsoft/TypeScript/blob/3ccbe804f850f40d228d3c875be952d94d39aa1d/src/compiler/moduleNameResolver.ts#L2465 | ||
*/ | ||
export function createPathsMatcher( | ||
tsconfig: TsConfigResult, | ||
) { | ||
if (!tsconfig.config.compilerOptions) { | ||
return null; | ||
} | ||
|
||
const { baseUrl, paths } = tsconfig.config.compilerOptions; | ||
if (!baseUrl && !paths) { | ||
return null; | ||
} | ||
|
||
const resolvedBaseUrl = path.resolve( | ||
path.dirname(tsconfig.path), | ||
baseUrl || '.', | ||
); | ||
|
||
const pathEntries = paths ? parsePaths(paths, baseUrl, resolvedBaseUrl) : []; | ||
|
||
return function pathsMatcher(specifier: string) { | ||
if (isRelativePathPattern.test(specifier)) { | ||
return []; | ||
} | ||
|
||
const patternPathEntries: PathEntry<StarPattern>[] = []; | ||
|
||
for (const pathEntry of pathEntries) { | ||
if (pathEntry.pattern === specifier) { | ||
return pathEntry.substitutions; | ||
} | ||
|
||
if (typeof pathEntry.pattern !== 'string') { | ||
patternPathEntries.push(pathEntry as PathEntry<StarPattern>); | ||
} | ||
} | ||
|
||
let matchedValue: PathEntry<StarPattern> | undefined; | ||
let longestMatchPrefixLength = -1; | ||
|
||
for (const pathEntry of patternPathEntries) { | ||
if ( | ||
isPatternMatch(pathEntry.pattern, specifier) | ||
&& pathEntry.pattern.prefix.length > longestMatchPrefixLength | ||
) { | ||
longestMatchPrefixLength = pathEntry.pattern.prefix.length; | ||
matchedValue = pathEntry; | ||
} | ||
} | ||
|
||
if (!matchedValue) { | ||
return ( | ||
baseUrl | ||
? [path.join(resolvedBaseUrl, specifier)] | ||
: [] | ||
); | ||
} | ||
|
||
const matchedPath = specifier.slice( | ||
matchedValue.pattern.prefix.length, | ||
specifier.length - matchedValue.pattern.suffix.length, | ||
); | ||
|
||
return matchedValue.substitutions.map( | ||
substitution => substitution.replace('*', matchedPath), | ||
); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
export type StarPattern = { | ||
prefix: string; | ||
suffix: string; | ||
}; | ||
|
||
export type PathEntry<T extends string | StarPattern> = { | ||
pattern: T; | ||
substitutions: string[]; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import type { StarPattern } from './types'; | ||
|
||
export const isRelativePathPattern = /^\.{1,2}\//; | ||
|
||
const starPattern = /\*/g; | ||
|
||
export const assertStarCount = ( | ||
pattern: string, | ||
errorMessage: string, | ||
) => { | ||
const starCount = pattern.match(starPattern); | ||
if (starCount && starCount.length > 1) { | ||
throw new Error(errorMessage); | ||
} | ||
}; | ||
|
||
export function parsePattern(pattern: string) { | ||
if (pattern.includes('*')) { | ||
const [prefix, suffix] = pattern.split('*'); | ||
return { prefix, suffix } as StarPattern; | ||
} | ||
|
||
return pattern; | ||
} | ||
|
||
export const isPatternMatch = ( | ||
{ prefix, suffix }: StarPattern, | ||
candidate: string, | ||
) => ( | ||
candidate.startsWith(prefix) | ||
&& candidate.endsWith(suffix) | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,4 +14,4 @@ export type TsConfigResult = { | |
* The resolved tsconfig.json file | ||
*/ | ||
config: TsConfigJsonResolved; | ||
} | null; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,8 @@ | ||
import { describe } from 'manten'; | ||
import specErrors from './specs/errors.spec'; | ||
import specFindsConfig from './specs/finds-config.spec'; | ||
import specExtends from './specs/extends.spec'; | ||
|
||
describe('get-tsconfig', ({ runTestSuite }) => { | ||
runTestSuite(specErrors); | ||
runTestSuite(specFindsConfig); | ||
runTestSuite(specExtends); | ||
runTestSuite(import('./specs/errors.spec')); | ||
runTestSuite(import('./specs/finds-config.spec')); | ||
runTestSuite(import('./specs/extends.spec')); | ||
runTestSuite(import('./specs/paths.spec')); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.