Skip to content

Commit

Permalink
feat(@ngtools/webpack): fix paths mapping support
Browse files Browse the repository at this point in the history
This is a similar version of #5033. Reverted in #6463 because of issue #6451.

This is a feature because we do not want it in 1.3.0
  • Loading branch information
hansl committed Aug 9, 2017
1 parent c475cf4 commit f454eb2
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 83 deletions.
110 changes: 45 additions & 65 deletions packages/@ngtools/webpack/src/paths-plugin.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,47 @@
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<any>,
options: Callback<any>,
message?: string,
messageOptional?: string): Callback<any>;
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, '\\$&');
}


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 _mappings: Mapping[];

private _absoluteBaseUrl: string;

Expand Down Expand Up @@ -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';

Expand All @@ -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;
Expand All @@ -99,7 +103,7 @@ export class PathsPlugin implements Tapable {
aliasPattern = new RegExp(`^${withStarCapturing}`);
}

this.mappings.push({
this._mappings.push({
onlyModule,
alias,
aliasPattern,
Expand All @@ -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<any>) => {
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>): 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<any>) => {
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);
});
}
}
4 changes: 4 additions & 0 deletions packages/@ngtools/webpack/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions packages/@ngtools/webpack/src/webpack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ export interface NormalModule {
resource: string;
}

export interface NormalModuleFactory {
plugin(event: string,
callback: (data: NormalModuleFactoryRequest, callback: Callback<any>) => void): any;
}

export interface NormalModuleFactoryRequest {
request: string;
contextInfo: { issuer: string };
}

export interface LoaderContext {
_module: NormalModule;

Expand Down
18 changes: 0 additions & 18 deletions tests/e2e/setup/010-build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
9 changes: 9 additions & 0 deletions tests/e2e/tests/build/ts-paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,33 @@ 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'`
}))
.then(() => appendToFile('src/app/app.component.ts', stripIndents`
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'));
}

0 comments on commit f454eb2

Please sign in to comment.