5
3

More than 3 years have passed since last update.

Riot.js v4 カスタムテンプレートを実装する

Posted at

Riot.js Advent Calendar 2019 の13日目が空いていたので埋めます。

はじめに

前回のRiot.js v4 HTMLのテンプレートエンジンにPugを使うで、@riotjs/compilerregisterPreprocessorで自由にテンプレートを登録できるということがわかりました。

これを検証してみようと思います。

動きを確認

まずはデフォルトのテンプレートエンジンの動きがどうなっているかを確認。
https://github.com/riot/compiler/blob/master/src/preprocessors.js

/riot/compiler/blob/master/src/preprocessors.js
export const preprocessors = Object.freeze({
  javascript: new Map(),
  css: new Map(),
  template: new Map().set('default', code => ({ code }))
})

なるほどなるほど、引数で受け取ったcodeを元になんやかんやして、{ code: "最終的なコード" }を返せば良さそうですね。
デフォルトでは何もしないこともわかりました。

動きを確認するために、適当に登録してみます。

webpack.config.js
const path = require('path');
const compiler = require('@riotjs/compiler');

// カスタムテンプレートを登録
compiler.registerPreprocessor('template', 'step_count', (code, options) => {
  console.log("Hello World!!");
  console.log("code:", code);
  console.log("options:", options);
  return {
    code: code
  };
});

module.exports = {
  mode: 'development',
  //mode: 'production',

  entry: './src/scripts/index.js',
  output: {
    path: path.resolve(__dirname, 'app/scripts'),
    filename: 'bundle.js',
    publicPath: '/scripts/',
  },

  devtool: 'inline',
  //devtool: 'source-map',

  module: {
    rules: [
      {
        test: /\.riot$/,
        exclude: /node_modules/,
        use: [{
          loader: '@riotjs/webpack-loader',
          options: {
            hot: true, // set it to true if you are using hmr
            // add here all the other @riotjs/compiler options riot.js.org/compiler
            template: 'step_count'
          }
        }]
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      }
    ]
  }
};
package.json
{
  "name": "riotv4-custom-template-sample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack --mode production --devtool source-map",
    "start": "webpack-dev-server --inline --watch --hot --colors --content-base app/ --open-page index.html --historyApiFallback true -d --port 4500"
  },
  "keywords": [],
  "author": "KAJIKEN <kentaro@kajiken.jp> (http://kajiken.jp)",
  "license": "MIT",
  "dependencies": {},
  "devDependencies": {
    "@babel/core": "^7.7.5",
    "@babel/preset-env": "^7.7.6",
    "@riotjs/compiler": "^4.5.3",
    "@riotjs/hot-reload": "^4.0.0",
    "@riotjs/webpack-loader": "^4.0.1",
    "babel-loader": "^8.0.6",
    "riot": "^4.7.1",
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.10",
    "webpack-dev-server": "^3.9.0"
  }
}

npm run buildでビルド。

ビルド中のログ
> webpack --mode production --devtool source-map

Hello World!!
code: <app>
  <p>{ props.message }</p>
</app>

options: { tagName: null,
  fragments: null,
  options:
   { template: 'step_count',
     file:
      'c:\\nodeapp\\riotv4-custom-template\\src\\scripts\\app.riot',
     scopedCss: true,
     hot: true },
  source: '<app>\r\n  <p>{ props.message }</p>\r\n</app>\r\n' }
Hash: b56824acee93ca59aa1d
Version: webpack 4.41.2
Time: 1403ms

動いていますね!
ファイルパスもoptionsから取れるので、コミットするなどファイルを使って何かすることもできそうです。

ステップカウント

とりあえずステップ数(とは名ばかりの行数)を計測してみます。

webpack.config.js
// カスタムテンプレートを登録
compiler.registerPreprocessor('template', 'step_count', (code, options) => {
  console.log(`*** Step Count *** ${ (new String(options.options.file)).replace(/.*\\/, "") }:${ code.split("\n").length }`);
  return {
    code: code
  };
});
ビルド中のログ
> webpack --mode production --devtool source-map

