Riot.js Advent Calendar 2019 の12日目が空いていたので埋めます。
@riotjs/compiler
@riotjs/compilerではtemplate
として登録した内容でコードを変更することが出来ます。
これを使ってPugをHTMLのテンプレートエンジンにすることができます。
Pugはnpm i -D pug
でインストール。
@riotjs/compilerでの指定
@riotjs/compilerのREADME.md
にある通り、registerPreprocessor
でテンプレートを登録してからcompile
のときにその名前を指定するようです。
// process your tag template before it will be compiled
registerPreprocessor('template', 'pug', function(code, { options }) {
const { file } = options
console.log('your file path is:', file)
return {
code: pug.render(code),
// no sourcemap here
map: null
}
})
const { code, map } = compile('<p>{hello}</p>', {
// specify the template preprocessor
template: 'pug'
})
@riotjs/webpack-loaderでの指定
@riotjs/webpack-loaderからこれをどうやって使うのかが分からなかったのでソースを見ました。
// compile and generate sourcemaps
const {code, map} = compile(
source,
{
...opts,
file: this.resourcePath
}
)
どうやらopts
に指定したものは全部@riotjs/compilerに渡してくれるようです。
というわけで、webpack.config.js
で指定します。
const path = require('path');
const compiler = require('@riotjs/compiler');
const pug = require('pug');
// pug のプリプロセッサを登録
compiler.registerPreprocessor('template', 'pug', (code, options) => {
const { file } = options;
return {
code: pug.render(code, {
filename: file,
pretty: true,
doctype: 'html'
})
};
});
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: /\.pug$/,
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: 'pug'
}
}]
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
}
};
import {component} from 'riot'
import App from './app.pug'
component(App)(document.getElementById('root'), {
message: 'Hello World'
});
my-app
p { props.message }
mixin menu(name)
li
a(href="/#" + name) #{name}
ul
+menu('home')
+menu('hello')
+menu('goodbye')
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Riot App</title>
</head>
<body>
<div id="root"></div>
<script src="/scripts/bundle.js"></script>
</body>
</html>
後は例のごとくお好み。
{
"name": "riotv4-pug-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.2",
"@babel/preset-env": "^7.7.1",
"@riotjs/compiler": "^4.5.2",
"@riotjs/hot-reload": "^4.0.0",
"@riotjs/webpack-loader": "^4.0.1",
"babel-loader": "^8.0.6",
"pug": "^2.0.4",
"riot": "^4.6.6",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.9.0"
}
}
{
"presets": [
[
"@babel/preset-env",
{
"modules": false,
"targets": [
">0.25%",
"not ie 11",
"not op_mini all"
]
}
]
]
}
ブラウザで見える結果はこのような状態になります。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Riot App</title>
</head>
<body>
<div id="root" is="my-app">
<p>Hello World</p>
<ul>
<li><a href="/#home">home</a></li>
<li><a href="/#hello">hello</a></li>
<li><a href="/#goodbye">goodbye</a></li>
</ul>
</div>
<script src="/scripts/bundle.js"></script>
</body>
</html>
まとめ
registerPreprocessor
で登録した内容に従う為、どんなテンプレートエンジンだろうと、テンプレートエンジンですらなかろうと出来る。
registerPreprocessor
で全ページ共通の処理(ステップ計測とか一律文字置換とか)をさせた上で、いつも通りriotファイルとして扱うことも出来そう。
別の場所からソースを取ってきて置き換えるとかもできると思うので、複数プロジェクトで共通のコンポーネントとかもいける。
夢が広がります。