シェルからの小さなホットリロード静的サイトジェネレーター。 Bash 4.4 以降を前提としています。
警告: ここにヤクがいます!
shite
の仕事は、私の Web サイトの作成を手伝うことです: https://evalapply.org したがって、 shite
のスコープ、(誤った)機能セット、洗練は常に本番グレードになり、本番は「私のマシン上で動作します」 )":)
目次
さて、 shite
ウェブサイトを作ることを目指しています。
これは、パイプライン化されたワークフローで構成される小さなパブリッシング システムであり、オプションでファイル イベントのストリーム (ホットリロード ビットの場合) によって駆動されます。
前世紀の Perl/PHP 紳士ハッカーなら驚かないでしょう。
それは、人がくだらない曲を口笛で吹き、ヤクの毛を剃るために存在します。
これは基本的にその動作です (参考: shite_templating_publish_sources
関数)。
cat " ${watch_dir} /sources/ ${url_slug} " |
__shite_templating_compile_source_to_html ${file_type} |
__shite_templating_wrap_content_html ${content_type} |
__shite_templating_wrap_page_html |
${html_formatter_fn} |
tee " ${watch_dir} /public/ ${slug} .html "
# The complete "business logic" is 300-ish lines as of this comment,
# counted as all lines except comments and blank lines.
grep -E -v " s?#|^$ "
./bin/{events,metadata,templating,utils,hotreload}.sh |
wc -l
興奮しすぎる前に、MIT ライセンスにより、この小さなシテ作成者があなたのシテを機能させられなかった場合、私はシテを与える必要がないことを意味していることを警告してもいいですか。貢献すると、さらに多くの警告が表示されます。
そして最後になりましたが、私はここに記載されているすべての文章をショーン・コネリーの声で読むことをここに宣言します。
shite
夢の中で願うのは…
何よりも、それ (「ビジネス ロジック」) を小さく保つことです。頭の中でキャッシュ、デバッグ、リファクタリングできるほど小さい。
スーパーユーザーの許可なしにインストールして使用すること。
ツールチェーンを極端に避けて依存関係を構築するため。 gems/npms/venvs/what-have-yous はありません。したがって、Bash はどこにでも存在するため、Bash が言語になります。特定の高度な機能が必要な場合は、 pandoc
やtidy
などの標準パッケージも利用できます。
古き良きヒアドキュメントで設定された単純な HTML による依存関係のないテンプレート。
シンプルなメタデータ システム、コンテンツの名前空間、静的なアセットの構成など。
Web サーバーはオプション (さらに言えば、あらゆる種類のサーバー プロセス)。結局のところ、私たちはfile://
ナビゲーションで問題なく動作する静的サイトを目指しています。
小さくて、構成可能で、純粋に機能的な、Unix ツールのようなパーツから構築すること。私はそのようなものが大好きなので。
REPL のような編集、保存、ビルド、プレビューのシームレスなワークフローを実現するためです。
久しぶりにブログを偶然再開してしまいました。単語をクラウドに取り込む前に、私は「最新の」静的サイト ジェネレーターについてあれこれ考えました。なぜなら、WordPress は前世紀のものだからです (そう自分に言い聞かせました)。その後、SSG Jamstack の特注テンプレート構築などの魔法に悩まされました。今、私はこれを作るという暗い道を歩いています。 shite: 静的サイト from シェル: パート 1/2 でブログに書かれています。
私は主に "hotreload" モードで shite を使用し、主に (orgmode で) 投稿を書いて (Firefox で) ライブ プレビューするために使用します。それほど主なものではありませんが、スタイルやページ テンプレートへの変更をホット プレビューするためのものです。少なくとも主なことは、投稿に延々と取り組んだ後、サイト全体の再構築を行うために「ホットリロードしない」モードでそれを使用することです。
以下にデモの例を示します。
基本的に、これは、 sources
の下にあるファイルを作成、更新、削除する場合、そのファイルは自動的に HTML に変換され、ローカルでpublic
に公開され、サイトが開いている Web ブラウザーで適切なページ ナビゲーションまたはリロード アクションを引き起こす必要があることを意味します。
クリーンな新しいターミナル セッションまたは tmux ペインで「メイン」スクリプトを呼び出します。
./shite.sh
./shite.sh
のshite_global_data
配列に設定したデフォルトに従って、Firefox でインデックス ファイルを開くことができます。
Emacs または Vim で、 sources
の下にあるコンテンツ ファイルを開きます。コンテンツを編集、保存し、ブラウザに表示されるのを確認します。 (はい、Emacs/Vim を指定するのはおかしなことです。inotify イベントに基づいてホットアクションをトリガーするからです。どうやら、エディターによってファイルの更新の方法が異なるようです。私は Emacs または Vim を使用しているので、それらが引き起こすイベントを監視しているので、私のマシンでは動作します。: ))。
多くの場合、ブラウザーはスクロール位置を記憶しており、これは適切です。ホットリロードは、まあ、ひどいこともあります。そこで、スペースを押してコンテンツ ファイルを保存し、ホットリロードを再度トリガーします。
CSS スタイルシートなどの静的アセットに移動します。背景色の値などを変更します。保存してブラウザで色の変化を確認します。
templates.sh
内のテンプレート フラグメントを微調整します -- たとえば、ブログ投稿テンプレートです。次に、ブログ投稿コンテンツ ファイルに切り替えて、変更したテンプレートを使用してページの構築をトリガーするように変更します (スペースキーを押して保存するなど)。
これはハックです。ソースの下のルートのindex.orgページは特別です。これを変更するということは、インデックス ページやタグの投稿リストを再構築し、RSS フィード、サイトマップ、robots.txt などの関連メタファイルも再構築することを意味します。
クリーンな新しいターミナル セッションで、「no」を指定してshite.sh
を呼び出し、必要に応じてデプロイメント環境のbase_url
を指定して呼び出します。
「ローカル」file:/// ナビゲーション用に完全なサイトを再構築します。まさに「サーバーレス」:)
./shite.sh " no "
私のドメインで公開するために完全なサイトを再構築します。
./shite.sh " no " " https://evalapply.org "
これらのフラグはシステムの動作を変更します。
SHITE_BUILD
を「ホット」に設定すると、イベント システムが「モニター」モードで実行され、ホットリロード動作が駆動されます。 「no」に設定すると、ブラウザーのホットリロードが抑制されます。SHITE_DEBUG_TEMPLATES
を「デバッグ」に設定すると、テンプレート化されたソース コンテンツを公開する前に、テンプレートが最初に取得されます。shite
の中はかなりユニクシーです。あるいはそう考えたいと思います。
コードは関数プログラミング スタイルの Bash です。すべては関数です。ほとんどの関数は純粋な関数、つまりそれ自体が小さな Unix ツールです。ほとんどのロジックはパイプライン指向です。 Shell は FP にとって悪い場所ではないため、これは驚くほどうまく機能します。
また、私は Clojure や Emacs のようなライブ/インタラクティブ ランタイムで作業するのが好きなので、 shite
を使用して記述するときにライブ インタラクティブな REPL のようなエクスペリエンスも必要としていました。
したがって、 shite
ホット ビルドと保存時の再ロードが可能な、完全にリアクティブなイベント駆動型システムになりました。
3 つの主要なディレクトリ名前空間があります。
sources
。public
ターゲットbin
URL 命名スキームは、 sources
の下のサブディレクトリ構造に従い、 pubilic
ディレクトリ構造の下にそのまま複製されます。これは標準的な URL 名前空間スキームであるため、公開されたコンテンツにも直接適用されます。同様に:
file:///absolute/path/to/shite/posts/slug/index.html
http://localhost:8080/posts/slug/index.html
https://your-domain-name.com/posts/slug/index.html
すべての「パブリック」関数の名前空間はshite_the_func_name
です。すべての「プライベート」関数は、 __shite_the_func_name
として名前空間が設定されます。
関数は次の目的で存在します。
クリーンな新しいターミナル セッションで:
source ./bin/utils_dev.sh
shitTABTAB
または__shiTABTAB
押します。type -a func_name
と入力して、関数の定義を出力し、その API を読み取ります。shite_global_data
とshite_page_data
を設定します。テンプレートは、ページのフラグメント (ヘッダー、フッター、ナビゲーションなど) 用と、完全なページ定義 (デフォルトのページ テンプレートなど) 用に存在します。これらは、ヒアドキュメントでラップされたプレーン HTML として書かれています。 ./bin/templates.sh
これらを提供します。
テンプレートには、さまざまなソースからの変数データが入力されます。
shite_global_data
にはサイト全体のメタデータが含まれ、 shite_page_data
にはページ固有のメタデータが含まれます。一部の外部プロセスは、ページを処理する前にこれらの配列を事前に設定する必要があります。たとえば、完全なページは次のように構築できます。
cat ./sample/hello.md |
pandoc -f markdown -t html |
cat << EOF
<!DOCTYPE html>
<html>
<head>
$( shite_template_common_meta )
$( shite_template_common_links )
${shite_page_data[canonical_url]}
</head>
<body ${shite_page_data[page_id]} >
$( shite_template_common_header )
<main>
$( cat - )
</main>
$( shite_template_common_footer )
</body>
</html>
EOF
shite
のメタデータ システムは、キーと値のペアとして定義されます。キーはメタデータ項目に名前を付け、そのタイプの値に関連付けられます。以下に例を示します。
前述したように、実行時メタデータは、連想配列shite_global_data
およびshite_page_data
によって環境内に運ばれます。これらは、外部ソースから更新されるだけでなく、直接構築によって設定される場合もあります。
各ページは、ページ上部の「前付」で独自のメタデータを指定できます。これは、他のソースから派生したページ メタデータに加えて使用されます。
shite
次のように、指定されたコンテンツ タイプと互換性のある構文を使用して前付を記述することを期待しています。
コメント行# SHITE_META
使用して、 shite
ページ固有のメタデータとして解析する必要がある組織スタイルのメタデータを区切ります。
# SHITE_META
#+title: This is a Title
#+slug: this/is/a/slug
#+date: Friday 26 August 2022 03:38:01 PM IST
#+tags: foo bar baz quxx
# SHITE_META
#+more_org_metadata: but not processed as shite metadata
#+still_more_org_metadata: and still not processed as shite metadata
* this is a top level heading
this is some orgmode content
#+TOC: headlines 1 local
** this is a sub heading
- this is a point
- this is another point
- a third point
Jekyll スタイルの YAML 前付を---
区切り文字で囲んで記述します。
---
TITLE : This is a Title
slug : this/is/a/slug
DATE : Friday 26 August 2022 03:38:01 PM IST
TAGS : foo BAR baz QUXX
---
# this is a heading
this is some markdown content
## this is a subheading
- this is a point
- this is another point
- a third point
<meta name="KEY" content="value">
という規則に従っている標準の<meta>
タグを単純に使用できます。
< meta name =" TITLE " content =" This is a Title " >
< meta name =" slug " content =" this/is/a/slug " >
< meta name =" DATE " content =" Friday 26 August 2022 03:38:01 PM IST " >
< meta name =" TAGS " content =" foo BAR baz QUXX " >
< h1 > This is a heading </ h1 >
< p > This is some text </ p >
< h2 > This is a subheading </ h2 >
< p >
< ul >
< li > This is a point </ li >
< li > This is another point. </ li >
< li > This is a third point. </ li >
</ ul >
</ p >
ここにヤクがいます!
Clojure/Lisp/Spreadsheet スタイルのインスタ映えするライブ インタラクティブ ワークフローに完全に甘やかされているので、シテ作成にもホット リロードとホット ナビゲートが必要です。
しかし、既知のインターネットの半分を依存関係としてダウンロードすることを望まないスタンドアロンのライブ Web 開発サーバー/ツールは存在していないようです。前にも言いましたが、これは私が絶対にやりたくないことです。
DuckSearch は Emacs の焦りモードを提供していて、これは非常に注目を集めていますが、これを Emacs に配線したくありません。幸運なことに、「inotify-tools」と「xdotool」をフィーチャーしたこのエキサイティングなブレインウェーブも配信されました: github.com/traviscross/inotify-refresh
ホットコピー!
なぜなら、コンピューターが私のためにF5 キーを連打することよりも熱いことがあるでしょうか?まるで私が心の奥底で本当に望んでいることを知っていたかのように。
イベント サブシステムは他のすべてに対して直交しており、システムの残りの部分と構成されます。
この設計は、泥沼の標準的なストリーミング アーキテクチャです。ファイル システム イベントを監視し、フィルタリング、重複排除、分析を行って、別のイベント プロセッサにルーティング (ティー) します。現在、そのようなプロセッサは 2 つだけです。 1 つはイベントに関連付けられたページまたはアセットをコンパイルして公開するため、もう 1 つは同じイベントに応じてブラウザーをホット リロード (またはホット ナビゲート) するためです。
ばかげてこれ:
# detect file events
__shite_detect_changes ${watch_dir} ' create,modify,close_write,moved_to,delete ' |
__shite_events_gen_csv ${watch_dir} |
# hot-compile-and-publish content, HTML, static, etc.
tee >( shite_templating_publish_sources > /dev/null ) |
# browser hot-reload
tee >( __shite_hot_cmd_public_events ${window_id} ${base_url} |
__shite_hot_cmd_exec )
イベントは、次のように構造化された単なる CSV レコードのストリームです。
unix_epoch_seconds,event_type,base_dir,sub_dir,url_slug,file_type,content_type `
イベント レコードのさまざまな部分を使用して、さまざまな種類のアクションを引き起こします。
前にリンクした inotify-refresh スクリプトは、一連のブラウザ ウィンドウを定期的に更新しようとします。しかし、私たちは非常に熱心に取り組みたいと考えています。コンテンツ ファイルや静的アセットに対する編集アクションは、シテを表示しているブラウザ タブでホット リロード/ナビゲーション アクションをインスタント トリガーする必要があります。
個別のリロード シナリオを定義したいと考えています。つまり、監視したいファイル イベントをマップできる、相互に排他的で集合的に網羅的なバケットです。
これを行うと、更新を一種の先行書き込みログとしてモデル化し、分析パイプラインを通じてイベントをパンチし、それらを完全一致シナリオに関連付けて、最終的にアクションを引き起こすことができます。例えば:
現在のタブを更新する場合
いつ家に帰りますか
次の場合にコンテンツに移動します
私たちはコンピューターに私たち自身のキーボード操作をエミュレートさせているため、私たちの個人的な操作が台無しになる可能性があります。テキストエディタでシテを書くことにこだわり、ホットリローディングをコンピュータに任せておけば、イライラすることはなくなるはずだ。
世界にはたくさんのヤクがいます。
真に普及したマルチサイト出版のモジョを実現するには:
shite
利用可能になるはずですこれは小さなヤクです。おそらくすぐにヤクシェーブするでしょう。
明らかに、一般的な git ホストの CI ジョブを使用してshite
ビルドをトリガーできます。しかし、私たちがすでに 1900 年代後半の最先端技術、つまり完全なストリーミングと完全なリアクティブに進歩しているのに、なぜ不格好な今世紀の技術を使用するのでしょうか?
Sarcasam はさておき、私が実行しているリモート マシン上で、ホット デプロイのサポートを追加するために同じイベント システムを使用できない理由がわかりません。
リモートボックス上:
sources
のクローンが安置されていますsources
に対して実行されます (ブラウザ監視を除く)。私のローカルボックスでは:
https://mydomain.com/posts/hello/index.html
で F5 キーを押します。リモート サーバーへのホット デプロイの場合は、SSH 経由で何らかの操作を行って、ブラウザの更新をローカル ボックスに戻します。
おそらく、「開発/製図」時間のセットアップ/分解シナリオでしょうか?新しいシテ執筆セッションを開始するために使用する「dev_server」関数でしょうか?
ここまでたどり着いて、まだ貢献したい場合は...
なぜ?
神聖で善いことすべての名の下に、なぜそうしたいのですか?これがマイペースの仕業であることは目に見えて明らかではないでしょうか? Bash は本物のプログラミング言語ですらないということを聞いたことがありませんか?そして、あなたの PR は永遠に衰え、あなたのコメントは名もなき空白に陥ることは、顔面をひっぱたかれるほど明白ではないでしょうか?
はい、パッチを送信するのはひどい考えです。
ただし、あなたのシテメーカーに関するあなたの希望や夢を私にメールしてください!私は Gmail で名ドット姓でメールを読みました。
私たちは一緒にくだらない曲を口笛で吹いたり、それぞれのヤクの毛を一緒に剃ったりすることができます。それぞれの特別な方法があります。
ソースが私たちとともにありますように。
この作品は、MIT ライセンスと CC By-SA 4.0 ライセンスに基づいて二重ライセンスされています。
SPDX ライセンス識別子: mit または cc-by-sa-4.0