Skip to content

Commit 4498d0a

Browse files
committed
update
1 parent e13f8cb commit 4498d0a

File tree

4 files changed

+261
-209
lines changed

4 files changed

+261
-209
lines changed

lib/Resolver.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,8 @@ const {
294294

295295
/**
296296
* @typedef {object} TsconfigFileData
297-
* @property {Array<AliasOption>} aliases tsconfig file data
297+
* @property {Array<AliasOption>} alias tsconfig file data
298+
* @property {Array<string>} modules tsconfig file data
298299
* @property {Set<string>} fileDependencies file dependencies
299300
*/
300301

@@ -308,7 +309,7 @@ const {
308309
* @property {string=} relativePath relative path
309310
* @property {boolean=} ignoreSymlinks true when need to ignore symlinks, otherwise false
310311
* @property {boolean=} fullySpecified true when full specified, otherwise false
311-
* @property {TsconfigFileData=} tsconfigFileData tsconfig file data
312+
* @property {TsconfigFileData | null=} tsconfigFileData tsconfig file data
312313
* @property {string=} __innerRequest inner request for internal usage
313314
* @property {string=} __innerRequest_request inner request for internal usage
314315
* @property {string=} __innerRequest_relativePath inner relative path for internal usage

lib/TsconfigPathsPlugin.js

Lines changed: 24 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,16 @@
55

66
"use strict";
77

8-
const path = require("path");
98
const { aliasResolveHandler } = require("./AliasUtils");
109
const { modulesResolveHandler } = require("./ModulesUtils");
10+
const TsconfigPathsUtils = require("./TsconfigPathsUtils");
1111

1212
/** @typedef {import("./Resolver")} Resolver */
1313
/** @typedef {import("./Resolver").ResolveStepHook} ResolveStepHook */
1414
/** @typedef {import("./AliasUtils").AliasOption} AliasOption */
1515
/** @typedef {import("./Resolver").ResolveRequest} ResolveRequest */
1616
/** @typedef {import("./Resolver").ResolveContext} ResolveContext */
17-
18-
/**
19-
* @typedef {object} TsconfigPathsPluginOptions
20-
* @property {string=} configFile - Path to tsconfig.json file
21-
* @property {string=} baseUrl - Override baseUrl from tsconfig.json
22-
* @property {string=} context - Context directory for resolving relative paths
23-
*/
17+
/** @typedef {import("./Resolver").TsconfigFileData} TsconfigFileData */
2418

2519
const DEFAULT_CONFIG_FILE = "tsconfig.json";
2620

@@ -46,14 +40,17 @@ module.exports = class TsconfigPathsPlugin {
4640
"TsconfigPathsPlugin",
4741
async (request, resolveContext, callback) => {
4842
try {
49-
const aliases = await this.beforeResolve(
43+
const tsconfigFileData = await this.loadTsconfigFile(
5044
resolver,
5145
request,
5246
resolveContext,
5347
);
48+
49+
if (!tsconfigFileData) return callback();
50+
5451
aliasResolveHandler(
5552
resolver,
56-
aliases.filter((option) => option.name !== "*"),
53+
tsconfigFileData.alias,
5754
aliasTarget,
5855
request,
5956
resolveContext,
@@ -71,18 +68,17 @@ module.exports = class TsconfigPathsPlugin {
7168
"TsconfigPathsPlugin",
7269
async (request, resolveContext, callback) => {
7370
try {
74-
const aliases = await this.beforeResolve(
71+
const tsconfigFileData = await this.loadTsconfigFile(
7572
resolver,
7673
request,
7774
resolveContext,
7875
);
79-
const directories = aliases
80-
.filter((option) => option.name === "*")
81-
.map((option) => /** @type {string} */ (option.alias));
76+
77+
if (!tsconfigFileData) return callback();
8278

8379
modulesResolveHandler(
8480
resolver,
85-
directories,
81+
tsconfigFileData.modules,
8682
moduleTarget,
8783
request,
8884
resolveContext,
@@ -100,178 +96,27 @@ module.exports = class TsconfigPathsPlugin {
10096
* @param {Resolver} resolver the resolver
10197
* @param {ResolveRequest} request the request
10298
* @param {ResolveContext} resolveContext the resolve context
103-
* @returns {Promise<Array<AliasOption>>} the pre-processed request
104-
*/
105-
async beforeResolve(resolver, request, resolveContext) {
106-
const tsconfigFileData =
107-
request.tsconfigFileData || (await this.loadAndConvertPaths(resolver));
108-
if (!tsconfigFileData) return [];
109-
const { aliases, fileDependencies } = tsconfigFileData;
110-
for (const fileDependency of fileDependencies) {
111-
if (resolveContext.fileDependencies) {
112-
resolveContext.fileDependencies.add(fileDependency);
113-
}
114-
}
115-
request.tsconfigFileData = tsconfigFileData;
116-
return aliases;
117-
}
118-
119-
/**
120-
* Load tsconfig.json (and referenced tsconfigs) and convert paths to AliasPlugin format
121-
* @param {Resolver} resolver the resolver
122-
* @returns {Promise<{aliases: AliasOption[], fileDependencies: Set<string>} | null>} Array of alias options for AliasPlugin
99+
* @returns {Promise<TsconfigFileData | null>} the pre-processed request
123100
*/
124-
async loadAndConvertPaths(resolver) {
125-
try {
126-
const fs = resolver.fileSystem;
127-
const configPath = path.isAbsolute(this.configFile)
128-
? this.configFile
129-
: resolver.join(process.cwd(), this.configFile);
130-
131-
const mainOptions = await this.readTsconfigCompilerOptions(
132-
resolver,
133-
configPath,
134-
);
135-
if (!mainOptions) return null;
136-
137-
/** @type {AliasOption[]} */
138-
const aliases = [];
139-
/** @type {Set<string>} */
140-
const fileDependencies = new Set();
141-
const result = {
142-
aliases,
143-
fileDependencies,
144-
};
145-
aliases.push(
146-
...this.convertPathsToAliases(
147-
resolver,
148-
mainOptions.paths,
149-
mainOptions.baseUrl,
150-
),
101+
async loadTsconfigFile(resolver, request, resolveContext) {
102+
if (typeof request.tsconfigFileData === "undefined") {
103+
request.tsconfigFileData = await TsconfigPathsUtils.loadTsconfigFile(
104+
resolver.fileSystem,
105+
this.configFile,
151106
);
152-
153-
// Collect references from the main tsconfig.json
154-
const tsconfigJson = await new Promise((resolve, reject) => {
155-
fs.readFile(configPath, "utf8", (err, data) => {
156-
if (err) reject(err);
157-
resolve(JSON.parse(/** @type {string} */ (data)));
158-
});
159-
});
160-
fileDependencies.add(configPath);
161-
162-
const references = Array.isArray(tsconfigJson.references)
163-
? tsconfigJson.references
164-
: [];
165-
166-
for (const ref of references) {
167-
/** @type {string} */
168-
// TypeScript allows string or object with path
169-
const refPathLike = typeof ref === "string" ? ref : ref && ref.path;
170-
if (!refPathLike) continue;
171-
let refPath = path.isAbsolute(refPathLike)
172-
? refPathLike
173-
: resolver.join(path.dirname(configPath), refPathLike);
174-
// If reference points to a directory, append tsconfig.json
175-
try {
176-
const stat = await new Promise((resolve, reject) => {
177-
fs.stat(refPath, (err, stat) => {
178-
if (err) reject(err);
179-
resolve(stat);
180-
});
181-
});
182-
if (stat.isDirectory()) {
183-
refPath = resolver.join(refPath, "tsconfig.json");
184-
}
185-
} catch (_e) {
186-
// if it doesn't exist as directory/file, try adding tsconfig.json
187-
if (!/\.json$/i.test(refPath)) {
188-
refPath = resolver.join(refPath, "tsconfig.json");
189-
}
190-
}
191-
192-
const refOptions = await this.readTsconfigCompilerOptions(
193-
resolver,
194-
refPath,
195-
);
196-
if (!refOptions) continue;
197-
fileDependencies.add(refPath);
198-
199-
aliases.push(
200-
...this.convertPathsToAliases(
201-
resolver,
202-
refOptions.paths,
203-
refOptions.baseUrl,
204-
),
205-
);
206-
}
207-
208-
return result;
209-
} catch (_error) {
210-
return null;
211107
}
212-
}
213108

214-
/**
215-
* Read tsconfig.json and return normalized compiler options
216-
* @param {Resolver} resolver the resolver
217-
* @param {string} absTsconfigPath absolute path to tsconfig.json
218-
* @returns {Promise<{ baseUrl: string, paths: {[key: string]: string[]} } | null>} the normalized compiler options
219-
*/
220-
async readTsconfigCompilerOptions(resolver, absTsconfigPath) {
221-
try {
222-
const fs = resolver.fileSystem;
223-
const json = await new Promise((resolve, reject) => {
224-
fs.readFile(absTsconfigPath, "utf8", (err, data) => {
225-
if (err) reject(err);
226-
resolve(JSON.parse(/** @type {string} */ (data)));
227-
});
228-
});
229-
const compilerOptions =
230-
json && json.compilerOptions ? json.compilerOptions : {};
231-
let { baseUrl } = compilerOptions;
232-
if (!baseUrl) {
233-
baseUrl = path.dirname(absTsconfigPath);
234-
} else if (!path.isAbsolute(baseUrl)) {
235-
baseUrl = resolver.join(path.dirname(absTsconfigPath), baseUrl);
236-
}
237-
const paths = compilerOptions.paths || {};
238-
return { baseUrl, paths };
239-
} catch (_e) {
109+
const { tsconfigFileData } = request;
110+
111+
if (!tsconfigFileData) {
240112
return null;
241113
}
242-
}
243-
244-
/**
245-
* Convert TypeScript paths configuration to AliasPlugin aliases
246-
* @param {Resolver} resolver the resolver
247-
* @param {{[key: string]: string[]}} paths TypeScript paths mapping
248-
* @param {string} baseUrl Base URL for resolving paths
249-
* @returns {AliasOption[]} Array of alias options
250-
*/
251-
convertPathsToAliases(resolver, paths, baseUrl) {
252-
/** @type {AliasOption[]} */
253-
const aliases = [];
254114

255-
for (const [pattern, mappings] of Object.entries(paths)) {
256-
// Handle exact matches (no wildcards)
257-
if (!pattern.includes("*")) {
258-
if (mappings.length > 0) {
259-
const targetPath = resolver.join(baseUrl, mappings[0]);
260-
aliases.push({ name: pattern, alias: targetPath });
261-
}
262-
} else {
263-
// Handle wildcard patterns by mapping the directory
264-
const aliasName = pattern.replace(/\/\*$/, "");
265-
// Convert targets like "dir/*" -> "dir"
266-
const aliasTargets = mappings.map((mapping) =>
267-
resolver.join(baseUrl, mapping.replace(/\/\*$/, "")),
268-
);
269-
if (aliasTargets.length > 0) {
270-
aliases.push({ name: aliasName, alias: aliasTargets[0] });
271-
}
115+
for (const fileDependency of tsconfigFileData.fileDependencies) {
116+
if (resolveContext.fileDependencies) {
117+
resolveContext.fileDependencies.add(fileDependency);
272118
}
273119
}
274-
275-
return aliases;
120+
return tsconfigFileData;
276121
}
277122
};

0 commit comments

Comments
 (0)