This repository has been archived by the owner on Mar 15, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathpackage.mjs
136 lines (120 loc) · 3.41 KB
/
package.mjs
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import { assert } from "../../util/index.mjs";
import { logErrorWhen } from "../../log/index.mjs";
import { ExternalAppmapError } from "../../error/index.mjs";
import { tokenizeShell, tokenizeCmdShell } from "./tokenize.mjs";
const isPrefixArray = (prefix, array) => {
const { length } = prefix;
if (length > array.length) {
return false;
} else {
for (let index = 0; index < length; index += 1) {
if (prefix[index] !== array[index]) {
return false;
}
}
return true;
}
};
const findScriptIndex = (tokens, index) => {
if (index < tokens.length) {
const token = tokens[index];
// In the node cli, `-` indicates that the script
// is read from the stdin. In that case, we should
// not return any index. For instance:
// `node - foo.js`
if (token === "-") {
return null;
// In the CLI of node, npm and npx: `--` indicates
// the the script argument is following.
} else if (token === "--") {
return index + 1 < tokens.length ? index + 1 : null;
// We only support named argument of the form
// `--foo=bar` and not `--foo bar`.
} else if (token.startsWith("-")) {
return findScriptIndex(tokens, index + 1);
} else {
return index;
}
} else {
return null;
}
};
const executables = [
["node"],
["npm", "exec"],
["npm", "x"],
["npx"],
["npm.cmd", "exec"],
["npm.cmd", "x"],
["npx.cmd"],
[],
];
export const splitTokens = (tokens) => {
const executable = executables.find((executable) =>
isPrefixArray(executable, tokens),
);
const positional = findScriptIndex(tokens, executable.length);
assert(
!logErrorWhen(
positional === null,
"could not parse and hook command because of missing positional argument, got %j",
tokens,
),
"could not parse and hook command",
ExternalAppmapError,
);
return {
__proto__: null,
exec: tokens.slice(0, positional + 1),
argv: tokens.slice(positional + 1),
};
};
export const sniffTokens = (tokens, name) => {
const executable = executables.find((executable) =>
isPrefixArray(executable, tokens),
);
const positional = findScriptIndex(tokens, executable.length);
return positional !== null && tokens[positional].includes(name);
};
const chopSetEnv = (tokens) => {
let index = 0;
while (
index < tokens.length &&
!tokens[index].startsWith("-") &&
tokens[index].includes("=")
) {
index += 1;
}
return {
head: tokens.slice(0, index),
body: tokens.slice(index),
};
};
const chopNothing = (tokens) => ({
head: [],
body: tokens,
});
const isCmdShell = (shell) => shell === "cmd" || shell === "cmd.exe";
const isPowerShell = (shell) =>
shell === "powershell" || shell === "powershell.exe";
const chop = (tokens, shell) =>
isCmdShell(shell) || isPowerShell(shell)
? chopNothing(tokens)
: chopSetEnv(tokens);
const tokenize = (source, shell) =>
isCmdShell(shell) ? tokenizeCmdShell(source) : tokenizeShell(source);
export const parseSource = (source, shell) => {
const tokens = tokenize(source, shell);
const { head, body } = chop(tokens, shell);
const { exec, argv } = splitTokens(body);
return {
__proto__: null,
exec: [...head, ...exec].join(" "),
argv: argv.join(" "),
};
};
export const sniffSource = (source, name, shell) => {
const tokens = tokenize(source, shell);
const { body } = chop(tokens, shell);
return sniffTokens(body, name);
};