From b3283c13bfe9fba3bda2e149fef5f1c4a12996d0 Mon Sep 17 00:00:00 2001 From: Chris Fitzgerald Date: Mon, 27 Feb 2017 01:11:19 -0800 Subject: [PATCH] fix(@ngtools/webpack): fix paths-plugin to allow '*' as alias Closes #5033 --- package.json | 1 - packages/@ngtools/webpack/package.json | 1 - packages/@ngtools/webpack/src/paths-plugin.ts | 123 +++++------------- packages/@ngtools/webpack/src/plugin.ts | 4 + packages/@ngtools/webpack/src/webpack.ts | 12 ++ tests/e2e/tests/build/ts-paths.ts | 6 + 6 files changed, 57 insertions(+), 90 deletions(-) diff --git a/package.json b/package.json index 2fe90c297ba1..cf710058f866 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,6 @@ "diff": "^3.1.0", "ember-cli-normalize-entity-name": "^1.0.0", "ember-cli-string-utils": "^1.0.0", - "enhanced-resolve": "^3.1.0", "exports-loader": "^0.6.3", "extract-text-webpack-plugin": "^2.1.0", "file-loader": "^0.10.0", diff --git a/packages/@ngtools/webpack/package.json b/packages/@ngtools/webpack/package.json index 511ad21ee138..889f163bfa72 100644 --- a/packages/@ngtools/webpack/package.json +++ b/packages/@ngtools/webpack/package.json @@ -25,7 +25,6 @@ "npm": ">= 3.0.0" }, "dependencies": { - "enhanced-resolve": "^3.1.0", "loader-utils": "^1.0.2", "magic-string": "^0.19.0", "source-map": "^0.5.6" diff --git a/packages/@ngtools/webpack/src/paths-plugin.ts b/packages/@ngtools/webpack/src/paths-plugin.ts index 305bd3aa071b..46ddde71d6a2 100644 --- a/packages/@ngtools/webpack/src/paths-plugin.ts +++ b/packages/@ngtools/webpack/src/paths-plugin.ts @@ -1,46 +1,25 @@ import * as path from 'path'; import * as ts from 'typescript'; -import {Request, ResolverPlugin, Callback, Tapable} 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; -} - -const createInnerCallback: CreateInnerCallback - = require('enhanced-resolve/lib/createInnerCallback'); -const getInnerRequest: (resolver: ResolverPlugin, request: Request) => string - = require('enhanced-resolve/lib/getInnerRequest'); - +import {Callback, Tapable, NormalModuleFactory, NormalModuleFactoryRequest} from './webpack'; function escapeRegExp(str: string): string { return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); } - 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; - - source: string; - target: string; - - private mappings: any; - private _absoluteBaseUrl: string; + private _mappings: any[] = []; private static _loadOptionsFromTsConfig(tsConfigPath: string, host?: ts.CompilerHost): ts.CompilerOptions { @@ -76,15 +55,13 @@ export class PathsPlugin implements Tapable { this._host = ts.createCompilerHost(this._compilerOptions, false); } - this.source = 'described-resolve'; - this.target = 'resolve'; - this._absoluteBaseUrl = path.resolve( path.dirname(this._tsConfigPath), this._compilerOptions.baseUrl || '.' ); - this.mappings = []; + this._nmf = options.nmf; + let paths = this._compilerOptions.paths || {}; Object.keys(paths).forEach(alias => { let onlyModule = alias.indexOf('*') === -1; @@ -99,7 +76,7 @@ export class PathsPlugin implements Tapable { aliasPattern = new RegExp(`^${withStarCapturing}`); } - this.mappings.push({ + this._mappings.push({ onlyModule, alias, aliasPattern, @@ -109,66 +86,36 @@ export class PathsPlugin implements Tapable { }); } - apply(resolver: ResolverPlugin): void { - let baseUrl = this._compilerOptions.baseUrl || '.'; - - if (baseUrl) { - resolver.apply(new ModulesInRootPlugin('module', this._absoluteBaseUrl, 'resolve')); - } - - this.mappings.forEach((mapping: any) => { - resolver.plugin(this.source, this.createPlugin(resolver, mapping)); - }); - } - - 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(); - } + apply(): void { + 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; } - 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 2d2eecf61f76..eae12be1d53d 100644 --- a/packages/@ngtools/webpack/src/plugin.ts +++ b/packages/@ngtools/webpack/src/plugin.ts @@ -341,7 +341,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..2e88c4e43150 100644 --- a/packages/@ngtools/webpack/src/webpack.ts +++ b/packages/@ngtools/webpack/src/webpack.ts @@ -39,6 +39,18 @@ 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/tests/build/ts-paths.ts b/tests/e2e/tests/build/ts-paths.ts index 8a39a803a1e8..46e7c2f55157 100644 --- a/tests/e2e/tests/build/ts-paths.ts +++ b/tests/e2e/tests/build/ts-paths.ts @@ -13,6 +13,10 @@ export default function() { ], '@shared/*': [ 'app/shared/*' + ], + '*': [ + '*', + 'app/shared/*' ] }; }) @@ -25,12 +29,14 @@ 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'; // 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) `)) .then(() => ng('build')); }