Skip to content

add middleware to process external links substituting for ExternalLinksIntegration #1469

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 17 commits into
base: master
Choose a base branch
from

Conversation

aymkx
Copy link
Contributor

@aymkx aymkx commented Mar 16, 2025

rehypeプラグインを容易に追加できるMarkdownおよびMDXによるページに対し、developmentモード( npm run dev 実行時)でも外部リンクのアイコンを表示するようにします。

最終的にはすべてのHTMLファイルにアイコンを挿入する必要があるため、ビルド時( npm run build 実行時)には既存の externalLinks インテグレーションは引き続き必要となります。

Copy link

github-actions bot commented Mar 16, 2025

🚀 Deployed on https://deploy-preview-1469--utelecon.netlify.app

@cm-ayf cm-ayf self-requested a review March 16, 2025 08:11
Copy link
Member

@cm-ayf cm-ayf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

↑間違えて承認しました……

@aymkx aymkx changed the title enable rehype-external-link in development mode add middleware to process external links substituting for ExternalLinksIntegration Mar 17, 2025
@aymkx
Copy link
Contributor Author

aymkx commented Mar 17, 2025

方針を変えます。
astro.config.tscompressHTML: false を設定して動作検証してください。

  1. ExternalLinksIntegrationがリダイレクトを処理しないように変更しました。これによるビルド結果を作成し保存しておきます。
git checkout 3c1adc1fbd11d50d8b0db90c33db81d78f6e45dd
npm run build
mv dist dist_old
  1. ExternalLinksIntegrationの利用をやめ、ミドルウェアによる処理に統一しました。これによるビルド結果を以前のものと比較してください。
git checkout b843690049692a7addc9d35a61da8f4f3841f891
npm run build
diff -urwB dist_old dist

手元の結果を貼っておきます。
astroがリダイレクトのために生成するHTMLは <html> タグ、 <head> タグが省略されており、rehype-stringifyによって再生成されるHTMLと完全には一致しません。

@aymkx aymkx requested a review from cm-ayf March 17, 2025 16:44
Copy link
Member

@cm-ayf cm-ayf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

だいぶ筋が良さそうな気がしています。ありがとうございます!

@aymkx
Copy link
Contributor Author

aymkx commented Mar 17, 2025

diffを小さくしました。検証が容易になったと思います。 src/lib/ExternalLinksIntegration.ts の変更点はこちらで見られます。

build のステップの前に astro.config.tscompressHTML: false を設定してください。

git checkout 3c1adc1fbd11d50d8b0db90c33db81d78f6e45dd
git checkout bcc461a5520aa2f7f05db5c3c35a16f7ad0d3a87 src/lib/ExternalLinksIntegration.ts
npx astro build --outDir dist_old

git switch -f 20250316_enable_rehype-external-link_in_development
npx astro build
diff -ruwB dist_old dist

結果がこんな感じです 今やったらdiffが消えていた……

diff -ruwB dist_old dist
dist/_astro のみに存在: hoisted.CX-P6Ak-.js
dist_old/_astro のみに存在: hoisted.CXkP7hn-.js
dist_old/_astro のみに存在: hoisted.DaB-PJ4H.js
dist/_astro のみに存在: hoisted.Dg1uxQGp.js
diff -ruwB dist_old/en/sitemap/index.html dist/en/sitemap/index.html
--- dist_old/en/sitemap/index.html	2025-03-18 04:35:29.901822419 +0900
+++ dist/en/sitemap/index.html	2025-03-18 04:37:23.532489908 +0900
@@ -67,7 +67,7 @@
 </style>
 <link rel="stylesheet" href="/_astro/index.DbmAw6hg.css">
 <style>.table:where(.astro-mvdw4mcu){word-break:break-all;font-size:.75em}.table:where(.astro-mvdw4mcu) th:where(.astro-mvdw4mcu){font-weight:400}.table:where(.astro-mvdw4mcu) td:where(.astro-mvdw4mcu),.table:where(.astro-mvdw4mcu) th:where(.astro-mvdw4mcu){padding:.25em}.table:where(.astro-mvdw4mcu) td:where(.astro-mvdw4mcu):first-child,.table:where(.astro-mvdw4mcu) th:where(.astro-mvdw4mcu):first-child{width:var(--side-width);min-width:var(--side-width)}.table:where(.astro-mvdw4mcu) td:where(.astro-mvdw4mcu):not(:first-child),.table:where(.astro-mvdw4mcu) th:where(.astro-mvdw4mcu):not(:first-child){width:var(--cell-width);min-width:var(--cell-width)}.table:where(.astro-mvdw4mcu) td:where(.astro-mvdw4mcu){text-align:right}.table:where(.astro-mvdw4mcu) thead:where(.astro-mvdw4mcu) th:where(.astro-mvdw4mcu){text-align:center}
-</style><script type="module" src="/_astro/hoisted.CXkP7hn-.js"></script></head>
+</style><script type="module" src="/_astro/hoisted.CX-P6Ak-.js"></script></head>
   <body class="astro-sckkx6r4">
     <header id="header" class="header astro-hnj6luw5">
   <div class="primary astro-hnj6luw5">
diff -ruwB dist_old/en/utokyo_account/mfa/initial/index.html dist/en/utokyo_account/mfa/initial/index.html
--- dist_old/en/utokyo_account/mfa/initial/index.html	2025-03-18 04:35:29.901822419 +0900
+++ dist/en/utokyo_account/mfa/initial/index.html	2025-03-18 04:37:24.466794247 +0900
@@ -62,7 +62,7 @@
   <link rel="stylesheet" href="/_astro/index.DbmAw6hg.css">
 <style>iframe:where(.astro-cdn5nafe){border:none}
 </style>
-<link rel="stylesheet" href="/_astro/index.fKcffvyP.css"><script type="module" src="/_astro/hoisted.DaB-PJ4H.js"></script></head>
+<link rel="stylesheet" href="/_astro/index.fKcffvyP.css"><script type="module" src="/_astro/hoisted.Dg1uxQGp.js"></script></head>
   <body class="astro-sckkx6r4">
     <header id="header" class="header astro-hnj6luw5">
   <div class="primary astro-hnj6luw5">
