@@ -159,12 +159,14 @@ class BroccoliTypeScriptCompiler extends Plugin {
159
159
160
160
this . _tsConfigFiles = tsconfig . files . splice ( 0 ) ;
161
161
162
- this . _tsOpts = ts . convertCompilerOptionsFromJson ( tsconfig . compilerOptions , '' , null ) . options ;
162
+ this . _tsOpts = ts . convertCompilerOptionsFromJson ( tsconfig [ 'compilerOptions' ] ,
163
+ this . inputPaths [ 0 ] , this . _tsConfigPath ) . options ;
163
164
this . _tsOpts . rootDir = '' ;
164
165
this . _tsOpts . outDir = '' ;
165
166
166
167
this . _tsServiceHost = new CustomLanguageServiceHost (
167
- this . _tsOpts , this . _rootFilePaths , this . _fileRegistry , this . inputPaths [ 0 ] ) ;
168
+ this . _tsOpts , this . _rootFilePaths , this . _fileRegistry , this . inputPaths [ 0 ] ,
169
+ tsconfig [ 'compilerOptions' ] . paths , this . _tsConfigPath ) ;
168
170
this . _tsService = ts . createLanguageService ( this . _tsServiceHost , ts . createDocumentRegistry ( ) ) ;
169
171
}
170
172
@@ -249,13 +251,15 @@ class BroccoliTypeScriptCompiler extends Plugin {
249
251
}
250
252
251
253
class CustomLanguageServiceHost {
252
- constructor ( compilerOptions , fileNames , fileRegistry , treeInputPath ) {
254
+ constructor ( compilerOptions , fileNames , fileRegistry , treeInputPath , paths , tsConfigPath ) {
253
255
this . compilerOptions = compilerOptions ;
254
256
this . fileNames = fileNames ;
255
257
this . fileRegistry = fileRegistry ;
256
258
this . treeInputPath = treeInputPath ;
257
259
this . currentDirectory = treeInputPath ;
258
260
this . defaultLibFilePath = ts . getDefaultLibFilePath ( compilerOptions ) . replace ( / \\ / g, '/' ) ;
261
+ this . paths = paths ;
262
+ this . tsConfigPath = tsConfigPath ;
259
263
this . projectVersion = 0 ;
260
264
}
261
265
@@ -272,23 +276,87 @@ class CustomLanguageServiceHost {
272
276
return this . projectVersion . toString ( ) ;
273
277
}
274
278
275
- /**
276
- * This method is called quite a bit to lookup 3 kinds of paths:
277
- * 1/ files in the fileRegistry
278
- * - these are the files in our project that we are watching for changes
279
- * - in the future we could add caching for these files and invalidate the cache when
280
- * the file is changed lazily during lookup
281
- * 2/ .d.ts and library files not in the fileRegistry
282
- * - these are not our files, they come from tsd or typescript itself
283
- * - these files change only rarely but since we need them very rarely, it's not worth the
284
- * cache invalidation hassle to cache them
285
- * 3/ bogus paths that typescript compiler tries to lookup during import resolution
286
- * - these paths are tricky to cache since files come and go and paths that was bogus in the
287
- * past might not be bogus later
288
- *
289
- * In the initial experiments the impact of this caching was insignificant (single digit %) and
290
- * not worth the potential issues with stale cache records.
291
- */
279
+ _resolveModulePathWithMapping ( moduleName ) {
280
+ // check if module name should be used as-is or it should be mapped to different value
281
+ let longestMatchedPrefixLength = 0 ;
282
+ let matchedPattern ;
283
+ let matchedWildcard ;
284
+ const paths = this . paths || { } ;
285
+
286
+ for ( let pattern of Object . keys ( paths ) ) {
287
+ if ( pattern . indexOf ( '*' ) != pattern . lastIndexOf ( '*' ) ) {
288
+ throw `Invalid path mapping pattern: "${ pattern } "` ;
289
+ }
290
+
291
+ let indexOfWildcard = pattern . indexOf ( '*' ) ;
292
+ if ( indexOfWildcard !== - 1 ) {
293
+ // check if module name starts with prefix, ends with suffix and these two don't overlap
294
+ let prefix = pattern . substr ( 0 , indexOfWildcard ) ;
295
+ let suffix = pattern . substr ( indexOfWildcard + 1 ) ;
296
+ if ( moduleName . length >= prefix . length + suffix . length &&
297
+ moduleName . startsWith ( prefix ) &&
298
+ moduleName . endsWith ( suffix ) ) {
299
+
300
+ // use length of matched prefix as betterness criteria
301
+ if ( longestMatchedPrefixLength < prefix . length ) {
302
+ longestMatchedPrefixLength = prefix . length ;
303
+ matchedPattern = pattern ;
304
+ matchedWildcard = moduleName . substr ( prefix . length , moduleName . length - suffix . length ) ;
305
+ }
306
+ }
307
+ } else {
308
+ // Pattern does not contain asterisk - module name should exactly match pattern to succeed.
309
+ if ( pattern === moduleName ) {
310
+ matchedPattern = pattern ;
311
+ matchedWildcard = undefined ;
312
+ break ;
313
+ }
314
+ }
315
+ }
316
+
317
+ if ( ! matchedPattern ) {
318
+ // We fallback to the old module resolution.
319
+ return undefined ;
320
+ // // no pattern was matched so module name can be used as-is
321
+ // let p = path.join(this.treeInputPath, moduleName);
322
+ // return fs.existsSync(p) ? p : undefined;
323
+ }
324
+
325
+ // some pattern was matched - module name needs to be substituted
326
+ let substitutions = this . paths [ matchedPattern ] ;
327
+ for ( let subst of substitutions ) {
328
+ if ( subst . indexOf ( '*' ) != subst . lastIndexOf ( '*' ) ) {
329
+ throw `Invalid substitution: "${ subst } " for pattern "${ matchedPattern } ".` ;
330
+ }
331
+ // replace * in substitution with matched wildcard
332
+ let p = matchedWildcard ? subst . replace ( '*' , matchedWildcard ) : subst ;
333
+ // if substituion is a relative path - combine it with baseUrl
334
+ p = path . isAbsolute ( p ) ? p : path . join ( this . treeInputPath , path . dirname ( this . tsConfigPath ) , p ) ;
335
+ if ( fs . existsSync ( p ) ) {
336
+ return p ;
337
+ }
338
+ }
339
+
340
+ return undefined ;
341
+ }
342
+
343
+ // /**
344
+ // * This method is called quite a bit to lookup 3 kinds of paths:
345
+ // * 1/ files in the fileRegistry
346
+ // * - these are the files in our project that we are watching for changes
347
+ // * - in the future we could add caching for these files and invalidate the cache when
348
+ // * the file is changed lazily during lookup
349
+ // * 2/ .d.ts and library files not in the fileRegistry
350
+ // * - these are not our files, they come from tsd or typescript itself
351
+ // * - these files change only rarely but since we need them very rarely, it's not worth the
352
+ // * cache invalidation hassle to cache them
353
+ // * 3/ bogus paths that typescript compiler tries to lookup during import resolution
354
+ // * - these paths are tricky to cache since files come and go and paths that was bogus in the
355
+ // * past might not be bogus later
356
+ // *
357
+ // * In the initial experiments the impact of this caching was insignificant (single digit %) and
358
+ // * not worth the potential issues with stale cache records.
359
+ // */
292
360
getScriptSnapshot ( tsFilePath ) {
293
361
var absoluteTsFilePath ;
294
362
if ( tsFilePath == this . defaultLibFilePath || path . isAbsolute ( tsFilePath ) ) {
@@ -306,10 +374,41 @@ class CustomLanguageServiceHost {
306
374
// so we we just return undefined when the path is not correct.
307
375
return undefined ;
308
376
}
309
-
310
377
return ts . ScriptSnapshot . fromString ( fs . readFileSync ( absoluteTsFilePath , FS_OPTS ) ) ;
311
378
}
312
379
380
+ resolveModuleNames ( moduleNames , containingFile ) /*: ResolvedModule[]*/ {
381
+ return moduleNames . map ( ( moduleName ) => {
382
+ for ( const ext of [ 'ts' , 'd.ts' ] ) {
383
+ const name = `${ moduleName } .${ ext } ` ;
384
+ const maybeModule = this . _resolveModulePathWithMapping ( name , containingFile ) ;
385
+ if ( maybeModule ) {
386
+ return {
387
+ resolvedFileName : maybeModule ,
388
+ isExternalLibraryImport : false
389
+ } ;
390
+ }
391
+ }
392
+
393
+ return ts . resolveModuleName ( moduleName , containingFile , this . compilerOptions , {
394
+ fileExists ( fileName ) {
395
+ return fs . existsSync ( fileName ) ;
396
+ } ,
397
+ readFile ( fileName ) {
398
+ return fs . readFileSync ( fileName , 'utf-8' ) ;
399
+ } ,
400
+ directoryExists ( directoryName ) {
401
+ try {
402
+ const stats = fs . statSync ( directoryName ) ;
403
+ return stats && stats . isDirectory ( ) ;
404
+ } catch ( e ) {
405
+ return false ;
406
+ }
407
+ }
408
+ } ) . resolvedModule ;
409
+ } ) ;
410
+ }
411
+
313
412
getCurrentDirectory ( ) {
314
413
return this . currentDirectory ;
315
414
}
0 commit comments