Go言語でJavaScriptを使ってオリジナルなコマンドシェルを作る（Go言語のアプリを統合する）

# 概要

 [Go言語でJavaScriptを使ってオリジナルなコマンドシェルを作る](https://qiita.com/h6591/items/6dec513868360becd02e) シリーズの第4弾です。


Go言語で作られたアプリやあるライブラリのサンプルなどをこのJavaScript環境に統合することを考えます。アプリによっては出来なかったり、手直しが必要なものがあります。

もともとこのコマンドシェルを作ったの自分が使いたいものを一つのバイナリでかつJavaScriptをコマンド替わりに使おうということです。なので便利な既存のGo製のツールやサンプルを統合しましょう。

前提としてgo moduleで話を進めますので、depなどを使っている方には申し訳ありません。


統合するアプリ

- https://github.com/simeji/jid のサンプル
- https://github.com/mum4k/termdash のDemo
    - [Go言語 コンソールモードでコンソール上にグラフ、ボタン、スクロールテキストを表示するtermdashの紹介](https://qiita.com/h6591/items/2964ec7c8d8e53d5ac0a) として投稿したものです

# 事前準備

モジュール名を`myshell`とします。アプリのルートディレクトリも`myshell`とします。

```sh
> cd myshell
> go mod init myshell
go: creating new go.mod: module myshell

> mkdir cmd
```

いままで作成した`main.go`を`myshell/cmd/main.go`と配置します。

```sh
> cd cmd
> go run main.go
go: finding github.com/dop251/goja latest
go: finding golang.org/x/text/unicode/norm latest
go: finding golang.org/x/text/collate latest
go: finding golang.org/x/text/cases latest
go: finding golang.org/x/text/language latest
go: finding golang.org/x/text/unicode latest
> print(10)
10
undefined
> exit
```

となりまして無事コンパイルできました。

これまで作成した`excel`や`web`のソースを分けるときはには注意してください。`main`パッケージで作成したので`js.Runtime`にアクセスできましたが違うパッケージにするとアクセスできなくなります。

# jidサンプルの統合

https://github.com/simeji/jid/blob/master/cmd/jid/jid.go
のソースを取り込みます。
取り込みますがJavaScriptとの相互使用はしません。あくまでコマンドとしてアプリの起動のみです。
当然アプリなので`main`があるのでそのままではダメです。
まず、関数名などが被らないように固有のパッケージ名にします。今回は`myshell/myjid`というパッケージ名します。ダウンロードしたソース`jid.go`は`myshell/myjid/jid.go`に配置します。

以下に変更したソースを示します。 `//myjid`が付いている箇所が変更箇所です。`os.exit`は全て`return`にしました。`flag.BoolVar`同じパラメータで一度しか実行できないので最初の起動のみにしました。このサンプルのJSONデータは標準入力でしたがJavaScriptからJSON文字列で渡すようにしました。`*goja.Runtime`も渡せばJavaScriptと相互使用ができるでしょう。`main`は`MainJid`としました。

```go:main.go
import	"myshell/myjid"

func initialSetting() *jsRuntime {

	rt.Set("jid",myjid.MainJid)

}

```

```go:jid.go
package myjid //myjid

import (
	"flag"
	"fmt"
	"strings"

	"github.com/simeji/jid"
)

const VERSION = "0.7.6"

var isJidExec = false //myjid

func MainJid(json string) { //myjid
	//content := os.Stdin  //myjid
	content := strings.NewReader(json) //myjid
	var qm bool
	var help bool
	var version bool
	var mono bool
	var pretty bool
	qs := "."

	if !isJidExec { //myjid
		flag.BoolVar(&qm, "q", false, "Output query mode")
		flag.BoolVar(&help, "h", false, "print a help")
		flag.BoolVar(&help, "help", false, "print a help")
		flag.BoolVar(&version, "version", false, "print the version and exit")
		flag.BoolVar(&mono, "M", false, "monochrome output mode")
		flag.BoolVar(&pretty, "p", false, "pretty print json result")
		flag.Parse()
		isJidExec = true //myjid
	} //myjid
	if help {
		flag.Usage()
		fmt.Println(getHelpString())
		//os.Exit(0) //myjid
		return //myjid
	}

	if version {
		fmt.Println(fmt.Sprintf("jid version v%s", VERSION))
		//os.Exit(0)  //myjid
		return //myjid
	}
	args := flag.Args()
	if len(args) > 0 {
		qs = args[0]
	}

	ea := &jid.EngineAttribute{
		DefaultQuery: qs,
		Monochrome:   mono,
		PrettyResult: pretty,
	}

	e, err := jid.NewEngine(content, ea)

	if err != nil {
		fmt.Println(err)
		//os.Exit(1) //myjid
		return //myjid
	}
	//os.Exit(run(e, qm)) //myjid
	run(e, qm) //myjid
}

func run(e jid.EngineInterface, qm bool) int {

	result := e.Run()
	if result.GetError() != nil {
		return 2
	}
	if qm {
		fmt.Printf("%s", result.GetQueryString())
	} else {
		fmt.Printf("%s", result.GetContent())
	}
	return 0
}

func getHelpString() string {
	return `

============ Load JSON from a file ==============

$ jid < file.json

============ With a JSON filter mode =============

TAB / CTRL-I
  Show available items and choice them

CTRL-W
  Delete from the cursor to the start of the word

CTRL-U
  Delete whole query

CTRL-F / Right Arrow
  Move cursor a character to the right

CTRL-B / Left Arrow
  Move cursor a character to the left

CTRL-A
  To the first character of the 'Filter'

CTRL-E
  To the end of the 'Filter'

CTRL-J
  Scroll json buffer 1 line downwards

CTRL-K
  Scroll json buffer 1 line upwards

CTRL-G
  Scroll json buffer to bottom

CTRL-T
  Scroll json buffer to top

CTRL-N
  Scroll json buffer 'Page Down'

CTRL-P
  Scroll json buffer 'Page Up'

CTRL-L
  Change view mode whole json or keys (only object)

ESC
  Hide a candidate box

`
}
```

実行してみます。Windowsの場合は`chcp 65001`にしてUTF-8にしてください。そうしないと`jid`で日本語表示が乱れます。

```javascript
chcp 65001
go run main.go
> doc = [
...>     {a:1, b:"1111111"},
...>     {a:2, b:"あいうえお"}
...> ]
[{"a":1,"b":"1111111"},{"a":2,"b":"あいうえお"}]
> json=JSON.stringify(doc)
[{"a":1,"b":"1111111"},{"a":2,"b":"あいうえお"}]
> jid(json)
```

doc変数はJavaScriptのオブジェクトなので`jid`には直接渡せませんので`JSON.stringify`で文字列にしてから`jid`に渡します。`jid`の画面に切り替わります。文字列を直接渡すときは必ず`a`などの項目名は`"a"`と引用符を付けてください。

```json
[Filter]> .[
[
  {
    "a": 1,
    "b": "1111111"
  },
  {
    "a": 2,
    "b": "あ い う え お "
  }
]
```
```json
[Filter]> .[1]
{
  "a": 2,
  "b": "あ い う え お "
}
```
```json
[Filter]> .[1].b
 "あ い う え お "
```

終了時には`CTRL+U`を押して初期状態に戻して`.q`で終了します。あまり詳しくないので他の操作があるかもしれません。

# termdashdemoデモを統合

パッケージは`myshell/mytermdash`とします。




https://github.com/mum4k/termdash/blob/master/termdashdemo/termdashdemo.go
をダウンロードして`myshell/mytermdash/termdashdemo.go`と配置します。

これに関してはソースの量が多いので変更箇所のみ掲載します。

```go:main.go
import	"myshell/mytermdash"

func initialSetting() *jsRuntime {

	rt.Set("dash",mytermdash.MainTermdash)

}
```

```go:termdashdemo.go
package main
  // を以下に変更
package mytermdash
```

```go:termdashdemo.go
func main() 
  // を以下に変更
func MainTermdash() 
```

これだけの変更で実行できます。

```javascript
go run main.go
> dash()
```

以下の画面に切り替わります。`ESC`を押せば終了します。中央の`text`や`linechart`などのボタンをマウスでクリックしてみてください。画面が変わります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258007/10ec76b5-6752-218a-fa76-02f76e1ca4aa.png)


# まとめ

パッケージを分けることによって関数や変数がバッティングしないようにします。そして`main`を適当な名称に変更してJavaScriptに関係付けます。オリジナルのアプリがコマンドライン引数を必要とする場合は`main`の引数として渡すなりの工夫が必要になります。

いろいろなコマンドを作成したり、既存のアプリを統合したりして自分が使いやすいツールとして作成していきます。Go言語には有用なパッケージが多数存在しますのでどんなツールになるか楽しみです。

また、JavaScriptのRuntimeを複数作って切り替えることも考えていますが、使い道があるかは分かりません。

Go言語パッケージの一覧は下記が有用です。

- [Awesome Go : 素晴らしい Go のフレームワーク・ライブラリ・ソフトウェアの数々](https://qiita.com/hatai/items/f31914f37dc6c53b2bce)
- 上記オリジナル
    - https://github.com/avelino/awesome-go


次回の投稿はこのシリーズの最後として、最初の投稿に書いた`goja`の簡単な改造を説明します。