1import { describe, expect, it } from "vite-plus/test";
2
3import { embeddedScripts, withEmbeddedScripts } from "./embeddedScripts.ts";
4
5function scriptsOf(command: string) {
6 return embeddedScripts(command).map((script) => {
7 script.starts.forEach((start, index) => {
8 expect(start).toBeLessThan(script.ends[index]!);
9 if (index > 0) expect(start).toBeGreaterThanOrEqual(script.ends[index - 1]!);
10 });
11 return [
12 script.language,
13 script.text,
14 command.slice(script.starts[0], script.ends.at(-1)),
15 ] as const;
16 });
17}
18
19const pythonScript = `from pathlib import Path
20import json
21
22# print every package.json name and version
23for p in sorted(Path('.').glob('**/package.json')):
24 data = json.loads(p.read_text())
25 print(f"{data['name']:16} {data.get('version', '?')}")
26`;
27
28describe("embeddedScripts", () => {
29 it("finds the script inside a Codex login-shell wrapper and the script inside that", () => {
30 const inner = `console.log('interrupt fixture tool started'); setTimeout(() => {}, 30000)`;
31 const command = `/bin/bash -lc "node -e \\"${inner}\\""`;
32 expect(scriptsOf(command)).toEqual([
33 ["shellscript", `node -e "${inner}"`, `node -e \\"${inner}\\"`],
34 ["javascript", inner, inner],
35 ]);
36 });
37
38 it("highlights a heredoc fed to an interpreter in that interpreter's language", () => {
39 const command = `python3 - <<'PY'\n${pythonScript}PY`;
40 expect(scriptsOf(command)).toEqual([["python", pythonScript, pythonScript]]);
41 });
42
43 it("maps escaped characters back to both source characters", () => {
44 const command = `/bin/zsh -lc "python3 - <<'PY'\nprint(\\"\\$HOME\\")\nPY"`;
45 const [shell, python] = scriptsOf(command);
46 expect(shell?.[1]).toBe(`python3 - <<'PY'\nprint("$HOME")\nPY`);
47 expect(python).toEqual(["python", `print("$HOME")\n`, `print(\\"\\$HOME\\")\n`]);
48 });
49
50 it.each([
51 [`python3 -c 'print(1)'`, "python", "print(1)"],
52 [`py -3 -c 'print(1)'`, "python", "print(1)"],
53 [`"C:\\Program Files\\Python312\\python.exe" -c 'print(1)'`, "python", "print(1)"],
54 [`node --eval "process.exit(0)"`, "javascript", "process.exit(0)"],
55 [`bun -e 'console.log(Bun.version)'`, "typescript", "console.log(Bun.version)"],
56 [`deno eval 'console.log(1)'`, "typescript", "console.log(1)"],
57 [`deno eval --config deno.json 'console.log(1)'`, "typescript", "console.log(1)"],
58 [`psql -f schema.sql -c 'select 1'`, "sql", "select 1"],
59 [`node -C development -e 'console.log(1)'`, "javascript", "console.log(1)"],
60 [`ruby -e 'puts 1'`, "ruby", "puts 1"],
61 [`ruby -I lib -e 'puts 1'`, "ruby", "puts 1"],
62 [`perl -E 'say 1'`, "perl", "say 1"],
63 [`sqlite3 app.db "select * from users"`, "sql", "select * from users"],
64 [`pwsh -NoProfile -Command "Get-ChildItem"`, "powershell", "Get-ChildItem"],
65 [`env FOO=1 sudo -u bob python3 -c 'print(1)'`, "python", "print(1)"],
66 [`env -- FOO=1 python3 -c 'print(1)'`, "python", "print(1)"],
67 [`node --eval="process.exit(0)"`, "javascript", "process.exit(0)"],
68 [`psql --command='select 1'`, "sql", "select 1"],
69 [`mysql -c -e 'select 1'`, "sql", "select 1"],
70 [`psql app <<'EOF'\nselect 1;\nEOF`, "sql", "select 1;\n"],
71 [`echo "$(node -e 'console.log(1)')"`, "javascript", "console.log(1)"],
72 [`python3 <<< 'print(1)'`, "python", "print(1)"],
73 ])("finds the inline script in %s", (command, language, script) => {
74 expect(scriptsOf(command)).toEqual([[language, script, expect.any(String)]]);
75 });
76
77 it.each([[`psql -c 'select 1' -c 'select 2'`], [`sqlite3 app.db "select 1" "select 2"`]])(
78 "finds every statement a SQL shell runs: %s",
79 (command) => {
80 expect(scriptsOf(command).map(([language, text]) => [language, text])).toEqual([
81 ["sql", "select 1"],
82 ["sql", "select 2"],
83 ]);
84 },
85 );
86
87 it.each([
88 ["the file `cat` writes", `cat > src/app.ts <<'EOF'\nconst a = 1;\nEOF`, "typescript"],
89 ["the file `tee` writes", `tee -a config.json <<EOF\n{}\nEOF`, "json"],
90 ["its delimiter", `kubectl apply -f - <<'YAML'\nkind: Pod\nYAML`, "yaml"],
91 ["the shell reading it", `bash <<'EOF'\necho hi\nEOF`, "shellscript"],
92 ])("takes a heredoc's language from %s", (_source, command, language) => {
93 expect(scriptsOf(command).map(([lang]) => lang)).toEqual([language]);
94 });
95
96 it.each([
97 ["a script file", `python3 build.py <<EOF\ndata\nEOF`],
98 ["a module", `python3 -m json.tool <<EOF\n{}\nEOF`],
99 ["a SQL file", `psql -f migrate.sql <<EOF\ny\nEOF`],
100 ["a SQL file given with =", `psql --file=migrate.sql <<EOF\ny\nEOF`],
101 ["plain commands", `rg -n "foo" src && git status`],
102 ["an unnamed heredoc into a plain command", `git commit -F - <<'EOF'\nfix: x\nEOF`],
103 ["an empty script", `bash -lc ' '`],
104 ["a heredoc `cat` sends to stdout while stderr goes to a file", `cat 2>log.ts <<'EOF'\nx\nEOF`],
105 ])("finds nothing for %s", (_case, command) => {
106 expect(scriptsOf(command)).toEqual([]);
107 });
108
109 it("trusts nothing in a command whose quoting does not parse", () => {
110 // Secret redaction removed an escaped quote's backslash, breaking the -lc argument.
111 const command = `/bin/zsh -lc "python3 -c 'token=[REDACTED_SECRET]"apiToken\\"]; print(token)'"`;
112 expect(scriptsOf(command)).toEqual([]);
113 });
114});
115
116describe("withEmbeddedScripts", () => {
117 function tokenize(text: string, language: string) {
118 if (language === "unknown") throw new Error("Language not found");
119 let lineStart = 0;
120 return text.split(/\r?\n/u).map((line) => {
121 const tokens = [...line.matchAll(/\S+|\s+/gu)].map((match) => ({
122 content: match[0],
123 offset: lineStart + match.index,
124 color: language,
125 }));
126 lineStart +=
127 line.length + (text.slice(lineStart + line.length).match(/^\r?\n/u)?.[0].length ?? 0);
128 return tokens;
129 });
130 }
131
132 function colorsOf(code: string, scripts = embeddedScripts(code)) {
133 const lines = withEmbeddedScripts(code, tokenize(code, "shellscript"), scripts, tokenize);
134 expect(lines.map((line) => line.map((token) => token.content).join("")).join("\n")).toBe(
135 code.replaceAll("\r\n", "\n"),
136 );
137 return lines.flat().map((token) => [token.content, token.color]);
138 }
139
140 it("colors each nested script with its own grammar, keeping the text", () => {
141 expect(colorsOf(`bash -lc "python3 -c 'print(1)'"`)).toEqual([
142 [`bash -lc "python3 -c '`, "shellscript"],
143 ["print(1)", "python"],
144 [`'"`, "shellscript"],
145 ]);
146 });
147
148 it("colors an escape sequence like the character it spells", () => {
149 expect(colorsOf(`bash -c "python3 -c 'print(\\"x\\")'"`)).toContainEqual([
150 `print(\\"x\\")`,
151 "python",
152 ]);
153 });
154
155 it("keeps the surrounding colors where a grammar is missing", () => {
156 const code = "cat > x.ts <<'EOF'\r\nbody\r\nEOF";
157 const [script] = embeddedScripts(code);
158 const characterColors = (scripts: ReturnType<typeof embeddedScripts>) =>
159 colorsOf(code, scripts).flatMap(([content, color]) => [...content!].map(() => color));
160 expect(characterColors([{ ...script!, language: "unknown" }])).toEqual(characterColors([]));
161 });
162});