Skip to content

Commit d80f57e

Browse files
committed
adding path mapping plugin
1 parent b04d3b3 commit d80f57e

File tree

4 files changed

+212
-10
lines changed

4 files changed

+212
-10
lines changed

packages/webpack/src/paths-plugin.ts

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
import * as path from 'path';
2+
import * as ts from 'typescript';
3+
import {Request, ResolverPlugin, Callback, Tapable} from './webpack';
4+
5+
6+
const ModulesInRootPlugin: new (a: string, b: string, c: string) => ResolverPlugin
7+
= require('enhanced-resolve/lib/ModulesInRootPlugin');
8+
9+
interface CreateInnerCallback {
10+
(callback: Callback<any>,
11+
options: Callback<any>,
12+
message?: string,
13+
messageOptional?: string): Callback<any>;
14+
}
15+
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+
21+
22+
function escapeRegExp(str: string): string {
23+
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
24+
}
25+
26+
27+
export interface PathsPluginOptions {
28+
tsConfigPath: string;
29+
compilerOptions?: ts.CompilerOptions;
30+
compilerHost?: ts.CompilerHost;
31+
}
32+
33+
export class PathsPlugin implements Tapable {
34+
private _tsConfigPath: string;
35+
private _compilerOptions: ts.CompilerOptions;
36+
private _host: ts.CompilerHost;
37+
38+
source: string;
39+
target: string;
40+
41+
private mappings: any;
42+
43+
private _absoluteBaseUrl: string;
44+
45+
private static _loadOptionsFromTsConfig(tsConfigPath: string, host: ts.CompilerHost):
46+
ts.CompilerOptions {
47+
const tsConfig = ts.readConfigFile(tsConfigPath, (path: string) => host.readFile(path));
48+
if (tsConfig.error) {
49+
throw tsConfig.error;
50+
}
51+
return tsConfig.config;
52+
}
53+
54+
constructor(options: PathsPluginOptions) {
55+
if (!options.hasOwnProperty('tsConfigPath')) {
56+
// This could happen in JavaScript.
57+
throw new Error('tsConfigPath option is mandatory.');
58+
}
59+
this._tsConfigPath = options.tsConfigPath;
60+
61+
if (options.hasOwnProperty('compilerHost')) {
62+
this._host = options.compilerHost;
63+
} else {
64+
this._host = ts.createCompilerHost(this._compilerOptions, false);
65+
}
66+
67+
if (options.hasOwnProperty('compilerOptions')) {
68+
this._compilerOptions = Object.assign({}, options.compilerOptions);
69+
} else {
70+
this._compilerOptions = PathsPlugin._loadOptionsFromTsConfig(this._tsConfigPath, this._host);
71+
}
72+
73+
this.source = 'described-resolve';
74+
this.target = 'resolve';
75+
76+
this._absoluteBaseUrl = path.resolve(
77+
path.dirname(this._tsConfigPath),
78+
this._compilerOptions.baseUrl || '.'
79+
);
80+
81+
this.mappings = [];
82+
let paths = this._compilerOptions.paths || {};
83+
Object.keys(paths).forEach(alias => {
84+
let onlyModule = alias.indexOf('*') === -1;
85+
let excapedAlias = escapeRegExp(alias);
86+
let targets = paths[alias];
87+
targets.forEach(target => {
88+
let aliasPattern: RegExp;
89+
if (onlyModule) {
90+
aliasPattern = new RegExp(`^${excapedAlias}$`);
91+
} else {
92+
let withStarCapturing = excapedAlias.replace('\\*', '(.*)');
93+
aliasPattern = new RegExp(`^${withStarCapturing}`);
94+
}
95+
96+
this.mappings.push({
97+
onlyModule,
98+
alias,
99+
aliasPattern,
100+
target: target
101+
});
102+
});
103+
});
104+
}
105+
106+
apply(resolver: ResolverPlugin) {
107+
let { baseUrl } = this._compilerOptions;
108+
109+
if (baseUrl) {
110+
resolver.apply(new ModulesInRootPlugin('module', this._absoluteBaseUrl, 'resolve'));
111+
}
112+
113+
this.mappings.forEach((mapping: any) => {
114+
resolver.plugin(this.source, this.createPlugin(resolver, mapping));
115+
});
116+
}
117+
118+
resolve(resolver: ResolverPlugin, mapping: any, request: any, callback: Callback<any>) {
119+
let innerRequest = getInnerRequest(resolver, request);
120+
if (!innerRequest) {
121+
return callback();
122+
}
123+
124+
let match = innerRequest.match(mapping.aliasPattern);
125+
if (!match) {
126+
return callback();
127+
}
128+
console.log(3, innerRequest);
129+
130+
let newRequestStr = mapping.target;
131+
if (!mapping.onlyModule) {
132+
newRequestStr = newRequestStr.replace('*', match[1]);
133+
}
134+
if (newRequestStr[0] === '.') {
135+
newRequestStr = path.resolve(this._absoluteBaseUrl, newRequestStr);
136+
}
137+
138+
let newRequest = Object.assign({}, request, {
139+
request: newRequestStr
140+
}) as Request;
141+
142+
return resolver.doResolve(
143+
this.target,
144+
newRequest,
145+
`aliased with mapping '${innerRequest}': '${mapping.alias}' to '${newRequestStr}'`,
146+
createInnerCallback(
147+
function(err, result) {
148+
if (arguments.length > 0) {
149+
return callback(err, result);
150+
}
151+
152+
// don't allow other aliasing or raw request
153+
callback(null, null);
154+
},
155+
callback
156+
)
157+
);
158+
}
159+
160+
createPlugin(resolver: ResolverPlugin, mapping: any) {
161+
return (request: any, callback: Callback<any>) => {
162+
try {
163+
this.resolve(resolver, mapping, request, callback);
164+
} catch (err) {
165+
callback(err);
166+
}
167+
};
168+
}
169+
}

