generated at
advanced-related-pages-2
外部projectを自分のprojectとみなしてリンク構造を計算するUserScript
old version:

このscriptでやること
リンク情報の格納
リンク情報の更新
指定したページの逆リンクを取得
このscriptでやらないこと
リンク情報のfetch
ページ本文やscrapboxのページカードの格納
advanced-related-pagesではやっていたが、責務の分離をしてそれらを別のscriptにやらせることにした

link networkの計算方法
全て同じprojectのリンクだとして計算する
大文字小文字の違いは無視する
全角半角スペースとアンダーバーの違いも無視する

data構造
リンク情報
各ページのリンク先ページタイトルを入れておく
基本的にapi/pages/:projectname/search/titlesで取得した情報と同じ
外部projectを透過的に扱うので、リンク先ページにはprojectの情報を入れる必要がない
ts
interface LinkCache { project: string, title: string, links: { title: string, }[]; }
逆リンク情報
各ページを参照しているページタイトルを保存する
LinkCache から必要な分だけ計算する
計算処理が重いので、一度に計算するようなことはしない
参照元のproject nameは保持しておく
scrapboxのページカードや本文をAPIから取得するのに必要
ts
interface BackLinksData { title: string, backLinks: { project: string, title: string, }[]; }

script.js
export class RelatedPageManager { constructor() { this._worker = new Worker('/api/code/takker/advanced-related-pages-2/link-calc-worker.js'); }

リンク情報の追加と削除
script.js
push(...linkCache) { this._worker.postMessage({type: 'push', linkCache: linkCache}); } clear() { this._worker.postMessage({type: 'clear'}); }

指定したページの逆リンク情報を取得する
既に backLinksData にあればそれを返す
なければ計算して返す
すぐに値がほしいので、WebWorkerの処理を待つ
script.js
async getBackLinks(title) { // workerに処理を委託して待つ const result = new Promise((resolve, reject) => this._worker.addEventListener('message', message => { resolve(message.data); }, {once: true})); this._worker.postMessage({type: 'get', title: title}); return await result; } // debug用 _log(msg, ...objects) { console.log(`[RelatedPageManager] ${msg}`, objects); } }

WebWorkerのコード
やること
データの格納
大文字小文字の変換とスペースをタブにする変換を行っておく
逆リンクの計算
link-calc-worker.js
let linkCache = []; let backLinksData = []; self.addEventListener('message', async message => { switch (message.data.type) { case 'push': push(...message.data.linkCache); break; case 'clear': clear(); break; case 'get': self.postMessage(getBackLinks(message.data.title)); break; } }); function push(...links) { // 文字列の変換 const format = title => title.toLowerCase().replace(/\s/g,'_'); const temp = links.map(data => { return { project: data.project, title: format(data.title), links: data.links .map(link => format(link)), };}); linkCache.push(...temp); backLinksData = []; _log(`Pushed link cache: %o`,linkCache); } function clear() { linkCache = []; backLinksData = []; } function getBackLinks(title) { // 計算済みの逆リンク情報があればそれを返す const backLinksCache = backLinksData.find(data => data.title === title)?.backLinks; if (backLinksCache) return backLinksCache; // なければ新たに計算し直す _log('Start calculating back links...') const backLinks = linkCache .flatMap(cache => cache.links.includes(title) ? [{project: cache.project, title: cache.title}] : []); _log(`Finish calculating ${backLinks.length} back links.`); return { title: title, backLinks: backLinks, }; } // debug用 function _log(msg, ...objects) { if (objects.length === 0) { console.log(`[link-calc-worker] ${msg}`); return; } console.log(`[link-calc-worker] ${msg} %o`, ...objects); }

#2021-02-05 13:23:42
#2021-01-15 15:54:23
#2020-12-10 05:29:43