PROMARI JOURNAL

そういえば、書く場所へは共有できなかった。Qiita・Zenn・note・Medium・アメブロへの共有ボタンを足しました

作成: / 公開: / 内容更新:

執筆:tamito0201 / 掲載・運営:プロマリ

記事の下の共有欄に、X や LINE はあるのに、読んだ人が自分の記事で紹介する Qiita・Zenn・note・Medium・アメブロへの入口がありませんでした。note は公式の共有入口へ、入口の無い4つは「題名と URL をコピーして投稿画面を開く」新しい動きで足しました。4.2.0 では、貼った先でそのまま記事のカードになるよう、共有先ごとの書き方をそろ…

PASS IT ON

ひとつの発見を、次の会話へ。

note は、カードを1枚差し込むだけで足りた

前のページで、5つのうち note にだけ、公式の共有の入口があると分かりました。入口があるなら、話は簡単です。このプラグインは、X やはてなブックマークと同じように、「入口の URL に、記事の情報を決まった名前で付けて開く」という動きをすでに持っているからです。この動きを、プラグインの中では open と呼んでいます。note は、この open の共有先として足せます。

では、足すために何をしたのか。実は、新しいファイルを1つ置いただけです。ほかのコードは、1行も開いていません。

図2-1 note は、カードを1枚差し込むだけ
図2-1 note は、カードを1枚差し込むだけ。現在のしくみ。共有先はデータのカードとして1枚ずつ並んでいます。公式の入口がある note は、カードを1枚差し込むだけで足りました。図2-1 note は、カードを1枚差し込むだけ。現在のしくみ。共有先はデータのカードとして1枚ずつ並んでいます。公式の入口がある note は、カードを1枚差し込むだけで足りました。

このプラグインでは、共有先を1つにつき1つのTOMLファイルで宣言します。destinations というフォルダに、copy.toml・email.toml・facebook.toml……と、共有先の名前のファイルが並んでいて、それぞれに「どの入口へ、どんな名前で、何を送るか」が書いてあります。カードの整理箱に、1社につき1枚ずつカードを立てているようなものです。note のために足したのは、そこへ差し込む1枚のカードでした。

TOMLdestinations/note.toml(アイコンの行を除いた実物)
# note's "Write on note" screen, its official share entry point (signed-out users return here after login).
# Icon: simple-icons 16.33.0 (note.svg), CC0 1.0. The logo remains a trademark of its owner.
key = 'note'
label = 'note'
brand_color = '#000000'
action = 'open'
endpoint = 'https://note.com/intent/post'

[params]
url = 'url'
hashtags = 'hashtagsCsv'

上から読んでいきます。最初の2行はコメントで、この入口が何なのかと、アイコンをどこから借りたのかを書いています。key は共有先の識別子で、ファイル名と同じ note です。label はボタンに出す名前、brand_color はボタンの色です。action = ‘open’ が「入口の URL を開く」動きを選ぶ行で、endpoint がその入口です。最後の [params] には、入口の URL に付ける項目の名前と、その中身を書きます。url という名前で記事の URL を、hashtags という名前でハッシュタグをカンマ区切りで付ける、という意味です。

このように、共有先を「データのカード」として持つ作りは、以前から決めていたものです。本編の連載では、この判断をADRという形の文書(ADR-0002)に残しています。そこに書いた目的の1つが、共有先を1つ足すとき、1ファイル足すだけで済む形を保つことでした。今回の note は、その目的どおりに足せた、いちばん素直な例です。連載2.2「OCP――共有先を一つ足したときの差分を追う」で追いかけた「足しても触らないコード」を、今回は本物の共有先で確かめたことになります。

ちなみに、カードを差し込んだあと、配布物を作る生成の手順(pnpm run build)を1回実行しています。カードを読み込んで、ブラウザ側のプログラムが使う一覧(generated/catalog.ts)と、配布する JavaScript を作り直すためです。この一覧や配布物は手で書き換えない決まりで、カードだけが正本です。

カードの何が、配布の前に確かめられるのか

カードを1枚差し込むだけ、と書くと、手軽すぎて不安に感じるかもしれません。書き間違えたらどうなるのか、と。実は、カードは配布物になる前に、必ず生成器(tools/config.py)の検査を通ります。note のカードも、差し込んだあとの build で検査を受けました。検査の中身は、どの共有先のカードにも共通の、次の項目です。

すべてのカードに共通の検査(tools/config.py)
項目確かめること止まったときの文の例
書式TOML として読めるかTOML として読めません
項目の種類決まった項目だけがあり、型が合っているか(知らない項目の名前を示して止まる)
key英小文字と数字だけで、ファイル名と同じかkey とファイル名をそろえてください
label空でないかlabel を空にしないでください
brand_color#rrggbb の形かbrand_color は #rrggbb で書いてください
icon<svg で始まり、色を currentColor で受け取るかicon は <svg …> で始まり fill=”currentColor” を使ってください
actionopen・copy・native・compose のどれかかaction は … のどれかにしてください
params送る項目の中身が、決まった種類(url・title・text など)かparams は「SNS 側の項目名 = …」の形にしてください

