Skip to content
This repository was archived by the owner on Dec 1, 2019. It is now read-only.

Commit 4ac1988

Browse files
committed
feat: initial impl of PathsPlugin, refs #156
1 parent e907105 commit 4ac1988

File tree

5 files changed

+189
-20
lines changed

5 files changed

+189
-20
lines changed

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"homepage": "https://github.com/s-panferov/awesome-typescript-loader",
3333
"dependencies": {
3434
"colors": "^1.1.2",
35+
"enhanced-resolve": "^2.2.2",
3536
"loader-utils": "^0.2.6",
3637
"lodash": "^4.13.1",
3738
"object-assign": "^4.1.0",
@@ -56,7 +57,7 @@
5657
"sinon": "^1.17.4",
5758
"temp": "^0.8.3",
5859
"tslint": "3.11.0-dev.0",
59-
"typescript": "^1.9.0-dev.20160620-1.0",
60+
"typescript": "^1.9.0-dev.20160625-1.0",
6061
"webpack": "2.1.0-beta.4"
6162
}
6263
}

src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { findCompiledModule, cache } from './cache';
1111
import * as helpers from './helpers';
1212
import { isTypeDeclaration } from './deps';
1313
import { QueryOptions, IWebPack, ensureInstance, ICompilerInstance } from './instance';
14+
import { PathsPlugin } from './paths-plugin';
1415

1516
let loaderUtils = require('loader-utils');
1617

@@ -221,5 +222,6 @@ class ForkCheckerPlugin {
221222
}
222223

223224
(loader as any).ForkCheckerPlugin = ForkCheckerPlugin;
225+
(loader as any).TsConfigPathsPlugin = PathsPlugin;
224226

225227
module.exports = loader;

src/instance.ts

+25-18
Original file line numberDiff line numberDiff line change
@@ -138,23 +138,8 @@ export function ensureInstance(webpack: IWebPack, query: QueryOptions, instanceN
138138
return exInstance;
139139
}
140140

141-
let compilerPath = query.compiler || 'typescript';
142-
143-
let tsImpl: typeof ts;
144-
let tsImplPath: string;
145-
try {
146-
tsImplPath = require.resolve(compilerPath);
147-
tsImpl = require(tsImplPath);
148-
} catch (e) {
149-
console.error(e);
150-
console.error(COMPILER_ERROR);
151-
process.exit(1);
152-
}
153-
154-
let compilerInfo: ICompilerInfo = {
155-
compilerPath,
156-
tsImpl,
157-
};
141+
let compilerInfo = setupTs(query.compiler);
142+
let { tsImpl } = compilerInfo;
158143

159144
let { configFilePath, compilerConfig, loaderConfig } = readConfigFile(process.cwd(), query, tsImpl);
160145

@@ -219,6 +204,28 @@ export function ensureInstance(webpack: IWebPack, query: QueryOptions, instanceN
219204
};
220205
}
221206

