Upgrade to Pro — share decks privately, control downloads, hide ads and more …

【20221019_toranoana.deno#9】ブログをlumeに移植しよう

 【20221019_toranoana.deno#9】ブログをlumeに移植しよう

toranoana.deno#9での発表資料です。
https://yumenosora.connpass.com/event/261266/

虎の穴ラボ株式会社

October 24, 2022
Tweet

More Decks by 虎の穴ラボ株式会社

Other Decks in Technology

Transcript

  1. Copyright (C) 2021 Toranoana Inc. All Rights Reserved.
    ブログをlumeに移植しよう


    虎の穴ラボ 藤原 佳顕

    1

    View full-size slide

  2. Copyright (C) 2021 Toranoana Inc. All Rights Reserved.
    アジェンダ

    ● 概要

    ● 取り扱う記事ファイルについて

    ● lumeについて

    ● lumeのプラグインについて

    ● プラグインで記事ファイルを変換する

    ● まとめ

    2

    View full-size slide

  3. Copyright (C) 2021 Toranoana Inc. All Rights Reserved.
    自己紹介

    3
    ● 名前

    ○ 藤原佳顕

    ● 仕事

    ○ FantiaとかCreatiaとか社内アプリとか

    ● 好み

    ○ Clojure、Rust

    ● 趣味

    ○ 格闘ゲーム

    ■ Melty Blood、Guilty Gear

    ○ ダライアス外伝


    View full-size slide

  4. Copyright (C) 2021 Toranoana Inc. All Rights Reserved.
    概要

    4
    ● 個人ブログをlumeに移植したのでやったことの紹介をします

    ○ https://zonuko.github.io/

    ● 現状で使っていたツール

    ○ Pelican: Pythonでの静的サイトジェネレータ

    ■ https://getpelican.com/

    ■ Markdown or restructuredTextで記事が書ける

    ○ github.io

    ■ URLでわかるようにビルド後のファイルをgithub.ioで公開


    View full-size slide

  5. Copyright (C) 2021 Toranoana Inc. All Rights Reserved.
    取り扱う記事ファイルについて

    5
    ● restructuredText(reST)について

    ○ いわゆるマークアップ言語

    ○ Docutils: パーサー等が詰め込まれたライブラリ(Python)

    ○ Sphinx: reSTとDocutilsを使ったPython用ドキュメンテーションツール

    ● マークアップの仕様等は公開されている

    ○ https://docutils.sphinx-users.jp/docutils/docs/ref/rst/restructuredtext.html

    ○ 気合があればパーサー等を自作することもできそうではある

    ● 周辺ツールもある

    ○ ブログで使ってる静的サイトジェネレーター(Pelican)

    ■ https://getpelican.com/


    View full-size slide

  6. Copyright (C) 2021 Toranoana Inc. All Rights Reserved.
    取り扱う記事ファイルについて

    6
    About
    #######################
    :date: 2016-12-25 21:59
    :tags: anout
    :summary: About
    About me
    =================
    - 名前
    - y-fujiwara
    - nuhera or zonuko (HN)
    About me

    名前
    y-fujiwara
    nuhera or zonuko (HN)


    View full-size slide

  7. Copyright (C) 2021 Toranoana Inc. All Rights Reserved.
    lumeについて

    7
    ● denoの静的サイトジェネレーター 

    ○ https://lume.land/

    ● プラグイン機能で任意の機能を追加できる 

    ● Denoなのでプラグインや設定ファイルはTypeScriptで書ける 

    ● netlifyやGitHub Pagesへのデプロイもサポートされている(プラグインがある) 

    ○ →もともとGitHub Pagesにあるサイトを置き換えたいので相性が良さそう 

    ● MarkdownやNunjucks、Pug(オプション)等のテンプレートシステムに対応している 

    ○ restructuredTextは対応されていない(プラグインもパット見た感じはなさそう) 

    ○ →プラグイン作るしか無いかも? 


    View full-size slide

  8. Copyright (C) 2021 Toranoana Inc. All Rights Reserved.
    lumeのプラグインについて


    8
    ● markdownの対応も実際はプラグインとして用意されている

    ○ https://github.com/lumeland/lume/blob/master/plugins/markdown.ts

    /** Register the plugin to support Markdown */
    export default function (userOptions?: DeepPartial) {
    const options = merge(defaults, userOptions);
    if (options.keepDefaultPlugins && userOptions?.plugins?.length) {
    options.plugins = defaults.plugins.concat(userOptions.plugins);
    }
    return function (site: Site) {
    // @ts-ignore: This expression is not callable.
    const engine = markdownIt(options.options);
    // Register markdown-it plugins
    options.plugins.forEach((plugin) =>
    Array.isArray(plugin) ? engine.use(...plugin) : engine.use(plugin)
    );
    for (const [name, rule] of Object.entries(options.rules)) {
    engine.renderer.rules[name] = rule;
    }
    // Load the pages
    site.loadPages(options.extensions, loader, new MarkdownEngine(engine));
    // Register the md filter
    site.filter("md", filter as Helper);
    function filter(string: string, inline = false): string {
    return inline
    ? engine.renderInline(string?.toString() || "").trim()
    : engine.render(string?.toString() || "").trim();
    }
    };
    }

    View full-size slide

  9. Copyright (C) 2021 Toranoana Inc. All Rights Reserved.
    lumeのプラグインについて


    9
    ● プラグインでファイルに対してどのような操作をするか決められる(公式のサンプル) 

    interface Options {
    message: string;
    }
    export default function (options: Options) {
    function addBanner(content: string): string {
    const banner = `/* ${options.message} */`;
    return $banner + "\n" + content;
    }
    return (site: Site) => {
    site.process([".css"], (page) => {
    page.content = addBanner(page.content as string);
    });
    };
    }
    大枠のルールは以下2つ

    ● 関数がデフォルトエクスポートされていること

    ● デフォルトエクスポートされた関数はSiteオブジェクトを引
    数に取る関数をreturnすること


    View full-size slide

  10. Copyright (C) 2021 Toranoana Inc. All Rights Reserved.
    プラグインで記事ファイルを変換する

    10
    ● ファイル拡張子.mdに対して、markdown-itでHTMLに変換をかけているっぽい

    ○ =>何でもいいので.rstファイルをHTMLに変換できれば良さそう

    ● reSTをHTMLに変換できるライブラリがあるか?(restructuredText、Sphinx、Docutilsあたりで検索)

    ○ deno.land/x

    ■ →なさそう

    ○ npm

    ■ あるっぽい https://github.com/thoward/rst2html

    ■ 中で使っているライブラリ https://github.com/seikichi/restructured

    ● 全部に対応しているわけではなさそう

    ○ crate.io

    ■ ある https://crates.io/crates/rst

    ■ レンダラーとかも同じライブラリ内にある https://crates.io/crates/rst_renderer

    View full-size slide

  11. Copyright (C) 2021 Toranoana Inc. All Rights Reserved.
    プラグインで記事ファイルを変換する

    11
    ● npmにあるものを使う or crate.ioのものをつかう or 自作

    ● npmにあるものを使う

    ○ 最近node_modulesサポートが入ったので使えそうではあるが、node_modulesが・・・

    ● crate.ioのものを使う

    ○ WebAssemblyを生成するか、ffiを使うかの二択

    ○ 何れにせよ自然な形で扱えそうではある

    ● 自作

    ○ パーサーを作ることが目的ではない(ブログを移行したいだけ)のでもっと簡単に行きたい

    ● denoからの取り扱いが自然なのが決定打でcrate.io(Rust)で作ることに

    View full-size slide

  12. Copyright (C) 2021 Toranoana Inc. All Rights Reserved.
    プラグインで記事ファイルを変換する

    12
    ● denoからWebAssemblyを扱う&Rustでdeno向けにWebAssemblyを作る 

    ○ denoのブログで紹介されていた方法が一番簡単そう(内部的にはwasm-bindgenぽい) 

    ■ https://deno.com/blog/wasmbuild 

    ○ rstライブラリをWebAssemblyにビルドできるかが勝負 

    ■ 内部のCargo.tomlの依存を見る限りは大丈夫そう 

    ● deno部分は以下のみ 

    ○ export { instantiate } from "./lib/rs_lib.generated.js"; 

    ● 最終的にはlume側でimportして使えれば良いので、WebAssembly側との受け渡しだけやってもらえれば良
    い


    View full-size slide

  13. Copyright (C) 2021 Toranoana Inc. All Rights Reserved.
    プラグインで記事ファイルを変換する

    13
    use rst_parser::parse;
    use rst_renderer::render_html;
    use std::str;
    use wasm_bindgen::prelude::*;
    #[wasm_bindgen]
    pub fn render_rst(rst: &str, is_standalone: bool) -> String {
    let content = rst.replace('\t', " ".repeat(8).as_ref());
    let document = parse(&content).unwrap();
    let mut s = Vec::new();
    render_html(&document, &mut s, is_standalone).unwrap();
    return str::from_utf8(&s).unwrap().to_string();
    }

    View full-size slide

  14. Copyright (C) 2021 Toranoana Inc. All Rights Reserved.
    プラグインで記事ファイルを変換する

    14
    ● lume側でプラグインを作る

    ○ 概ねmarkdownのプラグインを真似る

    export default function (userOptions?: DeepPartial) {
    const options = merge(defaults, userOptions);
    if (options.keepDefaultPlugins && userOptions?.plugins?.length) {
    options.plugins = defaults.plugins.concat(userOptions.plugins);
    }
    function filter(string: string, inline = false): Promise {
    if (inline) {
    return engine.renderInline(string?.toString() || "").then((doc) =>
    doc.trim()
    );
    }
    return engine.render(string?.toString() || "").then((doc) => doc.trim());
    }
    site.filter("rst", filter as Helper, true);
    };
    }

    View full-size slide

  15. Copyright (C) 2021 Toranoana Inc. All Rights Reserved.
    プラグインで記事ファイルを変換する

    15
    ● 拡張子rstに対してWebAssemblyで作ったレンダラーを呼び出しているだけ 

    ○ レンダラーはHTMLを返す 

    ● エンジンとして登録する部分のみ少し工夫が必要(実装は雑) 

    ○ lume側のEngineをimplementsする必要がある。とはいえこちらもレンダラー関数をラップするクラスを作るだけ 

    export class RstEngine implements Engine {
    constructor() {
    }
    deleteCache() {}
    render(
    content: string,
    _data?: Data,
    _filename?: string,
    ): Promise {
    return instantiate().then(({ render_rst }) => render_rst(content, true));
    }
    renderInline(content: string): Promise {
    return instantiate().then(({ render_rst }) => render_rst(content, false));
    }
    renderSync(
    _content: unknown,
    _data?: Data,
    _filename?: string,
    ): string {
    throw new Error("Not Emplements");
    }
    addHelper() {}
    }

    View full-size slide

  16. Copyright (C) 2021 Toranoana Inc. All Rights Reserved.
    プラグインで記事ファイルを変換する

    16
    ● テンプレート埋め込み 

    ○ lumeはファイルの先頭にyml形式でlayoutの指定や、変数埋め込みなどができる 

    ○ これがレンダリング時に行われている場合、プラグインの中でなにか必要かも? 

    ○ とはいえmarkdownのプラグインではそれっぽい処理はなさそう(markdown-it側にもない) 

    ○ よくわからんのでとりあえず設定入れてビルドしてみる 

    ---
    layout: layout.njk
    title: This is my website
    ---
    * this is
    * a list
    * with a nested list
    * and some subitems
    * and here the parent list continues

    View full-size slide

  17. Copyright (C) 2021 Toranoana Inc. All Rights Reserved.
    プラグインで記事ファイルを変換する

    17
    ● 結果:普通にビルドできた 

    ○ 左: layout.njk

    ○ 右: 生成されたHTML 





    {{ title }}


    {{ content | safe }}





    This is my website



    this is
    a list
    with a nested list
    and some subitems

    and here the parent list continues


    View full-size slide

  18. Copyright (C) 2021 Toranoana Inc. All Rights Reserved.
    プラグインで記事ファイルを変換する

    18
    ● そもそもこのようなマークアップへのメタデータ埋め込みについて 

    ○ Front-matterと呼ばれる書き方 

    ■ http://jekyllrb-ja.github.io/docs/front-matter/ 

    ● 一般的なものなので、テンプレート関係なくlume側が処理しているのでは? 

    ○ https://github.com/lumeland/lume/blob/master/deps/front_matter.ts 

    ○ 中身は以下のみ

    ■ export * from "https://deno.land/[email protected]/encoding/front_matter.ts ";

    ■ そもそもstdにライブラリがある 

    ○ 使っている箇所

    ■ https://github.com/lumeland/lume/blob/7aaafb370441641e921ab9257719d466c1fc6347/cor
    e/loaders/text.ts

    ■ コメントを読む限りはファイルロードした時点で処理される様子 


    View full-size slide

  19. Copyright (C) 2021 Toranoana Inc. All Rights Reserved.
    まとめ

    ● lumeにブログを移植するためにプラグインを作った話を紹介しました

    ● denoからWebAssemblyを使うもうちょい実践的な話を紹介しました

    ○ 前回はWebのAPI(instantiateStream)での読み込み方法を紹介しています

    ● 課題としては、ブログらしくするスタイルの調整などがあります

    ○ lumeのブログテンプレートもあるのでそちら使えば簡単かも?

    19

    View full-size slide