UNPKG

9.37 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.TsconfigPathsPlugin = void 0;
4const chalk = require("chalk");
5const TsconfigPaths = require("tsconfig-paths");
6const path = require("path");
7const Options = require("./options");
8const Logger = require("./logger");
9const util_1 = require("util");
10const getInnerRequest = require("enhanced-resolve/lib/getInnerRequest");
11class TsconfigPathsPlugin {
12 constructor(rawOptions = {}) {
13 this.source = "described-resolve";
14 this.target = "resolve";
15 const options = Options.getOptions(rawOptions);
16 this.extensions = options.extensions;
17 // const colors = new chalk.constructor({ enabled: options.colors });
18 this.log = Logger.makeLogger(options, new chalk.Instance({ level: options.colors ? undefined : 0 }));
19 const context = options.context || process.cwd();
20 const loadFrom = options.configFile || context;
21 const loadResult = TsconfigPaths.loadConfig(loadFrom);
22 if (loadResult.resultType === "failed") {
23 this.log.logError(`Failed to load ${loadFrom}: ${loadResult.message}`);
24 }
25 else {
26 this.log.logInfo(`tsconfig-paths-webpack-plugin: Using config file at ${loadResult.configFileAbsolutePath}`);
27 this.baseUrl = options.baseUrl || loadResult.baseUrl;
28 this.absoluteBaseUrl = options.baseUrl
29 ? path.resolve(options.baseUrl)
30 : loadResult.absoluteBaseUrl;
31 this.matchPath = TsconfigPaths.createMatchPathAsync(this.absoluteBaseUrl, loadResult.paths, options.mainFields);
32 }
33 }
34 apply(resolver) {
35 if (!resolver) {
36 this.log.logWarning("tsconfig-paths-webpack-plugin: Found no resolver, not applying tsconfig-paths-webpack-plugin");
37 return;
38 }
39 const { baseUrl } = this;
40 if (!baseUrl) {
41 // Nothing to do if there is no baseUrl
42 this.log.logWarning("tsconfig-paths-webpack-plugin: Found no baseUrl in tsconfig.json, not applying tsconfig-paths-webpack-plugin");
43 return;
44 }
45 // The file system only exists when the plugin is in the resolve context. This means it's also properly placed in the resolve.plugins array.
46 // If not, we should warn the user that this plugin should be placed in resolve.plugins and not the plugins array of the root config for example.
47 // This should hopefully prevent issues like: https://github.com/dividab/tsconfig-paths-webpack-plugin/issues/9
48 if (!("fileSystem" in resolver)) {
49 this.log.logWarning("tsconfig-paths-webpack-plugin: No file system found on resolver." +
50 " Please make sure you've placed the plugin in the correct part of the configuration." +
51 " This plugin is a resolver plugin and should be placed in the resolve part of the Webpack configuration.");
52 return;
53 }
54 // getHook will only exist in Webpack 4 & 5, if so we should comply to the Webpack 4 plugin system.
55 if ("getHook" in resolver && typeof resolver.getHook === "function") {
56 resolver
57 .getHook(this.source)
58 .tapAsync({ name: "TsconfigPathsPlugin" }, createPluginCallback(this.matchPath, resolver, this.absoluteBaseUrl, resolver.getHook(this.target), this.extensions));
59 }
60 else if ("plugin" in resolver) {
61 // This is the legacy (Webpack < 4.0.0) way of using the plugin system.
62 const legacyResolver = resolver;
63 legacyResolver.plugin(this.source, createPluginLegacy(this.matchPath, resolver, this.absoluteBaseUrl, this.target, this.extensions));
64 }
65 }
66}
67exports.TsconfigPathsPlugin = TsconfigPathsPlugin;
68function createPluginCallback(matchPath, resolver, absoluteBaseUrl, hook, extensions) {
69 const fileExistAsync = createFileExistAsync(resolver.fileSystem);
70 const readJsonAsync = createReadJsonAsync(resolver.fileSystem);
71 return (request, resolveContext, callback) => {
72 const innerRequest = getInnerRequest(resolver, request);
73 if (!innerRequest ||
74 innerRequest.startsWith(".") ||
75 innerRequest.startsWith("..")) {
76 return callback();
77 }
78 matchPath(innerRequest, readJsonAsync, fileExistAsync, extensions, (err, foundMatch) => {
79 if (err) {
80 return callback(err);
81 }
82 if (!foundMatch) {
83 return callback();
84 }
85 const newRequest = Object.assign(Object.assign({}, request), { request: foundMatch, path: absoluteBaseUrl });
86 // Only at this point we are sure we are dealing with the latest Webpack version (>= 4.0.0)
87 // So only now can we require the createInnerContext function.
88 // (It doesn't exist in legacy versions)
89 const createInnerContext = require("enhanced-resolve/lib/createInnerContext");
90 return resolver.doResolve(hook, newRequest, `Resolved request '${innerRequest}' to '${foundMatch}' using tsconfig.json paths mapping`,
91 // tslint:disable-next-line:no-any
92 createInnerContext(Object.assign({}, resolveContext)), (err2, result2) => {
93 // Pattern taken from:
94 // https://github.com/webpack/enhanced-resolve/blob/42ff594140582c3f8f86811f95dea7bf6774a1c8/lib/AliasPlugin.js#L44
95 if (err2) {
96 return callback(err2);
97 }
98 // Don't allow other aliasing or raw request
99 if (result2 === undefined) {
100 return callback(undefined, undefined);
101 }
102 console.log(`Returning with path ${util_1.inspect({ result2 }, false, 4, true)}`);
103 // tslint:disable-next-line:no-any
104 callback(undefined, result2);
105 });
106 });
107 };
108}
109function createPluginLegacy(matchPath, resolver, absoluteBaseUrl, target, extensions) {
110 const fileExistAsync = createFileExistAsync(resolver.fileSystem);
111 const readJsonAsync = createReadJsonAsync(resolver.fileSystem);
112 return (request, callback) => {
113 const innerRequest = getInnerRequest(resolver, request);
114 if (!innerRequest ||
115 innerRequest.startsWith(".") ||
116 innerRequest.startsWith("..")) {
117 return callback();
118 }
119 matchPath(innerRequest, readJsonAsync, fileExistAsync, extensions, (err, foundMatch) => {
120 if (err) {
121 return callback(err);
122 }
123 if (!foundMatch) {
124 return callback();
125 }
126 const newRequest = Object.assign(Object.assign({}, request), { request: foundMatch, path: absoluteBaseUrl });
127 // Only at this point we are sure we are dealing with a legacy Webpack version (< 4.0.0)
128 // So only now can we require the createInnerCallback function.
129 // (It's already deprecated and might be removed down the line).
130 const createInnerCallback = require("enhanced-resolve/lib/createInnerCallback");
131 return resolver.doResolve(target, newRequest, `Resolved request '${innerRequest}' to '${foundMatch}' using tsconfig.json paths mapping`, createInnerCallback(function (err2, result2) {
132 // Note:
133 // *NOT* using an arrow function here because arguments.length implies we have "this"
134 // That means "this" has to be in the current function scope, and not the scope above.
135 // Pattern taken from:
136 // https://github.com/s-panferov/awesome-typescript-loader/blob/10653beff85f555f1f3b5d4bfd7d21513d0e54a4/src/paths-plugin.ts#L169
137 if (arguments.length > 0) {
138 return callback(err2, result2);
139 }
140 // don't allow other aliasing or raw request
141 callback(undefined, undefined);
142 }, callback));
143 });
144 };
145}
146function readJson(fileSystem, path2, callback) {
147 if ("readJson" in fileSystem && fileSystem.readJson) {
148 return fileSystem.readJson(path2, callback);
149 }
150 fileSystem.readFile(path2, (err, buf) => {
151 if (err) {
152 return callback(err);
153 }
154 let data;
155 try {
156 // @ts-ignore This will crash if buf is undefined, which I guess it can be...
157 data = JSON.parse(buf.toString("utf-8"));
158 }
159 catch (e) {
160 return callback(e);
161 }
162 return callback(undefined, data);
163 });
164}
165function createReadJsonAsync(filesystem) {
166 // tslint:disable-next-line:no-any
167 return (path2, callback2) => {
168 readJson(filesystem, path2, (err, json) => {
169 // If error assume file does not exist
170 if (err || !json) {
171 callback2();
172 return;
173 }
174 callback2(undefined, json);
175 });
176 };
177}
178function createFileExistAsync(filesystem) {
179 return (path2, callback2) => {
180 filesystem.stat(path2, (err, stats) => {
181 // If error assume file does not exist
182 if (err) {
183 callback2(undefined, false);
184 return;
185 }
186 callback2(undefined, stats ? stats.isFile() : false);
187 });
188 };
189}