たとえば、note のカードの key をうっかり Note と大文字で書いたら、「key は英小文字と数字だけにしてください」と止まります。ファイル名を notes.toml にしてしまったら、「key とファイル名をそろえてください」と止まります。同じ名前の共有先が2つある、という状態も、この「ファイル名と key をそろえる」検査のおかげで作れません。フォルダの中に同じ名前のファイルは2つ置けないからです。

icon の検査で「色を currentColor で受け取るか」を見ているのは、アイコンの色を、ボタンの色の設定から変えられるようにするためです。アイコンの中に色を直接書いてしまうと、灰色のボタンの中に、そのサービスの色のロゴだけが浮いてしまいます。細かい決まりですが、1枚ずつ目で確かめるより、生成器にまとめて確かめてもらうほうが確実です。

共有 URL は、宛名の組み合わせ

カードに書いた情報から、実際にどんな URL ができるのかを見てみましょう。note のボタンを押すと、ブラウザの中で次のような URL が組み立てられます。

図2.2-1 note の共有 URL は、宛名の組み合わせ
図2.2-1 note の共有 URL は、宛名の組み合わせ。説明例。宛先・記事の URL・ハッシュタグの3つを、封筒の宛名のように決まった順に並べます。組み立てるのはブラウザ側の1か所です。図2.2-1 note の共有 URL は、宛名の組み合わせ。説明例。宛先・記事の URL・ハッシュタグの3つを、封筒の宛名のように決まった順に並べます。組み立てるのはブラウザ側の1か所です。

