Multi Vitamin & Mineral

Multi Vitamin & Mineral です。プログラムに関することを書いております。

inliner で JavaScript も CSS も画像も1つのHTMLファイルにできるって、マ?

f:id:hiranoon:20210223025906p:plain

inliner」というツールの紹介です。 HTML が参照している JavaScript、 CSS 、画像などの外部ファイル、それらをまとめて1つの HTML ファイルにしてくれるツールです。

このツール、ご存知でしたか? 私は全然知らんかったです。

  • ニッチなツールだけれども、
  • ヤリタイコトとマッチしていたら確実に役に立つ、
  • ちと古いのがタマニキズ、

そんなツールでした。使い方次第で便利に活用できそうなので紹介したいと思った次第です。

inliner とは?

inliner ができることは?

公式サイトの「What it does」にはこのように書かれています。(私的翻訳。)

github.com

  • CSS、 JavaScript、 画像、 動画、 CSS 内で使われている画像、これらを取得してくるよ。
  • それらを1つの HTML ファイルにまとめあげるよ。

ここには書かれていませんでしたが、対象は、ウェブサイトでも HTML ファイルでもどちらでもOKです。

そのまとめ上げる中で、ついでに以下も実施してくれるそうですね。

  • JavaScript は minify するよ。( uglify-js を使います)
  • CSS 内の白は取り除くよ。
  • 画像や動画は Base64 でエンコードするよ。( つまり文字列化するということ。)

どう使うか?

基本的にはコマンドラインで使うようです。 Windows であれば GitBash などから $ inliner http://remysharp.com みたいな感じで起動できます。

また、 new Inliner('http://remysharp.com', function (error, html) {...}); みたいなスクリプトを書く使い方も可能です。(本記事では取り上げません。。。)

ということで、詳細は後ほど。

ちと古いツールか?

2021年2月現在、最終更新日は「2017年4月5日」となっています。主要なファイルの更新日時は4、5年前。うーん、ちと古いですかね。ソースコードを見ても var が使われていて、 ES5 のようですし、最新の感はありませんね。

とはいえ、今現在使ってみた変換結果に問題がなければ使っても良いかと思っています。基本的にローカルPCで使うと思いますので。

inliner を使ってみよう

inliner は npm の利用を前提としているので、Node.js の実行環境は必須になります。 Node.js の環境を用意した上で、 GitBash などのコマンドラインから動かします。

inliner の実行環境の用意

前述の通り、 npm が必要になります。この記事では、 Windows10 で、 GitBash を使った例で説明しています。(もちろん cmd.exe や PowerShell からも動かせます。その方法もこの記事とほぼ同じになります。)

前提環境

前提となる環境です。今回の記事の内容は以下のバージョンで行っています。

ツール バージョン
Windows 10
Node.js v14.15.5
npm 6.14.11
git (git bash を追加います) 2.27.0.windows.1

これだけじゃ困る人は以下の記事で。

multimineral-tech.com

inliner のインストール

ローカルPCにインストールする場合

ローカルPC にインストールする場合は以下。 -g オプションを付けます。公式サイトではこちらを紹介していますね。(私はやってませんが。)

$ npm install -g inliner

この後は $ inliner ... で使えます。

プロジェクト内だけで使う場

プロジェクト内だけで使う場合のお話です。

以降、ゴニョゴニョ書きますが、

  • npm install --save-dev inliner でインストールして、
  • $ ./node_modules/.bin/inliner ... で使えるよ、

ってだけの話です。それを補足含めて書いています。

ということで、インストールは以下。( $ npm init -y は任意で。)

$ npm init -y
$ npm install --save-dev inliner

npm init -y は、実行したディレクトリ内で npm が使える状態を作るコマンドです。( npm init だけだと色々聞かれます。回答するのが面倒なのでこの例で -y オプションを付けています。すると全部 Yes と回答したのと同じ結果になります。)(当然ですが、すでに npm を使っている場合はこのコマンドは不要です。)

