Skip to content

Commit e0274ac

Browse files
Fix bug in local cheatsheet when injecting cheatsheet info (#3364)
Recent update to `node-html-parser` introduced html escaping in our data.
1 parent 2b287d9 commit e0274ac

6 files changed

Lines changed: 74 additions & 6 deletions

File tree

‎packages/lib-cheatsheet-local/src/app.tsx‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,8 @@ declare global {
1616
}
1717

1818
export function App() {
19+
if (document.cheatsheetInfo == null) {
20+
throw new Error("Cheatsheet info is not available on the document.");
21+
}
1922
return <Cheatsheet cheatsheetInfo={document.cheatsheetInfo} />;
2023
}

‎packages/lib-node-common/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"devDependencies": {
2222
"@types/js-yaml": "^4.0.9",
2323
"@types/lodash-es": "^4.17.12",
24+
"@types/node": "^24.13.4",
2425
"js-yaml": "^5.4.1"
2526
}
2627
}

‎packages/lib-node-common/src/Cheatsheet.ts‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { readFile, writeFile } from "node:fs/promises";
22
import path from "node:path";
3-
import { parse } from "node-html-parser";
43
import type { CheatsheetInfo, IDE } from "@cursorless/lib-common";
54
import {
65
getCheatsheetInfo,
@@ -9,6 +8,7 @@ import {
98
showWarning,
109
} from "@cursorless/lib-common";
1110
import type { FileSystemTalonSpokenForms } from "./FileSystemTalonSpokenForms";
11+
import { injectCheatsheetInfo } from "./injectCheatsheetInfo";
1212

1313
interface CheatSheetCommandArgV0 {
1414
version: 0;
@@ -47,12 +47,11 @@ export async function showCheatsheet(
4747
);
4848
const cheatsheetPath = path.join(ide.assetsRoot, "cheatsheet.html");
4949
const cheatsheetContent = await readFile(cheatsheetPath, "utf8");
50-
const root = parse(cheatsheetContent);
5150

52-
root.getElementById("cheatsheet-data")!.textContent =
53-
`document.cheatsheetInfo = ${JSON.stringify(cheatsheetInfo)};`;
54-
55-
await writeFile(arg.outputPath, root.toString());
51+
await writeFile(
52+
arg.outputPath,
53+
injectCheatsheetInfo(cheatsheetContent, cheatsheetInfo),
54+
);
5655
}
5756

5857
async function getCheatsheetInfoForCommand(
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { parse, TextNode } from "node-html-parser";
2+
import type { CheatsheetInfo } from "@cursorless/lib-common";
3+
4+
export function injectCheatsheetInfo(
5+
cheatsheetContent: string,
6+
cheatsheetInfo: CheatsheetInfo,
7+
): string {
8+
const root = parse(cheatsheetContent);
9+
const serializedCheatsheetInfo = JSON.stringify(cheatsheetInfo).replaceAll(
10+
"<",
11+
String.raw`\u003c`,
12+
);
13+
14+
root
15+
.getElementById("cheatsheet-data")!
16+
.set_content(
17+
new TextNode(`document.cheatsheetInfo = ${serializedCheatsheetInfo};`),
18+
);
19+
20+
return root.toString();
21+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import assert from "node:assert/strict";
2+
import { parse } from "node-html-parser";
3+
import type { CheatsheetInfo } from "@cursorless/lib-common";
4+
import { injectCheatsheetInfo } from "../src/injectCheatsheetInfo";
5+
6+
suite("Cheatsheet", () => {
7+
test("injects cheatsheet info as executable script text", () => {
8+
const cheatsheetInfo: CheatsheetInfo = {
9+
sections: [
10+
{
11+
id: "actions",
12+
name: "Actions",
13+
items: [
14+
{
15+
id: "test-action",
16+
type: "action",
17+
variations: [
18+
{
19+
spokenForm: "take <target>",
20+
description: "Select A & B </script>",
21+
},
22+
],
23+
},
24+
],
25+
},
26+
],
27+
};
28+
29+
const output = injectCheatsheetInfo(
30+
'<html><script id="cheatsheet-data">old data</script></html>',
31+
cheatsheetInfo,
32+
);
33+
const script = parse(output).getElementById("cheatsheet-data")!;
34+
35+
assert.equal(
36+
script.rawText,
37+
`document.cheatsheetInfo = ${JSON.stringify(cheatsheetInfo).replaceAll("<", String.raw`\u003c`)};`,
38+
);
39+
assert.doesNotMatch(script.rawText, /&(?:amp|lt|quot);/u);
40+
});
41+
});

‎pnpm-lock.yaml‎

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)