1
+ import * as fs from 'mz/fs' ;
2
+ import * as path from 'path' ;
1
3
import * as ts from 'typescript' ;
2
4
import { Logger , NoopLogger } from './logging' ;
3
5
import { combinePaths } from './match-files' ;
4
- import { InitializationOptions } from './request-type' ;
6
+ import { PluginSettings } from './request-type' ;
7
+ import { toUnixPath } from './util' ;
5
8
6
9
// Based on types and logic from TypeScript server/project.ts @
7
10
// https://github.com/Microsoft/TypeScript/blob/711e890e59e10aa05a43cb938474a3d9c2270429/src/server/project.ts
@@ -58,11 +61,17 @@ export class PluginLoader {
58
61
private globalPlugins : string [ ] = [ ] ;
59
62
private pluginProbeLocations : string [ ] = [ ] ;
60
63
61
- constructor ( private rootFilePath : string , private fs : ts . ModuleResolutionHost , initializationOptions ?: InitializationOptions , private logger = new NoopLogger ( ) , private requireModule : ( moduleName : string ) => any = require ) {
62
- if ( initializationOptions ) {
63
- this . allowLocalPluginLoads = initializationOptions . allowLocalPluginLoads || false ;
64
- this . globalPlugins = initializationOptions . globalPlugins || [ ] ;
65
- this . pluginProbeLocations = initializationOptions . pluginProbeLocations || [ ] ;
64
+ constructor (
65
+ private rootFilePath : string ,
66
+ private fs : ts . ModuleResolutionHost ,
67
+ pluginSettings ?: PluginSettings ,
68
+ private logger = new NoopLogger ( ) ,
69
+ private resolutionHost = new LocalModuleResolutionHost ( ) ,
70
+ private requireModule : ( moduleName : string ) => any = require ) {
71
+ if ( pluginSettings ) {
72
+ this . allowLocalPluginLoads = pluginSettings . allowLocalPluginLoads || false ;
73
+ this . globalPlugins = pluginSettings . globalPlugins || [ ] ;
74
+ this . pluginProbeLocations = pluginSettings . pluginProbeLocations || [ ] ;
66
75
}
67
76
}
68
77
@@ -117,7 +126,7 @@ export class PluginLoader {
117
126
return ;
118
127
}
119
128
}
120
- this . logger . info ( `Couldn't find ${ pluginConfigEntry . name } anywhere in paths: ${ searchPaths . join ( ',' ) } ` ) ;
129
+ this . logger . error ( `Couldn't find ${ pluginConfigEntry . name } anywhere in paths: ${ searchPaths . join ( ',' ) } ` ) ;
121
130
}
122
131
123
132
/**
@@ -126,10 +135,11 @@ export class PluginLoader {
126
135
* @param initialDir
127
136
*/
128
137
private resolveModule ( moduleName : string , initialDir : string ) : { } | undefined {
129
- this . logger . info ( `Loading ${ moduleName } from ${ initialDir } ` ) ;
130
- const result = this . requirePlugin ( initialDir , moduleName ) ;
138
+ const resolvedPath = toUnixPath ( path . resolve ( combinePaths ( initialDir , 'node_modules' ) ) ) ;
139
+ this . logger . info ( `Loading ${ moduleName } from ${ initialDir } (resolved to ${ resolvedPath } )` ) ;
140
+ const result = this . requirePlugin ( resolvedPath , moduleName ) ;
131
141
if ( result . error ) {
132
- this . logger . info ( `Failed to load module: ${ JSON . stringify ( result . error ) } ` ) ;
142
+ this . logger . error ( `Failed to load module: ${ JSON . stringify ( result . error ) } ` ) ;
133
143
return undefined ;
134
144
}
135
145
return result . module ;
@@ -141,8 +151,8 @@ export class PluginLoader {
141
151
* @param moduleName
142
152
*/
143
153
private requirePlugin ( initialDir : string , moduleName : string ) : RequireResult {
144
- const modulePath = this . resolveJavaScriptModule ( moduleName , initialDir , this . fs ) ;
145
154
try {
155
+ const modulePath = this . resolveJavaScriptModule ( moduleName , initialDir , this . fs ) ;
146
156
return { module : this . requireModule ( modulePath ) , error : undefined } ;
147
157
} catch ( error ) {
148
158
return { module : undefined , error } ;
@@ -158,11 +168,23 @@ export class PluginLoader {
158
168
private resolveJavaScriptModule ( moduleName : string , initialDir : string , host : ts . ModuleResolutionHost ) : string {
159
169
// TODO: this should set jsOnly=true to the internal resolver, but this parameter is not exposed on a public api.
160
170
const result =
161
- ts . nodeModuleNameResolver ( moduleName , /* containingFile */ initialDir . replace ( '\\' , '/' ) + '/package.json' , { moduleResolution : ts . ModuleResolutionKind . NodeJs , allowJs : true } , this . fs , undefined ) ;
171
+ ts . nodeModuleNameResolver ( moduleName , /* containingFile */ initialDir . replace ( '\\' , '/' ) + '/package.json' , { moduleResolution : ts . ModuleResolutionKind . NodeJs , allowJs : true } , this . resolutionHost , undefined ) ;
162
172
if ( ! result . resolvedModule ) {
163
173
// this.logger.error(result.failedLookupLocations);
164
174
throw new Error ( `Could not resolve JS module ${ moduleName } starting at ${ initialDir } .` ) ;
165
175
}
166
176
return result . resolvedModule . resolvedFileName ;
167
177
}
168
178
}
179
+
180
+ /**
181
+ * A local filesystem-based ModuleResolutionHost for plugin loading.
182
+ */
183
+ export class LocalModuleResolutionHost implements ts . ModuleResolutionHost {
184
+ fileExists ( fileName : string ) : boolean {
185
+ return fs . existsSync ( fileName ) ;
186
+ }
187
+ readFile ( fileName : string ) : string {
188
+ return fs . readFileSync ( fileName , 'utf8' ) ;
189
+ }
190
+ }
0 commit comments