Skip to content

Commit e389505

Browse files
committed
feat(@ngtools/webpack): fix paths mapping support
This is a similar version of angular#5033. Reverted in angular#6463 because of issue angular#6451. This is a feature because we do not want it in 1.3.0
1 parent 23da906 commit e389505

File tree

5 files changed

+69
-84
lines changed

5 files changed

+69
-84
lines changed
+46-66
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,47 @@
11
import * as path from 'path';
22
import * as ts from 'typescript';
3-
import {Request, ResolverPlugin, Callback, Tapable} from './webpack';
3+
import {
4+
ResolverPlugin,
5+
Callback,
6+
Tapable,
7+
NormalModuleFactory,
8+
NormalModuleFactoryRequest,
9+
} from './webpack';
410

511

612
const ModulesInRootPlugin: new (a: string, b: string, c: string) => ResolverPlugin
713
= require('enhanced-resolve/lib/ModulesInRootPlugin');
814

9-
interface CreateInnerCallback {
10-
(callback: Callback<any>,
11-
options: Callback<any>,
12-
message?: string,
13-
messageOptional?: string): Callback<any>;
15+
export interface Mapping {
16+
onlyModule: boolean;
17+
alias: string;
18+
aliasPattern: RegExp;
19+
target: string;
1420
}
1521

16-
const createInnerCallback: CreateInnerCallback
17-
= require('enhanced-resolve/lib/createInnerCallback');
18-
const getInnerRequest: (resolver: ResolverPlugin, request: Request) => string
19-
= require('enhanced-resolve/lib/getInnerRequest');
20-
2122

2223
function escapeRegExp(str: string): string {
23-
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
24+
return str.replace(/[\-\[\]\/{}()*+?.\\^$|]/g, '\\$&');
2425
}
2526

2627

2728
export interface PathsPluginOptions {
29+
nmf: NormalModuleFactory;
2830
tsConfigPath: string;
2931
compilerOptions?: ts.CompilerOptions;
3032
compilerHost?: ts.CompilerHost;
3133
}
3234

3335
export class PathsPlugin implements Tapable {
36+
private _nmf: NormalModuleFactory;
3437
private _tsConfigPath: string;
3538
private _compilerOptions: ts.CompilerOptions;
3639
private _host: ts.CompilerHost;
3740

3841
source: string;
3942
target: string;
4043

41-
private mappings: any;
44+
private _mappings: Mapping[];
4245

4346
private _absoluteBaseUrl: string;
4447

@@ -76,6 +79,7 @@ export class PathsPlugin implements Tapable {
7679
this._host = ts.createCompilerHost(this._compilerOptions, false);
7780
}
7881

82+
this._nmf = options.nmf;
7983
this.source = 'described-resolve';
8084
this.target = 'resolve';
8185

@@ -84,7 +88,7 @@ export class PathsPlugin implements Tapable {
8488
this._compilerOptions.baseUrl || '.'
8589
);
8690

87-
this.mappings = [];
91+
this._mappings = [];
8892
let paths = this._compilerOptions.paths || {};
8993
Object.keys(paths).forEach(alias => {
9094
let onlyModule = alias.indexOf('*') === -1;
@@ -99,7 +103,7 @@ export class PathsPlugin implements Tapable {
99103
aliasPattern = new RegExp(`^${withStarCapturing}`);
100104
}
101105

102-
this.mappings.push({
106+
this._mappings.push({
103107
onlyModule,
104108
alias,
105109
aliasPattern,
@@ -116,59 +120,35 @@ export class PathsPlugin implements Tapable {
116120
resolver.apply(new ModulesInRootPlugin('module', this._absoluteBaseUrl, 'resolve'));
117121
}
118122

119-
this.mappings.forEach((mapping: any) => {
120-
resolver.plugin(this.source, this.createPlugin(resolver, mapping));
121-
});
122-
}
123+
this._nmf.plugin('before-resolve', (request: NormalModuleFactoryRequest,
124+
callback: Callback<any>) => {
125+
for (let mapping of this._mappings) {
126+
const match = request.request.match(mapping.aliasPattern);
127+
if (!match) { continue; }
123128

124-
resolve(resolver: ResolverPlugin, mapping: any, request: any, callback: Callback<any>): any {
125-
let innerRequest = getInnerRequest(resolver, request);
126-
if (!innerRequest) {
127-
return callback();
128-
}
129-
130-
let match = innerRequest.match(mapping.aliasPattern);
131-
if (!match) {
132-
return callback();
133-
}
134-
135-
let newRequestStr = mapping.target;
136-
if (!mapping.onlyModule) {
137-
newRequestStr = newRequestStr.replace('*', match[1]);
138-
}
139-
if (newRequestStr[0] === '.') {
140-
newRequestStr = path.resolve(this._absoluteBaseUrl, newRequestStr);
141-
}
142-
143-
let newRequest = Object.assign({}, request, {
144-
request: newRequestStr
145-
}) as Request;
146-
147-
return resolver.doResolve(
148-
this.target,
149-
newRequest,
150-
`aliased with mapping '${innerRequest}': '${mapping.alias}' to '${newRequestStr}'`,
151-
createInnerCallback(
152-
function(err, result) {
153-
if (arguments.length > 0) {
154-
return callback(err, result);
155-
}
156-
157-
// don't allow other aliasing or raw request
158-
callback(null, null);
159-
},
160-
callback
161-
)
162-
);
163-
}
129+
let newRequestStr = mapping.target;
130+
if (!mapping.onlyModule) {
131+
newRequestStr = newRequestStr.replace('*', match[1]);
132+
}
164133

165-
createPlugin(resolver: ResolverPlugin, mapping: any): any {
166-
return (request: any, callback: Callback<any>) => {
167-
try {
168-
this.resolve(resolver, mapping, request, callback);
169-
} catch (err) {
170-
callback(err);
134+
const moduleResolver: ts.ResolvedModuleWithFailedLookupLocations =
135+
ts.nodeModuleNameResolver(
136+
newRequestStr,
137+
this._absoluteBaseUrl,
138+
this._compilerOptions,
139+
this._host
140+
);
141+
const moduleFilePath = moduleResolver.resolvedModule ?
142+
moduleResolver.resolvedModule.resolvedFileName : '';
143+
144+
if (moduleFilePath) {
145+
return callback(null, Object.assign({}, request, {
146+
request: moduleFilePath.includes('.d.ts') ? newRequestStr : moduleFilePath
147+
}));
148+
}
171149
}
172-
};
150+
151+
return callback(null, request);
152+
});
173153
}
174154
}

packages/@ngtools/webpack/src/plugin.ts

+4
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,11 @@ export class AotPlugin implements Tapable {
369369
cb();
370370
}
371371
});
372+
});
373+
374+
compiler.plugin('normal-module-factory', (nmf: any) => {
372375
compiler.resolvers.normal.apply(new PathsPlugin({
376+
nmf,
373377
tsConfigPath: this._tsConfigPath,
374378
compilerOptions: this._compilerOptions,
375379
compilerHost: this._compilerHost

packages/@ngtools/webpack/src/webpack.ts

+10
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ export interface NormalModule {
3939
resource: string;
4040
}
4141

42+
export interface NormalModuleFactory {
43+
plugin(event: string,
44+
callback: (data: NormalModuleFactoryRequest, callback: Callback<any>) => void): any;
45+
}
46+
47+
export interface NormalModuleFactoryRequest {
48+
request: string;
49+
contextInfo: { issuer: string };
50+
}
51+
4252
export interface LoaderContext {
4353
_module: NormalModule;
4454

tests/e2e/setup/010-build.ts

-18
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,6 @@ export default function() {
1313
}
1414

1515
return npm('run', 'build', '--', '--local')
16-
.then(() => console.log('Updating package.json from dist...'))
17-
.then(() => Promise.all(Object.keys(packages).map(pkgName => {
18-
return updateJsonFile(join(packages[pkgName].dist, 'package.json'), json => {
19-
Object.keys(packages).forEach(pkgName => {
20-
if (!json['dependencies']) {
21-
json['dependencies'] = {};
22-
}
23-
if (!json['devDependencies']) {
24-
json['devDependencies'] = {};
25-
}
26-
if (json['dependencies'] && pkgName in json['dependencies']) {
27-
json['dependencies'][pkgName] = packages[pkgName].dist;
28-
} else if (json['devDependencies'] && pkgName in json['devDependencies']) {
29-
json['devDependencies'][pkgName] = packages[pkgName].dist;
30-
}
31-
});
32-
});
33-
})))
3416
.then(() => {
3517
if (!argv.nightly && !argv['ng-sha']) {
3618
return;

tests/e2e/tests/build/ts-paths.ts

+9
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,33 @@ export default function() {
1313
],
1414
'@shared/*': [
1515
'app/shared/*'
16+
],
17+
'*': [
18+
'*',
19+
'app/shared/*'
1620
]
1721
};
1822
})
1923
.then(() => createDir('src/app/shared'))
2024
.then(() => writeMultipleFiles({
25+
'src/meaning-too.ts': 'export var meaning = 42;',
2126
'src/app/shared/meaning.ts': 'export var meaning = 42;',
2227
'src/app/shared/index.ts': `export * from './meaning'`
2328
}))
2429
.then(() => appendToFile('src/app/app.component.ts', stripIndents`
2530
import { meaning } from 'app/shared/meaning';
2631
import { meaning as meaning2 } from '@shared';
2732
import { meaning as meaning3 } from '@shared/meaning';
33+
import { meaning as meaning4 } from 'meaning';
34+
import { meaning as meaning5 } from 'meaning-too';
2835
2936
// need to use imports otherwise they are ignored and
3037
// no error is outputted, even if baseUrl/paths don't work
3138
console.log(meaning)
3239
console.log(meaning2)
3340
console.log(meaning3)
41+
console.log(meaning4)
42+
console.log(meaning5)
3443
`))
3544
.then(() => ng('build'));
3645
}

0 commit comments

Comments
 (0)