improvement: separate md loader & renderer

This commit is contained in:
Kazhnuz 2023-02-13 21:26:47 +01:00
parent 6d360e1026
commit d0f284dc36
2 changed files with 122 additions and 88 deletions

View file

@ -1,93 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, onBeforeUpdate } from "vue"; import { ref, onMounted, onBeforeUpdate } from "vue";
import { marked } from "marked";
import { useTocStore } from "../stores/toc";
import axios from "axios"; import axios from "axios";
import { useRoute } from "vue-router"; import MarkdownRender from "./markdown/MarkdownRender.vue";
import { useConfigStore } from "@/stores/config";
const props = defineProps<{ const props = defineProps<{
path: string; path: string;
order?: number; order?: number;
}>(); }>();
const htmlContent = ref("");
const loadedPage = ref(""); const loadedPage = ref("");
const markdown = ref("");
var specialQuote: Map<string, { class: string; text: string }> = new Map();
specialQuote.set("NOTE", { class: "info", text: "Information :" });
specialQuote.set("SUCCESS", { class: "success", text: "Success :" });
specialQuote.set("WARNING", { class: "warning", text: "Warning :" });
specialQuote.set("DANGER", { class: "danger", text: "Danger :" });
var renderer = new marked.Renderer();
const toc = useTocStore();
const config = useConfigStore();
const route = useRoute();
var tocNbr = 1;
renderer.heading = function (text, level, raw) {
var anchor = "#" + raw.toLowerCase().replace(/[^\w]+/g, "-");
if (level === 2) {
toc.addTocLine(route.path, {
anchor: anchor,
order: (props.order ?? 1) * 100 + tocNbr,
text: text,
});
tocNbr++;
}
return `<h${level} id="${anchor}">${text}</h${level}>\n`;
};
renderer.blockquote = function (quote) {
var bqClass = "";
var newQuote = quote;
for (const [key, quoteData] of specialQuote) {
if (quote.includes(`[!${key}]`)) {
bqClass = `bg-${quoteData?.class}`;
newQuote = newQuote.replace(
`[!${key}]`,
`<strong>${quoteData?.text}</strong>`
);
}
}
newQuote = newQuote.replace("\n", "<br />");
return `<blockquote class="${bqClass}">${newQuote}</blockquote>`;
};
const variable = {
name: "variable",
level: "inline", // Is this a block-level or inline-level tokenizer?
start(src: string) {
return src.match(/{{/)?.index;
}, // Hint to Marked.js to stop and check for a match
tokenizer(
src: string
): { type: string; raw: string; [index: string]: any } | undefined {
const rule = /\{\{([A-Za-z0-9_]+)\}\}/; // Regex for the complete token, anchor to string start
const match = rule.exec(src);
if (match) {
return {
// Token to generate
type: "variable", // Should match "name" above
raw: match[0], // Text to consume from the source
["varName"]: match[1],
};
}
},
renderer(token: { type: string; raw: string; [index: string]: any }): string {
const varName = token["varName"] as string | null;
const value = `${config.getVar(varName ?? "")}`;
return value;
},
};
marked.setOptions({
renderer: renderer,
});
marked.use({ extensions: [variable] });
function refresh() { function refresh() {
const markdownFileUrl = `/${props.path}.md`; const markdownFileUrl = `/${props.path}.md`;
@ -99,21 +22,17 @@ function refresh() {
axios axios
.get(markdownFileUrl) .get(markdownFileUrl)
.then((response) => { .then((response) => {
tocNbr = 1; markdown.value = response.data;
htmlContent.value = marked.parse(response.data);
}) })
.catch( .catch(
() => () =>
(htmlContent.value = marked.parse( (markdown.value =
"# 404 Not Found \n \n La page recherchée n'a pas pu être trouvée" "# 404 Not Found \n \n La page recherchée n'a pas pu être trouvée")
))
); );
} }
onMounted(() => { onMounted(() => {
setTimeout(() => { refresh();
refresh();
}, 100);
}); });
onBeforeUpdate(() => { onBeforeUpdate(() => {
@ -123,7 +42,11 @@ onBeforeUpdate(() => {
<template> <template>
<article> <article>
<div v-html="htmlContent" class="markdown"></div> <MarkdownRender
:markdown="markdown"
:order="order"
v-if="markdown !== ''"
></MarkdownRender>
<slot></slot> <slot></slot>
</article> </article>
</template> </template>

View file

@ -0,0 +1,111 @@
<script setup lang="ts">
import { ref, onMounted, onBeforeUpdate } from "vue";
import { marked } from "marked";
import { useTocStore } from "../../stores/toc";
import { useRoute } from "vue-router";
import { useConfigStore } from "@/stores/config";
const props = defineProps<{
markdown: string;
order?: number;
}>();
const htmlContent = ref("");
var specialQuote: Map<string, { class: string; text: string }> = new Map();
specialQuote.set("NOTE", { class: "info", text: "Information :" });
specialQuote.set("SUCCESS", { class: "success", text: "Success :" });
specialQuote.set("WARNING", { class: "warning", text: "Warning :" });
specialQuote.set("DANGER", { class: "danger", text: "Danger :" });
var renderer = new marked.Renderer();
const toc = useTocStore();
const config = useConfigStore();
const route = useRoute();
var tocNbr = 1;
renderer.heading = function (text, level, raw) {
var anchor = "#" + raw.toLowerCase().replace(/[^\w]+/g, "-");
if (level === 2) {
toc.addTocLine(route.path, {
anchor: anchor,
order: (props.order ?? 1) * 100 + tocNbr,
text: text,
});
tocNbr++;
}
return `<h${level} id="${anchor}">${text}</h${level}>\n`;
};
renderer.blockquote = function (quote) {
var bqClass = "";
var newQuote = quote;
for (const [key, quoteData] of specialQuote) {
if (quote.includes(`[!${key}]`)) {
bqClass = `bg-${quoteData?.class}`;
newQuote = newQuote.replace(
`[!${key}]`,
`<strong>${quoteData?.text}</strong>`
);
}
}
newQuote = newQuote.replace("\n", "<br />");
return `<blockquote class="${bqClass}">${newQuote}</blockquote>`;
};
const variable = {
name: "variable",
level: "inline", // Is this a block-level or inline-level tokenizer?
start(src: string) {
return src.match(/{{/)?.index;
}, // Hint to Marked.js to stop and check for a match
tokenizer(
src: string
): { type: string; raw: string; [index: string]: any } | undefined {
const rule = /\{\{([A-Za-z0-9_]+)\}\}/; // Regex for the complete token, anchor to string start
const match = rule.exec(src);
if (match) {
return {
// Token to generate
type: "variable", // Should match "name" above
raw: match[0], // Text to consume from the source
["varName"]: match[1],
};
}
},
renderer(token: { type: string; raw: string; [index: string]: any }): string {
const varName = token["varName"] as string | null;
const value = `${config.getVar(varName ?? "")}`;
return value;
},
};
marked.setOptions({
renderer: renderer,
});
marked.use({ extensions: [variable] });
function render() {
tocNbr = 1;
if (htmlContent.value === "") {
htmlContent.value = marked.parse(props.markdown);
}
}
onMounted(() => {
render();
});
onBeforeUpdate(() => {
render();
});
</script>
<template>
<article>
<div v-html="htmlContent" class="markdown"></div>
<slot></slot>
</article>
</template>