3
1

More than 3 years have passed since last update.

Riot.js v4 HTMLのテンプレートエンジンにPugを使う

Posted at

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からこれをどうやって使うのかが分からなかったのでソースを見ました。

/riot/webpack-loader/blob/master/src/index.js
  // compile and generate sourcemaps
  const {code, map} = compile(
    source,
    {
      ...opts,
      file: this.resourcePath
    }
  )

どうやらoptsに指定したものは全部@riotjs/compilerに渡してくれるようです。

というわけで、webpack.config.jsで指定します。

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']
          }
        }
      }
    ]
  }
};
index.js
import {component} from 'riot'
import App from './app.pug'

component(App)(document.getElementById('root'), {
  message: 'Hello World'
});
app.pug
my-app
  p { props.message }

  mixin menu(name)
    li
      a(href="/#" + name) #{name}

  ul
    +menu('home')
    +menu('hello')
    +menu('goodbye')
index.html
<!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>

後は例のごとくお好み。

package.json
{
  "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"
  }
}
.babelrc
{
  "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ファイルとして扱うことも出来そう。
別の場所からソースを取ってきて置き換えるとかもできると思うので、複数プロジェクトで共通のコンポーネントとかもいける。
夢が広がります。

3
1
0

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
3
1