URL は、封筒の宛名のように、決まった順に3つの部分を並べてできています。最初が宛先、つまりカードの endpoint に書いた入口(https://note.com/intent/post)です。次が ?url= に続く記事の URL で、このとき記事の URL には、どこから来た訪問かを計測するためのUTMが付きます。utm_source には共有先の名前(note)が入ります。最後が &hashtags= に続くハッシュタグで、promari.jp の設定では Promari です。

組み立てているのは、プラグインの中の ShareDestination というクラスの shareUrl という関数、ただ1か所です。

TypeScriptweb/src/domain/model/ShareDestination.ts(共有 URL を組み立てる関数)
shareUrl(request: ShareRequest): string {
  if (!this.#endpoint) return request.url;
  const pairs = Object.entries(this.#params)
    .map(([name, field]) => [name, FIELD[field](request)] as const)
    .filter(([, value]) => value !== '')
    .map(([name, value]) => `${UriEncoder.encode(name)}=${UriEncoder.encode(value)}`);
  return this.#endpoint + (pairs.length ? `?${pairs.join('&')}` : '');
}

上から読んでいきます。2行目は、入口の無い共有先(URL のコピーなど)のための道で、記事の URL をそのまま返します。3行目で、カードの [params] に書いた項目を1つずつ取り出し、4行目で、その項目の中身(記事の URL やハッシュタグ)を読み出します。5行目で、中身が空の項目は捨てます。ハッシュタグを設定していないサイトなら、hashtags= は付きません。6行目で、名前と中身の両方をエンコードしてから「名前=中身」の形にし、最後に入口の後ろへ ? と & でつなぎます。

ここで大事なのは、共有先ごとの違いは、すべてカードの中の「データ」にあり、この関数は共有先の名前を1つも知らないことです。note を足しても、この関数は変わりません。X にはテキストと via を、LINE にはテキストを、note にはハッシュタグを、というふうに、送る項目の違いはカードの [params] が受け持ちます。同じ関数が、どのカードを渡されても同じやり方で URL を組み立てるので、共有先を足すたびにこの関数を直す必要がないのです。

エンコードを最後の1か所でまとめて行っているのにも理由があります。記事の URL の中には & や = のような記号が含まれることがあり、そのまま並べると、どこまでが url の中身で、どこからが次の項目なのか、受け取る側が区別できなくなります。日本語の題名なども、そのままでは URL に入れられません。そこで、組み立ての直前に、名前も中身もまとめて安全な形に置き換えています。組み立てる場所を1つにすれば、エンコードのし忘れや、二重にエンコードしてしまう間違いも1か所で防げます。

記事の URL に付く、見えない名札

note の共有 URL の中に入る記事の URL には、UTMという見えない名札が付くと書きました。この名札の中身も見ておきましょう。promari.jp の設定では、共有した URL に次の3つの項目を付けています。

共有した URL に付く UTM の項目(promari.jp の設定)
項目設定の値note のボタンで付く値意味
utm_source{destination}noteどの共有先から来たか
utm_mediumsocialsocialどんな種類の経路から来たか
utm_campaignshareshare何の取り組みから来たか

設定の utm_source に書いた {destination} は、共有先の名前に置き換わる印です。note のボタンなら note、Qiita のボタンなら qiita が入ります。こうしておくと、note で紹介された記事から誰かが読みに来たとき、計測の画面で「note から来た訪問」として数えられます。共有ボタンが押された回数だけでなく、共有された先から読者が戻ってきた回数まで、共有先ごとに追いかけられるわけです。

この名札は、3ページ目で見る compose でコピーする URL にも、同じように付きます。compose の4つは入口が無いので、URL の組み立てには関わりませんが、「コピーする URL」はほかの共有先と同じ規則で作ります。共有先によって名札の付け方が違うと、あとで数字を比べられなくなるからです。

ここで、note を compose ではなく open で足した判断についても、書いておきます。公式の入口がある note は、compose にそろえず open で足しました。5つを同じ動きにそろえるより、それぞれのサービスが用意した一番よい道を使うほうが、読者にとって確かだと考えたからです。

ログインしていないと、改札を1回通ってから戻ってくる

note の入口には、もう1つ確かめておきたいことがありました。読者が note にログインしていないとき、ボタンを押すとどうなるかです。

共有ボタンを押す人の中には、そのブラウザで note にログインしていない人もいます。もしログインしていない人がボタンを押して、エラーの画面が出たり、note のトップページに飛ばされて記事の URL が失われたりしたら、せっかくの入口が台無しです。そこで、ログインしていない状態で入口を開き、何が返ってくるかを確かめました。

図2.4-1 ログインしていないと、改札を1回通ってから戻ってくる
図2.4-1 ログインしていないと、改札を1回通ってから戻ってくる。実測。ログインしていなくても、note が行き先を覚えて、ログインのあとで書く画面へ戻してくれます。こちらでは何も足していません。図2.4-1 ログインしていないと、改札を1回通ってから戻ってくる。実測。ログインしていなくても、note が行き先を覚えて、ログインのあとで書く画面へ戻してくれます。こちらでは何も足していません。

入口の URL(記事の URL 付き)を開くと、note のサーバーは、その場で投稿画面を返すのではなく、302 転送でログイン画面へ案内しました。案内先の URL を見ると、redirectPath という項目に、最初に開いた入口の URL が、記事の URL ごと入っています。つまり note は、「ログインが済んだら、ここへ戻ってきてね」という行き先を覚えたまま、ログイン画面へ回してくれるのです。駅の改札で、切符を忘れた人がいったん窓口へ回り、そこから元のホームへ戻ってくるようなものです。

ログインしていない状態で note の入口を開いたときの流れ(2026-10-01 に確かめた)
順番起きることURL
1共有ボタンを押すnote.com/intent/post?url=(記事の URL)
2note がログイン画面へ転送する(302)note.com/login?redirectPath=(1の URL)
3読者がログインする(本人が入力)
4note が1の画面へ戻す「noteで書く」の画面

ここで大事なのは、この遠回りのために、こちらのコードは何も足していないことです。行き先を覚えて戻すのは、入口を用意した note の仕事です。公式の入口を使う良さは、こうした「ログインしていない人」「スマホのアプリを入れている人」といった、共有の周りのいろいろな場合を、サービスの側が面倒を見てくれるところにあります。

なお、1から2への転送までは、ブラウザの外から確かめた事実です。3と4、つまりログインしたあとに本当に「noteで書く」の画面へ戻るかは、note の仕様に任せている部分で、この記事の中で自動では確かめていません。入口が公式のものなので、そう動くことを note が約束している、という位置付けです。

こうして note は、カード1枚で片付きました。次のページでは、いよいよ入口の無い4つです。カードを1枚差し込むだけでは、どうにもならない相手でした。

考えてみる:note の入口にも、題名を渡せたらよかったのでは?

note の公式の入口が受け取るのは、記事の URL とハッシュタグです。題名を渡す項目は用意されていません。カードの [params] に title = ‘title’ と書き足すことはできますが、note がその項目を読まなければ、URL が長くなるだけで何も起きません。受け取られない項目を送るのは、動いているように見えて何も起きない、いちばん気づきにくい失敗です。そのため、公式の説明にある項目だけを送っています。入口に渡せない情報は、入口の先で読者が書き足す、という役割の分け方です。

COMMENTS
コメント…

このページ(2ページ目)の感想・質問・設計へのコメント

PASS IT ON

この気づきを、誰かにも。

この記事を書いた人

Takaomi Murasaki

Promari SNS Shareの開発を通して、Webの仕組みや設計の選び方を紹介しています。動く実装と、その判断に至るまでを記事に残しています。

公開記事 11 件

プロフィールを見る
THANK YOU FOR READING.すべての記事へ ↗

FROM INSIGHT TO IMPACT

「できたらいいな」を、
動く仕組みに。

記事で見つけたヒントを、あなたの事業へ。
新しいサービスも、手間のかかる業務も。
いまの課題から、つくるべきものを一緒に考えます。

開発・AI・研修の実績を見る

まだ、仕様書はいりません。

「何から始める?」から、ご一緒に。

課題がまとまっていなくても大丈夫。テーマを選ぶと相談文をご用意します。
連絡先の必須入力は、お名前とメールアドレスだけ。

まずは課題の整理から相談する

ご相談後の流れ

  1. 01 内容を確認
  2. 02 メールでご連絡
  3. 03 課題・進め方をご相談
送信だけで契約やお申込みが確定することはありません。