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