プレーンテキスト貼り付け時の誤った自動リンク変換を修正

basecamp/lexxy

Foo::Bar::Bazport:8080 のようなコロンを含むプレーンテキストが誤ってリンクとして変換されていた問題を修正しました。URL コンストラクタを用いた判定を廃止し、scheme:// または www. で始まる文字列のみを対象とする正規表現に置き換えています。

背景

これまでの実装では、isUrl() 関数が new URL(string) の成否でURL判定を行っていました。しかし URL コンストラクタは RFC 3986 に基づく広範な構文を受け入れるため、Foo::Bar::Baz(Rubyの名前空間表記)や port:8080time:9:00 のようなコロンを含む文字列も有効なURLとして解釈してしまいます。これらの文字列は scheme:path 形式のURLとして構文上は合法であり、コンストラクタが例外を投げないためです。

その結果、エディタへのプレーンテキスト貼り付け時に意図しないリンクノードが生成されるという問題が発生していました。

技術的な変更

isUrl() を廃止し、より厳格な判定を行う isAutolinkableURL() に置き換えました。判定ロジックは new URL() によるパースから正規表現マッチに変わっています。

変更前:

export function isUrl(string) {
  try {
    new URL(string)
    return true
  } catch {
    return false
  }
}

変更後:

export function isAutolinkableURL(string) {
  return /^(?:[a-z0-9]+:\/\/|www\.)[^\s]+$/i.test(string)
}

新しい正規表現は以下の2つの条件を必要とします:

  • 文字列が scheme://(英数字スキームの後に ://)または www. で始まること
  • 空白文字を含まないこと

この正規表現はBasecampの url_pastes.js を元にしており、本番環境で長年使用されてきた実績があります。clipboard.js 内の呼び出し箇所も isUrl から isAutolinkableURL へ変更されています。

設計判断

関数名を isAutolinkableURL に変更したことで、この判定が「RFC 3986 準拠の完全なURL検証」ではなく「自動リンク化の対象かどうか」という、より狭い意図を持つものであることが明示されています。

正規表現の選択も意図的です。scheme:// の形式を要求することで、mailto:user@example.com のような :// を持たないURIスキームは対象外となります。これはエディタの自動リンク機能として実用的なスコープを定義したものであり、URLの完全な検証を目的としていません。また、大文字小文字を区別しない i フラグにより、HTTPS://example.comWWW.example.com のような入力も正しく処理されます。

テストには thingsThatMightBeURIs として18パターンのケースが追加されており、リンク化すべきURL(https://http://www. 形式)と、リンク化すべきでない文字列(:: 区切りの名前空間、key:value 形式)の両方を網羅しています。

まとめ

本PRは、汎用的なURLパーサーをエディタの自動リンク機能に流用していた設計上の不一致を解消しています。「何がURLか」という問いへの答えを RFC 3986 の定義からユーザーが貼り付けてリンク化したいと想定される表現に絞り込むことで、意図しない変換が発生しない実装に置き換えました。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
25c2b6e5

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
リトライ後承認
Review Count:
2回 (改善を経て承認)
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

「リード文(総論)→セクション群(各論)→まとめ(結論)」の3部構成が明確です。特に、任意項目である「設計判断」セクションが設けられており、PRの背景にある意図を深く解説できています。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(```javascript:path/to/file.js)およびGitHubのPRリンク記法([#1042](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

URLコンストラクタの挙動や正規表現の解説など、専門知識を持つエンジニアを対象とした適切な技術レベルと表現で書かれています。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論の構成になっており、各段落もトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が非常によく守られています。これにより、高い可読性が確保されています。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内のコードブロックは、提供されたDiffの内容(src/helpers/string_helper.jsの変更)を正確に反映しています。ファイル名も一致しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「RFC 3986」「URIスキーム」などの技術用語が、文脈に沿って正確かつ適切に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

「URLコンストラクタが広範な構文を受け入れる」という問題の根本原因や、正規表現の挙動に関する説明が技術的に正確で論理的です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内のすべての主張(正規表現の元ネタ、関数名変更の意図など)は、PRのDescriptionやDiff内のコードで裏付けられており、ハルシネーションは見られません。特に、Diff内のテストコードから「18パターン」という具体的な数値を拾っている点は優れています。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#1042)や、テストケースの数(18パターン)が正確に記載されています。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトル「プレーンテキスト貼り付け時の誤った自動リンク変換を修正」は、PRのタイトル「Don't auto-link plain text containing colons」の内容を的確に要約しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

記事には、バージョンのサポート状況やリリース日程など、PR情報に記載のない外部知識は含まれていません。すべての情報が提供されたソースに基づいています。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

「...変換されていた問題」「...長年使用されてきた実績」など、記事内の時間表現はPR情報と一致しており、歪曲は見られません。