-
Notifications
You must be signed in to change notification settings - Fork 55
/
Copy pathRemoteContent.astro
123 lines (107 loc) · 3.28 KB
/
RemoteContent.astro
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
---
/**
* Remote content fetcher. Remote content is assumed to be markdown.
*/
// @ts-ignore
import { Marked } from "marked";
import {ensureLanguageIsLoaded, getCachedHighlighter, resolveLanguage} from "./shiki";
export interface Props {
/**
* Url to fetch code from. Also allows selecting specific line(s) of code with
* the Github line syntax.
* E.g., https://raw.githubusercontent.com/FusionAuth/fusionauth-example-client-libraries/main/ruby/Gemfile#L3
*/
url: string;
/**
* Optional tags marking beginning and end of content to include
* Loosely based on https://docs.asciidoctor.org/asciidoc/latest/directives/include-tagged-regions/
* Note: Currently only supports one set of tags. If the tags are enclosed in comments, make sure to have the comments on one line.
*
* See https://raw.githubusercontent.com/FusionAuth/fusionauth-example-scripts/main/client-side-password-rules/README.md and
* src/content/docs/customize/look-and-feel/client-side-password-rule-validation.mdx for an example.
*/
tags?: string;
}
const { url, tags } = Astro.props as Props;
let remoteSelectedContent = "not available";
try {
const remoteResponse = await fetch(url);
const remoteContent = await remoteResponse.text();
if (tags) {
remoteSelectedContent = selectTagged(remoteContent, tags);
} else {
const matchLines = url.match(/.*#L(\d+)(-L(\d+))?$/);
if (matchLines) {
remoteSelectedContent = selectLines(
remoteContent,
parseInt(matchLines[1]),
parseInt(matchLines[3]),
);
} else {
remoteSelectedContent = remoteContent.trim();
}
}
} catch (e) {
console.log("Couldn't retrieve remote code", e);
}
function selectTagged(content: string, tags: string): string {
const lines = content.split("\n");
const startLine = lines.findIndex((line) => line.includes(`tag::${tags}`));
const endLine = lines.findIndex((line) => line.includes(`end::${tags}`));
// remove trailing multi-line HTML comment
if (startLine < lines.length - 1) {
lines[startLine + 1] = lines[startLine + 1].replace("-->", "");
}
// remove preceding multi-line HTML comment
lines[endLine - 1] = lines[endLine - 1].replace("<!--", "");
return lines
.slice(startLine + 1, endLine)
.join("\n")
.replace(/\s+$/g, "");
}
function selectLines(
content: string,
lineNum: number,
lineEnd: number,
): string {
const lines = content.split("\n");
if (lineNum && lineEnd) {
return lines.slice(lineNum - 1, lineEnd - 1).join("\n");
} else if (lineNum) {
return lines[lineNum - 1];
} else {
return content;
}
}
const highlighter = await getCachedHighlighter();
const remark = new Marked();
remark.use({
extensions: [
{
name: "code",
renderer: (token) => {
try {
// @ts-ignore
return highlighter.codeToHtml(token.text, {
lang: resolveLanguage(token.lang),
theme: "github-dark",
});
} catch (e) {
console.error(e);
return "";
}
},
},
],
async: true,
walkTokens: async (token) => {
if (token.type === "code") {
await ensureLanguageIsLoaded(highlighter, token.lang, {url});
}
},
});
const html = remark.parse(remoteSelectedContent);
---
<article>
<Fragment set:html={html} />
</article>