UNPKG

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