🤢症状
AsciiDoc 文書作成中に kroki でグラフ描画しようとしたらプレビュー画面に表示されなかった。
Chrome の Developper Tools からコンソールエラーを見てみると net::ERR_BLOCKED_BY_RESPONSE.NotSameOriginAfterDefaultedToSameOriginByCoep
のエラー表示。
App | Version |
---|---|
VS Code |
1.84.1 |
AsciiDoc (VS Code 拡張機能) |
v3.1.7 |
🔍原因
vscode.dev
ドメインを介してアクセスしてるので CORS 問題か?
と思ったら Cross-Origin-Resource-Policy(CORP) レスポンスヘッダー未定義によるエラーみたい。
上記エラーは Cross-Origin-Embedder-Policy(COEP) レスポンスヘッダーにおいて、明示的に許可を与えていない外部 origin のリソースが読み込まれることを防止する設定[1]の場合に発生する。
つまり vscode.dev
側の COEP 設定に起因する。
よって当然これは kroki サービスだけの問題ではなく、他の CORP レスポンスヘッダーが定義されていないサービスもブロックされる。
🚑対策
とりあえず今回は vscode.dev
上のプレビューで画像ブロックされてしまうことを回避できればいいので、 <img>
タグに crossorigin
属性をつけることにした[2]。
<img src="https://example.com/hoge.png"> (1)
<img src="https://example.com/hoge.png" crossorigin> (2)
1 | COEP ブロックされる。 |
2 | <img> や <link> タグなどに crossorigin 属性をつけると COEP によるブロックは回避できる(CORS 対応の必要あり)。📖 HTML 属性: crossorigin - HTML: ハイパーテキストマークアップ言語 | MDN |
なお AsciiDoc の基本機能では自由に HTML 属性を追加することはできないため、Asciidoc.js のカスタマイズ機能を利用する。
Asciidoctor.js Extensions で crossorigin
属性を追加する
asciidoctor-vscode
ではカスタマイズ機能の一つである Asciidoctor.js Extensions が利用できる[3]ため、それにより crossorigin
属性を自動で追加するようにした。
方法としては Postprocessor でHTML変換後の文字列を正規表現で置換させる。
📖 Postprocessor Extension Example | Asciidoctor Docs
-
Asciidoctor.js の拡張機能を有効化する
.vscode/settings.json{ "asciidoc.extensions.registerWorkspaceExtensions": true }
-
.asciidoctor/lib
ディレクトリ下に拡張機能(*.js
)ファイルを作成するcrossorigin 属性を追加する拡張機能ファイルの例
.asciidoctor/lib/crossorigin.js// @ts-check /** * @typedef {object} CrossoriginTarget - crossorigin 属性をつけたい対象の情報を格納。 * @prop {string[]} urls - 置換対象とするURL * @prop {'audio'|'img'|'link'|'script'|'video'} tag - 置換対象とするHTMLタグ名 */ /** * @typedef {*} Postprocessor * * @typedef {object} PostprocessorDsl * @prop {(block: (this: Postprocessor, document: Document, output: string) => void) => void} process */ /** * 置換対象とする正規表現パターンを作成。 * @param {CrossoriginTarget} target * @returns {RegExp} */ const createPattern = (target) => { const orUrl = `(?:${target.urls.join('|')})` switch (target.tag) { case 'img': return new RegExp(`(<img .*?src="${orUrl}[^>]*)>`, 'g') default: console.warn( `[crossorigin Extension]: ${target.tag} tag is not implements.` ) break } return new RegExp('') } /** * Postprocessor 拡張機能の実装。 * 外部参照の <img> タグに crossorigin 属性を挿入する。 * @this {PostprocessorDsl} */ function processor() { /** @type {CrossoriginTarget[]} */ const targets = [ { tag: 'img', urls: ['https://img.shields.io', 'https://kroki.io', 'http://localhost'], }, ] const self = this self.process((doc, output) => { const replaced = targets .map((target) => createPattern(target)) .reduce( (source, pattern) => source.replace(pattern, '$1 crossorigin>'), output ) return replaced }) } /** * 上記 processor() を Postprocessor として登録する register 関数を公開。 * @param {*} registry */ module.exports.register = (registry) => { typeof registry.postprocessor === 'function' ? registry.postprocessor(processor) : console.warn( '[crossorigin Extension]: registry.postprocessor is not function.' ) }
以上で作成した拡張機能が適用される。