packages/webpack/src/plugin.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {createResolveDependenciesFromContextMap} from './utils';
1212
import {WebpackCompilerHost} from './compiler_host';
1313
import {resolveEntryModuleFromMain} from './entry_resolver';
1414
import {StaticSymbol} from '@angular/compiler-cli';
15+
import {Tapable} from './webpack';
16+
import {PathsPlugin} from './paths-plugin';
1517

1618

1719
/**
@@ -54,7 +56,7 @@ export class ModuleRoute {
5456
}
5557

5658

57-
export class AotPlugin {
59+
export class AotPlugin implements Tapable {
5860
private _entryModule: ModuleRoute;
5961
private _compilerOptions: ts.CompilerOptions;
6062
private _angularCompilerOptions: ngCompiler.AngularCompilerOptions;
@@ -65,6 +67,7 @@ export class AotPlugin {
6567
private _compilerHost: WebpackCompilerHost;
6668
private _resourceLoader: WebpackResourceLoader;
6769
private _lazyRoutes: { [route: string]: string };
70+
private _tsConfigPath: string;
6871

6972
private _donePromise: Promise<void>;
7073
private _compiler: any = null;
@@ -96,17 +99,18 @@ export class AotPlugin {
9699
if (!options.hasOwnProperty('tsConfigPath')) {
97100
throw new Error('Must specify "tsConfigPath" in the configuration of @ngtools/webpack.');
98101
}
102+
this._tsConfigPath = options.tsConfigPath;
99103

100104
// Check the base path.
101-
let basePath = path.resolve(process.cwd(), path.dirname(options.tsConfigPath));
102-
if (fs.statSync(options.tsConfigPath).isDirectory()) {
103-
basePath = options.tsConfigPath;
105+
let basePath = path.resolve(process.cwd(), path.dirname(this._tsConfigPath));
106+
if (fs.statSync(this._tsConfigPath).isDirectory()) {
107+
basePath = this._tsConfigPath;
104108
}
105109
if (options.hasOwnProperty('basePath')) {
106110
basePath = options.basePath;
107111
}
108112

109-
const tsConfig = tsc.readConfiguration(options.tsConfigPath, basePath);
113+
const tsConfig = tsc.readConfiguration(this._tsConfigPath, basePath);
110114
this._rootFilePath = tsConfig.parsed.fileNames
111115
.filter(fileName => !/\.spec\.ts$/.test(fileName));
112116

@@ -155,13 +159,19 @@ export class AotPlugin {
155159
this._compiler = compiler;
156160

157161
compiler.plugin('context-module-factory', (cmf: any) => {
162+
cmf.resolvers.normal.apply(new PathsPlugin({
163+
tsConfigPath: this._tsConfigPath,
164+
compilerOptions: this._compilerOptions,
165+
compilerHost: this._compilerHost
166+
}));
167+
158168
cmf.plugin('before-resolve', (request: any, callback: (err?: any, request?: any) => void) => {
159169
if (!request) {
160170
return callback();
161171
}
162172

163173
request.request = this.genDir;
164-
request.recursive = true;
174+
request.recursive = true;
165175
request.dependencies.forEach((d: any) => d.critical = false);
166176
return callback(null, request);
167177
});
@@ -237,8 +247,11 @@ export class AotPlugin {
237247
);
238248

239249
patchReflectorHost(codeGenerator);
240-
promise = codeGenerator.codegen({transitiveModules: true});
250+
promise = promise.then(() => codeGenerator.codegen({
251+
transitiveModules: true
252+
}));
241253
}
254+
242255
this._donePromise = promise
243256
.then(() => {
244257
const diagnostics = this._program.getGlobalDiagnostics();

packages/webpack/src/refactor.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,6 @@ export class TypeScriptFileRefactor {
152152

153153
transpile(compilerOptions: ts.CompilerOptions): TranspileOutput {
154154
const source = this._sourceString.toString();
155-
console.log(0, this._fileName);
156-
console.log(source);
157-
console.log('---');
158155
const result = ts.transpileModule(source, {
159156
compilerOptions,
160157
fileName: this._fileName

packages/webpack/src/webpack.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export interface Request {
2+
request?: Request;
3+
relativePath: string;
4+
}
5+
6+
export interface Callback<T> {
7+
(err?: Error | null, result?: T): void;
8+
}
9+
10+
export interface ResolverCallback {
11+
(request: Request, callback: Callback<void>): void;
12+
}
13+
14+
export interface Tapable {
15+
apply(plugin: ResolverPlugin): void;
16+
}
17+
18+
export interface ResolverPlugin extends Tapable {
19+
plugin(source: string, cb: ResolverCallback): void;
20+
doResolve(target: string, req: Request, desc: string, callback: Callback<any>): void;
21+
join(relativePath: string, innerRequest: Request): Request;
22+
}
23+

0 commit comments

Comments
 (0)