diff -ruwB dist_old/sitemap/index.html dist/sitemap/index.html
--- dist_old/sitemap/index.html	2025-03-18 04:35:29.921613208 +0900
+++ dist/sitemap/index.html	2025-03-18 04:37:33.945206963 +0900
@@ -67,7 +67,7 @@
 </style>
 <link rel="stylesheet" href="/_astro/index.DbmAw6hg.css">
 <style>.table:where(.astro-mvdw4mcu){word-break:break-all;font-size:.75em}.table:where(.astro-mvdw4mcu) th:where(.astro-mvdw4mcu){font-weight:400}.table:where(.astro-mvdw4mcu) td:where(.astro-mvdw4mcu),.table:where(.astro-mvdw4mcu) th:where(.astro-mvdw4mcu){padding:.25em}.table:where(.astro-mvdw4mcu) td:where(.astro-mvdw4mcu):first-child,.table:where(.astro-mvdw4mcu) th:where(.astro-mvdw4mcu):first-child{width:var(--side-width);min-width:var(--side-width)}.table:where(.astro-mvdw4mcu) td:where(.astro-mvdw4mcu):not(:first-child),.table:where(.astro-mvdw4mcu) th:where(.astro-mvdw4mcu):not(:first-child){width:var(--cell-width);min-width:var(--cell-width)}.table:where(.astro-mvdw4mcu) td:where(.astro-mvdw4mcu){text-align:right}.table:where(.astro-mvdw4mcu) thead:where(.astro-mvdw4mcu) th:where(.astro-mvdw4mcu){text-align:center}
-</style><script type="module" src="/_astro/hoisted.CXkP7hn-.js"></script></head>
+</style><script type="module" src="/_astro/hoisted.CX-P6Ak-.js"></script></head>
   <body class="astro-sckkx6r4">
     <header id="header" class="header astro-hnj6luw5">
   <div class="primary astro-hnj6luw5">
diff -ruwB dist_old/utokyo_account/mfa/initial/index.html dist/utokyo_account/mfa/initial/index.html
--- dist_old/utokyo_account/mfa/initial/index.html	2025-03-18 04:35:29.921613208 +0900
+++ dist/utokyo_account/mfa/initial/index.html	2025-03-18 04:37:35.123237240 +0900
@@ -62,7 +62,7 @@
   <link rel="stylesheet" href="/_astro/index.DbmAw6hg.css">
 <style>iframe:where(.astro-cdn5nafe){border:none}
 </style>
-<link rel="stylesheet" href="/_astro/index.fKcffvyP.css"><script type="module" src="/_astro/hoisted.DaB-PJ4H.js"></script></head>
+<link rel="stylesheet" href="/_astro/index.fKcffvyP.css"><script type="module" src="/_astro/hoisted.Dg1uxQGp.js"></script></head>
   <body class="astro-sckkx6r4">
     <header id="header" class="header astro-hnj6luw5">
   <div class="primary astro-hnj6luw5">

スクリプトがちょっと変わっているようです(整形後)

