-
-
Notifications
You must be signed in to change notification settings - Fork 22
Expand file tree
/
Copy pathjest-resolver.cjs
More file actions
85 lines (80 loc) · 2.67 KB
/
jest-resolver.cjs
File metadata and controls
85 lines (80 loc) · 2.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
const path = require("path");
const fs = require("fs");
/**
* Custom jest resolver that handles:
* 1. Package.json "imports" field (#/ subpath imports) — per-package resolution
* 2. TypeScript .js → .ts extension mapping for ESM imports
*
* Replaces jest-ts-webcompat-resolver with full #/ import support.
*/
module.exports = (request, options) => {
// Handle #/ subpath imports by reading the nearest package.json
if (request.startsWith("#/")) {
const packageRoot = findPackageRoot(options.basedir);
if (packageRoot) {
const pkgJson = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf-8"));
if (pkgJson.imports) {
const mapping = pkgJson.imports["#/*"];
if (mapping) {
const base = typeof mapping === "string" ? mapping : mapping.source || mapping.default;
if (base) {
const prefix = base.replace(/\*$/, "");
const suffix = request.slice(2);
if (suffix.includes("..") || path.isAbsolute(suffix)) {
throw new Error(`Unsafe import path: ${request}`);
}
const resolved = path.resolve(packageRoot, prefix, suffix);
const normalizedRoot = path.resolve(packageRoot) + path.sep;
if (!resolved.startsWith(normalizedRoot)) {
throw new Error(`Import escapes package root: ${request}`);
}
return resolveWithExtensions(resolved);
}
}
}
}
}
// Handle .js → .ts extension mapping for TypeScript ESM imports
if (request.endsWith(".js")) {
const tsRequest = request.slice(0, -3) + ".ts";
try {
return options.defaultResolver(tsRequest, options);
} catch {
// Try .tsx
}
const tsxRequest = request.slice(0, -3) + ".tsx";
try {
return options.defaultResolver(tsxRequest, options);
} catch {
// Fall through to default
}
}
return options.defaultResolver(request, options);
};
function findPackageRoot(dir) {
let current = dir;
while (current !== path.dirname(current)) {
if (fs.existsSync(path.join(current, "package.json"))) {
const pkg = path.join(current, "package.json");
const content = JSON.parse(fs.readFileSync(pkg, "utf-8"));
if (content.imports) {
return current;
}
}
current = path.dirname(current);
}
return null;
}
function resolveWithExtensions(filePath) {
const base = filePath.replace(/\.js$/, "");
for (const ext of [".ts", ".tsx", ".js"]) {
const fullPath = base + ext;
if (fs.existsSync(fullPath)) {
return fullPath;
}
}
if (fs.existsSync(filePath)) {
return filePath;
}
throw new Error(`Cannot resolve: ${filePath}`);
}