@@ -141,29 +141,7 @@ abstract class PathString extends string {
141141 * components of this path refers to when resolved relative to the
142142 * given `root` folder.
143143 */
144- Path resolveUpTo ( int n , Folder root ) {
145- n = 0 and result .getContainer ( ) = root and root = getARootFolder ( )
146- or
147- exists ( Path base , string next | next = getComponent ( this , n - 1 , base , root ) |
148- // handle empty components and the special "." folder
149- ( next = "" or next = "." ) and
150- result = base
151- or
152- // handle the special ".." folder
153- next = ".." and result = base .( ConsPath ) .getParent ( )
154- or
155- // special handling for Windows drive letters when resolving absolute path:
156- // the extractor populates "C:/" as a folder that has path "C:/" but name ""
157- n = 1 and
158- next .regexpMatch ( "[A-Za-z]:" ) and
159- root .getBaseName ( ) = "" and
160- root .toString ( ) = next .toUpperCase ( ) + "/" and
161- result = base
162- or
163- // default case
164- result = TConsPath ( base , next )
165- )
166- }
144+ Path resolveUpTo ( int n , Folder root ) { result = resolveUpTo ( this , n , root , _) }
167145
168146 /**
169147 * Gets the absolute path that this path refers to when resolved relative to
@@ -172,16 +150,57 @@ abstract class PathString extends string {
172150 Path resolve ( Folder root ) { result = resolveUpTo ( getNumComponent ( ) , root ) }
173151}
174152
153+ /**
154+ * Gets the absolute path that the sub-path consisting of the first `n`
155+ * components of this path refers to when resolved relative to the
156+ * given `root` folder.
157+ */
158+ private Path resolveUpTo ( PathString p , int n , Folder root , boolean inTS ) {
159+ n = 0 and result .getContainer ( ) = root and root = p .getARootFolder ( ) and inTS = false
160+ or
161+ exists ( Path base , string next | next = getComponent ( p , n - 1 , base , root , inTS ) |
162+ // handle empty components and the special "." folder
163+ ( next = "" or next = "." ) and
164+ result = base
165+ or
166+ // handle the special ".." folder
167+ next = ".." and result = base .( ConsPath ) .getParent ( )
168+ or
169+ // special handling for Windows drive letters when resolving absolute path:
170+ // the extractor populates "C:/" as a folder that has path "C:/" but name ""
171+ n = 1 and
172+ next .regexpMatch ( "[A-Za-z]:" ) and
173+ root .getBaseName ( ) = "" and
174+ root .toString ( ) = next .toUpperCase ( ) + "/" and
175+ result = base
176+ or
177+ // default case
178+ result = TConsPath ( base , next )
179+ )
180+ }
181+
175182/**
176183 * Gets the `i`th component of the path `str`, where `base` is the resolved path one level up.
177184 * Supports that the root directory might be compiled output from TypeScript.
185+ * `inTS` is true if the result is TypeScript that is compiled into the path specified by `str`.
178186 */
179- private string getComponent ( PathString str , int n , Path base , Folder root ) {
180- base = str .resolveUpTo ( n , root ) and
181- (
182- result = str .getComponent ( n )
183- or
184- result = TypeScriptOutDir:: getOriginalTypeScriptFolder ( str .getComponent ( n ) , base .getContainer ( ) )
187+ private string getComponent ( PathString str , int n , Path base , Folder root , boolean inTS ) {
188+ exists ( boolean prevTS |
189+ base = resolveUpTo ( str , n , root , prevTS ) and
190+ (
191+ result = str .getComponent ( n ) and prevTS = inTS
192+ or
193+ // If we are in a TypeScript source folder, try to replace file endings with ".ts" or ".tsx"
194+ n = str .getNumComponent ( ) - 1 and
195+ prevTS = true and
196+ inTS = prevTS and
197+ result = str .getComponent ( n ) .regexpCapture ( "^(.*)\\.js$" , 1 ) + "." + [ "ts" , "tsx" ]
198+ or
199+ prevTS = false and
200+ inTS = true and
201+ result =
202+ TypeScriptOutDir:: getOriginalTypeScriptFolder ( str .getComponent ( n ) , base .getContainer ( ) )
203+ )
185204 )
186205}
187206
@@ -194,21 +213,35 @@ private module TypeScriptOutDir {
194213 */
195214 string getOriginalTypeScriptFolder ( string outdir , Folder parent ) {
196215 exists ( JSONObject tsconfig |
197- tsconfig .getFile ( ) .getBaseName ( ) = "tsconfig.json" and
198- tsconfig .isTopLevel ( ) and
199- tsconfig .getFile ( ) .getParentContainer ( ) = parent
200- |
201- outdir =
202- tsconfig
203- .getPropValue ( "compilerOptions" )
204- .( JSONObject )
205- .getPropValue ( "outDir" )
206- .( JSONString )
207- .getValue ( ) and
208- result = getEffectiveRootDirFromTSConfig ( tsconfig )
216+ outdir = removeLeadingSlash ( getOutDir ( tsconfig , parent ) ) and
217+ result = removeLeadingSlash ( getEffectiveRootDirFromTSConfig ( tsconfig ) )
209218 )
210219 }
211220
221+ /**
222+ * Removes a leading dot and/or slash from `raw`.
223+ */
224+ bindingset [ raw]
225+ private string removeLeadingSlash ( string raw ) {
226+ result = raw .regexpCapture ( "^\\.?/?([\\w.\\-]+)$" , 1 )
227+ }
228+
229+ /**
230+ * Gets the `outDir` option from a tsconfig file from the folder `parent`.
231+ */
232+ private string getOutDir ( JSONObject tsconfig , Folder parent ) {
233+ tsconfig .getFile ( ) .getBaseName ( ) .regexpMatch ( "tsconfig.*\\.json" ) and
234+ tsconfig .isTopLevel ( ) and
235+ tsconfig .getFile ( ) .getParentContainer ( ) = parent and
236+ result =
237+ tsconfig
238+ .getPropValue ( "compilerOptions" )
239+ .( JSONObject )
240+ .getPropValue ( "outDir" )
241+ .( JSONString )
242+ .getValue ( )
243+ }
244+
212245 /**
213246 * Gets the directory that contains the TypeScript source files.
214247 * Based on the tsconfig.json file `tsconfig`.
0 commit comments