@curvenote/cli
Version:
CLI Client library for Curvenote
80 lines (79 loc) • 2.43 kB
JavaScript
import { fileWarn } from 'myst-common';
const BSKY_HOST = 'bsky.app';
const BSKY_TRANSFORM_SOURCE = 'LinkTransform:Bluesky';
function updateLinkTextIfEmpty(link, title) {
if (!title)
return;
const text = [{ type: 'text', value: title }];
if (link.children?.length === 1 &&
link.children[0]?.type === 'text' &&
link.children[0]?.value === (link.urlSource || link.url)) {
link.children = text;
return;
}
if (!link.children || link.children.length === 0) {
link.children = text;
}
}
function parseBlueskyUrl(urlSource) {
let url;
try {
url = new URL(urlSource);
}
catch {
return undefined;
}
if (url.host !== BSKY_HOST)
return undefined;
// Post: /profile/{handle}/post/{rkey}
const postMatch = url.pathname.match(/^\/profile\/([^/]+)\/post\/([^/]+)/);
if (postMatch) {
const [, handle, postId] = postMatch;
return [`@${handle}`, { kind: 'post', handle, postId }];
}
// Profile: /profile/{handle}
const profileMatch = url.pathname.match(/^\/profile\/([^/]+)/);
if (profileMatch) {
const [, handle] = profileMatch;
return [`@${handle}`, { kind: 'profile', handle }];
}
return undefined;
}
/**
* Recognizes Bluesky profile and post links and enriches them with
* structured data so the frontend can render Bluesky cards/hovercards.
*
* Supported URL forms:
* https://bsky.app/profile/{handle}
* https://bsky.app/profile/{handle}/post/{rkey}
*/
export class BlueskyTransformer {
protocol = 'bluesky';
test(uri) {
if (!uri)
return false;
try {
const url = new URL(uri);
return url.host === BSKY_HOST && url.pathname.startsWith('/profile/');
}
catch {
return false;
}
}
transform(link, file) {
const urlSource = link.urlSource || link.url;
const parsed = parseBlueskyUrl(urlSource);
if (!parsed) {
fileWarn(file, `Found Bluesky URL but could not parse it: ${urlSource}`, {
node: link,
source: BSKY_TRANSFORM_SOURCE,
});
return false;
}
const [defaultText, data] = parsed;
link.data = { ...link.data, ...data };
link.internal = false;
updateLinkTextIfEmpty(link, defaultText);
return true;
}
}