くらげになりたい。

くらげのようにふわふわ生きたい日曜プログラマなブログ。趣味の備忘録です。

BrickHub/masonでテンプレート(bricks)を作ってみる

前回の続き。オレオレテンプレートの作り方をみてみる(*´ω`*)

公式ドキュメントだとこのあたり。

雛形の作成

$ mason new example
✓ Generated 5 file(s). (34ms)
  created example/brick.yaml
  created example/README.md
  created example/CHANGELOG.md
  created example/LICENSE
  created example/__brick__/HELLO.md

Bricksの構成

mason newを実行するとこれらを作ってくれる。

example/
├── CHANGELOG.md
├── LICENSE
├── README.md
├── __brick__
│   └── HELLO.md
└── brick.yaml

メインは__brick__brick.yamlの2つ。

brick.yaml

brick.yamlはbrickのマニフェストファイル。
名前(name)、説明(description)や受け取る引数(vars)を定義。

name: example
description: A new brick created with the Mason CLI.
version: 0.1.0+1
environment:
  mason: ">=0.1.0-dev.50 <0.1.0"
vars:
  name:
    type: string
    description: Your name
    default: Dash
    prompt: What is your name?

__brick__

テンプレートの本体。HELLO.mdはこんな感じ。

# Hello {{name}}!

テンプレートの記法はmustache

mustache記法

テンプレートの書き方はこのあたりを見るといい感じ。

基本的な書き方

{{! コメント }}

{{! ** 変数 }}
{{name}}   {{! HTMLエスケープあり }}
{{{name}}} {{! HTMLエスケープなし }}


{{! ** 分岐 }}
{{#flag}}
  <b>trueの場合</b>
{{/flag}}
{{^flag}}
  <b>falseの場合</b>
{{/flag}}


{{! ** ループ }}
{{#items}}
  <b>{{name}}</b>
{{/repo}}

{{! ** 関数 }}
{{name.pascalCase()}}

使える関数(Lambdas)はこのあたり。
camelCase,constantCase,dotCaseなどいろいろある。

分岐とループが同じ{{# }}なのでパッと見分かりづらい。。

ファイルパス

ファイル名を動的に決めたい場合は、
ファイル名自体にmustacheを含めればOK

__brick__/{{name.snakeCase()}}.md

ファイル名だけじゃなく、パスを含めて動的に決める場合は、
{{% %}}を利用する。

__brick__/{{% url %}}というファイルがある場合、
mason make app_icon --url path/to/icon.pngとすると、
urlで指定したpath/to/icon.pngというファイルが作成される。

テンプレートの共通化/include

テンプレート内で他のテンプレートを使い回せるPartialsという機能もある。
例えば、ヘッダやフッタは共通化したいときなど。スニペット的なやつ。

{{~ xxx }}というファイルを用意すると他のテンプレートからincludeでき、
実行時に生成の対象にはならない。

├── HELLO.md
├── {{~ footer.md }}
└── {{~ header.md }}

Partialsをincludeするときは{{> xxx }}を使えばOK。
HELLO.mdの例だとこんな感じ。

{{> header.md }}

Hello {{name}}!

{{> footer.md }}

HELLO.md以外でも同じheader/footerを使いたい場合に便利。
もちろん、Partials内でも変数などは利用できる。

hooks

実行前後に追加処理を設定することもできる。

mason new example --hooksという感じに、
--hooksをつけるとhooks/ディレクトリも作成してくれる。

├── __brick__
├── brick.yaml
└── hooks
    ├── post_gen.dart
    ├── pre_gen.dart
    └── pubspec.yaml

pre_gen.dartは実行前の処理。
変数の読み取りや追加などの例。

// pre_gen.dart
import 'dart:io';
import 'package:mason/mason.dart';

void run(HookContext context) {
  // Read vars.
  final name = context.vars['name'];

  // Use the `Logger` instance.
  context.logger.info('Hello $name!');

  // Update vars.
  context.vars['current_year'] = DateTime.now().year;
}

post_gen.dartは実行後の処理。
flutter packages getを実行する例。

// post_gen.dart
import 'dart:io';
import 'package:mason/mason.dart';

Future<void> run(HookContext context) async {
  final progress = context.logger.progress('Installing packages');

  // Run `flutter packages get` after generation.
  await Process.run('flutter', ['packages', 'get']);

  progress.complete();
}

作ったbrickを利用する

他のbrickと同様、mason.yamlに指定すればOK

bricks:
  # 公開されているbrick: brick名を指定
  hello: 0.1.0+1
  # Git上のbrick: URLとパスを指定
  widget:
    git:
      url: https://github.com/felangel/mason.git
      path: bricks/widget
  # ローカルのbrick: パスを指定
  example: bricks/example 

mason addを利用する場合はこんな感じ。

$ mason add example --path bricks/example

おまけ: Bundle

作成したbrickをアプリに含めたり、アップロードしやすくなるよう、
1ファイルにまとめる機能がある。

$ mason new example --hooks
$ mason bundle ./example/ -o ./bundle/
$ ls ./bundle
example.bundle

$ mason bundle ./example/ -t dart -o ./bundle/
$ ls ./bundle
example_bundle.dart

バンドルには2つの形式があり、デフォルトはUniversal。
Dartを利用したい場合は-t dartをつけて実行する。

  • Universal - a platform-agnostic bundle primarily used when publishing to BrickHub
  • Dart - a Dart specific bundle which can be used to programmatically generate code from a brick in other Dart applications

バンドルをもとに戻すときは、mason unbundleを使う。

# Universal Bundle
mason unbundle ./path/to/bundle -o ./path/to/destination/

# Dart Bundle
mason unbundle ./path/to/bundle -t dart -o ./path/to/destination/

また、アプリ内でバンドルを利用する場合は、こんな感じらしい。
./path/to/my/bundle.dart-t dartをつけたバンドル。

import 'package:mason/mason.dart';

import './path/to/my/bundle.dart';

Future<void> main() async {
  // Create a MasonGenerator from the existing bundle.
  final generator = MasonGenerator.fromBundle(myBundle);

  // Generate code based on the bundled brick.
  await generator.generate(...);
}

以上!! なんかオレオレbrickが書ける気がしてきた(*´ω`*)