@@ -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,6 +276,74 @@ class CustomLanguageServiceHost {
272
276
return this . projectVersion . toString ( ) ;
273
277
}
274
278
279
+ /**
280
+ * Resolve a moduleName based on the path mapping defined in the tsconfig.
281
+ * @param moduleName The module name to resolve.
282
+ * @returns {?string } A string that is the path of the module, if found, or undefined.
283
+ * @private
284
+ */
285
+ _resolveModulePathWithMapping ( moduleName ) {
286
+ // check if module name should be used as-is or it should be mapped to different value
287
+ let longestMatchedPrefixLength = 0 ;
288
+ let matchedPattern ;
289
+ let matchedWildcard ;
290
+ const paths = this . paths || { } ;
291
+
292
+ for ( let pattern of Object . keys ( paths ) ) {
293
+ if ( pattern . indexOf ( '*' ) != pattern . lastIndexOf ( '*' ) ) {
294
+ throw `Invalid path mapping pattern: "${ pattern } "` ;
295
+ }
296
+
297
+ let indexOfWildcard = pattern . indexOf ( '*' ) ;
298
+ if ( indexOfWildcard !== - 1 ) {
299
+ // check if module name starts with prefix, ends with suffix and these two don't overlap
300
+ let prefix = pattern . substr ( 0 , indexOfWildcard ) ;
301
+ let suffix = pattern . substr ( indexOfWildcard + 1 ) ;
302
+ if ( moduleName . length >= prefix . length + suffix . length &&
303
+ moduleName . startsWith ( prefix ) &&
304
+ moduleName . endsWith ( suffix ) ) {
305
+
306
+ // use length of matched prefix as betterness criteria
307
+ if ( longestMatchedPrefixLength < prefix . length ) {
308
+ longestMatchedPrefixLength = prefix . length ;
309
+ matchedPattern = pattern ;
310
+ matchedWildcard = moduleName . substr ( prefix . length , moduleName . length - suffix . length ) ;
311
+ }
312
+ }
313
+ } else {
314
+ // Pattern does not contain asterisk - module name should exactly match pattern to succeed.
315
+ if ( pattern === moduleName ) {
316
+ matchedPattern = pattern ;
317
+ matchedWildcard = undefined ;
318
+ break ;
319
+ }
320
+ }
321
+ }
322
+
323
+ if ( ! matchedPattern ) {
324
+ // We fallback to the old module resolution.
325
+ return undefined ;
326
+ }
327
+
328
+ // some pattern was matched - module name needs to be substituted
329
+ let substitutions = this . paths [ matchedPattern ] ;
330
+ for ( let subst of substitutions ) {
331
+ if ( subst . indexOf ( '*' ) != subst . lastIndexOf ( '*' ) ) {
332
+ throw `Invalid substitution: "${ subst } " for pattern "${ matchedPattern } ".` ;
333
+ }
334
+ // replace * in substitution with matched wildcard
335
+ let p = matchedWildcard ? subst . replace ( '*' , matchedWildcard ) : subst ;
336
+ // if substituion is a relative path - combine it with baseUrl
337
+ p = path . isAbsolute ( p ) ? p : path . join ( this . treeInputPath , path . dirname ( this . tsConfigPath ) , p ) ;
338
+ if ( fs . existsSync ( p ) ) {
339
+ return p ;
340
+ }
341
+ }
342
+
343
+ // This is an error; there was a match but no corresponding mapping was valid.
344
+ throw `Path matched pattern "${ matchedPattern } " but no valid substitution was found.` ;
345
+ }
346
+
275
347
/**
276
348
* This method is called quite a bit to lookup 3 kinds of paths:
277
349
* 1/ files in the fileRegistry
@@ -310,6 +382,38 @@ class CustomLanguageServiceHost {
310
382
return ts . ScriptSnapshot . fromString ( fs . readFileSync ( absoluteTsFilePath , FS_OPTS ) ) ;
311
383
}
312
384
385
+ resolveModuleNames ( moduleNames , containingFile ) /*: ResolvedModule[]*/ {
386
+ return moduleNames . map ( ( moduleName ) => {
387
+ for ( const ext of [ 'ts' , 'd.ts' ] ) {
388
+ const name = `${ moduleName } .${ ext } ` ;
389
+ const maybeModule = this . _resolveModulePathWithMapping ( name , containingFile ) ;
390
+ if ( maybeModule ) {
391
+ return {
392
+ resolvedFileName : maybeModule ,
393
+ isExternalLibraryImport : false
394
+ } ;
395
+ }
396
+ }
397
+
398
+ return ts . resolveModuleName ( moduleName , containingFile , this . compilerOptions , {
399
+ fileExists ( fileName ) {
400
+ return fs . existsSync ( fileName ) ;
401
+ } ,
402
+ readFile ( fileName ) {
403
+ return fs . readFileSync ( fileName , 'utf-8' ) ;
404
+ } ,
405
+ directoryExists ( directoryName ) {
406
+ try {
407
+ const stats = fs . statSync ( directoryName ) ;
408
+ return stats && stats . isDirectory ( ) ;
409
+ } catch ( e ) {
410
+ return false ;
411
+ }
412
+ }
413
+ } ) . resolvedModule ;
414
+ } ) ;
415
+ }
416
+
313
417
getCurrentDirectory ( ) {
314
418
return this . currentDirectory ;
315
419
}
0 commit comments