次に npm install --save-dev inliner です。 --save-dev オプションで、このプロジェクト内のコマンドからのみ実行できるようにしています(ビルドした本番モジュールには含めない)。

インストールの結果、 node_modules フォルダに inliner がダウンロードされます。加えて package.json の内容が以下のように変更されます。

{
  // ...中略...
  "devDependencies": {
    "inliner": "^1.13.1"
  }
}

インストールした inliner 本体は ./node_modules/.bin/ に入るので $ ./node_modules/.bin/inliner ... で使えるようになります。

package.json の中に npm scripts として書く場合は ./node_modules/.bin/ は不要です。( ./node_modules/.bin/ は npm scripts のスコープに含まれるのです。)

{
  // ...中略...
  "scripts": {
    "build": "inliner src.html > dist.html",
    // ...中略...
  },
  // ...中略...
}

inliner の実行

では、使ってみましょう。

※ 前述したとおり、「プロジェクト内だけで使う場合」のコマンドは $ ./node_modules/.bin/inliner ... とする必要があります。ですが、以降では $ inliner ... で統一して記載していますのでご注意ください。

ウェブサイトの HTML を取得する

標準出力される

まずは(公式の最初にあったような)悪い例から。うちのサイトの記事を取得してみましょう。

$ inliner https://multimineral-tech.com/entry/2021/02/19/024215

サンプルをそのまま実行するとコンソールが。。。
サンプルをそのまま実行するとコンソールが。。。

うーん、これはヒドイ。。。

しばらくの間、コンソールが文字だらけに。コマンドの実行結果は標準出力されるんですね。まあそうっすよね。それが自然だ。

ファイルに書き出す

というわけで、 > article.html を追加してみました。

$ inliner https://multimineral-tech.com/entry/2021/02/19/024215 > article.html
‣ 404 on https%3A%2F%2Fcdn-ak.f.st-hatena.com%2Fimages%2Ffotolife%2Fh%2Fhiranoon%2F20210210%2F20210210232018.png
‣ 404 on https%3A%2F%2Fcdn-ak.f.st-hatena.com%2Fimages%2Ffotolife%2Fh%2Fhiranoon%2F20210210%2F20210210225851.png
‣ 404 on https%3A%2F%2Fcdn-ak.f.st-hatena.com%2Fimages%2Ffotolife%2Fh%2Fhiranoon%2F20201225%2F20201225191231.png
‣ 404 on https%3A%2F%2Fcdn-ak.f.st-hatena.com%2Fimages%2Ffotolife%2Fh%2Fhiranoon%2F20201216%2F20201216194527.png
‣ 404 on https%3A%2F%2Fcdn-ak.f.st-hatena.com%2Fimages%2Ffotolife%2Fh%2Fhiranoon%2F20210115%2F20210115195805.png
97% remaining: js(3)
Last job: 404 on https%3A%2F%2Fcdn-ak.f.st-hatena.com%2Fimages%2Ffotolife%2Fh%2Fhiranoon%2F20210115%2F20210115195805.png
Time: 9s 929.738ms

これで article.html が生成されました。

ブラウザで表示してみる

では、 article.html をブラウザでそのまま開いてみましょう。

inliner の変換結果
inliner の変換結果

お!? いい感じでは?

実際のサイトは下記のような表示になります。

実際のウェブサイトの表示
実際のウェブサイトの表示

ほぼ同じでは?

うちのサイト、更新日は JavaScript で表示しているのですが、上手く動いています。 CSS も画像もバッチリ表示されていい感じです。

ソーシャルボタンがちょっと違っています。まあこの部分は iframe を使っていたりと脂っこい部分なので致し方なしとしましょう。

いずれにせよ、ブラウザでサイトを保存した場合はここまでキレイに表示できませんね。

inliner の変換結果の確認

取得できた article.html の中から部分的にピックアップして見ていきましょう。

以降、「★変換前:」は元のページの HTML の抜粋で、「★変換後:」は article.html の抜粋になります。また、コードが長い場合はどちらも「中略」として記載しています。

