diff --git a/package.json b/package.json index b875fdb5..4ff878e9 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,6 @@ "focus-trap": "^7.5.4", "mark.js": "8.11.1", "minisearch": "^6.3.0", - "mrmime": "^2.0.0", "shikiji": "^0.9.15", "shikiji-core": "^0.9.15", "shikiji-transformers": "^0.9.15", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 13c8f4d2..f10ff0e9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,9 +41,6 @@ importers: minisearch: specifier: ^6.3.0 version: 6.3.0 - mrmime: - specifier: ^2.0.0 - version: 2.0.0 shikiji: specifier: ^0.9.15 version: 0.9.15 @@ -3338,6 +3335,7 @@ packages: /mrmime@2.0.0: resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} engines: {node: '>=10'} + dev: true /ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} diff --git a/src/client/app/router.ts b/src/client/app/router.ts index 3a7d8cb5..02d98944 100644 --- a/src/client/app/router.ts +++ b/src/client/app/router.ts @@ -1,7 +1,6 @@ import { reactive, inject, markRaw, nextTick, readonly } from 'vue' import type { Component, InjectionKey } from 'vue' -import { lookup } from 'mrmime' -import { notFoundPageData } from '../shared' +import { notFoundPageData, treatAsHtml } from '../shared' import type { PageData, PageDataPayload, Awaitable } from '../shared' import { inBrowser, withBase } from './utils' import { siteDataRef } from './data' @@ -182,8 +181,7 @@ export function createRouter( link.baseURI ) const currentUrl = window.location - const mimeType = lookup(pathname) - // only intercept inbound links + // only intercept inbound html links if ( !e.ctrlKey && !e.shiftKey && @@ -191,8 +189,7 @@ export function createRouter( !e.metaKey && !target && origin === currentUrl.origin && - // intercept only html and unknown types (assume html) - (!mimeType || mimeType === 'text/html') + treatAsHtml(pathname) ) { e.preventDefault() if ( diff --git a/src/client/theme-default/support/utils.ts b/src/client/theme-default/support/utils.ts index f63e855a..b04efa17 100644 --- a/src/client/theme-default/support/utils.ts +++ b/src/client/theme-default/support/utils.ts @@ -1,7 +1,6 @@ import { withBase } from 'vitepress' -import { lookup } from 'mrmime' import { useData } from '../composables/data' -import { isExternal } from '../../shared' +import { isExternal, treatAsHtml } from '../../shared' export function throttleAndDebounce(fn: () => void, delay: number): () => void { let timeoutId: NodeJS.Timeout @@ -28,7 +27,7 @@ export function normalizeLink(url: string): string { isExternal(url) || url.startsWith('#') || !protocol.startsWith('http') || - (/\.(?!html|md)\w+($|\?)/i.test(url) && lookup(url)) + !treatAsHtml(pathname) ) return url diff --git a/src/node/markdown/plugins/link.ts b/src/node/markdown/plugins/link.ts index 841cd4cd..45621daf 100644 --- a/src/node/markdown/plugins/link.ts +++ b/src/node/markdown/plugins/link.ts @@ -4,7 +4,12 @@ import type MarkdownIt from 'markdown-it' import { URL } from 'url' -import { EXTERNAL_URL_RE, isExternal, type MarkdownEnv } from '../../shared' +import { + EXTERNAL_URL_RE, + isExternal, + treatAsHtml, + type MarkdownEnv +} from '../../shared' const indexRE = /(^|.*\/)index.md(#?.*)$/i @@ -35,13 +40,15 @@ export const linkPlugin = ( } hrefAttr[1] = url } else { + const { pathname, protocol } = new URL(url, 'http://a.com') + if ( - // internal anchor links + // skip internal anchor links !url.startsWith('#') && - // mail/custom protocol links - new URL(url, 'http://a.com').protocol.startsWith('http') && - // links to files (other than html/md) - !/\.(?!html|md)\w+($|\?)/i.test(url) + // skip mail/custom protocol links + protocol.startsWith('http') && + // skip links to files (other than html/md) + treatAsHtml(pathname) ) { normalizeHref(hrefAttr, env) } else if (url.startsWith('#')) { diff --git a/src/node/markdownToVue.ts b/src/node/markdownToVue.ts index 6901f464..fd3b0d88 100644 --- a/src/node/markdownToVue.ts +++ b/src/node/markdownToVue.ts @@ -14,7 +14,8 @@ import { slash, type HeadConfig, type MarkdownEnv, - type PageData + type PageData, + treatAsHtml } from './shared' import { getGitTimestamp } from './utils/getGitTimestamp' import { processIncludes } from './utils/processIncludes' @@ -145,7 +146,8 @@ export async function createMarkdownToVueRenderFn( if (links) { const dir = path.dirname(file) for (let url of links) { - if (/\.(?!html|md)\w+($|\?)/i.test(url)) continue + const { pathname } = new URL(url, 'http://a.com') + if (!treatAsHtml(pathname)) continue url = url.replace(/[?#].*$/, '').replace(/\.(html|md)$/, '') if (url.endsWith('/')) url += `index` diff --git a/src/shared/shared.ts b/src/shared/shared.ts index 9dd05e34..d0436bc4 100644 --- a/src/shared/shared.ts +++ b/src/shared/shared.ts @@ -174,3 +174,14 @@ export function sanitizeFileName(name: string): string { export function slash(p: string): string { return p.replace(/\\/g, '/') } + +// md, html? are intentionally omitted, see treatAsHtml +const commonMimeTypes = `3g2,3gp,7z,aac,abw,ai,aif,aifc,aiff,arc,asf,asr,asx,au,avi,avif,axs,azw,bin,bmp,bz,bz2,c,cda,cer,class,crl,crt,csh,css,csv,dcr,der,dll,doc,docx,eot,eps,epub,exe,gif,gtar,gz,gzip,ico,ics,ief,jar,jpe,jpeg,jpg,js,json,jsonld,latex,m3u,man,mdb,mht,mhtml,mid,midi,mjs,mov,mp2,mp3,mp4,mpa,mpe,mpeg,mpg,mpkg,mpp,odp,ods,odt,oga,ogv,ogx,opus,otf,p10,p12,p7b,p7c,p7m,p7r,p7s,pbm,pdf,pfx,php,png,ppt,pptx,ps,pub,qt,rar,roff,rtf,rtx,ser,sh,spc,svg,swf,t,tar,tcl,tex,texi,texinfo,tgz,tif,tiff,tr,ts,tsv,ttf,txt,ua,viv,vivo,vsd,wav,weba,webm,webp,woff,woff2,xbm,xhtml,xls,xlsx,xml,xul,zip` + +const set = new Set(commonMimeTypes.split(',')) + +export function treatAsHtml(filename: string): boolean { + const ext = filename.split('.').pop()?.toLowerCase() + + return ext == null || !set.has(ext) +}