@@ -17,209 +17,208 @@ File getFileFromFolderImport(Folder folder) {
1717private signature predicate exprSig ( Expr e ) ;
1818
1919module ResolveExpr< exprSig / 1 shouldResolveExpr> {
20+ final private class FinalExpr = Expr ;
2021
21- private final class FinalExpr = Expr ;
22+ private class RelevantPathExpr extends FinalExpr {
23+ RelevantPathExpr ( ) { shouldResolveExpr ( this ) }
2224
23- private class RelevantPathExpr extends FinalExpr {
24- RelevantPathExpr ( ) { shouldResolveExpr ( this ) }
25+ string getValue ( ) { result = this . getStringValue ( ) }
26+ }
2527
26- string getValue ( ) { result = this .getStringValue ( ) }
27- }
28+ /**
29+ * Gets a tsconfig file to use as fallback for handling paths in `c`.
30+ *
31+ * This holds for files and folders where no tsconfig seems to include it,
32+ * but it has one or more tsconfig files in parent directories.
33+ */
34+ private TSConfig getFallbackTSConfig ( Container c ) {
35+ not c = any ( TSConfig t ) .getAnIncludedContainer ( ) and
36+ (
37+ c = result .getFolder ( )
38+ or
39+ result = getFallbackTSConfig ( c .getParentContainer ( ) )
40+ )
41+ }
2842
29- /**
30- * Gets a tsconfig file to use as fallback for handling paths in `c`.
31- *
32- * This holds for files and folders where no tsconfig seems to include it,
33- * but it has one or more tsconfig files in parent directories.
34- */
35- private TSConfig getFallbackTSConfig ( Container c ) {
36- not c = any ( TSConfig t ) .getAnIncludedContainer ( ) and
37- (
38- c = result .getFolder ( )
43+ /**
44+ * Gets the TSConfig file relevant for resolving `expr`.
45+ */
46+ pragma [ nomagic]
47+ private TSConfig getTSConfigFromPathExpr ( RelevantPathExpr expr ) {
48+ result .getAnIncludedContainer ( ) = expr .getFile ( )
3949 or
40- result = getFallbackTSConfig ( c .getParentContainer ( ) )
41- )
42- }
43-
44- /**
45- * Gets the TSConfig file relevant for resolving `expr`.
46- */
47- pragma [ nomagic]
48- private TSConfig getTSConfigFromPathExpr ( RelevantPathExpr expr ) {
49- result .getAnIncludedContainer ( ) = expr .getFile ( )
50- or
51- result = getFallbackTSConfig ( expr .getFile ( ) )
52- }
50+ result = getFallbackTSConfig ( expr .getFile ( ) )
51+ }
5352
54- /**
55- * Holds if `path` is relative, in the sense that it should be resolved relative to its enclosing folder.
56- */
57- bindingset [ path]
58- pragma [ inline_late]
59- predicate isRelativePath ( string path ) { path .regexpMatch ( "\\.\\.?(?:[/\\\\].*)?" ) }
60-
61- /**
62- * Gets the NPM package name from the beginning of the given import path, e.g.
63- * gets `foo` from `foo/bar`, and `@example/foo` from `@example/foo/bar`.
64- */
65- pragma [ nomagic]
66- private string getPackagePrefixFromPathExpr ( RelevantPathExpr expr ) {
67- result = expr .getValue ( ) .regexpFind ( "^(@[^/\\\\]+[/\\\\])?[^@./\\\\][^/\\\\]*" , _, _)
68- }
53+ /**
54+ * Holds if `path` is relative, in the sense that it should be resolved relative to its enclosing folder.
55+ */
56+ bindingset [ path]
57+ pragma [ inline_late]
58+ predicate isRelativePath ( string path ) { path .regexpMatch ( "\\.\\.?(?:[/\\\\].*)?" ) }
59+
60+ /**
61+ * Gets the NPM package name from the beginning of the given import path, e.g.
62+ * gets `foo` from `foo/bar`, and `@example/foo` from `@example/foo/bar`.
63+ */
64+ pragma [ nomagic]
65+ private string getPackagePrefixFromPathExpr ( RelevantPathExpr expr ) {
66+ result = expr .getValue ( ) .regexpFind ( "^(@[^/\\\\]+[/\\\\])?[^@./\\\\][^/\\\\]*" , _, _)
67+ }
6968
70- private Variable dirname ( ) { result .getName ( ) = "__dirname" }
69+ private Variable dirname ( ) { result .getName ( ) = "__dirname" }
7170
72- /** Holds if `add` is a relevant path expression of form `__dirname + expr`. */
73- private predicate prefixedByDirname ( Expr expr ) {
74- expr = dirname ( ) .getAnAccess ( )
75- or
76- prefixedByDirname ( expr .( AddExpr ) .getLeftOperand ( ) )
77- or
78- prefixedByDirname ( expr .( CallExpr ) .getArgument ( 0 ) )
79- }
80-
81- /**
82- * Holds if `expr` matches a path mapping, and should thus be resolved as `newPath` relative to `base`.
83- */
84- pragma [ nomagic]
85- private predicate resolveViaPathMapping ( RelevantPathExpr expr , Container base , string newPath ) {
86- // Handle tsconfig mappings such as `{ "paths": { "@/*": "./src/*" }}`
87- exists ( TSConfig config , string value |
88- config = getTSConfigFromPathExpr ( expr ) .getExtendedTSConfig * ( ) and
89- value = expr .getValue ( ) and
90- base = config .getBaseUrlFolderOrOwnFolder ( )
91- |
92- config .hasExactPathMapping ( value , newPath )
71+ /** Holds if `add` is a relevant path expression of form `__dirname + expr`. */
72+ private predicate prefixedByDirname ( Expr expr ) {
73+ expr = dirname ( ) .getAnAccess ( )
74+ or
75+ prefixedByDirname ( expr .( AddExpr ) .getLeftOperand ( ) )
9376 or
94- exists ( string pattern , string suffix , string mappedPath |
95- config .hasPrefixPathMapping ( pattern , mappedPath ) and
96- value = pattern + suffix and
97- newPath = mappedPath + suffix
77+ prefixedByDirname ( expr .( CallExpr ) .getArgument ( 0 ) )
78+ }
79+
80+ /**
81+ * Holds if `expr` matches a path mapping, and should thus be resolved as `newPath` relative to `base`.
82+ */
83+ pragma [ nomagic]
84+ private predicate resolveViaPathMapping ( RelevantPathExpr expr , Container base , string newPath ) {
85+ // Handle tsconfig mappings such as `{ "paths": { "@/*": "./src/*" }}`
86+ exists ( TSConfig config , string value |
87+ config = getTSConfigFromPathExpr ( expr ) .getExtendedTSConfig * ( ) and
88+ value = expr .getValue ( ) and
89+ base = config .getBaseUrlFolderOrOwnFolder ( )
90+ |
91+ config .hasExactPathMapping ( value , newPath )
92+ or
93+ exists ( string pattern , string suffix , string mappedPath |
94+ config .hasPrefixPathMapping ( pattern , mappedPath ) and
95+ value = pattern + suffix and
96+ newPath = mappedPath + suffix
97+ )
9898 )
99- )
100- or
101- // Handle imports referring to a package by name, where we have a package.json
102- // file for that package in the codebase.
103- //
104- // This part only handles the "exports" property of package.json. "main" and "modules" are
105- // handled further down because their semantics are easier to handle there.
106- exists ( PackageJsonEx pkg , string packageName , string remainder |
107- packageName = getPackagePrefixFromPathExpr ( expr ) and
108- pkg .getDeclaredPackageName ( ) = packageName and
109- remainder = expr .getValue ( ) .suffix ( packageName .length ( ) ) .regexpReplaceAll ( "^[/\\\\]" , "" )
110- |
111- // "exports": { ".": "./foo.js" }
112- // "exports": { "./foo.js": "./foo/impl.js" }
113- pkg .hasExactPathMappingTo ( remainder , base ) and
114- newPath = ""
11599 or
116- // "exports": { "./*": "./foo/*" }
117- exists ( string prefix |
118- pkg .hasPrefixPathMappingTo ( prefix , base ) and
119- remainder = prefix + newPath
100+ // Handle imports referring to a package by name, where we have a package.json
101+ // file for that package in the codebase.
102+ //
103+ // This part only handles the "exports" property of package.json. "main" and "modules" are
104+ // handled further down because their semantics are easier to handle there.
105+ exists ( PackageJsonEx pkg , string packageName , string remainder |
106+ packageName = getPackagePrefixFromPathExpr ( expr ) and
107+ pkg .getDeclaredPackageName ( ) = packageName and
108+ remainder = expr .getValue ( ) .suffix ( packageName .length ( ) ) .regexpReplaceAll ( "^[/\\\\]" , "" )
109+ |
110+ // "exports": { ".": "./foo.js" }
111+ // "exports": { "./foo.js": "./foo/impl.js" }
112+ pkg .hasExactPathMappingTo ( remainder , base ) and
113+ newPath = ""
114+ or
115+ // "exports": { "./*": "./foo/*" }
116+ exists ( string prefix |
117+ pkg .hasPrefixPathMappingTo ( prefix , base ) and
118+ remainder = prefix + newPath
119+ )
120120 )
121- )
122- }
123-
124- pragma [ noopt]
125- private predicate relativePathExpr ( RelevantPathExpr expr , Container base , string path ) {
126- expr instanceof RelevantPathExpr and
127- path = expr .getValue ( ) and
128- isRelativePath ( path ) and
129- exists ( File file |
130- file = expr .getFile ( ) and
131- base = file .getParentContainer ( )
132- )
133- }
121+ }
134122
135- /**
136- * Holds if `expr` should be resolved as `path` relative to `base`.
137- */
138- pragma [ nomagic]
139- private predicate shouldResolve ( RelevantPathExpr expr , Container base , string path ) {
140- // Relative paths are resolved from their enclosing folder
141- relativePathExpr ( expr , base , path )
142- or
143- // Paths prefixed by __dirname should be resolved from the root dir, because __dirname
144- // currently has a getValue() that returns its absolute path.
145- prefixedByDirname ( expr ) and
146- not exists ( base .getParentContainer ( ) ) and // get root dir
147- path = expr .getValue ( )
148- or
149- resolveViaPathMapping ( expr , base , path )
150- or
151- // Resolve from baseUrl of relevant tsconfig.json file
152- path = expr .getValue ( ) and
153- not isRelativePath ( path ) and
154- base = getTSConfigFromPathExpr ( expr ) .getBaseUrlFolder ( )
155- or
156- // If the path starts with the name of a package, but did not match any path mapping,
157- // resolve relative to the enclosing directory of that package.
158- // Note that `getFileFromFolderImport` may subsequently redirect this to the package's "main",
159- // so we don't have to deal with that here.
160- exists ( PackageJson pkg , string packageName |
161- packageName = getPackagePrefixFromPathExpr ( expr ) and
162- pkg .getDeclaredPackageName ( ) = packageName and
163- path = expr .getValue ( ) .suffix ( packageName .length ( ) ) .regexpReplaceAll ( "^[/\\\\]" , "" ) and
164- base = pkg .getFolder ( )
165- )
166- }
123+ pragma [ noopt]
124+ private predicate relativePathExpr ( RelevantPathExpr expr , Container base , string path ) {
125+ expr instanceof RelevantPathExpr and
126+ path = expr .getValue ( ) and
127+ isRelativePath ( path ) and
128+ exists ( File file |
129+ file = expr .getFile ( ) and
130+ base = file .getParentContainer ( )
131+ )
132+ }
167133
168- private module ResolverConfig implements Folder:: ResolveSig {
169- predicate shouldResolve ( Container base , string path ) { shouldResolve ( _, base , path ) }
134+ /**
135+ * Holds if `expr` should be resolved as `path` relative to `base`.
136+ */
137+ pragma [ nomagic]
138+ private predicate shouldResolve ( RelevantPathExpr expr , Container base , string path ) {
139+ // Relative paths are resolved from their enclosing folder
140+ relativePathExpr ( expr , base , path )
141+ or
142+ // Paths prefixed by __dirname should be resolved from the root dir, because __dirname
143+ // currently has a getValue() that returns its absolute path.
144+ prefixedByDirname ( expr ) and
145+ not exists ( base .getParentContainer ( ) ) and // get root dir
146+ path = expr .getValue ( )
147+ or
148+ resolveViaPathMapping ( expr , base , path )
149+ or
150+ // Resolve from baseUrl of relevant tsconfig.json file
151+ path = expr .getValue ( ) and
152+ not isRelativePath ( path ) and
153+ base = getTSConfigFromPathExpr ( expr ) .getBaseUrlFolder ( )
154+ or
155+ // If the path starts with the name of a package, but did not match any path mapping,
156+ // resolve relative to the enclosing directory of that package.
157+ // Note that `getFileFromFolderImport` may subsequently redirect this to the package's "main",
158+ // so we don't have to deal with that here.
159+ exists ( PackageJson pkg , string packageName |
160+ packageName = getPackagePrefixFromPathExpr ( expr ) and
161+ pkg .getDeclaredPackageName ( ) = packageName and
162+ path = expr .getValue ( ) .suffix ( packageName .length ( ) ) .regexpReplaceAll ( "^[/\\\\]" , "" ) and
163+ base = pkg .getFolder ( )
164+ )
165+ }
170166
171- predicate getAnAdditionalChild = JSPaths :: getAnAdditionalChild / 2 ;
172- }
167+ private module ResolverConfig implements Folder :: ResolveSig {
168+ predicate shouldResolve ( Container base , string path ) { shouldResolve ( _ , base , path ) }
173169
174- private module Resolver = Folder:: Resolve< ResolverConfig > ;
170+ predicate getAnAdditionalChild = JSPaths:: getAnAdditionalChild / 2 ;
171+ }
175172
176- private Container resolvePathExpr1 ( RelevantPathExpr expr ) {
177- exists ( Container base , string path |
178- shouldResolve ( expr , base , path ) and
179- result = Resolver:: resolve ( base , path )
180- )
181- }
173+ private module Resolver = Folder:: Resolve< ResolverConfig > ;
182174
183- File resolveExpr ( RelevantPathExpr expr ) {
184- result = resolvePathExpr1 ( expr )
185- or
186- result = getFileFromFolderImport ( resolvePathExpr1 ( expr ) )
187- }
175+ private Container resolvePathExpr1 ( RelevantPathExpr expr ) {
176+ exists ( Container base , string path |
177+ shouldResolve ( expr , base , path ) and
178+ result = Resolver:: resolve ( base , path )
179+ )
180+ }
188181
189- module Debug {
190- class PathExprToDebug extends RelevantPathExpr {
191- PathExprToDebug ( ) { this .getValue ( ) = "vs/nls" }
182+ File resolveExpr ( RelevantPathExpr expr ) {
183+ result = resolvePathExpr1 ( expr )
184+ or
185+ result = getFileFromFolderImport ( resolvePathExpr1 ( expr ) )
192186 }
193187
194- query PathExprToDebug pathExprs ( ) { any ( ) }
188+ module Debug {
189+ class PathExprToDebug extends RelevantPathExpr {
190+ PathExprToDebug ( ) { this .getValue ( ) = "vs/nls" }
191+ }
195192
196- query TSConfig getTSConfigFromPathExpr_ ( PathExprToDebug expr ) {
197- result = getTSConfigFromPathExpr ( expr )
198- }
193+ query PathExprToDebug pathExprs ( ) { any ( ) }
199194
200- query string getPackagePrefixFromPathExpr_ ( PathExprToDebug expr ) {
201- result = getPackagePrefixFromPathExpr ( expr )
202- }
195+ query TSConfig getTSConfigFromPathExpr_ ( PathExprToDebug expr ) {
196+ result = getTSConfigFromPathExpr ( expr )
197+ }
203198
204- query predicate resolveViaPathMapping_ ( PathExprToDebug expr , Container base , string newPath ) {
205- resolveViaPathMapping ( expr , base , newPath )
206- }
199+ query string getPackagePrefixFromPathExpr_ ( PathExprToDebug expr ) {
200+ result = getPackagePrefixFromPathExpr ( expr )
201+ }
207202
208- query predicate shouldResolve_ ( PathExprToDebug expr , Container base , string newPath ) {
209- shouldResolve ( expr , base , newPath )
210- }
203+ query predicate resolveViaPathMapping_ ( PathExprToDebug expr , Container base , string newPath ) {
204+ resolveViaPathMapping ( expr , base , newPath )
205+ }
211206
212- query Container resolvePathExpr1_ ( PathExprToDebug expr ) { result = resolvePathExpr1 ( expr ) }
207+ query predicate shouldResolve_ ( PathExprToDebug expr , Container base , string newPath ) {
208+ shouldResolve ( expr , base , newPath )
209+ }
213210
214- query File resolveExpr_ ( PathExprToDebug expr ) { result = resolveExpr ( expr ) }
211+ query Container resolvePathExpr1_ ( PathExprToDebug expr ) { result = resolvePathExpr1 ( expr ) }
215212
216- // Some predicates that are usually small enough that they don't need restriction
217- query File getPackageMainFile ( PackageJsonEx pkg ) { result = pkg .getMainFile ( ) }
213+ query File resolveExpr_ ( PathExprToDebug expr ) { result = resolveExpr ( expr ) }
218214
219- query predicate guessPackageJsonMain1_ = guessPackageJsonMain1 / 1 ;
215+ // Some predicates that are usually small enough that they don't need restriction
216+ query File getPackageMainFile ( PackageJsonEx pkg ) { result = pkg .getMainFile ( ) }
220217
221- query predicate guessPackageJsonMain2_ = guessPackageJsonMain2 / 1 ;
218+ query predicate guessPackageJsonMain1_ = guessPackageJsonMain1 / 1 ;
222219
223- query predicate getFileFromFolderImport_ = getFileFromFolderImport / 1 ;
224- }
220+ query predicate guessPackageJsonMain2_ = guessPackageJsonMain2 / 1 ;
221+
222+ query predicate getFileFromFolderImport_ = getFileFromFolderImport / 1 ;
223+ }
225224}
0 commit comments