diff --git a/packages/@ngtools/webpack/src/paths-plugin.ts b/packages/@ngtools/webpack/src/paths-plugin.ts index 305bd3aa071b..f79dbd0343c2 100644 --- a/packages/@ngtools/webpack/src/paths-plugin.ts +++ b/packages/@ngtools/webpack/src/paths-plugin.ts @@ -1,23 +1,24 @@ import * as path from 'path'; import * as ts from 'typescript'; -import {Request, ResolverPlugin, Callback, Tapable} from './webpack'; +import { + ResolverPlugin, + Callback, + Tapable, + NormalModuleFactory, + NormalModuleFactoryRequest, +} from './webpack'; const ModulesInRootPlugin: new (a: string, b: string, c: string) => ResolverPlugin = require('enhanced-resolve/lib/ModulesInRootPlugin'); -interface CreateInnerCallback { - (callback: Callback, - options: Callback, - message?: string, - messageOptional?: string): Callback; +export interface Mapping { + onlyModule: boolean; + alias: string; + aliasPattern: RegExp; + target: string; } -const createInnerCallback: CreateInnerCallback - = require('enhanced-resolve/lib/createInnerCallback'); -const getInnerRequest: (resolver: ResolverPlugin, request: Request) => string - = require('enhanced-resolve/lib/getInnerRequest'); - function escapeRegExp(str: string): string { return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); @@ -25,12 +26,14 @@ function escapeRegExp(str: string): string { export interface PathsPluginOptions { + nmf: NormalModuleFactory; tsConfigPath: string; compilerOptions?: ts.CompilerOptions; compilerHost?: ts.CompilerHost; } export class PathsPlugin implements Tapable { + private _nmf: NormalModuleFactory; private _tsConfigPath: string; private _compilerOptions: ts.CompilerOptions; private _host: ts.CompilerHost; @@ -38,7 +41,7 @@ export class PathsPlugin implements Tapable { source: string; target: string; - private mappings: any; + private _mappings: Mapping[]; private _absoluteBaseUrl: string; @@ -76,6 +79,7 @@ export class PathsPlugin implements Tapable { this._host = ts.createCompilerHost(this._compilerOptions, false); } + this._nmf = options.nmf; this.source = 'described-resolve'; this.target = 'resolve'; @@ -84,7 +88,7 @@ export class PathsPlugin implements Tapable { this._compilerOptions.baseUrl || '.' ); - this.mappings = []; + this._mappings = []; let paths = this._compilerOptions.paths || {}; Object.keys(paths).forEach(alias => { let onlyModule = alias.indexOf('*') === -1; @@ -99,7 +103,7 @@ export class PathsPlugin implements Tapable { aliasPattern = new RegExp(`^${withStarCapturing}`); } - this.mappings.push({ + this._mappings.push({ onlyModule, alias, aliasPattern, @@ -116,59 +120,35 @@ export class PathsPlugin implements Tapable { resolver.apply(new ModulesInRootPlugin('module', this._absoluteBaseUrl, 'resolve')); } - this.mappings.forEach((mapping: any) => { - resolver.plugin(this.source, this.createPlugin(resolver, mapping)); - }); - } + this._nmf.plugin('before-resolve', (request: NormalModuleFactoryRequest, + callback: Callback) => { + for (let mapping of this._mappings) { + const match = request.request.match(mapping.aliasPattern); + if (!match) { continue; } - resolve(resolver: ResolverPlugin, mapping: any, request: any, callback: Callback): any { - let innerRequest = getInnerRequest(resolver, request); - if (!innerRequest) { - return callback(); - } - - let match = innerRequest.match(mapping.aliasPattern); - if (!match) { - return callback(); - } - - let newRequestStr = mapping.target; - if (!mapping.onlyModule) { - newRequestStr = newRequestStr.replace('*', match[1]); - } - if (newRequestStr[0] === '.') { - newRequestStr = path.resolve(this._absoluteBaseUrl, newRequestStr); - } - - let newRequest = Object.assign({}, request, { - request: newRequestStr - }) as Request; - - return resolver.doResolve( - this.target, - newRequest, - `aliased with mapping '${innerRequest}': '${mapping.alias}' to '${newRequestStr}'`, - createInnerCallback( - function(err, result) { - if (arguments.length > 0) { - return callback(err, result); - } - - // don't allow other aliasing or raw request - callback(null, null); - }, - callback - ) - ); - } + let newRequestStr = mapping.target; + if (!mapping.onlyModule) { + newRequestStr = newRequestStr.replace('*', match[1]); + } - createPlugin(resolver: ResolverPlugin, mapping: any): any { - return (request: any, callback: Callback) => { - try { - this.resolve(resolver, mapping, request, callback); - } catch (err) { - callback(err); + const moduleResolver: ts.ResolvedModuleWithFailedLookupLocations = + ts.nodeModuleNameResolver( + newRequestStr, + this._absoluteBaseUrl, + this._compilerOptions, + this._host + ); + const moduleFilePath = moduleResolver.resolvedModule ? + moduleResolver.resolvedModule.resolvedFileName : ''; + + if (moduleFilePath) { + return callback(null, Object.assign({}, request, { + request: moduleFilePath.includes('.d.ts') ? newRequestStr : moduleFilePath + })); + } } - }; + + return callback(null, request); + }); } } diff --git a/packages/@ngtools/webpack/src/plugin.ts b/packages/@ngtools/webpack/src/plugin.ts index 229a1bf0b7db..01c87d113ad2 100644 --- a/packages/@ngtools/webpack/src/plugin.ts +++ b/packages/@ngtools/webpack/src/plugin.ts @@ -374,7 +374,11 @@ export class AotPlugin implements Tapable { cb(); } }); + }); + + compiler.plugin('normal-module-factory', (nmf: any) => { compiler.resolvers.normal.apply(new PathsPlugin({ + nmf, tsConfigPath: this._tsConfigPath, compilerOptions: this._compilerOptions, compilerHost: this._compilerHost diff --git a/packages/@ngtools/webpack/src/webpack.ts b/packages/@ngtools/webpack/src/webpack.ts index f04b5d7dc765..e374f8001ca9 100644 --- a/packages/@ngtools/webpack/src/webpack.ts +++ b/packages/@ngtools/webpack/src/webpack.ts @@ -39,6 +39,16 @@ export interface NormalModule { resource: string; } +export interface NormalModuleFactory { + plugin(event: string, + callback: (data: NormalModuleFactoryRequest, callback: Callback) => void): any; +} + +export interface NormalModuleFactoryRequest { + request: string; + contextInfo: { issuer: string }; +} + export interface LoaderContext { _module: NormalModule; diff --git a/tests/e2e/setup/010-build.ts b/tests/e2e/setup/010-build.ts index f90ed034cd0b..7506f8aa0093 100644 --- a/tests/e2e/setup/010-build.ts +++ b/tests/e2e/setup/010-build.ts @@ -13,24 +13,6 @@ export default function() { } return npm('run', 'build', '--', '--local') - .then(() => console.log('Updating package.json from dist...')) - .then(() => Promise.all(Object.keys(packages).map(pkgName => { - return updateJsonFile(join(packages[pkgName].dist, 'package.json'), json => { - Object.keys(packages).forEach(pkgName => { - if (!json['dependencies']) { - json['dependencies'] = {}; - } - if (!json['devDependencies']) { - json['devDependencies'] = {}; - } - if (json['dependencies'] && pkgName in json['dependencies']) { - json['dependencies'][pkgName] = packages[pkgName].dist; - } else if (json['devDependencies'] && pkgName in json['devDependencies']) { - json['devDependencies'][pkgName] = packages[pkgName].dist; - } - }); - }); - }))) .then(() => { if (!argv.nightly && !argv['ng-sha']) { return; diff --git a/tests/e2e/tests/build/ts-paths.ts b/tests/e2e/tests/build/ts-paths.ts index 8a39a803a1e8..691fb87e41c0 100644 --- a/tests/e2e/tests/build/ts-paths.ts +++ b/tests/e2e/tests/build/ts-paths.ts @@ -13,11 +13,16 @@ export default function() { ], '@shared/*': [ 'app/shared/*' + ], + '*': [ + '*', + 'app/shared/*' ] }; }) .then(() => createDir('src/app/shared')) .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'` })) @@ -25,12 +30,16 @@ export default function() { import { meaning } from 'app/shared/meaning'; import { meaning as meaning2 } from '@shared'; import { meaning as meaning3 } from '@shared/meaning'; + import { meaning as meaning4 } from 'meaning'; + import { meaning as meaning5 } from 'meaning-too'; // need to use imports otherwise they are ignored and // no error is outputted, even if baseUrl/paths don't work console.log(meaning) console.log(meaning2) console.log(meaning3) + console.log(meaning4) + console.log(meaning5) `)) .then(() => ng('build')); }