@@ -17,63 +17,114 @@ export function resolvePathAndUpdateNode(
17
17
moduleName : string ,
18
18
updaterFn : ( newPath : ts . StringLiteral ) => ts . Node | tsThree . Node | undefined
19
19
) : ts . Node | undefined {
20
- const { sourceFile, compilerOptions, tsInstance, config, rootDirs, implicitExtensions, factory } = context ;
21
-
22
- /* Have Compiler API attempt to resolve */
23
- const { resolvedModule, failedLookupLocations } = tsInstance . resolveModuleName (
24
- moduleName ,
25
- sourceFile . fileName ,
26
- compilerOptions ,
27
- tsInstance . sys
28
- ) ;
29
-
30
- if ( resolvedModule ?. isExternalLibraryImport ) return node ;
31
-
32
- let outputPath : string ;
33
- if ( ! resolvedModule ) {
34
- const maybeURL = failedLookupLocations [ 0 ] ;
35
- if ( ! isURL ( maybeURL ) ) return node ;
36
- outputPath = maybeURL ;
37
- } else {
38
- const { extension, resolvedFileName } = resolvedModule ;
20
+ const { sourceFile, compilerOptions, tsInstance, config, implicitExtensions, factory } = context ;
21
+ const tags = getStatementTags ( ) ;
39
22
40
- const fileName = sourceFile . fileName ;
41
- let filePath = tsInstance . normalizePath ( path . dirname ( sourceFile . fileName ) ) ;
42
- let modulePath = path . dirname ( resolvedFileName ) ;
43
-
44
- /* Handle rootDirs mapping */
45
- if ( config . useRootDirs && rootDirs ) {
46
- let fileRootDir = "" ;
47
- let moduleRootDir = "" ;
48
- for ( const rootDir of rootDirs ) {
49
- if ( isBaseDir ( rootDir , resolvedFileName ) && rootDir . length > moduleRootDir . length ) moduleRootDir = rootDir ;
50
- if ( isBaseDir ( rootDir , fileName ) && rootDir . length > fileRootDir . length ) fileRootDir = rootDir ;
51
- }
23
+ // Skip if @no -transform-path specified
24
+ if ( tags ?. shouldSkip ) return node ;
52
25
53
- /* Remove base dirs to make relative to root */
54
- if ( fileRootDir && moduleRootDir ) {
55
- filePath = path . relative ( fileRootDir , filePath ) ;
56
- modulePath = path . relative ( moduleRootDir , modulePath ) ;
57
- }
26
+ const resolutionResult = resolvePath ( tags ?. overridePath ) ;
27
+
28
+ // Skip if can't be resolved
29
+ if ( ! resolutionResult || ! resolutionResult . outputPath ) return node ;
30
+
31
+ const { outputPath, filePath } = resolutionResult ;
32
+
33
+ // Check if matches exclusion
34
+ if ( filePath && context . excludeMatchers )
35
+ for ( const matcher of context . excludeMatchers ) if ( matcher . match ( filePath ) ) return node ;
36
+
37
+ return updaterFn ( factory . createStringLiteral ( outputPath ) ) as ts . Node | undefined ;
38
+
39
+ /* ********************************************************* *
40
+ * Helpers
41
+ * ********************************************************* */
42
+
43
+ function resolvePath ( overridePath : string | undefined ) : { outputPath : string ; filePath ?: string } | undefined {
44
+ /* Handle overridden path -- ie. @transform-path ../my/path) */
45
+ if ( overridePath ) {
46
+ return {
47
+ outputPath : filePathToOutputPath ( overridePath , path . extname ( overridePath ) ) ,
48
+ filePath : overridePath ,
49
+ } ;
58
50
}
59
51
60
- outputPath = tsInstance . normalizePath (
61
- path . join ( path . relative ( filePath , modulePath ) , path . basename ( resolvedFileName ) )
52
+ /* Have Compiler API attempt to resolve */
53
+ const { resolvedModule, failedLookupLocations } = tsInstance . resolveModuleName (
54
+ moduleName ,
55
+ sourceFile . fileName ,
56
+ compilerOptions ,
57
+ tsInstance . sys
62
58
) ;
63
59
64
- /* Check if matches exclusion */
65
- if ( context . excludeMatchers )
66
- for ( const matcher of context . excludeMatchers )
67
- if ( matcher . match ( outputPath ) ) return node ;
60
+ // No transform for node-modules
61
+ if ( resolvedModule ?. isExternalLibraryImport ) return void 0 ;
68
62
69
- // Remove extension if implicit
70
- if ( extension && implicitExtensions . includes ( extension ) ) outputPath = outputPath . slice ( 0 , - extension . length ) ;
63
+ /* Handle non-resolvable module */
64
+ if ( ! resolvedModule ) {
65
+ const maybeURL = failedLookupLocations [ 0 ] ;
66
+ if ( ! isURL ( maybeURL ) ) return void 0 ;
67
+ return { outputPath : maybeURL } ;
68
+ }
71
69
72
- if ( ! outputPath ) return node ;
70
+ /* Handle resolved module */
71
+ const { extension, resolvedFileName } = resolvedModule ;
72
+ return {
73
+ outputPath : filePathToOutputPath ( resolvedFileName , extension ) ,
74
+ filePath : resolvedFileName ,
75
+ } ;
76
+ }
73
77
74
- outputPath = outputPath [ 0 ] === "." ? outputPath : `./${ outputPath } ` ;
78
+ function filePathToOutputPath ( filePath : string , extension : string | undefined ) {
79
+ if ( path . isAbsolute ( filePath ) ) {
80
+ let sourceFileDir = tsInstance . normalizePath ( path . dirname ( sourceFile . fileName ) ) ;
81
+ let moduleDir = path . dirname ( filePath ) ;
82
+
83
+ /* Handle rootDirs mapping */
84
+ if ( config . useRootDirs && context . rootDirs ) {
85
+ let fileRootDir = "" ;
86
+ let moduleRootDir = "" ;
87
+ for ( const rootDir of context . rootDirs ) {
88
+ if ( isBaseDir ( rootDir , filePath ) && rootDir . length > moduleRootDir . length ) moduleRootDir = rootDir ;
89
+ if ( isBaseDir ( rootDir , sourceFile . fileName ) && rootDir . length > fileRootDir . length ) fileRootDir = rootDir ;
90
+ }
91
+
92
+ /* Remove base dirs to make relative to root */
93
+ if ( fileRootDir && moduleRootDir ) {
94
+ sourceFileDir = path . relative ( fileRootDir , sourceFileDir ) ;
95
+ moduleDir = path . relative ( moduleRootDir , moduleDir ) ;
96
+ }
97
+ }
98
+
99
+ /* Make path relative */
100
+ filePath = tsInstance . normalizePath ( path . join ( path . relative ( sourceFileDir , moduleDir ) , path . basename ( filePath ) ) ) ;
101
+ }
102
+
103
+ // Remove extension if implicit
104
+ if ( extension && implicitExtensions . includes ( extension ) ) filePath = filePath . slice ( 0 , - extension . length ) ;
105
+
106
+ return filePath [ 0 ] === "." || isURL ( filePath ) ? filePath : `./${ filePath } ` ;
75
107
}
76
108
77
- const newStringLiteral = factory . createStringLiteral ( outputPath ) ;
78
- return updaterFn ( newStringLiteral ) as ts . Node | undefined ;
109
+ function getStatementTags ( ) {
110
+ const targetNode = tsInstance . isStatement ( node )
111
+ ? node
112
+ : tsInstance . findAncestor ( node , tsInstance . isStatement ) ?? node ;
113
+ const jsDocTags = tsInstance . getJSDocTags ( targetNode ) ;
114
+
115
+ const trivia = targetNode . getFullText ( sourceFile ) . slice ( 0 , targetNode . getLeadingTriviaWidth ( sourceFile ) ) ;
116
+ const commentTags = new Map < string , string | undefined > ( ) ;
117
+ const regex = / ^ \s * \/ \/ \/ ? \s * @ ( t r a n s f o r m - p a t h | n o - t r a n s f o r m - p a t h ) (?: [ ^ \S \r \n ] ( .+ ?) ) ? $ / gm;
118
+
119
+ for ( let match = regex . exec ( trivia ) ; match ; match = regex . exec ( trivia ) ) commentTags . set ( match [ 1 ] , match [ 2 ] ) ;
120
+
121
+ return {
122
+ overridePath :
123
+ commentTags . get ( "transform-path" ) ??
124
+ jsDocTags ?. find ( ( t ) => t . tagName . text . toLowerCase ( ) === "transform-path" ) ?. comment ,
125
+ shouldSkip :
126
+ commentTags . has ( "no-transform-path" ) ||
127
+ ! ! jsDocTags ?. find ( ( t ) => t . tagName . text . toLowerCase ( ) === "no-transform-path" ) ,
128
+ } ;
129
+ }
79
130
}
0 commit comments