*** Step Count *** goodbye.riot:6
*** Step Count *** message.riot:22
*** Step Count *** hello.riot:4
*** Step Count *** app.riot:9
Hash: 6dc6caa7dcd7ec4463ef
Version: webpack 4.41.2
Time: 2757ms

モールス信号HTMLテンプレートエンジン

webpack.config.js
const path = require('path');
const compiler = require('@riotjs/compiler');

// モールス信号パターン
const pattern = {
  "・-": "a",
  "-・・・": "b",
  "-・-・": "c",
  "-・・": "d",
  "": "e",
  "・・-・": "f",
  "--・": "g",
  "・・・・": "h",
  "・・": "i",
  "・---": "j",
  "-・-": "k",
  "・-・・": "l",
  "--": "m",
  "-・": "n",
  "---": "o",
  "・--・": "p",
  "--・-": "q",
  "・-・": "r",
  "・・・": "s",
  "": "t",
  "・・-": "u",
  "・・・-": "v",
  "・--": "w",
  "-・・-": "x",
  "-・--": "y",
  "--・・": "z",
  "・----": "1",
  "・・---": "2",
  "・・・--": "3",
  "・・・・-": "4",
  "・・・・・": "5",
  "-・・・・": "6",
  "--・・・": "7",
  "---・・": "8",
  "----・": "9",
  "-----": "0",
  "-・・・・-": "-"
};

// モールス信号解読
const decode = target => target.split(" ").map(word => pattern[word] || word).join("");

// モールス信号テンプレートを登録
compiler.registerPreprocessor('template', 'morse', code => {
  var reg = /(<\/{0,1})(.+?)(>)/g;
  var match;
  var ret = code;
  while ((match = reg.exec(code)) !== null) {
    ret = ret.replace(match[0], `${ match[1] }${ decode(match[2]) }${ match[3] }`);
  }
  console.log(ret);
  return {
    code: ret
  };
});

module.exports = {
  mode: 'development',
  //mode: 'production',

  entry: './src/scripts/index.js',
  output: {
    path: path.resolve(__dirname, 'app/scripts'),
    filename: 'bundle.js',
    publicPath: '/scripts/',
  },

  devtool: 'inline',
  //devtool: 'source-map',

  module: {
    rules: [
      {
        test: /\.riot$/,
        exclude: /node_modules/,
        use: [{
          loader: '@riotjs/webpack-loader',
          options: {
            hot: true, // set it to true if you are using hmr
            // add here all the other @riotjs/compiler options riot.js.org/compiler
            template: 'morse'
          }
        }]
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      }
    ]
  }
};
app.riot
<・- ・--・ ・--・>
  <・--・>{ props.message }</・--・>
</・- ・--・ ・--・>
hello.riot
<-- -・-- -・・・・- ・・・・  ・-・・ ・-・・ --->
  <・・・・ ・---->Hello World!!</・・・・ ・---->
</-- -・-- -・・・・- ・・・・  ・-・・ ・-・・ --->
goodbye.riot
<-- -・-- -・・・・- --・ --- --- -・・ -・・・ -・-- >
  <・--・>Goodbye World!!</・--・>
</-- -・-- -・・・・- --・ --- --- -・・ -・・・ -・-- >
message.riot
<-- -・-- -・・・・- --  ・・・ ・・・ ・- --・ >
  <-・・ ・・ ・・・->{ props.message.slice(0, 1).toUpperCase() }{ props.message.slice(1) } World!!</-・・ ・・ ・・・->
</-- -・-- -・・・・- --  ・・・ ・・・ ・- --・ >

ビルド!

ビルド中のログ
> webpack --mode production --devtool source-map

<my-goodbye>
  <p>Goodbye World!!</p>
</my-goodbye>

<my-message>
  <div>{ props.message.slice(0, 1).toUpperCase() }{ props.message.slice(1) } World!!</div>
</my-message>

<my-hello>
  <h1>Hello World!!</h1>
</my-hello>

<app>
  <p>{ props.message }</p>
</app>

Hash: 8b904bfcedfcd87ff364
Version: webpack 4.41.2
Time: 2239ms

モールス信号HTMLテンプレートエンジンここに爆誕!

5
3
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3