Revert "Various Marketplace UI fixes & enhancements (#21691)" (#21692)

This reverts commit 39a2e4af5a.
This commit is contained in:
Rijk van Zanten
2024-03-05 08:22:31 -05:00
committed by GitHub
parent 39a2e4af5a
commit fd2f4542e7
28 changed files with 109 additions and 750 deletions

View File

@@ -73,7 +73,6 @@
"@mapbox/mapbox-gl-draw": "1.4.3",
"@mapbox/mapbox-gl-draw-static-mode": "1.0.1",
"@mapbox/mapbox-gl-geocoder": "5.0.2",
"@mdit/plugin-tasklist": "0.8.0",
"@ngneat/falso": "7.2.0",
"@pinia/testing": "0.1.3",
"@popperjs/core": "2.11.8",
@@ -93,7 +92,6 @@
"@types/lodash": "4.14.202",
"@types/mapbox__mapbox-gl-draw": "1.4.6",
"@types/mapbox__mapbox-gl-geocoder": "4.7.7",
"@types/markdown-it": "13.0.7",
"@types/qrcode": "1.5.5",
"@types/semver": "7.5.8",
"@types/wellknown": "0.5.8",
@@ -121,7 +119,6 @@
"flatpickr": "4.6.13",
"geojson": "0.5.0",
"happy-dom": "13.6.2",
"highlight.js": "11.9.0",
"histoire": "0.17.9",
"html-entities": "2.5.0",
"json-to-graphql-query": "2.2.5",
@@ -129,11 +126,7 @@
"lodash": "4.17.21",
"mapbox-gl": "1.13.3",
"maplibre-gl": "1.15.3",
"markdown-it": "14.0.0",
"markdown-it-anchor": "8.6.7",
"markdown-it-emoji": "3.0.0",
"mdast-util-from-markdown": "2.0.0",
"mdast-util-to-string": "4.0.0",
"marked": "12.0.0",
"micromustache": "8.0.3",
"mime": "4.0.1",
"mitt": "3.0.1",

View File

@@ -25,7 +25,6 @@ test('Mount component', () => {
test('length prop', async () => {
const wrapper = mount(VPagination, {
props: {
modelValue: 1,
length: 5,
},
global,
@@ -41,7 +40,6 @@ test('length prop', async () => {
test('totalVisible prop', async () => {
const wrapper = mount(VPagination, {
props: {
modelValue: 1,
length: 5,
totalVisible: 3,
},

View File

@@ -1,18 +1,35 @@
<script setup lang="ts">
import { computed } from 'vue';
const props = defineProps<{
/** Currently selected page */
modelValue: number;
/** The amount of pages to render */
length: number;
const props = defineProps({
/** Disables the pagination */
disabled?: boolean;
disabled: {
type: Boolean,
default: false,
},
/** The amount of pages to render */
length: {
type: Number,
required: true,
validator: Number.isInteger,
},
/** Specify the max total visible pagination numbers */
totalVisible?: number;
totalVisible: {
type: Number,
default: undefined,
validator: (val: number) => val >= 0,
},
/** Currently selected page */
modelValue: {
type: Number,
default: null,
},
/** Show first/last buttons */
showFirstLast?: boolean;
}>();
showFirstLast: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(['update:modelValue']);

View File

@@ -1,7 +1,5 @@
import { i18n } from '@/lang';
import { md } from '@/utils/md';
import { notify } from '@/utils/notify';
import { Directive, DirectiveBinding } from 'vue';
import { md } from '@/utils/md';
const Markdown: Directive = {
beforeMount: markdown,
@@ -14,44 +12,6 @@ function markdown(el: Element, binding: DirectiveBinding) {
} else {
el.innerHTML = md(binding.value ?? '');
}
const codeBlocks = el.querySelectorAll('pre');
if (codeBlocks.length === 0 || !navigator?.clipboard?.writeText) return;
for (const codeBlock of codeBlocks) {
const code = codeBlock.querySelector('code');
if (!code) continue;
const copyButton = document.createElement('button');
copyButton.classList.add('copy');
copyButton.setAttribute('title', i18n.global.t('copy'));
copyButton.addEventListener('click', async () => {
let text = code.innerText;
const isShell = /language-(shellscript|shell|bash|sh|zsh)/.test(codeBlock.className);
if (isShell) text = text.replace(/^ *(\$|>) /gm, '').trim();
try {
await navigator?.clipboard?.writeText(text);
notify({
title: i18n.global.t('copy_raw_value_success'),
});
} catch {
notify({
type: 'error',
title: i18n.global.t('copy_raw_value_fail'),
});
}
copyButton.blur();
});
codeBlock.prepend(copyButton);
}
}
export default Markdown;

View File

@@ -100,13 +100,10 @@ github: GitHub
beta: Beta
n_downloads: '{n} Downloads'
downloads: Downloads
compatibility: Compatibility
compatibility_not_guaranteed: Compatibility not guaranteed
compatibility_not_guaranteed_copy:
The author has indicated the compatibility of this extension to be {hostVersion}, which doesn't match your project's
version ({currentVersion})
last_publish: Last Publish
license: License
enable: Enable
disable: Disable
custom: Custom
@@ -1191,8 +1188,9 @@ uninstall_locked: Cannot uninstall extensions that weren't installed through the
uninstall: Uninstall
installed: Installed
open_in_marketplace: Open in Marketplace
local_extension: Local Extension
module_extension: Node Module Extension
source_registry: Marketplace
source_local: Local
source_module: Node Module
collection: Collection
collections: Collections
content: Content

View File

@@ -106,30 +106,17 @@ const uninstall = async () => {
<v-list-item-icon v-tooltip="t(`extension_${type}`)"><v-icon :name="icon" small /></v-list-item-icon>
<v-list-item-content>
<span class="monospace">
{{ extension.schema?.name ?? extension.meta.folder }}
<router-link
v-if="extension.schema?.name && extension.meta.source === 'registry'"
v-tooltip="t('open_in_marketplace')"
class="link"
:to="`/settings/marketplace/extension/${extension.id}`"
>
{{ extension.schema?.name }}
</router-link>
<span v-else>{{ extension.schema?.name ?? extension.meta.folder }}</span>
{{ ' ' }}
<v-chip v-if="extension.schema?.version" class="version" small>{{ extension.schema.version }}</v-chip>
<span v-if="!extension.bundle" class="source">
<router-link
v-if="extension.meta.source === 'registry'"
v-tooltip="t('open_in_marketplace')"
:to="`/settings/marketplace/extension/${extension.id}`"
>
<v-icon class="source marketplace" name="storefront" />
</router-link>
<v-icon
v-else-if="extension.meta.source === 'local'"
v-tooltip="t('local_extension')"
class="local"
name="folder"
/>
<v-icon
v-else-if="extension.meta.source === 'module'"
v-tooltip="t('module_extension')"
class="module"
name="deployed_code"
/>
</span>
</span>
</v-list-item-content>
@@ -179,16 +166,14 @@ const uninstall = async () => {
margin-left: 12px;
}
.version {
margin-right: 8px;
.link {
&:hover {
text-decoration: underline;
}
}
.source {
--v-icon-color: var(--theme--foreground);
.marketplace {
--v-icon-color: var(--theme--primary);
}
.version {
margin-right: 8px;
}
.state {

View File

@@ -4,8 +4,6 @@ import { useExtensionsStore } from '@/stores/extensions';
import { localizedFormatDistanceStrict } from '@/utils/localized-format-distance-strict';
import type { RegistryListResponse } from '@directus/extensions-registry';
import { abbreviateNumber } from '@directus/utils';
import { fromMarkdown } from 'mdast-util-from-markdown';
import { toString } from 'mdast-util-to-string';
import { computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { formatName } from '../utils/format-name';
@@ -21,15 +19,6 @@ const props = defineProps<{
const icon = computed(() => extensionTypeIconMap[props.extension.type]);
const chip = computed(() => t(`extension_${props.extension.type}`));
/* TODO: Handle this in registry */
const description = computed(() => {
if (!props.extension.description || props.extension.description === 'Please enter a description for your extension')
return null;
const mdast = fromMarkdown(props.extension.description);
return toString(mdast);
});
</script>
<template>
@@ -38,8 +27,8 @@ const description = computed(() => {
<v-list-item-content>
<div class="name">
{{ formatName(extension) }}
<v-chip v-if="showType" outlined x-small class="chip type">{{ chip }}</v-chip>
<v-chip v-if="extensionsStore.extensionIds.includes(extension.id)" outlined x-small class="chip installed">
<v-chip v-if="showType" outlined x-small class="chip">{{ chip }}</v-chip>
<v-chip v-if="extensionsStore.extensionIds.includes(extension.id)" outlined x-small class="chip">
{{ t('installed') }}
</v-chip>
</div>
@@ -47,7 +36,7 @@ const description = computed(() => {
{{ extension.publisher.github_name ?? extension.publisher.username }}
<v-icon v-if="extension.publisher.verified" name="verified" x-small />
</div>
<div v-if="description" class="description">{{ description }}</div>
<div v-if="extension.description" class="description">{{ extension.description }}</div>
<div v-else class="description">{{ t('no_description') }}</div>
</v-list-item-content>
<div class="meta">
@@ -69,7 +58,7 @@ const description = computed(() => {
</v-list-item>
</template>
<style lang="scss" scoped>
<style scoped>
.extension-list-item {
--v-list-item-padding: 20px;
}
@@ -114,16 +103,8 @@ const description = computed(() => {
.chip {
margin-inline-start: 4px;
vertical-align: 2px;
&.type {
--v-chip-color: var(--theme--primary);
--v-chip-background-color: var(--theme--primary-subdued);
}
&.installed {
--v-chip-color: var(--theme--success);
--v-chip-background-color: var(--theme--success-subdued);
}
--v-chip-color: var(--theme--primary);
--v-chip-background-color: var(--theme--primary-subdued);
}
.license.known {

View File

@@ -2,7 +2,6 @@
withDefaults(
defineProps<{
icon: string;
title?: string;
color?: 'primary' | 'subdued' | 'foreground' | 'warning';
href?: string;
to?: string;
@@ -16,15 +15,9 @@ withDefaults(
</script>
<template>
<v-list-item
:title="!hasTooltip ? title : undefined"
:class="[color, { 'has-tooltip': hasTooltip }]"
:clickable="!!href || !!to"
:href="href"
:to="to"
>
<v-list-item :class="[color, { 'has-tooltip': hasTooltip }]" :clickable="!!href || !!to" :href="href" :to="to">
<v-list-item-icon>
<slot name="icon"><v-icon :name="icon" small :title="hasTooltip ? title : undefined" /></slot>
<slot name="icon"><v-icon :name="icon" small /></slot>
</v-list-item-icon>
<v-list-item-content :class="{ monospace }"><slot /></v-list-item-content>
</v-list-item>

View File

@@ -1,14 +1,12 @@
<script setup lang="ts">
import VBanner from '@/components/v-banner.vue';
import type { RegistryAccountResponse } from '@directus/extensions-registry';
import { computed, ref } from 'vue';
import { computed } from 'vue';
const props = defineProps<{
account: RegistryAccountResponse['data'];
}>();
const avatarImgError = ref(false);
const hasMeta = computed(() => {
const account = props.account;
return !!account.github_location || !!account.github_company;
@@ -17,13 +15,9 @@ const hasMeta = computed(() => {
<template>
<VBanner icon="person">
<template v-if="account.github_avatar_url && !avatarImgError" #avatar>
<template v-if="account.github_avatar_url" #avatar>
<div class="avatar">
<img
:src="account.github_avatar_url"
:alt="account.github_name ?? account.username"
@error="avatarImgError = true"
/>
<img :src="account.github_avatar_url" :alt="account.github_name ?? account.username" />
</div>
</template>

View File

@@ -32,10 +32,8 @@ const npmLink = computed(() => {
</div>
<v-list class="list">
<div class="grid">
<MetadataItem v-if="account.username" icon="npm" :href="npmLink" title="NPM" monospace>
{{ account.username }}
</MetadataItem>
<MetadataItem v-if="account.github_username" icon="github" title="GitHub" :href="githubLink" monospace>
<MetadataItem v-if="account.username" icon="npm" :href="npmLink" monospace>{{ account.username }}</MetadataItem>
<MetadataItem v-if="account.github_username" icon="github" :href="githubLink" monospace>
{{ account.github_username }}
</MetadataItem>
<MetadataItem v-if="account.github_blog" icon="link" :href="account.github_blog">

View File

@@ -7,12 +7,12 @@ import { computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { formatName } from '../../../utils/format-name';
const { t } = useI18n();
const props = defineProps<{
extension: RegistryDescribeResponse['data'];
}>();
const { t } = useI18n();
const icon = computed(() => extensionTypeIconMap[props.extension.type]);
const newestVersion = computed(() => props.extension.versions.at(0)!);
</script>

View File

@@ -5,10 +5,10 @@ import { unexpectedError } from '@/utils/unexpected-error';
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
const props = defineProps<{ extensionId: string; versionId: string }>();
const { t } = useI18n();
const props = defineProps<{ extensionId: string; versionId: string }>();
const extensionsStore = useExtensionsStore();
const serverStore = useServerStore();

View File

@@ -1,7 +1,8 @@
<script setup lang="ts">
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
defineProps<{
id: string;
username: string;
@@ -9,21 +10,11 @@ defineProps<{
githubName: string | null;
githubAvatarUrl: string | null;
}>();
const { t } = useI18n();
const avatarImgError = ref(false);
</script>
<template>
<v-button class="author" secondary full-width align="left" :to="`/settings/marketplace/account/${id}`">
<img
v-if="githubAvatarUrl && !avatarImgError"
:src="githubAvatarUrl"
:alt="githubName ?? username"
class="avatar"
@error="avatarImgError = true"
/>
<img v-if="githubAvatarUrl" :src="githubAvatarUrl" :alt="githubName ?? username" class="avatar" />
<v-icon v-else name="face" left />
{{ githubName ?? username }}
<v-icon v-if="verified" v-tooltip="t('verified')" class="verified" name="verified" small />

View File

@@ -33,7 +33,6 @@ const label = computed(() =>
hostVersion: hostVersion,
})
"
:title="t('compatibility')"
:icon="icon"
:color="isCompatible ? 'subdued' : 'warning'"
has-tooltip

View File

@@ -9,7 +9,7 @@ const { t, d } = useI18n();
</script>
<template>
<MetadataItem v-tooltip="d(publishDate, 'long')" has-tooltip icon="event" :title="t('last_publish')">
<MetadataItem v-tooltip="d(publishDate, 'long')" has-tooltip icon="event">
{{
t('last_updated_relative', {
relativeTime: localizedFormatDistanceStrict(new Date(publishDate), new Date(), {

View File

@@ -8,5 +8,5 @@ const { t, n } = useI18n();
</script>
<template>
<MetadataItem icon="save_alt" :title="t('Downloads')">{{ t('n_downloads', { n: n(downloads) }) }}</MetadataItem>
<MetadataItem icon="save_alt">{{ t('n_downloads', { n: n(downloads) }) }}</MetadataItem>
</template>

View File

@@ -2,13 +2,13 @@
import MetadataItem from '../../../components/metadata-item.vue';
import { useI18n } from 'vue-i18n';
defineProps<{ license: string | null }>();
const { t } = useI18n();
defineProps<{ license: string | null }>();
</script>
<template>
<MetadataItem icon="policy" :title="t('license')" :class="{ known: !!license }">
<MetadataItem icon="policy" :class="{ known: !!license }">
{{ license ?? t('unknown') }}
</MetadataItem>
</template>

View File

@@ -3,13 +3,11 @@ import { formatFilesize } from '@/utils/format-filesize';
import { useI18n } from 'vue-i18n';
import MetadataItem from '../../../components/metadata-item.vue';
defineProps<{ fileCount: number; unpackedSize: number }>();
const { t } = useI18n();
defineProps<{ fileCount: number; unpackedSize: number }>();
</script>
<template>
<MetadataItem icon="folder_open" :title="t('Size')">
{{ formatFilesize(unpackedSize) }} ({{ t('n_files', fileCount) }})
</MetadataItem>
<MetadataItem icon="folder_open">{{ formatFilesize(unpackedSize) }} ({{ t('n_files', fileCount) }})</MetadataItem>
</template>

View File

@@ -1,12 +1,9 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n';
import MetadataItem from '../../../components/metadata-item.vue';
defineProps<{ version: string }>();
const { t } = useI18n();
</script>
<template>
<MetadataItem icon="sell" :title="t('Version')">v{{ version }}</MetadataItem>
<MetadataItem icon="sell">v{{ version }}</MetadataItem>
</template>

View File

@@ -71,9 +71,6 @@ const maintainers = computed(() => {
<ExtensionMetadataDate :publish-date="latestVersion.publish_date" />
<ExtensionMetadataLicense :license="extension.license" />
<ExtensionMetadataSize :unpacked-size="latestVersion.unpacked_size" :file-count="latestVersion.file_count" />
<MetadataItem icon="npm" title="NPM" :href="`https://www.npmjs.com/package/${extension.name}`">
<v-text-overflow :text="extension.name" />
</MetadataItem>
<MetadataItem v-if="latestVersion.url_homepage" icon="link" :href="latestVersion.url_homepage">
{{ t('homepage') }}
</MetadataItem>

View File

@@ -1,53 +1,16 @@
<script setup lang="ts">
import { RegistryDescribeResponse } from '@directus/extensions-registry';
import { useRouteHash } from '@vueuse/router';
import { computed, onMounted, watch } from 'vue';
import { useI18n } from 'vue-i18n';
const props = defineProps<{
extension: RegistryDescribeResponse['data'];
defineProps<{
readme: string | null;
}>();
const { t } = useI18n();
const hash = useRouteHash();
onMounted(() => {
if (!hash.value || typeof hash.value !== 'string') return;
const element = document.querySelector(hash.value);
element?.scrollIntoView();
});
watch(hash, (value) => {
if (!value || typeof value !== 'string') return;
const element = document.querySelector(value);
element?.scrollIntoView();
});
const base = computed(() => {
const urlRepository = props.extension.versions.at(0)!.url_repository;
if (!urlRepository) return null;
const url = new URL(urlRepository);
if (url.host !== 'github.com') return null;
const path = url.pathname.replace(/\.git$/, '');
const match = /^\/([\w-.]+)\/([\w-.]+)$/.exec(path);
if (!match) return;
const [_, owner, repo] = match;
return {
link: `https://github.com/${owner}/${repo}/blob/HEAD/`,
image: `https://raw.githubusercontent.com/${owner}/${repo}/HEAD/`,
};
});
</script>
<template>
<v-notice v-if="!extension.readme" class="notice">{{ t('extension_readme_missing') }}</v-notice>
<div v-else v-md="{ value: extension.readme, target: '_blank', base }" class="readme" />
<v-notice v-if="!readme" class="notice">{{ t('extension_readme_missing') }}</v-notice>
<div v-else v-md="{ value: readme, target: '_blank' }" class="readme" />
</template>
<style scoped lang="scss">

View File

@@ -71,7 +71,7 @@ const navigateBack = () => {
<div class="grid">
<ExtensionBanner class="banner" :extension="extension" />
<ExtensionMetadata class="metadata" :extension="extension" />
<ExtensionReadme class="readme" :extension="extension" />
<ExtensionReadme class="readme" :readme="extension.readme" />
</div>
</div>
</template>

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { EXTENSION_TYPES } from '@directus/extensions';
import { watchDebounced } from '@vueuse/core';
import { computed, ref, watch } from 'vue';
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
const type = defineModel<string | null>('type');
@@ -16,10 +16,6 @@ const props = defineProps<{
const searchInputValue = ref(search.value);
watch(search, (value) => {
if (searchInputValue.value !== value) searchInputValue.value = value;
});
watchDebounced(
searchInputValue,
(val) => {

View File

@@ -15,24 +15,10 @@ const { t } = useI18n();
const perPage = 10;
const search = useRouteQuery<string | null>('search', null, {
transform: (value) => (Array.isArray(value) ? value[0] : value),
});
const page = useRouteQuery<number>('page', 1, {
transform: (value) => Number(Array.isArray(value) ? value[0] : value) || 1,
mode: 'push',
});
const type = useRouteQuery<string | null>('type', null, {
transform: (value) => (Array.isArray(value) ? value[0] : value),
mode: 'push',
});
const sort = useRouteQuery<'popular' | 'recent' | 'downloads'>('sort', 'popular', {
transform: (value) => (Array.isArray(value) ? value[0] : value),
mode: 'push',
});
const search = useRouteQuery('search');
const page = useRouteQuery('page', 1);
const type = useRouteQuery('type');
const sort = useRouteQuery<'popular' | 'recent' | 'downloads'>('sort', 'popular');
watch([search, sort, type], (newVal, oldVal) => {
if (isEqual(newVal, oldVal) === false) {

View File

@@ -104,8 +104,7 @@ code {
}
pre {
position: relative;
padding: 20px;
padding: 1em;
overflow: auto;
font-weight: 500;
font-size: 15px;
@@ -113,78 +112,6 @@ pre {
line-height: 24px;
background-color: var(--theme--background-normal);
border-radius: var(--theme--border-radius);
&:hover > button.copy,
> button.copy:focus {
opacity: 1;
}
&.highlight {
&:hover > span.lang,
> button.copy:focus + span.lang {
opacity: 0;
}
span.lang {
position: absolute;
top: 2px;
right: 8px;
z-index: 2;
margin: 0;
font-size: 12px;
font-weight: 500;
color: var(--theme--foreground);
transition: opacity var(--slow);
}
}
button.copy {
position: absolute;
top: 12px;
right: 12px;
z-index: 3;
color: var(--theme--foreground);
border: var(--theme--border-width) solid var(--theme--foreground);
border-radius: var(--theme--border-radius);
width: 40px;
height: 40px;
opacity: 0;
transition:
color var(--medium),
border-color var(--medium),
background-color var(--medium),
opacity var(--slow);
display: block;
font-family: 'Material Symbols';
font-weight: normal;
font-size: 24px;
font-style: normal;
line-height: 1;
letter-spacing: normal;
text-transform: none;
white-space: nowrap;
word-wrap: normal;
direction: ltr;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
font-feature-settings: 'liga';
font-variation-settings:
'FILL' 0,
'wght' 400,
'GRAD' 0,
'opsz' 24;
&::after {
content: 'content_copy';
}
&:hover,
&.copied {
color: var(--theme--foreground-accent);
border-color: var(--theme--foreground-accent);
}
}
}
blockquote {
@@ -240,16 +167,3 @@ figure figcaption {
color: #999;
text-align: center;
}
.task-list-container {
padding-inline-start: 0;
list-style-position: inside;
.task-list-item {
list-style: none;
label {
display: unset;
}
}
}

View File

@@ -64,9 +64,9 @@
font-style: italic;
}
// .hljs-strong {
// font-weight: bold;
// }
.hljs-strong {
// font-weight: bold;
}
.hljs-function {
color: var(--purple);

View File

@@ -1,115 +1,30 @@
import createDOMPurify from 'dompurify';
import MarkdownIt from 'markdown-it';
import { tasklist as tasklistPlugin } from '@mdit/plugin-tasklist';
import hljs from 'highlight.js';
import anchorPlugin from 'markdown-it-anchor';
// @ts-ignore
import { full as emojiPlugin } from 'markdown-it-emoji';
import { marked } from 'marked';
import dompurify from 'dompurify';
type Options = {
target: '_blank' | '_self' | '_parent' | '_top';
base?: {
link?: string;
image?: string;
};
};
const markdownit = MarkdownIt({
html: true,
linkify: true,
highlight: (value, language) => {
if (language && hljs.getLanguage(language)) {
try {
return `<pre class="highlight language-${language}"><span class="lang">${language}</span><code class="hljs">${
hljs.highlight(value, { language, ignoreIllegals: true }).value
}</code></pre>`;
} catch {
// Ignore
}
}
return `<pre><code>${value}</code></pre>`;
},
})
.use(emojiPlugin)
.use(anchorPlugin)
.use(tasklistPlugin);
const renderer = new marked.Renderer();
/**
* Render and sanitize a markdown string
*/
export function md(value: string, options: Options = { target: '_self' }): string {
const dompurify = createDOMPurify();
dompurify.addHook('afterSanitizeAttributes', (node) => {
if (node.tagName === 'A') {
sanitizeLink(node as HTMLAnchorElement, options);
}
if (node.tagName === 'IMG') {
sanitizeImage(node as HTMLImageElement, options);
if (node.tagName === 'A' && node.getAttribute('target') === '_blank') {
node.setAttribute('rel', 'noopener noreferrer');
}
});
const markdown = markdownit.render(value);
renderer.link = function (href, title, text) {
const link = marked.Renderer.prototype.link.apply(this, [href, title, text]);
return link.replace('<a', `<a target="${options.target}"`);
};
return dompurify.sanitize(markdown);
}
function sanitizeLink(node: HTMLAnchorElement, options: Options) {
const url = node.getAttribute('href');
if (!url) return;
try {
new URL(url, 'http://example.com');
} catch {
node.removeAttribute('href');
return;
}
let isAbsolute = true;
try {
new URL(url);
} catch {
isAbsolute = false;
}
if (!isAbsolute) {
if (url.startsWith('#')) return;
if (options.base?.link) node.setAttribute('href', new URL(url, options.base.link).toString());
}
node.setAttribute('target', options.target);
if (options.target === '_blank') node.setAttribute('rel', 'noopener noreferrer');
}
function sanitizeImage(node: HTMLImageElement, options: Options) {
if (node.getAttribute('alt')?.trim() === '') node.removeAttribute('alt');
const url = node.getAttribute('src');
if (!url) return;
try {
new URL(url, 'http://example.com');
} catch {
node.removeAttribute('src');
return;
}
node.setAttribute('loading', 'lazy');
let isAbsolute = true;
try {
new URL(url);
} catch {
isAbsolute = false;
}
if (!isAbsolute && options.base?.image) node.setAttribute('src', new URL(url, options.base.image).toString());
const markdown = marked.parse(value, {
renderer,
}) as string; /* Would only be a promise if used with async extensions */
return dompurify.sanitize(markdown, { ADD_ATTR: ['target'] });
}

324
pnpm-lock.yaml generated
View File

@@ -643,9 +643,6 @@ importers:
'@mapbox/mapbox-gl-geocoder':
specifier: 5.0.2
version: 5.0.2
'@mdit/plugin-tasklist':
specifier: 0.8.0
version: 0.8.0(markdown-it@14.0.0)
'@ngneat/falso':
specifier: 7.2.0
version: 7.2.0
@@ -703,9 +700,6 @@ importers:
'@types/mapbox__mapbox-gl-geocoder':
specifier: 4.7.7
version: 4.7.7
'@types/markdown-it':
specifier: 13.0.7
version: 13.0.7
'@types/qrcode':
specifier: 1.5.5
version: 1.5.5
@@ -787,9 +781,6 @@ importers:
happy-dom:
specifier: 13.6.2
version: 13.6.2
highlight.js:
specifier: 11.9.0
version: 11.9.0
histoire:
specifier: 0.17.9
version: 0.17.9(sass@1.71.1)(vite@5.1.4)
@@ -811,21 +802,9 @@ importers:
maplibre-gl:
specifier: 1.15.3
version: 1.15.3(mapbox-gl@1.13.3)
markdown-it:
specifier: 14.0.0
version: 14.0.0
markdown-it-anchor:
specifier: 8.6.7
version: 8.6.7(@types/markdown-it@13.0.7)(markdown-it@14.0.0)
markdown-it-emoji:
specifier: 3.0.0
version: 3.0.0
mdast-util-from-markdown:
specifier: 2.0.0
version: 2.0.0
mdast-util-to-string:
specifier: 4.0.0
version: 4.0.0
marked:
specifier: 12.0.0
version: 12.0.0
micromustache:
specifier: 8.0.3
version: 8.0.3
@@ -991,7 +970,7 @@ importers:
version: 5.3.3
vite:
specifier: 5.1.4
version: 5.1.4(@types/node@18.19.21)
version: 5.1.4(sass@1.71.1)
vite-plugin-dts:
specifier: 3.7.3
version: 3.7.3(rollup@4.12.0)(typescript@5.3.3)(vite@5.1.4)
@@ -1966,7 +1945,7 @@ importers:
version: 5.3.3
vite:
specifier: 5.1.4
version: 5.1.4(@types/node@18.19.21)
version: 5.1.4(sass@1.71.1)
vite-plugin-dts:
specifier: 3.7.3
version: 3.7.3(rollup@4.12.0)(typescript@5.3.3)(vite@5.1.4)
@@ -5466,19 +5445,6 @@ packages:
engines: {node: '>=6.0.0'}
dev: true
/@mdit/plugin-tasklist@0.8.0(markdown-it@14.0.0):
resolution: {integrity: sha512-vfOTZdXIL/jk/ConUqCODI5WuqgB9qiBGc+wIa7UMhe73KcpwFeGFJVQZm9AvjhXDDYqznJxSMVRP/TN7TxVVw==}
engines: {node: '>= 18'}
peerDependencies:
markdown-it: ^14.0.0
peerDependenciesMeta:
markdown-it:
optional: true
dependencies:
'@types/markdown-it': 13.0.7
markdown-it: 14.0.0
dev: true
/@microsoft/api-extractor-model@7.28.3:
resolution: {integrity: sha512-wT/kB2oDbdZXITyDh2SQLzaWwTOFbV326fP0pUwNW00WeliARs0qjmXBWmGWardEzp2U3/axkO3Lboqun6vrig==}
dependencies:
@@ -7440,25 +7406,12 @@ packages:
'@types/mdurl': 1.0.5
dev: true
/@types/markdown-it@13.0.7:
resolution: {integrity: sha512-U/CBi2YUUcTHBt5tjO2r5QV/x0Po6nsYwQU4Y04fBS6vfoImaiZ6f8bi3CjTCxBPQSO1LMyUqkByzi8AidyxfA==}
dependencies:
'@types/linkify-it': 3.0.5
'@types/mdurl': 1.0.5
dev: true
/@types/mdast@3.0.15:
resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==}
dependencies:
'@types/unist': 2.0.10
dev: true
/@types/mdast@4.0.3:
resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==}
dependencies:
'@types/unist': 3.0.2
dev: true
/@types/mdurl@1.0.5:
resolution: {integrity: sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==}
dev: true
@@ -7683,10 +7636,6 @@ packages:
resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==}
dev: true
/@types/unist@3.0.2:
resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==}
dev: true
/@types/uuid@9.0.8:
resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==}
dev: true
@@ -10465,12 +10414,6 @@ packages:
resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==}
engines: {node: '>=8'}
/devlop@1.1.0:
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
dependencies:
dequal: 2.0.3
dev: true
/dezalgo@1.0.4:
resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==}
dependencies:
@@ -12296,11 +12239,6 @@ packages:
engines: {node: '>=8'}
dev: true
/highlight.js@11.9.0:
resolution: {integrity: sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==}
engines: {node: '>=12.0.0'}
dev: true
/histoire@0.17.9(sass@1.71.1)(vite@5.1.4):
resolution: {integrity: sha512-z5Jb9QwbOw0TKvpkU0v7+CxJG6hIljIKMhWXzOfteteRZGDFElpTEwbr5/8EdPI6VTdF/k76fqZ07nmS9YdUvA==}
hasBin: true
@@ -13669,12 +13607,6 @@ packages:
uc.micro: 1.0.6
dev: true
/linkify-it@5.0.0:
resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
dependencies:
uc.micro: 2.1.0
dev: true
/liquidjs@10.10.1:
resolution: {integrity: sha512-h699VW79OLoshCTjFF02tmRCrd8t/E49LSIsjLwlg4k0TbMVjxsCRXVUEsURXbfKl3HUln2cShlDQCrSNm2YaA==}
engines: {node: '>=14'}
@@ -14097,16 +14029,6 @@ packages:
markdown-it: 12.3.2
dev: true
/markdown-it-anchor@8.6.7(@types/markdown-it@13.0.7)(markdown-it@14.0.0):
resolution: {integrity: sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==}
peerDependencies:
'@types/markdown-it': '*'
markdown-it: '*'
dependencies:
'@types/markdown-it': 13.0.7
markdown-it: 14.0.0
dev: true
/markdown-it-attrs@4.1.6(markdown-it@12.3.2):
resolution: {integrity: sha512-O7PDKZlN8RFMyDX13JnctQompwrrILuz2y43pW2GagcwpIIElkAdfeek+erHfxUOlXWPsjFeWmZ8ch1xtRLWpA==}
engines: {node: '>=6'}
@@ -14120,10 +14042,6 @@ packages:
resolution: {integrity: sha512-zLftSaNrKuYl0kR5zm4gxXjHaOI3FAOEaloKmRA5hijmJZvSjmxcokOLlzycb/HXlUFWzXqpIEoyEMCE4i9MvQ==}
dev: true
/markdown-it-emoji@3.0.0:
resolution: {integrity: sha512-+rUD93bXHubA4arpEZO3q80so0qgoFJEKRkRbjKX8RTdca89v2kfyF+xR3i2sQTwql9tpPZPOQN5B+PunspXRg==}
dev: true
/markdown-it@12.3.2:
resolution: {integrity: sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==}
hasBin: true
@@ -14135,18 +14053,6 @@ packages:
uc.micro: 1.0.6
dev: true
/markdown-it@14.0.0:
resolution: {integrity: sha512-seFjF0FIcPt4P9U39Bq1JYblX0KZCjDLFFQPHpL5AzHpqPEKtosxmdq/LTVZnjfH7tjt9BxStm+wXcDBNuYmzw==}
hasBin: true
dependencies:
argparse: 2.0.1
entities: 4.5.0
linkify-it: 5.0.0
mdurl: 2.0.0
punycode.js: 2.3.1
uc.micro: 2.1.0
dev: true
/marked@12.0.0:
resolution: {integrity: sha512-Vkwtq9rLqXryZnWaQc86+FHLC6tr/fycMfYAhiOIXkrNmeGAyhSxjqu0Rs1i0bBqw5u0S7+lV9fdH2ZSVaoa0w==}
engines: {node: '>= 18'}
@@ -14189,25 +14095,6 @@ packages:
- supports-color
dev: true
/mdast-util-from-markdown@2.0.0:
resolution: {integrity: sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==}
dependencies:
'@types/mdast': 4.0.3
'@types/unist': 3.0.2
decode-named-character-reference: 1.0.2
devlop: 1.1.0
mdast-util-to-string: 4.0.0
micromark: 4.0.0
micromark-util-decode-numeric-character-reference: 2.0.1
micromark-util-decode-string: 2.0.0
micromark-util-normalize-identifier: 2.0.0
micromark-util-symbol: 2.0.0
micromark-util-types: 2.0.0
unist-util-stringify-position: 4.0.0
transitivePeerDependencies:
- supports-color
dev: true
/mdast-util-frontmatter@1.0.1:
resolution: {integrity: sha512-JjA2OjxRqAa8wEG8hloD0uTU0kdn8kbtOWpPP94NBkfAlbxn4S8gCGf/9DwFtEeGPXrDcNXdiDjVaRdUFqYokw==}
dependencies:
@@ -14258,12 +14145,6 @@ packages:
'@types/mdast': 3.0.15
dev: true
/mdast-util-to-string@4.0.0:
resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
dependencies:
'@types/mdast': 4.0.3
dev: true
/mdn-data@2.0.14:
resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==}
dev: false
@@ -14272,10 +14153,6 @@ packages:
resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==}
dev: true
/mdurl@2.0.0:
resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==}
dev: true
/media-typer@0.3.0:
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
engines: {node: '>= 0.6'}
@@ -14365,27 +14242,6 @@ packages:
uvu: 0.5.6
dev: true
/micromark-core-commonmark@2.0.0:
resolution: {integrity: sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==}
dependencies:
decode-named-character-reference: 1.0.2
devlop: 1.1.0
micromark-factory-destination: 2.0.0
micromark-factory-label: 2.0.0
micromark-factory-space: 2.0.0
micromark-factory-title: 2.0.0
micromark-factory-whitespace: 2.0.0
micromark-util-character: 2.1.0
micromark-util-chunked: 2.0.0
micromark-util-classify-character: 2.0.0
micromark-util-html-tag-name: 2.0.0
micromark-util-normalize-identifier: 2.0.0
micromark-util-resolve-all: 2.0.0
micromark-util-subtokenize: 2.0.0
micromark-util-symbol: 2.0.0
micromark-util-types: 2.0.0
dev: true
/micromark-extension-frontmatter@1.1.1:
resolution: {integrity: sha512-m2UH9a7n3W8VAH9JO9y01APpPKmNNNs71P0RbknEmYSaZU5Ghogv38BYO94AI5Xw6OYfxZRdHZZ2nYjs/Z+SZQ==}
dependencies:
@@ -14403,14 +14259,6 @@ packages:
micromark-util-types: 1.1.0
dev: true
/micromark-factory-destination@2.0.0:
resolution: {integrity: sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==}
dependencies:
micromark-util-character: 2.1.0
micromark-util-symbol: 2.0.0
micromark-util-types: 2.0.0
dev: true
/micromark-factory-label@1.1.0:
resolution: {integrity: sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==}
dependencies:
@@ -14420,15 +14268,6 @@ packages:
uvu: 0.5.6
dev: true
/micromark-factory-label@2.0.0:
resolution: {integrity: sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==}
dependencies:
devlop: 1.1.0
micromark-util-character: 2.1.0
micromark-util-symbol: 2.0.0
micromark-util-types: 2.0.0
dev: true
/micromark-factory-space@1.1.0:
resolution: {integrity: sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==}
dependencies:
@@ -14436,13 +14275,6 @@ packages:
micromark-util-types: 1.1.0
dev: true
/micromark-factory-space@2.0.0:
resolution: {integrity: sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==}
dependencies:
micromark-util-character: 2.1.0
micromark-util-types: 2.0.0
dev: true
/micromark-factory-title@1.1.0:
resolution: {integrity: sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==}
dependencies:
@@ -14452,15 +14284,6 @@ packages:
micromark-util-types: 1.1.0
dev: true
/micromark-factory-title@2.0.0:
resolution: {integrity: sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==}
dependencies:
micromark-factory-space: 2.0.0
micromark-util-character: 2.1.0
micromark-util-symbol: 2.0.0
micromark-util-types: 2.0.0
dev: true
/micromark-factory-whitespace@1.1.0:
resolution: {integrity: sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==}
dependencies:
@@ -14470,15 +14293,6 @@ packages:
micromark-util-types: 1.1.0
dev: true
/micromark-factory-whitespace@2.0.0:
resolution: {integrity: sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==}
dependencies:
micromark-factory-space: 2.0.0
micromark-util-character: 2.1.0
micromark-util-symbol: 2.0.0
micromark-util-types: 2.0.0
dev: true
/micromark-util-character@1.2.0:
resolution: {integrity: sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==}
dependencies:
@@ -14486,25 +14300,12 @@ packages:
micromark-util-types: 1.1.0
dev: true
/micromark-util-character@2.1.0:
resolution: {integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==}
dependencies:
micromark-util-symbol: 2.0.0
micromark-util-types: 2.0.0
dev: true
/micromark-util-chunked@1.1.0:
resolution: {integrity: sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==}
dependencies:
micromark-util-symbol: 1.1.0
dev: true
/micromark-util-chunked@2.0.0:
resolution: {integrity: sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==}
dependencies:
micromark-util-symbol: 2.0.0
dev: true
/micromark-util-classify-character@1.1.0:
resolution: {integrity: sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==}
dependencies:
@@ -14513,14 +14314,6 @@ packages:
micromark-util-types: 1.1.0
dev: true
/micromark-util-classify-character@2.0.0:
resolution: {integrity: sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==}
dependencies:
micromark-util-character: 2.1.0
micromark-util-symbol: 2.0.0
micromark-util-types: 2.0.0
dev: true
/micromark-util-combine-extensions@1.1.0:
resolution: {integrity: sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==}
dependencies:
@@ -14528,25 +14321,12 @@ packages:
micromark-util-types: 1.1.0
dev: true
/micromark-util-combine-extensions@2.0.0:
resolution: {integrity: sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==}
dependencies:
micromark-util-chunked: 2.0.0
micromark-util-types: 2.0.0
dev: true
/micromark-util-decode-numeric-character-reference@1.1.0:
resolution: {integrity: sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==}
dependencies:
micromark-util-symbol: 1.1.0
dev: true
/micromark-util-decode-numeric-character-reference@2.0.1:
resolution: {integrity: sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==}
dependencies:
micromark-util-symbol: 2.0.0
dev: true
/micromark-util-decode-string@1.1.0:
resolution: {integrity: sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==}
dependencies:
@@ -14556,55 +14336,26 @@ packages:
micromark-util-symbol: 1.1.0
dev: true
/micromark-util-decode-string@2.0.0:
resolution: {integrity: sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==}
dependencies:
decode-named-character-reference: 1.0.2
micromark-util-character: 2.1.0
micromark-util-decode-numeric-character-reference: 2.0.1
micromark-util-symbol: 2.0.0
dev: true
/micromark-util-encode@1.1.0:
resolution: {integrity: sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==}
dev: true
/micromark-util-encode@2.0.0:
resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==}
dev: true
/micromark-util-html-tag-name@1.2.0:
resolution: {integrity: sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==}
dev: true
/micromark-util-html-tag-name@2.0.0:
resolution: {integrity: sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==}
dev: true
/micromark-util-normalize-identifier@1.1.0:
resolution: {integrity: sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==}
dependencies:
micromark-util-symbol: 1.1.0
dev: true
/micromark-util-normalize-identifier@2.0.0:
resolution: {integrity: sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==}
dependencies:
micromark-util-symbol: 2.0.0
dev: true
/micromark-util-resolve-all@1.1.0:
resolution: {integrity: sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==}
dependencies:
micromark-util-types: 1.1.0
dev: true
/micromark-util-resolve-all@2.0.0:
resolution: {integrity: sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==}
dependencies:
micromark-util-types: 2.0.0
dev: true
/micromark-util-sanitize-uri@1.2.0:
resolution: {integrity: sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==}
dependencies:
@@ -14613,14 +14364,6 @@ packages:
micromark-util-symbol: 1.1.0
dev: true
/micromark-util-sanitize-uri@2.0.0:
resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==}
dependencies:
micromark-util-character: 2.1.0
micromark-util-encode: 2.0.0
micromark-util-symbol: 2.0.0
dev: true
/micromark-util-subtokenize@1.1.0:
resolution: {integrity: sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==}
dependencies:
@@ -14630,31 +14373,14 @@ packages:
uvu: 0.5.6
dev: true
/micromark-util-subtokenize@2.0.0:
resolution: {integrity: sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==}
dependencies:
devlop: 1.1.0
micromark-util-chunked: 2.0.0
micromark-util-symbol: 2.0.0
micromark-util-types: 2.0.0
dev: true
/micromark-util-symbol@1.1.0:
resolution: {integrity: sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==}
dev: true
/micromark-util-symbol@2.0.0:
resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==}
dev: true
/micromark-util-types@1.1.0:
resolution: {integrity: sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==}
dev: true
/micromark-util-types@2.0.0:
resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==}
dev: true
/micromark@2.11.4:
resolution: {integrity: sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==}
dependencies:
@@ -14688,30 +14414,6 @@ packages:
- supports-color
dev: true
/micromark@4.0.0:
resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==}
dependencies:
'@types/debug': 4.1.12
debug: 4.3.4
decode-named-character-reference: 1.0.2
devlop: 1.1.0
micromark-core-commonmark: 2.0.0
micromark-factory-space: 2.0.0
micromark-util-character: 2.1.0
micromark-util-chunked: 2.0.0
micromark-util-combine-extensions: 2.0.0
micromark-util-decode-numeric-character-reference: 2.0.1
micromark-util-encode: 2.0.0
micromark-util-normalize-identifier: 2.0.0
micromark-util-resolve-all: 2.0.0
micromark-util-sanitize-uri: 2.0.0
micromark-util-subtokenize: 2.0.0
micromark-util-symbol: 2.0.0
micromark-util-types: 2.0.0
transitivePeerDependencies:
- supports-color
dev: true
/micromatch@4.0.5:
resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
engines: {node: '>=8.6'}
@@ -16870,11 +16572,6 @@ packages:
end-of-stream: 1.4.4
once: 1.4.0
/punycode.js@2.3.1:
resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==}
engines: {node: '>=6'}
dev: true
/punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
@@ -18148,7 +17845,6 @@ packages:
/source-map@0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
requiresBuild: true
/source-map@0.8.0-beta.0:
resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==}
@@ -19432,10 +19128,6 @@ packages:
resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==}
dev: true
/uc.micro@2.1.0:
resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==}
dev: true
/ufo@1.3.2:
resolution: {integrity: sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==}
dev: true
@@ -19560,12 +19252,6 @@ packages:
'@types/unist': 2.0.10
dev: true
/unist-util-stringify-position@4.0.0:
resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
dependencies:
'@types/unist': 3.0.2
dev: true
/unist-util-visit-children@2.0.2:
resolution: {integrity: sha512-+LWpMFqyUwLGpsQxpumsQ9o9DG2VGLFrpz+rpVXYIEdPy57GSy5HioC0g3bg/8WP9oCLlapQtklOzQ8uLS496Q==}
dependencies: