forked from vercel/pkg
-
-
Notifications
You must be signed in to change notification settings - Fork 16
/
follow.ts
116 lines (93 loc) · 2.83 KB
/
follow.ts
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import { sync, SyncOpts } from 'resolve';
import fs from 'fs';
import path from 'path';
import { toNormalizedRealPath } from './common';
import type { PackageJson } from './types';
const PROOF = 'a-proof-that-main-is-captured.js';
function parentDirectoriesContain(parent: string, directory: string) {
let currentParent = parent;
while (true) {
if (currentParent === directory) {
return true;
}
const newParent = path.dirname(currentParent);
if (newParent === currentParent) {
return false;
}
currentParent = newParent;
}
}
interface FollowOptions extends Pick<SyncOpts, 'basedir' | 'extensions'> {
ignoreFile?: string;
catchReadFile?: (file: string) => void;
catchPackageFilter?: (config: PackageJson, base: string, dir: string) => void;
}
export function follow(x: string, opts: FollowOptions) {
// TODO async version
return new Promise<string>((resolve) => {
resolve(
sync(x, {
basedir: opts.basedir,
extensions: opts.extensions,
isFile: (file) => {
if (
opts.ignoreFile &&
path.join(path.dirname(opts.ignoreFile), PROOF) === file
) {
return true;
}
let stat;
try {
stat = fs.statSync(file);
} catch (e) {
const ex = e as NodeJS.ErrnoException;
if (ex && (ex.code === 'ENOENT' || ex.code === 'ENOTDIR'))
return false;
throw ex;
}
return stat.isFile() || stat.isFIFO();
},
isDirectory: (directory) => {
if (
opts.ignoreFile &&
parentDirectoriesContain(opts.ignoreFile, directory)
) {
return false;
}
let stat;
try {
stat = fs.statSync(directory);
} catch (e) {
const ex = e as NodeJS.ErrnoException;
if (ex && (ex.code === 'ENOENT' || ex.code === 'ENOTDIR')) {
return false;
}
throw ex;
}
return stat.isDirectory();
},
readFileSync: (file) => {
if (opts.ignoreFile && opts.ignoreFile === file) {
return Buffer.from(`{"main":"${PROOF}"}`);
}
if (opts.catchReadFile) {
opts.catchReadFile(file);
}
return fs.readFileSync(file);
},
packageFilter: (config, base, dir) => {
if (opts.catchPackageFilter) {
opts.catchPackageFilter(config, base, dir);
}
return config;
},
/** function to synchronously resolve a potential symlink to its real path */
// realpathSync?: (file: string) => string;
realpathSync: (file) => {
const file2 = toNormalizedRealPath(file);
return file2;
},
}),
);
});
}