JavaScript の変換結果

★変換前:

<script type="text/javascript" src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<script>
  document.addEventListener('DOMContentLoaded', function () {
    var menuBtn = $('#menu-btn');
    var menuContent = $('#menu-content');
    /*中略*/
  });
</script>

★変換後:

<script type="text/javascript" src="https://code.jquery.com/jquery-1.9.1.min.js"></script> <script>document.addEventListener("DOMContentLoaded",function(){var n=$("#menu-btn"),e=$("#menu-content");/*中略*/});</script>

2つ目の <script> タグの JavaScript のコードがコンパクトになっています。 minify が効いていますね。

一方で、1つ目の <script> タグで行っている外部ファイルの jQuery の読み込み部分はそのままです。

-m オプションで外部 JavaScript が取り込める

1つの HTML ファイルに外部 JavaScript ファイルもまとめるには -m オプションを付けると良いです。

$ inliner -m https://multimineral-tech.com/entry/2021/02/19/024215 > article-m.html

★変換後( -m オプション付き):

<script type="text/javascript">/*! jQuery v1.9.1 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license
//@ sourceMappingURL=jquery.min.map
*/(function(e,t){var n,r,i=typeof t,o=e.document,a=e.location,s=e.jQuery,u=e.$,l={},c=[],p="1.9.1",f=c.concat,d=c.push,h=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,b=function(e,t){/*中略*/)(window);</script> <script>document.addEventListener("DOMContentLoaded",function(){var n=$("#menu-btn"),e=$("#menu-content");/*中略*/});</script>

上記の通り jQuery も読み込んでまとめることができました。

CSS の変換結果

★変換前:

<link rel="stylesheet" type="text/css" href="https://cdn.blog.st-hatena.com/css/blog.css?version=4c5b1e236bf5f5214756766d711e87cb228f40db&amp;env=production"/>

★変換後:

<style>.clearfix{display:block;*zoom:1}.clearfix:after{display:block;visibility:hidden;font-size:0;height:0;clear:both;content:"."}.inline-block{display:inline-block;*display:inline;*zoom:1}...中略...</style>

読み込まれて、かつ、コンパクトになっています。 JavaScript と違ってオプションの -m なしで展開されました。

画像の変換結果

★変換前:

<img src="https://cdn-ak.f.st-hatena.com/images/fotolife/h/hiranoon/20210219/20210219022720.png" alt="f:id:hiranoon:20210219022720p:plain" title="" class="hatena-fotolife" itemprop="image">

★変換後:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABLAAAAJ2CAYAAABPQHtc...中略..." alt="f:id:hiranoon:20210219022720p:plain" title class="hatena-fotolife" itemprop="image">

画像が文字列データに圧縮されました。この方法で、画像をファイルにせずに HTML ファイルに埋め込んでいるんですね。(ちなみに、この部分はブログのアイキャッチ画像だったのですが、100万文字くらいに変換されました。長げぇ。)

変換まとめ

CSS、画像は、素直に1つの HTML ファイルにまとめることができました。

JavaScript の外部ファイルは -m を付ける必要がありました。用途にもよりますが、この点が注意ですね。

(おまけ)公式サイトの example を見る

利用方法は公式サイトの usage.txt にかかれていました。

github.com

何とも分かりにくいところにあるな。。。(って、 $ inliner --help で表示されるものと同じみたいなんで分かりにくくても問題ないんですけどね。)先程の -m のようなオプションについても説明が書かれています

さて、ここには example は4つ掲載されています。それらを順々に見ていたいと思います。(後半2つは特にマニアックなので飛ばしてOKです。)

ウェブサイトを取得する

$ inliner https://twitter.com > twitter.html

これは先程試したコマンドとほぼ同等ですね。ウェブサイトを1つの HTML ファイルに変換してくれます。

圧縮しないオプションを付ける

$ inliner -ni local-file.html > local-file.min.html

ローカルにある local-file.html というファイルを local-file.min.html に変換する例です。変換元はウェブサイトでも HTML ファイルでもどちらでもOKです。

ついている -ni オプションは以下のような意味になります。

  • -n (= --nocompress) は CSS と HTML を圧縮しないオプションです。

    • これを付けるとスペースや改行はそのままになります。
    • 逆にこれを付けないと、スペースや改行を除去してくれます。
  • -i (= --noimages) は画像を文字列化しないオプションです。

    • これを付けると <img src="https://..."> のようなタグになります。
    • 逆にこれを付けないと <img src="data:image/png;base64,iVBORw0KGg..."> のように画像を文字列にしてくれます。

文字コードを指定する

$ inliner -e windows-1253 http://foofootos.gr > foofootos-utf8.html

文字コードを指定したパターンです。

ヘッダーを指定する

$ inliner -H 'User-Agent: Inliner Custom' https://httpbin.org/headers

HTTPヘッダを付与してアクセスすることができます。 curl みたいに使えるとのことです。

※これはちょっと話がそれますが、、、 https://httpbin.org は「HTTPリクエストとレスポンスを返してくれるサービス」です。その中で更に https://httpbin.org/headers とするとヘッダ情報だけ返してくれます。実際にやってみると以下のような結果が返ってきます。

{ "headers": { "Host": "httpbin.org", "User-Agent": "Inliner Custom" } } 

httpbin.org さんが「こういうHTTPヘッダを受け付けたぜ」と教えてくれる訳です。HTTPヘッダとして付与した 'User-Agent: Inliner Custom' が有効であったと分かりました。

(おまけ)私的活用方法

はてなブログを運用している中で、デザインのカスタマイズや JavaScript を使ったパーツを作ったりしております。はてなブログは、管理画面にて HTML (JavaScript も含む) や CSS を追加できる仕組みになっています。

その作業の中で、以下の2点を実現したかったです。

  1. デザインの確認のため、ローカルPCにブラウザで表示確認ができる HTML が欲しい。
  2. JavaScript を、はてなブログの管理画面にそのまま貼り付けられる形式にしたい。

デザイン確認用の HTML の取得

これはまさに今回の例で実行したそのものです。inliner で取得するとブラウザで直接確認できる1つの HTML ファイルが手に入ります。

HTML にそのまま貼り付けられる JavaScript の取得

ちょっとヤヤコシイですが、、、うちのブログのサイドバーのカテゴリは、 JavaScript コードを独自で作っています。一部抜粋すると以下。

// (1) src/category.js
document.addEventListener('DOMContentLoaded', function () {
  // 中略
  const rebuildSidebarCategory = (settings) => {
    // 中略
  };
  // 中略
});

これはそのまま貼り付けられないので、以下の形式に変換したいんです。1. ES5 へ変換して、 2. minify して、3. HTMLの <script> タグで囲んだ形式です。

<!-- (3) dist/category.html -->
<script>"use strict";document.addEventListener("DOMContentLoaded",function(){var ...中略...</script>

やりたいことの 3.にある <script> タグで囲むってのが意外と厄介でして、これに inliner が使えます。ついでに 2.の minify も実施してくれます。(1. の ES5 への変換は Babel を使っています。)

(1) src/category.js (先に掲載)と (2) src/category.html (以下)の2ファイルを用意します。

<!-- (2) src/category.html -->
<script src="src/category.js"></script>

この状態で inliner を使います。

$ inliner src/category.html > dist/category.html

(2) src/category.html 内にある (1) src/category.js を読み込み、 minify した結果を (3) dist/category.html に出力してくれます。これで望みのモノが得られました!

・・・と、かなりマニアックな話にはなりましたが、うちではこのような活用をしています。お役立ち情報じゃなかったかも知れませんが、タイトルに(おまけ)と付けたので許してください。

あとがき

inliner 、ちょっと古めのツールで、ちょっとニッチな感じもしますが、使い所があれば便利に活用できそうです。私ははてなブログのカスタマイズというニッチな用途で大変助かりました。