モチベーション
最近GitHubが対応したことで一気に熱を帯びているmermeidだが、
PlantUMLからmermeidへの変換器が意外と落ちていなかった。
そこで、今回はPlantUMLからmermeidへの変換器を自作してみることにした。
(こちらの内容は自分のブログでも公開していますのでぜひご覧下さい。変換器はこちらでリリース予定です。)
言葉の説明
PlantUML
コードベースで作画ができるツール。古くから使われていて慣れ親しんでいる人も多いはず。
ER図、シーケンス図など, 様々な図の作成ができる。
PlantUML
mermeid
mermeidはpumlと同様、テキストベースで作図ができるライブラリである。
表現力はPlantUMLに比べ少し劣る印象ではあるものの、
javascript製のライブラリであるため、PlantUMLに比べ環境構築が容易で、web系のツールと親和性も良さそう。
(NotionやGithubが対応したのもそういった背景があるのかもしれない。)
mermeid
PlantUMLはかなり表現力のあるツールなので、今回はまずER図に絞って変換器を実装してみることにした。
両者の構文の比較
Entityの変換
Entityの書き方に対して、両者を比較してみる。
PlantUML
entity "Entity01" as e01 {
*e1_id : number <<generated>>
--
*name : text
description : text
}
mermeid
Entity01 {
e1_id number
name text
description text
}
mermeidの場合、entityであることを宣言する必要がなく、カラムもタイプと命名の2つだけでかなりシンプルだ。
さらにentityに対して代替の命名ができないことも特徴(これがRelation変換部にも関わってくる。)
Relationの変換
Entityと同じく両者を比較してみる
PlantUML
e01 ||..o{ e02
e01 |o..o{ e03
mermeid
Entity01 ||--o{ Entity02 : places
Entity01 |o--o{ Entity03 : places
まず、mermeidにはentityに対して命名を割り当てる機構がないため
名付けが変わっている場合は元のものに置き換えなくてはならない。
またそれぞれのリレーションに対しては、説明を割り当てなくてはならない。
(上記でいうところの"places"の部分)
さらに(細かな違いではあるが)リレーションに対して『..』は使えないようだ。
(PlantUMLの場合は破線が表現できる。)
こちらは使えるようです🙇♂️
その他
mermeidの場合、erDiagram
という文を一番最初に入れ、ER図であることを明示的に宣言しなくてはいけないようである。
(PlantUMLに比べ文法が簡潔になっているのもこのためだろう。)
コード
完成したコードがこちら(今回はwebでの公開を意識してnodeで書いてみた)
const fs = require('fs')
const text = fs.readFileSync("puml.txt", 'utf8')
const lines = text.toString().split('\n')
let mermeid_text = 'erDiagram\n'
const entity_name_array = []
for (const line of lines) {
if(line.includes('entity')){ // entity開始
const entity_name = line.split(' ')[1].replace(/[\"]/g,"")
if(line.includes('as')){ //entityに対して命名がなされている場合
const entity_replaced_name = line.split('as')[1].replace('{',"").replaceAll(' ',"")
entity_name_array.push([entity_name, entity_replaced_name])
}
mermeid_text += `${entity_name} {\n`
} else if (line.includes(':')){ // カラム
columns = line.split(':').map((column) => column.trim())
column_text = ''
if(columns.length == 1){
column_text = `string ${columns[0].replace('*', '')}`
}else{
column_text = `${columns[0].replace('*', '')} ${columns[1].split(' ')[0]}`
}
mermeid_text += ` ${column_text}\n`
} else if(line == '}'){ //entity終了
mermeid_text += '}\n'
} else if (line.includes('--') && line.length <= 4){
// 不要なので無視
} else if (line.match(new RegExp('[o|}]{0,2}(..|--)[o|{]{0,2}')) != null) { //relation
new_relation = `${line.replace('..', '--')} : places\n`
entity_name_array.forEach((entity_name_el) => {
new_relation = new_relation.replace(entity_name_el[1], entity_name_el[0])
})
mermeid_text += new_relation
} else {
mermeid_text += `${line.replace('..', '--')}\n`
}
}
// 書き込み
fs.writeFile("mermeid.txt", mermeid_text, (err) => {
if (err) throw err
console.log('正常に書き込みが完了しました')
});
今後やろうとしていること
package文への対応
PlantUMLのER図ではpackageの作成ができるが、mermeidは未対応のよう。
したがってPlantUMLにpackageに関する文があった場合、それを取り除く必要がある。
が、ここまでくると1行ごとの文解析では間に合わないので、良い解析の仕方がないかを模索中。
webでの公開
上記の通りmermeidはwebとの親和性が高い。
せっかくなので、puml to mermeidの変換器をwebで公開してみようと思う。
ER図以外への対応
こちらも上述の通り、PlantUMLは ER図以外にもシーケンス図など様々な図をサポートしている。
余裕があればこれらの図に関しても変換器を作ってみたい。