1import { codeToTokens } from "@pierre/highlights";
2import { pierreDark, pierreLight } from "@pierre/highlights/themes";
3import { describe, expect, it, vi } from "vite-plus/test";
4β
5import { resolveHighlightLanguage } from "./highlightLanguage";
6import { highlightCode, highlightCommand, tokenizeCode, type HighlightedLines } from "./highlights";
7β
8vi.mock("react-native-nitro-modules", async () => {
9 const { createWasmHighlightsModule } = await import("./highlights.testing");
10 const highlights = createWasmHighlightsModule();
11 return { NitroModules: { hasHybridObject: () => true, createHybridObject: () => highlights } };
12});
13β
14describe("highlightCode", () => {
15 it("produces @pierre/highlights' own tokens and colors", () => {
16 const code = [
17 "/** π docs\r",
18 " */",
19 "export const greet = (name: string) => <b title={`hi ${name}`}>Γ©</b>;",
20 "const multi = `first",
21 "second`; // done",
22 "",
23 ].join("\n");
24β
25 for (const [scheme, theme] of [
26 ["light", pierreLight],
27 ["dark", pierreDark],
28 ] as const) {
29 const expected = codeToTokens(code, { lang: "tsx", theme }).tokens.map((line) =>
30 line.map((token) => ({
31 content: token.content,
32 color: token.color ?? null,
33 fontStyle: token.fontStyle ?? null,
34 })),
35 );
36 expect(highlightCode(code, "tsx", scheme)).toEqual(expected);
37 }
38 });
39β
40 it("colors a command's embedded script with that script's grammar", () => {
41 const body = "import json\r\nprint(json.dumps({'ok': True}))";
42 const code = `python3 - <<'PY'\r\n${body}\r\nPY`;
43 const colors = (lines: HighlightedLines) =>
44 lines.map((line) => line.map(({ content, color }) => [content, color]));
45 const lines = highlightCommand(code, "shellscript", "dark");
46β
47 expect(lines.map((line) => line.map((token) => token.content).join(""))).toEqual(
48 code.split("\r\n"),
49 );
50 expect(colors(lines.slice(1, 3))).toEqual(colors(highlightCode(body, "python", "dark")));
51 });
52β
53 it("keeps an over-long line as one plain token", () => {
54 const longLine = `const data = "${"x".repeat(1_000)}";`;
55 const [first, second] = highlightCode(`let a = 1;\n${longLine}`, "typescript", "dark");
56β
57 expect(first?.length).toBeGreaterThan(1);
58 expect(second).toEqual([{ content: longLine, color: null, fontStyle: null }]);
59 });
60});
61β
62it("normalizes only requested lines from full-source records, including CRLF and a long comment opener", () => {
63 const code = `const first = 1;\r\n${" ".repeat(1_001)}/*\r\ninside\r\n*/\r\nconst last = 2;`;
64 const source = tokenizeCode(code, "typescript");
65 expect(source.byteLength).toBeGreaterThan(0);
66 for (let line = 0; line < 5; line++) {
67 expect(source.highlightLine(line, "dark")).toEqual(
68 highlightCode(code, "typescript", "dark")[line],
69 );
70 }
71 expect(source.highlightLine(2, "dark")?.[0]?.color).toBe("#737373");
72 expect(source.highlightLine(100, "dark")).toBeUndefined();
73});
74β
75it.each([
76 "asm",
77 "assembly",
78 "s",
79 ...["asm", "ASM", "s", "S", "nasm", "NASM"].map((ext) => `src/start.${ext}`),
80])("highlights assembly in %s", (hint) => {
81 const language = resolveHighlightLanguage(
82 hint.includes("/") ? { path: hint } : { language: hint },
83 );
84 const code = "mov eax, 42 ; answer\nret";
85 for (const scheme of ["light", "dark"] as const) {
86 const tokens = highlightCode(code, language, scheme);
87 expect(tokens.map((line) => line.map((token) => token.content).join("")).join("\n")).toBe(code);
88 expect(new Set(tokens.flat().map((token) => token.color)).size).toBeGreaterThan(1);
89 }
90});