diff -ruwB dist_old/_astro/hoisted.DaB-PJ4H.js dist/_astro/hoisted.Dg1uxQGp.js
--- dist_old/_astro/hoisted.CXkP7hn-.js	2025-03-18 04:34:54.098710950 +0900
+++ dist/_astro/hoisted.CX-P6Ak-.js	2025-03-18 04:37:11.532203971 +0900
@@ -1 +1 @@
-import"./hoisted.XmsHf2pJ.js";import"./hoisted.B3IcW9gy.js";import"./hoisted.DaB-PJ4H.js";
+import"./hoisted.XmsHf2pJ.js";import"./hoisted.B3IcW9gy.js";import"./hoisted.Dg1uxQGp.js";
diff -ruwB dist_old/_astro/hoisted.CXkP7hn-.js dist/_astro/hoisted.CX-P6Ak-.js
--- dist_old/_astro/hoisted.DaB-PJ4H.js	2025-03-18 04:40:23.224284094 +0900
+++ dist/_astro/hoisted.Dg1uxQGp.js	2025-03-18 04:40:39.183460882 +0900
@@ -23,12 +23,9 @@
   }
 });
 r((e, t) => {
-  (document.querySelector(`#tab-list-${e}`).hidden = t === "selector"),
-    document.querySelectorAll(`button[id^="tab-${e}"]`).forEach((n) => {
-      n.ariaSelected = `${n.id === `tab-${e}-${t}`}`;
-    }),
-    document.querySelectorAll(`div[id^="panel-${e}"]`).forEach((n) => {
-      n.hidden = n.id !== `panel-${e}-${t}`;
+  t !== "select" &&
+    document.querySelectorAll(`input[name^="radio-${e}"]`).forEach((n) => {
+      n.checked = n.value === t;
     });
 });
 function a() {
@@ -36,14 +33,17 @@
   o(e, t);
 }
 document.addEventListener("DOMContentLoaded", () => {
-  document.querySelectorAll('button[id^="tab"][role="tab"]').forEach((e) => {
-    e.addEventListener("click", a);
+  document.querySelectorAll('input[name^="radio"]').forEach((e) => {
+    e.addEventListener("change", a);
   });
 });
 r((e, t) => {
-  t !== "select" &&
-    document.querySelectorAll(`input[name^="radio-${e}"]`).forEach((n) => {
-      n.checked = n.value === t;
+  (document.querySelector(`#tab-list-${e}`).hidden = t === "selector"),
+    document.querySelectorAll(`button[id^="tab-${e}"]`).forEach((n) => {
+      n.ariaSelected = `${n.id === `tab-${e}-${t}`}`;
+    }),
+    document.querySelectorAll(`div[id^="panel-${e}"]`).forEach((n) => {
+      n.hidden = n.id !== `panel-${e}-${t}`;
     });
 });
 function d() {
@@ -51,8 +51,8 @@
   o(e, t);
 }
 document.addEventListener("DOMContentLoaded", () => {
-  document.querySelectorAll('input[name^="radio"]').forEach((e) => {
-    e.addEventListener("change", d);
+  document.querySelectorAll('button[id^="tab"][role="tab"]').forEach((e) => {
+    e.addEventListener("click", d);
   });
 });
 function l() {

場所が入れ替わってるだけのように見えます。謎

@syobonpastel syobonpastel added the web-development Tasks of web-development category label Mar 18, 2025
@cm-ayf
Copy link
Member

cm-ayf commented Mar 18, 2025

approve していいような気もする vs リグレッションがあったらこわい

@aymkx
Copy link
Contributor Author

aymkx commented Mar 19, 2025

何度もすみません、追加で修正をいれました。ExternalLinksIntegration利用時から以下の差分が生じます。

diff -ruwB dist-old dist
diff -ruwB dist-old/en/microsoft/install.html dist/en/microsoft/install.html
--- dist-old/en/microsoft/install.html	2025-03-19 21:50:22.851550528 +0900
+++ dist/en/microsoft/install.html	2025-03-19 21:48:06.496904564 +0900
@@ -292,7 +292,7 @@
 <h2 id="caution">Cautions</h2>
 <ul>
 <li><strong>Office apps that are installed on a PC can only be used by students and faculty members employed by the University of Tokyo</strong>.</li>
-<li><strong>Installation requires that <a href="https://utelecon.adm.u-tokyo.ac.jp/en/utokyo_account/mfa/" rel="noopener noreferrer" target="_blank">Using Multi-Factor Authentication (MFA) for UTokyo Accounts<span class="external-link"></span></a> is already set up</strong>.</li>
+<li><strong>Installation requires that <a href="https://utelecon.adm.u-tokyo.ac.jp/en/utokyo_account/mfa/">Using Multi-Factor Authentication (MFA) for UTokyo Accounts</a> is already set up</strong>.</li>
 <li>You can install the apps on up to 5 PCs.</li>
 <li>You need to connect to the internet at least once every 30 days for license authentication. The license authentication is done automatically when you are connected to the internet, so you do not need to perform any special operations.</li>
 </ul>
diff -ruwB dist-old/en/research_computing/utokyo_azure/index.html dist/en/research_computing/utokyo_azure/index.html
--- dist-old/en/research_computing/utokyo_azure/index.html	2025-03-19 21:50:22.851550528 +0900
+++ dist/en/research_computing/utokyo_azure/index.html	2025-03-19 21:48:08.606976254 +0900
@@ -353,7 +353,7 @@
 <h4 id="utokyo-slack-channel"><a href="https://app.slack.com/client/E0312P7H7MX/C088H15FH3L?_gl=1*163sxt6*_gcl_au*MTUzNTE1MDYzOS4xNzM2ODIwNzQw" rel="noopener noreferrer" target="_blank">UTokyo Slack channel<span class="external-link"></span></a></h4>
 <ul>
 <li>Joining to <code>UTokyo Azure Users Community</code></li>
-<li>How to Join > <a href="https://utelecon.adm.u-tokyo.ac.jp/en/slack/join" rel="noopener noreferrer" target="_blank">Joining an open workspace in UTokyo Slack<span class="external-link"></span></a></li>
+<li>How to Join > <a href="https://utelecon.adm.u-tokyo.ac.jp/en/slack/join">Joining an open workspace in UTokyo Slack</a></li>
 </ul>
 <h4 id="microsoft-copilot"><a href="/notice/2024/03-microsoft-copilot">Microsoft Copilot</a></h4>
 <ul>
diff -ruwB dist-old/en/support/index.html dist/en/support/index.html
--- dist-old/en/support/index.html	2025-03-19 21:50:22.851550528 +0900
+++ dist/en/support/index.html	2025-03-19 21:48:10.387036250 +0900
@@ -326,7 +326,7 @@
 
 <h4 id="transfer-of-accounts-and-data-in-the-event-of-a-change-of-affiliation-or-employment-status">Transfer of accounts and data in the event of a change of affiliation or employment status.</h4>
 <p>Before the inquiry, please see the page below. Please contact your department office if you have any queries about the enrollment period.</p>
-<p><a href="https://utelecon.adm.u-tokyo.ac.jp/en/systems/leave/" rel="noopener noreferrer" target="_blank">Notice on Account Revocation due to Graduation, Resignation or Change of Affiliation<span class="external-link"></span></a></p>
+<p><a href="https://utelecon.adm.u-tokyo.ac.jp/en/systems/leave/">Notice on Account Revocation due to Graduation, Resignation or Change of Affiliation</a></p>
 <h4 id="unable-to-sign-in-to-a-utokyo-account-with-multi-factor-authentication-sign-in-using-a-smartphone-etc">Unable to sign in to a UTokyo Account with multi-factor authentication (sign-in using a smartphone, etc.)</h4>
 <p>Please check the following page before inquiring. It describes how to use multi-factor authentication and what to do if you are unable to sign in due to a malfunction or model change of your smartphone.</p>
 <p><a href="/en/utokyo_account/mfa/">Using Multi-Factor Authentication (MFA) for UTokyo Accounts</a></p>
diff -ruwB dist-old/en/zoom/usage/reaction/index.html dist/en/zoom/usage/reaction/index.html
--- dist-old/en/zoom/usage/reaction/index.html	2025-03-19 21:50:22.861550879 +0900
+++ dist/en/zoom/usage/reaction/index.html	2025-03-19 21:48:13.227131767 +0900
@@ -394,7 +394,7 @@
 <li>You can select the types of emojis from either “All emojis” or “Selected emojis”. If you choose “Selected emojis”, the participants will be able to express their reactions using only the six standard emojis.</li>
 <li>How to enable or disable:
 <ol>
-<li>Sign in Zoom following the steps of <a href="https://utelecon.adm.u-tokyo.ac.jp/zoom/" rel="noopener noreferrer" target="_blank">“Sign-in Methods for Zoom (in Japanese)”<span class="external-link"></span></a>.</li>
+<li>Sign in Zoom following the steps of <a href="https://utelecon.adm.u-tokyo.ac.jp/zoom/">“Sign-in Methods for Zoom (in Japanese)”</a>.</li>
 <li>Click on “Config” and then click “Settings (<a href="https://u-tokyo-ac-jp.zoom.us/profile/setting" rel="noopener noreferrer" target="_blank">https://u-tokyo-ac-jp.zoom.us/profile/setting<span class="external-link"></span></a>).”<img src="/_astro/aII3igAs.8_fig_zoom_usage_reaction_YWbwm.webp" alt="" width="544" height="546" loading="lazy" decoding="async"></li>
 <li>Click on “Meeting” and then click “In Meeting (Basic)”.<img src="/_astro/7cSj3IBg.9_fig_zoom_usage_reaction_1gal3q.webp" alt="" width="832" height="534" loading="lazy" decoding="async"></li>
 <li>Choose to enable “Non-verbal feedback” and “Meeting reactions” respectively (Note: the “emoji” feature may be referred to as “Meeting reactions” or “Reactions in meetings” etc.).<img src="/_astro/1wZ388H4.10_fig_zoom_usage_reaction_1aAz2Q.webp" alt="" width="556" height="452" loading="lazy" decoding="async"></li>
@@ -406,7 +406,7 @@
 <p>You can set up skin tone that you desire for your Reactions in Zoom.</p>
 <h4 id="on-pc-1">On pc</h4>
 <ol>
-<li>Sign in to Zoom following the steps of “<a href="https://utelecon.adm.u-tokyo.ac.jp/zoom/" rel="noopener noreferrer" target="_blank">Signing in in from the Zoom App (in Japanese)<span class="external-link"></span></a>”.</li>
+<li>Sign in to Zoom following the steps of “<a href="https://utelecon.adm.u-tokyo.ac.jp/zoom/">Signing in in from the Zoom App (in Japanese)</a>”.</li>
 <li>Click “Settings” after you click on the picture of your profile in the upper right.</li>
 <li>Select the “General” tab and then select your preferred skin tone from the “Skin tone” of “Reactions”.</li>
 </ol>
diff -ruwB dist-old/events/2021-03-25/index.html dist/events/2021-03-25/index.html
--- dist-old/events/2021-03-25/index.html	2025-03-19 21:50:22.861550879 +0900
+++ dist/events/2021-03-25/index.html	2025-03-19 21:48:14.477173729 +0900
@@ -316,7 +316,7 @@
 <ul>
 <li>ECCS クラウドメール(設定した文字列@g.ecc.u-tokyo.ac.jp)アカウントでGoogleにサインインしている必要があります<br>
 上手くアクセスできない場合は<a href="https://mail.google.com/a/g.ecc.u-tokyo.ac.jp" rel="noopener noreferrer" target="_blank">こちら(https://mail.google.com/a/g.ecc.u-tokyo.ac.jp)<span class="external-link"></span></a>からECCS クラウドメールでサインインしてください.<br>
-ECCSクラウドメールを利用したことがない方は<a href="https://utelecon.adm.u-tokyo.ac.jp/faculty_members/#%E6%83%85%E5%A0%B1%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%82%92%E4%BD%BF%E3%81%86%E3%81%9F%E3%82%81%E3%81%AB%E5%BF%85%E9%A0%88%E3%81%AE%E6%89%8B%E9%A0%86" target="_blank" rel="noopener noreferrer">「オンライン授業を始めるために」<span class="external-link"></span></a>の「ECCSクラウドメール:パスワードとメールアドレスを設定する」を参考にして、アカウント利用の初期設定をしてください</li>
+ECCSクラウドメールを利用したことがない方は<a href="https://utelecon.adm.u-tokyo.ac.jp/faculty_members/#%E6%83%85%E5%A0%B1%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%82%92%E4%BD%BF%E3%81%86%E3%81%9F%E3%82%81%E3%81%AB%E5%BF%85%E9%A0%88%E3%81%AE%E6%89%8B%E9%A0%86" target="_blank">「オンライン授業を始めるために」</a>の「ECCSクラウドメール:パスワードとメールアドレスを設定する」を参考にして、アカウント利用の初期設定をしてください</li>
 </ul>
 </li>
 <li>
diff -ruwB dist-old/microsoft/install.html dist/microsoft/install.html
--- dist-old/microsoft/install.html	2025-03-19 21:50:22.861550879 +0900
+++ dist/microsoft/install.html	2025-03-19 21:48:18.687318326 +0900
@@ -312,7 +312,7 @@
 <h2 id="caution">注意事項</h2>
 <ul>
 <li><strong>PCにインストールして利用するOfficeアプリは,学生及び東京大学に雇用されている教職員のみが利用できます</strong>.</li>
-<li><strong>インストールには<a href="https://utelecon.adm.u-tokyo.ac.jp/utokyo_account/mfa/" rel="noopener noreferrer" target="_blank">UTokyo Accountの多要素認証<span class="external-link"></span></a>が設定済みであることが必要です</strong>.</li>
+<li><strong>インストールには<a href="https://utelecon.adm.u-tokyo.ac.jp/utokyo_account/mfa/">UTokyo Accountの多要素認証</a>が設定済みであることが必要です</strong>.</li>
 <li>最大5台のPCにインストールすることが可能です.</li>
 <li>ライセンス認証のため,少なくとも30日に一度インターネットに接続する必要があります.ライセンス認証はインターネット接続時に自動で行われるため,特別な操作を行う必要はありません.</li>
 </ul>
diff -ruwB dist-old/notice/2022/0810-wifi.html dist/notice/2022/0810-wifi.html
--- dist-old/notice/2022/0810-wifi.html	2025-03-19 21:50:22.861550879 +0900
+++ dist/notice/2022/0810-wifi.html	2025-03-19 21:48:19.557348883 +0900
@@ -279,7 +279,7 @@
 <li>作業影響:上記時間帯でUTokyo WiFiの新規アカウント発行利用ができなくなります。また、UTokyo WiFi への接続が不安定になります。影響範囲は UTokyo WiFi が利用可能な全ての基地局(部局整備部分も含む)となります。</li>
 </ul>
 <p>作業完了次第こちらにアナウンスします。
-<a href="https://utelecon.adm.u-tokyo.ac.jp/notice/2022/0810-wifi" rel="noopener noreferrer" target="_blank">https://utelecon.adm.u-tokyo.ac.jp/notice/2022/0810-wifi<span class="external-link"></span></a></p>
+<a href="https://utelecon.adm.u-tokyo.ac.jp/notice/2022/0810-wifi">https://utelecon.adm.u-tokyo.ac.jp/notice/2022/0810-wifi</a></p>
 <p>本件に関する連絡先<br>
 UTokyo WiFi タスクフォース <code>[email protected]</code></p>
           
diff -ruwB dist-old/notice/2025/0117-cs.html dist/notice/2025/0117-cs.html
--- dist-old/notice/2025/0117-cs.html	2025-03-19 21:50:22.861550879 +0900
+++ dist/notice/2025/0117-cs.html	2025-03-19 21:48:21.617421151 +0900
@@ -307,7 +307,7 @@
 <ul>
 <li>業務内容:概ね以下の内容で,基本的にすべての業務をオンラインで行います.
 <ul>
-<li>東京大学のオンライン授業・Web会議に関する問い合わせを受け付けている「<a href="https://utelecon.adm.u-tokyo.ac.jp/support/" rel="noopener noreferrer" target="_blank">サポート窓口<span class="external-link"></span></a>」において,オペレータとして利用者からの問い合わせに対応すること.</li>
+<li>東京大学のオンライン授業・Web会議に関する問い合わせを受け付けている「<a href="https://utelecon.adm.u-tokyo.ac.jp/support/">サポート窓口</a>」において,オペレータとして利用者からの問い合わせに対応すること.</li>
 <li>サポート対応に必要な情報の共有や能力の向上のためのミーティングに適宜参加すること.</li>
 </ul>
 </li>
diff -ruwB dist-old/research_computing/mdx/deploy.html dist/research_computing/mdx/deploy.html
--- dist-old/research_computing/mdx/deploy.html	2025-03-19 21:50:22.861550879 +0900
+++ dist/research_computing/mdx/deploy.html	2025-03-19 21:48:25.267546390 +0900
@@ -333,7 +333,7 @@
 <li><a href="https://www.microsoft.com/ja-jp/software-download/" rel="noopener noreferrer" target="_blank">https://www.microsoft.com/ja-jp/software-download/<span class="external-link"></span></a></li>
 </ul>
 <p>から入手できますが、仮想マシン上で利用するためには、利用者が自分で Windows OS のプロダクト・キー(利用ライセンス)を購入しなければなりません。
-研究・教育目的であれば <a href="https://utelecon.adm.u-tokyo.ac.jp/microsoft/adt4t/" rel="noopener noreferrer" target="_blank">Azure Dev Tools for Teaching<span class="external-link"></span></a> からプロダクト・キーを無料で入手できます。
+研究・教育目的であれば <a href="https://utelecon.adm.u-tokyo.ac.jp/microsoft/adt4t/">Azure Dev Tools for Teaching</a> からプロダクト・キーを無料で入手できます。
 Azure Dev tools for Teaching にアクセスして、ソフトウェアの一覧から利用したい Windows OS を選んでクリックすると、右側にパネルが現れます。
 パネルをスクロールすると一番下にプロダクト・キーを見るためのボタンがありますから、これをクリックしてプロダクト・キーを入手します。</p>
 <p>なお Windows 11 は、インストール先の PC が Trusted Platform Module (TPM) と呼ばれる特別なハードウェアを備えていないとインストールできません。
diff -ruwB dist-old/research_computing/utokyo_azure/ai/aml.html dist/research_computing/utokyo_azure/ai/aml.html
--- dist-old/research_computing/utokyo_azure/ai/aml.html	2025-03-19 21:50:22.861550879 +0900
+++ dist/research_computing/utokyo_azure/ai/aml.html	2025-03-19 21:48:25.357549854 +0900
@@ -347,7 +347,7 @@
           <h2 id="about">概要</h2>
 <p>Azure Machine Learning なら Jupyter notebook のようなインタフェースで GPU を使った Python プログラミング等ができます.
 このページは Azure Machine Learning を本サービスで利用する場合の初期手続き例です.</p>
-<p>参考:<a href="https://utelecon.adm.u-tokyo.ac.jp/events/2025-02-21/slides/5-4_notebook.pdf" rel="noopener noreferrer" target="_blank"><strong>ノートブックを使おう -Google ColaboratoryのようにJupyter Notebookを使って開発したい!-</strong><span class="external-link"></span></a> (2025.02.21 UTokyo Azure 説明会資料)</p>
+<p>参考:<a href="https://utelecon.adm.u-tokyo.ac.jp/events/2025-02-21/slides/5-4_notebook.pdf"><strong>ノートブックを使おう -Google ColaboratoryのようにJupyter Notebookを使って開発したい!-</strong></a> (2025.02.21 UTokyo Azure 説明会資料)</p>
 <h2 id="select_service">1.サービスを選択する</h2>
 <div class="box"><ul>
 <li>まだサブスクリプションをお持ちでない場合は,事前に以下サイトよりサブスクリプションを申請してください.
diff -ruwB dist-old/research_computing/utokyo_azure/ai/openai.html dist/research_computing/utokyo_azure/ai/openai.html
--- dist-old/research_computing/utokyo_azure/ai/openai.html	2025-03-19 21:50:22.861550879 +0900
+++ dist/research_computing/utokyo_azure/ai/openai.html	2025-03-19 21:48:25.457553705 +0900
@@ -336,7 +336,7 @@
           
           <h2 id="about">概要</h2>
 <p>Azure OpenAI を本サービスで利用する場合の初期手続き例です.</p>
-<p>参考:<a href="https://utelecon.adm.u-tokyo.ac.jp/events/2025-02-21/slides/5-2_OpenAI.pdf" rel="noopener noreferrer" target="_blank"><strong>OpenAIのサービスを利用しよう</strong><span class="external-link"></span></a> (2025.02.21 UTokyo Azure 説明会資料)</p>
+<p>参考:<a href="https://utelecon.adm.u-tokyo.ac.jp/events/2025-02-21/slides/5-2_OpenAI.pdf"><strong>OpenAIのサービスを利用しよう</strong></a> (2025.02.21 UTokyo Azure 説明会資料)</p>
 <h2 id="select_service">1.サービスを選択する</h2>
 <div class="box"><ul>
 <li>まだサブスクリプションをお持ちでない場合は,事前に以下サイトよりサブスクリプションを申請してください.
diff -ruwB dist-old/research_computing/utokyo_azure/group/index.html dist/research_computing/utokyo_azure/group/index.html
--- dist-old/research_computing/utokyo_azure/group/index.html	2025-03-19 21:50:22.861550879 +0900
+++ dist/research_computing/utokyo_azure/group/index.html	2025-03-19 21:48:25.777566025 +0900
@@ -304,7 +304,7 @@
         <div id="content" class="astro-sckkx6r4">
           
           <p><strong>サブスクリプション</strong>や<strong>リソース</strong>(スコープ)を研究室のメンバーや共同研究者で共有したい場合は,代表者がサブスクリプションを申請し,そのサブスクリプションにメンバーを適切な権限(ロール)で追加することで共有することが可能になります.その際,どのリソースに,誰を,どのロールで割り当てるかで共有者ができることが変わってきます.</p>
-<p>参考:<a href="https://utelecon.adm.u-tokyo.ac.jp/events/2025-02-21/slides/5-1_add_owner.pdf" rel="noopener noreferrer" target="_blank"><strong>所有者の追加</strong><span class="external-link"></span></a> (2025.02.21 UTokyo Azure 説明会資料)</p>
+<p>参考:<a href="https://utelecon.adm.u-tokyo.ac.jp/events/2025-02-21/slides/5-1_add_owner.pdf"><strong>所有者の追加</strong></a> (2025.02.21 UTokyo Azure 説明会資料)</p>
 <h2 id="推奨されるロール割り当て">推奨されるロール割り当て</h2>
 
 
diff -ruwB dist-old/research_computing/utokyo_azure/index.html dist/research_computing/utokyo_azure/index.html
--- dist-old/research_computing/utokyo_azure/index.html	2025-03-19 21:50:22.861550879 +0900
+++ dist/research_computing/utokyo_azure/index.html	2025-03-19 21:48:25.987574112 +0900
@@ -353,7 +353,7 @@
 <li>OpenAI の AI システムをブラウザ経由で利用するサービスや,それをAPI で利用するサービス.</li>
 </ul>
 <p>なども提供されています.また,計算機の種類としてもCPU, GPU多種多様なプロセッサやメモリ量を選択でき,様々な用途,分野で利用が可能です.</p>
-<p>参考: <a href="https://utelecon.adm.u-tokyo.ac.jp/events/2025-02-21/slides/1_overview.pdf" rel="noopener noreferrer" target="_blank"><strong>UTokyo Azureの概要</strong><span class="external-link"></span></a> (2025.02.21 UTokyo Azure 説明会資料)</p>
+<p>参考: <a href="https://utelecon.adm.u-tokyo.ac.jp/events/2025-02-21/slides/1_overview.pdf"><strong>UTokyo Azureの概要</strong></a> (2025.02.21 UTokyo Azure 説明会資料)</p>
 <h2 id="基本方針目標">基本方針・目標</h2>
 <p>運用は情報基盤センター,情報システム部,情報システム本部が共同で行います.提供にあたっての基本方針・目標は以下のとおりです.</p>
 <ul>
@@ -393,7 +393,7 @@
 </ul>
 </li>
 </ul>
-<p>参考:<a href="https://utelecon.adm.u-tokyo.ac.jp/events/2025-02-21/slides/3_application_guidelines.pdf" rel="noopener noreferrer" target="_blank"><strong>利用申請の流れと注意点</strong><span class="external-link"></span></a> (2025.02.21 UTokyo Azure 説明会資料)</p>
+<p>参考:<a href="https://utelecon.adm.u-tokyo.ac.jp/events/2025-02-21/slides/3_application_guidelines.pdf"><strong>利用申請の流れと注意点</strong></a> (2025.02.21 UTokyo Azure 説明会資料)</p>
 <h2 id="主なサービス利用法">主なサービス・利用法</h2>
 <ul>
 <li>研究・教育でよく現れる利用シーンを想定して、Azure をあまり使ったことがないというユーザー向けにクイックスタートできる手順を説明しています.</li>
@@ -465,7 +465,7 @@
 <li>
 <p><strong>参加方法</strong></p>
 <ul>
-<li><a href="https://utelecon.adm.u-tokyo.ac.jp/slack/join" rel="noopener noreferrer" target="_blank">UTokyo Slackに自由に参加できるワークスペースの中から選んで参加する<span class="external-link"></span></a>のページの内容を参考に,UTokyo Azure Users Community への参加をご選択ください.</li>
+<li><a href="https://utelecon.adm.u-tokyo.ac.jp/slack/join">UTokyo Slackに自由に参加できるワークスペースの中から選んで参加する</a>のページの内容を参考に,UTokyo Azure Users Community への参加をご選択ください.</li>
 </ul>
 </li>
 </ul>
diff -ruwB dist-old/research_computing/utokyo_azure/virtualmachine/index.html dist/research_computing/utokyo_azure/virtualmachine/index.html
--- dist-old/research_computing/utokyo_azure/virtualmachine/index.html	2025-03-19 21:50:22.861550879 +0900
+++ dist/research_computing/utokyo_azure/virtualmachine/index.html	2025-03-19 21:48:25.947572572 +0900
@@ -342,7 +342,7 @@
           
           <h2 id="about">概要</h2>
 <p>本サービスで多く利用されると思われる,仮想マシン(Azure のサービス名はVirtual Machines)の作成を最低限の構成で行う一例です.</p>
-<p>参考: <a href="https://utelecon.adm.u-tokyo.ac.jp/events/2025-02-21/slides/5-3_create_VM.pdf" rel="noopener noreferrer" target="_blank"><strong>仮想マシンを作ろう -13BのLlama-3モデルをLoRA fine-tuningしたい-</strong><span class="external-link"></span></a> (2025.02.21 UTokyo Azure 説明会資料)</p>
+<p>参考: <a href="https://utelecon.adm.u-tokyo.ac.jp/events/2025-02-21/slides/5-3_create_VM.pdf"><strong>仮想マシンを作ろう -13BのLlama-3モデルをLoRA fine-tuningしたい-</strong></a> (2025.02.21 UTokyo Azure 説明会資料)</p>
 <h2 id="select_service">1.サービスを選択する</h2>
 <div class="box"><ul>
 <li>まだサブスクリプションをお持ちでない場合は,事前に以下サイトよりサブスクリプションを申請してください.
diff -ruwB dist-old/support/index.html dist/support/index.html
--- dist-old/support/index.html	2025-03-19 21:50:22.861550879 +0900
+++ dist/support/index.html	2025-03-19 21:48:26.667600277 +0900
@@ -345,7 +345,7 @@
 
 <h4 id="所属や雇用形態が変わる場合のアカウントデータの引き継ぎについて">所属や雇用形態が変わる場合のアカウント・データの引き継ぎについて</h4>
 <p>問い合わせの前に次のページをご確認ください.在籍期間に関する問い合わせは所属部局へお願いします.</p>
-<p><a href="https://utelecon.adm.u-tokyo.ac.jp/systems/leave/" rel="noopener noreferrer" target="_blank">卒業・退職や所属変更によるアカウント失効についての注意<span class="external-link"></span></a></p>
+<p><a href="https://utelecon.adm.u-tokyo.ac.jp/systems/leave/">卒業・退職や所属変更によるアカウント失効についての注意</a></p>
 <h4 id="utokyo-accountの多要素認証スマホなどを使ったサインインができなくなった">UTokyo Accountの多要素認証(スマホなどを使ったサインイン)ができなくなった</h4>
 <p>問い合わせの前に次のページをご確認ください.多要素認証の利用方法や,スマホの故障や機種変更などで本人確認ができずサインインできなくなった際の対応方法などが記載されています.</p>
 <p><a href="/utokyo_account/mfa/">UTokyo Accountにおける多要素認証の利用について</a></p>
diff -ruwB dist-old/utokyo_wifi/wired_lan.html dist/utokyo_wifi/wired_lan.html
--- dist-old/utokyo_wifi/wired_lan.html	2025-03-19 21:50:22.861550879 +0900
+++ dist/utokyo_wifi/wired_lan.html	2025-03-19 21:48:27.807644121 +0900
@@ -284,7 +284,7 @@
 <ul>
 <li>お使いのブラウザ(Chrome,Firefox,Safariなど)を開いてください.</li>
 <li>最初にブラウザを開くと,認証ページが自動的に表示されます.</li>
-<li>表示されない場合は,新しいタブを開き,任意のウェブサイト(例: <a href="http://utelecon.adm.u-tokyo.ac.jp/" rel="noopener noreferrer" target="_blank">http://utelecon.adm.u-tokyo.ac.jp/<span class="external-link"></span></a> など)にアクセスしてください.自動的に認証ページが表示されます.</li>
+<li>表示されない場合は,新しいタブを開き,任意のウェブサイト(例: <a href="http://utelecon.adm.u-tokyo.ac.jp/">http://utelecon.adm.u-tokyo.ac.jp/</a> など)にアクセスしてください.自動的に認証ページが表示されます.</li>
 </ul>
 </li>
 <li>認証情報を入力する

@aymkx aymkx requested a review from cm-ayf March 19, 2025 15:13
README.md Outdated
@@ -114,7 +114,8 @@ Markdownファイルのフロントマターにかける設定は以下の通り
- 外部リンクは,別タブで開くのが一般的です.
- すべての外部リンクに対してこの属性を明示的に付与するのは冗長であり,また忘れる可能性も高いため,自動で付与すべきです.
- 実装
- [`ExternalLinksIntegration.ts`](src/lib/ExternalLinksIntegration.ts)で実現しています.ここでは,ビルド後に全てのHTMLファイルをRehypeで改めてパースし,[`rehype-external-links`](https://github.com/rehypejs/rehype-external-links)を適用しています.
- [Astroのミドルウェア機能](https://docs.astro.build/ja/guides/middleware/)を用いて[`externalLinks.ts`](src/middleware/externalLinks.ts)で実現しています.ここでは,アクセスしてきたユーザーに配信するHTMLをレスポンスの途中で一度Rehypeでパースし,[`rehype-external-links`](https://github.com/rehypejs/rehype-external-links)を適用してから送信するという動作によりページを処理しています.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここでは、……

これは微妙に偽で、実際はレンダリング時に後処理を噛ませているだけですね

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

そういうシミュレーションなのはわかるんですが、なんて言うといいですかね

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

作成された HTML に後処理を施します?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここでは,レンダリング後のHTMLをdistに保存する前に一度Rehypeでパースし,rehype-external-linksを適用するという動作によりページを処理しています.

にしました

Comment on lines 45 to 48
const processor = unified()
.use(rehypeParse)
.use(rehypeExternalLinks, rehypeExternalLinksOptions)
.use(rehypeStringify);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

今気づいたんですが、request ごとに processor を作ってて使い回してないんですね……。
かなり嬉しくないです。options.test が動的に作れなくなるのを考慮しても、1つを使い回すようにしたいです。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

どうしますかね……モジュールレベルの状態作ります?

Copy link
Contributor Author

@aymkx aymkx Mar 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

compare to f947016

案1
diff --git a/src/middleware/externalLinks.ts b/src/middleware/externalLinks.ts
index 1b0909cab..c20218783 100644
--- a/src/middleware/externalLinks.ts
+++ b/src/middleware/externalLinks.ts
@@ -1,4 +1,5 @@
 import type { MiddlewareHandler } from "astro";
+import type { Element } from "hast";
 import rehypeExternalLinks, {
   type Options as RehypeExternalLinksOptions,
 } from "rehype-external-links";
@@ -6,11 +7,30 @@ import rehypeParse from "rehype-parse";
 import rehypeStringify from "rehype-stringify";
 import { unified } from "unified";
 
+type Anchor = Element & { tagName: "a"; properties: { href: string } };
+
+function isAnchor(e: Element): e is Anchor {
+  return e.tagName === "a" && typeof e.properties.href === "string";
+}
+
+const state: { hostname?: string } = {};
+
 const rehypeExternalLinksOptions: RehypeExternalLinksOptions = {
   target: "_blank",
   rel: ["noopener", "noreferrer"],
   content: { type: "text", value: "" },
   contentProperties: { className: ["external-link"] },
+  test: (e: Element) => {
+    if (!isAnchor(e)) {
+      // shall be unreachable
+      return true;
+    }
+    try {
+      return new URL(e.properties.href).hostname !== state.hostname;
+    } catch {
+      return true;
+    }
+  },
 };
 
 const processor = unified()
@@ -20,7 +40,12 @@ const processor = unified()
 
 const mimeHtmlPattern = /^\s*text\/html(?:[\s;].*|$)/;
 
-const onRequest: MiddlewareHandler = async (_, next) => {
+export const onRequest: MiddlewareHandler = async (
+  { site: { hostname } = {} },
+  next
+) => {
+  state.hostname = hostname;
+
   const response = await next();
 
   if (response.headers.get("content-type")?.match(mimeHtmlPattern)) {
@@ -35,5 +60,3 @@ const onRequest: MiddlewareHandler = async (_, next) => {
 
   return response;
 };
-
-export default onRequest;
案2
diff --git a/src/middleware/externalLinks.ts b/src/middleware/externalLinks.ts
index 1b0909cab..c26979829 100644
--- a/src/middleware/externalLinks.ts
+++ b/src/middleware/externalLinks.ts
@@ -1,31 +1,63 @@
 import type { MiddlewareHandler } from "astro";
-import rehypeExternalLinks, {
-  type Options as RehypeExternalLinksOptions,
-} from "rehype-external-links";
+import type { Element, Root } from "hast";
+import rehypeExternalLinks from "rehype-external-links";
 import rehypeParse from "rehype-parse";
 import rehypeStringify from "rehype-stringify";
-import { unified } from "unified";
+import { unified, type Processor } from "unified";
 
-const rehypeExternalLinksOptions: RehypeExternalLinksOptions = {
-  target: "_blank",
-  rel: ["noopener", "noreferrer"],
-  content: { type: "text", value: "" },
-  contentProperties: { className: ["external-link"] },
-};
+type Anchor = Element & { tagName: "a"; properties: { href: string } };
+
+function isAnchor(e: Element): e is Anchor {
+  return e.tagName === "a" && typeof e.properties.href === "string";
+}
 
-const processor = unified()
-  .use(rehypeParse)
-  .use(rehypeExternalLinks, rehypeExternalLinksOptions)
-  .use(rehypeStringify);
+class State {
+  hostname?: string;
+  private _processor: Processor<Root, Root, undefined, Root, string>;
+
+  constructor() {
+    this._processor = unified()
+      .use(rehypeParse)
+      .use(rehypeExternalLinks, {
+        target: "_blank",
+        rel: ["noopener", "noreferrer"],
+        content: { type: "text", value: "" },
+        contentProperties: { className: ["external-link"] },
+        test: (e: Element) => {
+          if (!isAnchor(e)) {
+            // shall be unreachable
+            return true;
+          }
+          try {
+            return new URL(e.properties.href).hostname !== this.hostname;
+          } catch {
+            return true;
+          }
+        },
+      })
+      .use(rehypeStringify);
+  }
+
+  get processor() {
+    return this._processor;
+  }
+}
+
+const state = new State();
 
 const mimeHtmlPattern = /^\s*text\/html(?:[\s;].*|$)/;
 
-const onRequest: MiddlewareHandler = async (_, next) => {
+export const onRequest: MiddlewareHandler = async (
+  { site: { hostname } = {} },
+  next
+) => {
+  state.hostname = hostname;
+
   const response = await next();
 
   if (response.headers.get("content-type")?.match(mimeHtmlPattern)) {
     return new Response(
-      (await processor.process(await response.text())).value,
+      (await state.processor.process(await response.text())).value,
       {
         status: response.status,
         headers: response.headers,
@@ -35,5 +67,3 @@ const onRequest: MiddlewareHandler = async (_, next) => {
 
   return response;
 };
-
-export default onRequest;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hostname チェックって必要でしょうか……?
むしろ hostname チェックは行わず、utelecon の絶対URLがあったら表示が変になって気付ける、のほうがいい気がしています。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

表示が変になって気付ける

現状気付けてないのがdevモードで気付けるようになる、という話ではありますね、たしかに

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ちなみに、「utelecon内部のページであっても外部リンクとして(別タブで)開かせたい場合は絶対URLを書くだけでよい」という暗黙知ができたとしても、あんまりネガティブには思わないですか?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
web-development Tasks of web-development category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants