Skip to content

Commit 66dc567

Browse files
committed
minor(@repo/nuxt-core): pre-parse MDC for prim tooltip
1 parent 1e45946 commit 66dc567

3 files changed

Lines changed: 128 additions & 94 deletions

File tree

packages/nuxt-core/layers/primitive-tooltip/app/components/PrimTooltip.vue

Lines changed: 16 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<template>
22
<UTooltip
3-
v-if="primitive && primitive.name"
3+
v-if="!isNested && primitive && primitive.name"
44
:ui="{ content: 'primitive-tooltip ring-0 bg-transparent shadow-none ' }"
55
:content="{
66
side: 'top',
@@ -11,17 +11,7 @@
1111
:delay-duration="300"
1212
:disabled="isNested"
1313
>
14-
<code class="netlogo-command" :primitive-name="primitive.name">
15-
<Anchor
16-
:href="primitive.url"
17-
class="netlogo-wiki-link"
18-
:data-display-text="primitive.name"
19-
:target="external ? '_blank' : '_self'"
20-
:rel="external ? 'noopener noreferrer' : null"
21-
>{{ props.name }}</Anchor
22-
>
23-
</code>
24-
14+
<PrimitiveMarkup :name="primitive.name" :url="primitive.url" />
2515
<template #content>
2616
<UPageCard
2717
varian="outline"
@@ -65,19 +55,19 @@
6555
<hr class="bg-slate-200 mb-0" />
6656
</div>
6757

68-
<div class="prose prose-tight p-0 m-0 mt-1 w-full">
58+
<div class="prose prose-tight p-0 m-0 mt-1 w-full min-h-10">
6959
<MDC
7060
v-if="primitive.examples"
7161
:cache-key="`${primitive.name}-examples`"
7262
tag="div"
7363
unwrap="p"
7464
class="dict_entry"
75-
:value="examples"
65+
:value="primitive.examples"
7666
/>
7767

7868
<MDC
79-
tag="div"
80-
:cache-key="`${primitive.name}-description`"
69+
:cache-key="primitive.name + 'description'"
70+
class="mt-2"
8171
:value="primitive.description ?? ''"
8272
/>
8373

@@ -104,31 +94,25 @@
10494
</UPageCard>
10595
</template>
10696
</UTooltip>
107-
<code
108-
v-else
109-
class="netlogo-command"
110-
:primitive-name="props.name"
111-
aria-label="Unknown NetLogo primitive"
112-
>
97+
<PrimitiveMarkup
98+
v-else-if="isDisabled || isNested"
99+
:name="props.name"
100+
:url="primitive?.url"
101+
v-bind="$attrs"
102+
/>
103+
<PrimitiveMarkup v-else :name="props.name" v-bind="$attrs">
113104
<span v-if="$slots['default'] == null" class="netlogo-wiki-link text-red-500 bold">{{
114105
props.name
115106
}}</span>
116-
<slot v-else />
117-
</code>
107+
</PrimitiveMarkup>
118108
</template>
119109

120110
<script setup lang="ts">
121-
/**
122-
* TODO:
123-
* - [ ] Reduce memory footprint
124-
* - [ ] Fix hydration issues
125-
*/
126-
import { escapeHTML } from "@repo/utils/std/string";
127111
import { useNoPrimitive } from "../composables/usePrimitive";
112+
import PrimitiveMarkup from "./PrimitiveMarkup.vue";
128113
129114
defineOptions({
130-
// Allowing client-side renderer to avoid SSR
131-
// hydration issues in the development server.
115+
ssr: true,
132116
client: false,
133117
});
134118
@@ -145,22 +129,6 @@ const isDisabled = inject<boolean>("prim-tooltip-disabled", false);
145129
146130
const { primitive } = isDisabled ? useNoPrimitive() : await usePrimitive({ name: props.name });
147131
148-
const examples = computed(() => {
149-
return `<h5 class="m-0">${
150-
primitive.value?.examples
151-
?.map(
152-
(ex: string) => `
153-
<span class="prim-example font-mono m-0 block [&_p]:m-0! [&_p]:font-bold">
154-
155-
${escapeHTML(ex)}
156-
157-
</span>
158-
`,
159-
)
160-
.join("") ?? ""
161-
}</h5>`;
162-
});
163-
164132
const external = computed(() => {
165133
return primitive.value?.url?.startsWith("http");
166134
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<template>
2+
<code class="netlogo-command" :data-primitive-name="name">
3+
<Anchor
4+
:href="url"
5+
class="netlogo-wiki-link"
6+
:data-display-text="name"
7+
:target="external ? '_blank' : '_self'"
8+
:rel="external ? 'noopener noreferrer' : null"
9+
:external="false"
10+
v-bind="$attrs"
11+
>{{ name }}</Anchor
12+
>
13+
</code>
14+
</template>
15+
16+
<script setup lang="ts">
17+
const props = defineProps<{
18+
name: string;
19+
url?: string;
20+
}>();
21+
22+
const external = computed(() => {
23+
return props.url?.startsWith("http");
24+
});
25+
</script>
Lines changed: 87 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,20 @@
11
import { Primitives } from "@repo/common-data";
2+
import { escapeHTML } from "@repo/utils/std/string";
23

3-
type Primitive = ReturnType<typeof Primitives.prototype.getPrimByName> | null;
4+
type ParsedMarkdown = Awaited<ReturnType<typeof parseMarkdown>>;
5+
type Primitive = ReturnType<typeof Primitives.prototype.getPrimByName> | null | undefined;
6+
type ParsedPrimitive =
7+
| (Omit<NonNullable<Primitive>, "description" | "examples"> & {
8+
description: ParsedMarkdown | null;
9+
examples: ParsedMarkdown | null;
10+
})
11+
| undefined
12+
| null;
413
let primitives: Primitives | undefined = undefined;
514

6-
function convertRelativeMarkdownLinksToAbsolute(markdown: string, baseUrl: string): string {
7-
const markdownLinkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
8-
const convertedMarkdown = markdown.replace(markdownLinkRegex, (match, text, url) => {
9-
if (!url.startsWith("http://") && !url.startsWith("https://")) {
10-
const absoluteUrl = new URL(url, baseUrl).href;
11-
return `[${text}](${absoluteUrl})`;
12-
}
13-
return match;
14-
});
15-
16-
return convertedMarkdown;
17-
}
18-
19-
function parsePrimitive(prim: Primitive): Primitive {
20-
if (!prim) return null;
21-
if (prim.description) {
22-
prim.description = convertRelativeMarkdownLinksToAbsolute(
23-
prim.description,
24-
"https://docs.netlogo.org/",
25-
);
26-
}
27-
28-
return prim;
29-
}
30-
31-
export const usePrimitive = async ({ name }: { name: string }) => {
15+
const usePrimitive = async ({ name }: { name: string }) => {
3216
if (import.meta.dev) {
33-
const { data } = await useAsyncData("primitives", () => queryCollection("primitives").all(), {
34-
deep: false,
35-
server: true,
36-
dedupe: "defer",
37-
});
38-
39-
if (data.value) {
40-
primitives = Primitives.getInstance(data.value[0]?.primitives ?? []);
41-
}
42-
43-
const prim = parsePrimitive(primitives?.getPrimByName(name));
44-
45-
return {
46-
primitive: computed(() => prim),
47-
pending: computed(() => typeof primitives === "undefined" && !primitives),
48-
error: computed(() => null),
49-
};
17+
return usePrimitiveDev({ name });
5018
}
5119

5220
const { data, pending, error } = await useAsyncData(
@@ -58,7 +26,7 @@ export const usePrimitive = async ({ name }: { name: string }) => {
5826
dedupe: "defer",
5927
transform: async (allPrimitivesData) => {
6028
const primitives = Primitives.getInstance(allPrimitivesData[0]?.primitives ?? []);
61-
const prim = parsePrimitive(primitives.getPrimByName(name));
29+
const prim = await parsePrimitive(primitives.getPrimByName(name));
6230

6331
return prim ?? null;
6432
},
@@ -77,10 +45,83 @@ export const usePrimitive = async ({ name }: { name: string }) => {
7745
};
7846
};
7947

80-
export const useNoPrimitive = () => {
48+
const usePrimitiveDev = async ({ name }: { name: string }) => {
49+
const { data } = await useAsyncData("primitives", () => queryCollection("primitives").all(), {
50+
deep: false,
51+
server: true,
52+
dedupe: "defer",
53+
});
54+
55+
if (data.value) {
56+
primitives = Primitives.getInstance(data.value[0]?.primitives ?? []);
57+
}
58+
59+
const prim = await parsePrimitive(primitives?.getPrimByName(name));
60+
61+
return {
62+
primitive: computed(() => prim),
63+
pending: computed(() => typeof primitives === "undefined" && !primitives),
64+
error: computed(() => null),
65+
};
66+
};
67+
68+
const useNoPrimitive = () => {
8169
return {
8270
primitive: computed(() => null),
8371
pending: computed(() => false),
8472
error: computed(() => null),
8573
};
8674
};
75+
76+
// Utilities
77+
function convertRelativeMarkdownLinksToAbsolute(markdown: string, baseUrl: string): string {
78+
const markdownLinkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
79+
const convertedMarkdown = markdown.replace(markdownLinkRegex, (match, text, url) => {
80+
if (!url.startsWith("http://") && !url.startsWith("https://")) {
81+
const absoluteUrl = new URL(url, baseUrl).href;
82+
return `[${text}](${absoluteUrl})`;
83+
}
84+
return match;
85+
});
86+
87+
return convertedMarkdown;
88+
}
89+
90+
async function parsePrimitive(prim: Primitive): Promise<ParsedPrimitive | null> {
91+
if (!prim) return null;
92+
if (prim.description) {
93+
prim.description = convertRelativeMarkdownLinksToAbsolute(
94+
prim.description,
95+
"https://docs.netlogo.org/",
96+
);
97+
}
98+
99+
const [parsedDescription, parsedExamples] = await Promise.all([
100+
prim.description ? await parseMarkdown(prim.description) : null,
101+
prim.examples ? await parseMarkdown(buildExamples(prim)) : null,
102+
]);
103+
104+
return {
105+
...prim,
106+
description: parsedDescription,
107+
examples: parsedExamples,
108+
};
109+
}
110+
111+
function buildExamples(primitive: Primitive | null): string {
112+
return `<h5 class="m-0">${
113+
primitive?.examples
114+
?.map(
115+
(ex: string) => `
116+
<span class="prim-example font-mono m-0 block [&_p]:m-0! [&_p]:font-bold">
117+
118+
${escapeHTML(ex)}
119+
120+
</span>
121+
`,
122+
)
123+
.join("") ?? ""
124+
}</h5>`;
125+
}
126+
127+
export { useNoPrimitive, usePrimitive };

0 commit comments

Comments
 (0)