Riot.js Advent Calendar 2019 の13日目が空いていたので埋めます。
はじめに
前回のRiot.js v4 HTMLのテンプレートエンジンにPugを使うで、@riotjs/compilerのregisterPreprocessor
で自由にテンプレートを登録できるということがわかりました。
これを検証してみようと思います。
動きを確認
まずはデフォルトのテンプレートエンジンの動きがどうなっているかを確認。
https://github.com/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: "最終的なコード" }
を返せば良さそうですね。
デフォルトでは何もしないこともわかりました。
動きを確認するために、適当に登録してみます。
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']
}
}
}
]
}
};
{
"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
から取れるので、コミットするなどファイルを使って何かすることもできそうです。
ステップカウント
とりあえずステップ数(とは名ばかりの行数)を計測してみます。
// カスタムテンプレートを登録
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テンプレートエンジン
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']
}
}
}
]
}
};
<・- ・--・ ・--・>
<・--・>{ props.message }</・--・>
</・- ・--・ ・--・>
<-- -・-- -・・・・- ・・・・ ・ ・-・・ ・-・・ --->
<・・・・ ・---->Hello World!!</・・・・ ・---->
</-- -・-- -・・・・- ・・・・ ・ ・-・・ ・-・・ --->
<-- -・-- -・・・・- --・ --- --- -・・ -・・・ -・-- ・>
<・--・>Goodbye World!!</・--・>
</-- -・-- -・・・・- --・ --- --- -・・ -・・・ -・-- ・>
<-- -・-- -・・・・- -- ・ ・・・ ・・・ ・- --・ ・>
<-・・ ・・ ・・・->{ 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テンプレートエンジンここに爆誕!