From ee1a92c7f54b1fa2f26bc73632641c8d4d0b726d Mon Sep 17 00:00:00 2001
From: Hans Larsen <hans@hansl.ca>
Date: Tue, 22 Aug 2017 13:18:41 -0700
Subject: [PATCH] fix(@ngtools/webpack): fix paths mapping recursive support

Some path mappings (see issue) were not resolving properly when they were
used in deep paths.

Fixes #7341
---
 packages/@ngtools/webpack/src/paths-plugin.ts | 38 +++++++++++--------
 tests/e2e/tests/build/ts-paths.ts             | 19 +++++++---
 2 files changed, 36 insertions(+), 21 deletions(-)

diff --git a/packages/@ngtools/webpack/src/paths-plugin.ts b/packages/@ngtools/webpack/src/paths-plugin.ts
index 290ca1082f2b..0c5d16da62c0 100644
--- a/packages/@ngtools/webpack/src/paths-plugin.ts
+++ b/packages/@ngtools/webpack/src/paths-plugin.ts
@@ -122,29 +122,37 @@ export class PathsPlugin implements Tapable {
 
     this._nmf.plugin('before-resolve', (request: NormalModuleFactoryRequest,
                                         callback: Callback<any>) => {
+      // Only work on TypeScript issuers.
+      if (!request.contextInfo.issuer || !request.contextInfo.issuer.endsWith('.ts')) {
+        return callback(null, request);
+      }
+
       for (let mapping of this._mappings) {
         const match = request.request.match(mapping.aliasPattern);
         if (!match) { continue; }
-
         let newRequestStr = mapping.target;
         if (!mapping.onlyModule) {
           newRequestStr = newRequestStr.replace('*', match[1]);
         }
-
-        const moduleResolver: ts.ResolvedModuleWithFailedLookupLocations =
-          ts.nodeModuleNameResolver(
-            newRequestStr,
-            this._absoluteBaseUrl,
-            this._compilerOptions,
-            this._host
-          );
-        const moduleFilePath = moduleResolver.resolvedModule ?
-          moduleResolver.resolvedModule.resolvedFileName : '';
-
+        const moduleResolver = ts.resolveModuleName(
+          request.request,
+          request.contextInfo.issuer,
+          this._compilerOptions,
+          this._host
+        );
+        let moduleFilePath = moduleResolver.resolvedModule
+                          && moduleResolver.resolvedModule.resolvedFileName;
+
+        // If TypeScript gives us a .d.ts it's probably a node module and we need to let webpack
+        // do the resolution.
+        if (moduleFilePath && moduleFilePath.endsWith('.d.ts')) {
+          moduleFilePath = moduleFilePath.replace(/\.d\.ts$/, '.js');
+          if (!this._host.fileExists(moduleFilePath)) {
+            continue;
+          }
+        }
         if (moduleFilePath) {
-          return callback(null, Object.assign({}, request, {
-            request: moduleFilePath.includes('.d.ts') ? newRequestStr : moduleFilePath
-          }));
+          return callback(null, Object.assign({}, request, { request: moduleFilePath }));
         }
       }
 
diff --git a/tests/e2e/tests/build/ts-paths.ts b/tests/e2e/tests/build/ts-paths.ts
index 691fb87e41c0..4978528c0795 100644
--- a/tests/e2e/tests/build/ts-paths.ts
+++ b/tests/e2e/tests/build/ts-paths.ts
@@ -1,12 +1,12 @@
 import {updateTsConfig} from '../../utils/project';
-import {writeMultipleFiles, appendToFile, createDir} from '../../utils/fs';
+import {writeMultipleFiles, appendToFile, createDir, replaceInFile} from '../../utils/fs';
 import {ng} from '../../utils/process';
 import {stripIndents} from 'common-tags';
 
 
 export default function() {
   return updateTsConfig(json => {
-    json['compilerOptions']['baseUrl'] = '.';
+    json['compilerOptions']['baseUrl'] = './';
     json['compilerOptions']['paths'] = {
       '@shared': [
         'app/shared'
@@ -14,9 +14,8 @@ export default function() {
       '@shared/*': [
         'app/shared/*'
       ],
-      '*': [
-        '*',
-        'app/shared/*'
+      '@root/*': [
+        './*'
       ]
     };
   })
@@ -24,7 +23,15 @@ export default function() {
   .then(() => writeMultipleFiles({
     'src/meaning-too.ts': 'export var meaning = 42;',
     'src/app/shared/meaning.ts': 'export var meaning = 42;',
-    'src/app/shared/index.ts': `export * from './meaning'`
+    'src/app/shared/index.ts': `export * from './meaning'`,
+  }))
+  .then(() => replaceInFile('src/app/app.module.ts', './app.component', '@root/app/app.component'))
+  .then(() => ng('build'))
+  .then(() => updateTsConfig(json => {
+    json['compilerOptions']['paths']['*'] = [
+      '*',
+      'app/shared/*'
+    ];
   }))
   .then(() => appendToFile('src/app/app.component.ts', stripIndents`
     import { meaning } from 'app/shared/meaning';