207+
export function setupTs(compiler: string): ICompilerInfo {
208+
let compilerPath = compiler || 'typescript';
209+
210+
let tsImpl: typeof ts;
211+
let tsImplPath: string;
212+
try {
213+
tsImplPath = require.resolve(compilerPath);
214+
tsImpl = require(tsImplPath);
215+
} catch (e) {
216+
console.error(e);
217+
console.error(COMPILER_ERROR);
218+
process.exit(1);
219+
}
220+
221+
let compilerInfo: ICompilerInfo = {
222+
compilerPath,
223+
tsImpl,
224+
};
225+
226+
return compilerInfo;
227+
}
228+
222229
function setupCache(loaderConfig: LoaderConfig, tsImpl: typeof ts, webpack: IWebPack, babelImpl: any) {
223230
let cacheIdentifier = null;
224231
if (loaderConfig.useCache) {
@@ -292,7 +299,7 @@ export interface Configs {
292299
loaderConfig: LoaderConfig;
293300
}
294301

295-
function readConfigFile(baseDir: string, query: QueryOptions, tsImpl: typeof ts): Configs {
302+
export function readConfigFile(baseDir: string, query: QueryOptions, tsImpl: typeof ts): Configs {
296303
let configFilePath: string;
297304
if (query.tsconfig && query.tsconfig.match(/\.json$/)) {
298305
configFilePath = query.tsconfig;

src/paths-plugin.ts

+158
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import { setupTs, readConfigFile, LoaderConfig } from './instance';
2+
import * as path from 'path';
3+
4+
const ModulesInRootPlugin: new (a: string, b: string, c: string) => ResolverPlugin
5+
= require('enhanced-resolve/lib/ModulesInRootPlugin');
6+
7+
const createInnerCallback: CreateInnerCallback = require('enhanced-resolve/lib/createInnerCallback');
8+
const getInnerRequest: getInnerRequest = require('enhanced-resolve/lib/getInnerRequest');
9+
10+
type CreateInnerCallback = (callback: Callback, options: Callback, message?: string, messageOptional?: string) => Callback;
11+
type getInnerRequest = (resolver: Resolver, request: Request) => string;
12+
13+
export interface Request {
14+
request?: Request;
15+
relativePath: string;
16+
}
17+
18+
export interface Callback {
19+
(err?: Error, result?: any): void;
20+
21+
log?: any;
22+
stack?: any;
23+
missing?: any;
24+
}
25+
26+
type ResolverCallback = (request: Request, callback: Callback) => void;
27+
28+
interface ResolverPlugin {
29+
apply(resolver: Resolver): void;
30+
}
31+
32+
export interface Resolver {
33+
apply(plugin: ResolverPlugin): void;
34+
plugin(source: string, cb: ResolverCallback);
35+
doResolve(target: string, req: Request, desc: string, Callback);
36+
join(relativePath: string, innerRequest: Request): Request;
37+
}
38+
39+
interface Mapping {
40+
onlyModule: boolean;
41+
alias: string;
42+
aliasPattern: RegExp;
43+
target: string;
44+
}
45+
46+
function escapeRegExp(str) {
47+
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
48+
}
49+
50+
export class PathsPlugin implements ResolverPlugin {
51+
source: string;
52+
target: string;
53+
ts: typeof ts;
54+
configFilePath: string;
55+
options: ts.CompilerOptions;
56+
57+
baseUrl: string;
58+
mappings: Mapping[];
59+
absoluteBaseUrl: string;
60+
61+
constructor(config: LoaderConfig & ts.CompilerOptions = {} as any) {
62+
this.source = 'described-resolve';
63+
this.target = 'resolve';
64+
65+
this.ts = setupTs(config.compiler).tsImpl;
66+
67+
let { configFilePath, compilerConfig } = readConfigFile(process.cwd(), config, this.ts);
68+
this.options = compilerConfig.options;
69+
this.configFilePath = configFilePath;
70+
71+
this.baseUrl = this.options.baseUrl;
72+
this.absoluteBaseUrl = path.resolve(
73+
path.dirname(this.configFilePath),
74+
this.baseUrl
75+
);
76+
77+
this.mappings = [];
78+
let paths = this.options.paths || {};
79+
Object.keys(paths).forEach(alias => {
80+
let onlyModule = alias.indexOf('*') === -1;
81+
let excapedAlias = escapeRegExp(alias);
82+
let targets = paths[alias];
83+
targets.forEach(target => {
84+
let aliasPattern: RegExp;
85+
if (onlyModule) {
86+
aliasPattern = new RegExp(`^${excapedAlias}$`);
87+
} else {
88+
let withStarCapturing = excapedAlias.replace('\\*', '(.*)');
89+
aliasPattern = new RegExp(`^${withStarCapturing}`);
90+
}
91+
92+
this.mappings.push({
93+
onlyModule,
94+
alias,
95+
aliasPattern,
96+
target: target
97+
});
98+
});
99+
});
100+
}
101+
102+
apply(resolver: Resolver) {
103+
let { baseUrl, mappings } = this;
104+
105+
if (baseUrl) {
106+
resolver.apply(new ModulesInRootPlugin("module", this.absoluteBaseUrl, "resolve"));
107+
}
108+
109+
mappings.forEach(mapping => {
110+
resolver.plugin(this.source, this.createPlugin(resolver, mapping));
111+
});
112+
}
113+
114+
createPlugin(resolver: Resolver, mapping: Mapping) {
115+
return (request, callback) => {
116+
let innerRequest = getInnerRequest(resolver, request);
117+
if (!innerRequest) {
118+
return callback();
119+
}
120+
121+
let match = innerRequest.match(mapping.aliasPattern);
122+
if (!match) {
123+
return callback();
124+
}
125+
126+
let newRequestStr = mapping.target;
127+
if (!mapping.onlyModule) {
128+
newRequestStr = newRequestStr.replace('*', match[1]);
129+
}
130+
131+
if (newRequestStr[0] === '.') {
132+
newRequestStr = path.resolve(this.absoluteBaseUrl, newRequestStr);
133+
}
134+
135+
let newRequest: Request = Object.assign({}, request, {
136+
request: newRequestStr
137+
});
138+
139+
return resolver.doResolve(
140+
this.target,
141+
newRequest,
142+
"aliased with mapping '" + innerRequest + "': '" + mapping.alias + "' to '" + newRequestStr + "'",
143+
createInnerCallback(
144+
function(err, result) {
145+
console.log(err, result, arguments.length > 0);
146+
if (arguments.length > 0) {
147+
return callback(err, result);
148+
}
149+
150+
// don't allow other aliasing or raw request
151+
callback(null, null);
152+
},
153+
callback
154+
)
155+
);
156+
};
157+
}
158+
}

src/tsconfig.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
"sourceMap": true,
1010
"removeComments": true,
1111
"preserveConstEnums": true,
12-
"suppressImplicitAnyIndexErrors": true
12+
"suppressImplicitAnyIndexErrors": true,
13+
"lib": [ "es6", "dom" ]
1314
},
1415
"compileOnSave": false,
1516
"exclude": [

0 commit comments

Comments
 (0)