-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
preopens.ts
81 lines (68 loc) · 2.48 KB
/
preopens.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
import { dirname, delimiter } from './std/path.ts'
import xArgs, { FlagDescriptor, BinaryFlag, MAIN_COMMAND } from './x/args.ts'
import getWasiPath from './wasi-path.ts'
/**
* Extract names of files and folders from an array of CLI arguments
* @param args CLI arguments
* @returns Either `null` or an array
* * `null`: When filenames and dirnames are judged to be unnecessary
* (i.e. when user asks for `--help` or `--version`)
* * an array of names: When filenames and dirnames are necessary
*/
function getTargetFiles(args: readonly string[]) {
const flagAlias = (x: string): FlagDescriptor => ({ alias: [x] })
const res = xArgs
.with(BinaryFlag('help', flagAlias('h')))
.with(BinaryFlag('version', flagAlias('V')))
.with(BinaryFlag('stdio'))
.parse(args)
if (res.tag !== MAIN_COMMAND) {
throw new Error('Something goes wrong.')
}
// when the following flags present, sane-fmt won't touch the filesystem,
// preopens become an unnecessary burden on performance
const { help, version, stdio } = res.value
if (help || version || stdio) return null
return res.remaining().rawValues()
}
/**
* Convert a `PATH`-like string into an object to add to `preopens`
* @param env Value of `SANE_FMT_DENO_PREOPENS` environment variable
* @returns Object compatible with `preopens`
*/
function parsePreopensEnv(env?: string): Record<string, string> {
if (!env) return {}
const entries = env.split(delimiter).map(path => [getWasiPath(path), path])
return Object.fromEntries(entries)
}
/**
* Create a `preopens` object
* @param args CLI arguments
* @param env Value of `SANE_FMT_DENO_PREOPENS` environment variable
* @returns Promise that resolves to a `preopens` object
*/
export async function preopens(args: readonly string[], env?: string): Promise<Record<string, string>> {
const preopens: Record<string, string> = parsePreopensEnv(env)
const targetFiles = getTargetFiles(args)
if (!targetFiles) return preopens
if (!targetFiles.length) return { '.': '.', ...preopens }
const directories = await Promise.all(
targetFiles.map(async name => {
try {
const stats = await Deno.lstat(name)
return stats.isDirectory ? name : dirname(name)
} catch (error) {
if (error instanceof Deno.errors.NotFound) {
return null
}
throw error
}
}),
)
for (const name of directories) {
if (name === null) continue
preopens[getWasiPath(name)] = name
}
return preopens
}
export default preopens