20200123のNode.jsに関する記事は10件です。

Node.jsからFBX SDK Pythonを呼べるDockerイメージ作った

Node.jsからFBX SDK Pythonを呼べるDockerイメージ作った

とある事情により、Node.jsからFBX SDK Pythonを呼ぶ必要があったので、Dockerイメージを作りました。

作ったDockerイメージは以下に公開しました。
https://hub.docker.com/r/seguropus/fbx-sdk-python-nodejs

サンプルコード

サンプルコードを以下に置きます。
https://github.com/segurvita/docker-fbx-sdk-python-nodejs

サンプルコードの使い方

# Dockerイメージをビルド
docker-compose build

# Dockerコンテナを起動
docker-compose up

これで、以下のような表示が出れば成功です。

fbx-sdk-python-nodejs    | # FBX SDK can read the following formats.
fbx-sdk-python-nodejs    | 00 FBX (*.fbx)
fbx-sdk-python-nodejs    | 01 AutoCAD DXF (*.dxf)
fbx-sdk-python-nodejs    | 02 Alias OBJ (*.obj)
fbx-sdk-python-nodejs    | 03 3D Studio 3DS (*.3ds)
fbx-sdk-python-nodejs    | 04 Collada DAE (*.dae)
fbx-sdk-python-nodejs    | 05 Alembic ABC (*.abc)
fbx-sdk-python-nodejs    | 06 Biovision BVH (*.bvh)
fbx-sdk-python-nodejs    | 07 Motion Analysis HTR (*.htr)      
fbx-sdk-python-nodejs    | 08 Motion Analysis TRC (*.trc)      
fbx-sdk-python-nodejs    | 09 Acclaim ASF (*.asf)
fbx-sdk-python-nodejs    | 10 Acclaim AMC (*.amc)
fbx-sdk-python-nodejs    | 11 Vicon C3D (*.c3d)
fbx-sdk-python-nodejs    | 12 Adaptive Optics AOA (*.aoa)      
fbx-sdk-python-nodejs    | 13 Superfluo MCD (*.mcd)
fbx-sdk-python-nodejs    | 14 (*.zip)
fbx-sdk-python-nodejs exited with code 0

表示されているのは、FBX SDKが読み込み可能なファイル形式の一覧です。

これが表示されたということは、無事にFBX SDK Pythonにアクセスできているということになります。

何が起きてるのか?

まず、 docker-compose up でDockerコンテナーが起動します。

Dockerコンテナーは、起動したらNode.jsのコード index.js を実行します。

index.js はPythonのコード main.py を呼びます。

main.py はFBX SDK Pythonから対応フォーマット一覧を取得して、表示します。

Dockerfile

PythonとNode.jsが同居したDockerイメージを公開している方がいたので、そちらをもとに Dockerfile を作成してみました。

Dockerfile
# Python 2.7とNode.js 12が入ったAlpine
FROM nikolaik/python-nodejs:python2.7-nodejs12-alpine

# apkでライブラリ更新
RUN apk update && \
    apk add \
    curl \
    libxml2 \
    libstdc++

# FBX SDKをダウンロード
RUN curl -L \
    https://damassets.autodesk.net/content/dam/autodesk/www/adn/fbx/20195/fbx20195_fbxpythonsdk_linux.tar.gz \
    -o /tmp/fbx20195_fbxpythonsdk_linux.tar.gz

# インストール先フォルダ作成
RUN mkdir -p /python-fbx/install

# FBX SDKを解凍
RUN tar -zxvf \
    /tmp/fbx20195_fbxpythonsdk_linux.tar.gz \
    -C /python-fbx && \
    printf "yes\nn" | \
    /python-fbx/fbx20195_fbxpythonsdk_linux \
    /python-fbx/install

# FBX SDKをインストール
RUN cp /python-fbx/install/lib/Python27_ucs4_x64/* \
    /usr/local/lib/python2.7/site-packages/

# python-shellをインストール
RUN npm install -g python-shell

# 一時ファイルを削除
RUN rm -r /python-fbx
RUN rm /tmp/fbx20195_fbxpythonsdk_linux.tar.gz

# 環境変数NODE_PATHを設定
ENV NODE_PATH /usr/local/lib/node_modules

python-shell というのは、Node.jsからPythonを呼ぶためのライブラリです。

グローバル領域にインストールしたので、index.jsが require で取得できるように、環境変数 NODE_PATH を設定しています。

docker-compose.yml

docker-compose.yml はこんな感じです。

docker-compose.yml
version: '3'
services:
  fbx-sdk-python-nodejs:
    image: 'seguropus/fbx-sdk-python-nodejs'
    container_name: 'fbx-sdk-python-nodejs'
    build:
      context: ./
      dockerfile: ./Dockerfile
    volumes:
      - .:/src
    working_dir: /src
    command: node index.js

Dockerコンテナー起動時に index.js が実行されます。

index.js

index.jsはこんな感じです。

index.js
const pythonShell = require('python-shell');

// python-shellのオプション
const pyOption = {
    mode: 'text',
    pythonPath: '/usr/local/bin/python',
    pythonOptions: ['-u'],
    scriptPath: '/src',
}

// main.pyを実行
const pyShell = new pythonShell.PythonShell('main.py', pyOption);

// Pythonの標準出力を表示
pyShell.on('message', (message) => {
    console.log(message);
});

// 終了処理
pyShell.end(function (err, code, signal) {
    if (err) {
        console.error(err);
    }
    console.log('The exit code was: ' + code);
});

python-shellmain.py を呼んでいます。

main.py

main.py はこんな感じです。

main.py
from fbx import *


def list_reader_format(manager):
    print('# FBX SDK can read the following formats.')
    for formatIndex in range(manager.GetIOPluginRegistry().GetReaderFormatCount()):
        description = manager.GetIOPluginRegistry().GetReaderFormatDescription(formatIndex)
        print(formatIndex, description)


def main():
    # Create
    manager = FbxManager.Create()
    scene = FbxScene.Create(manager, "fbxScene")

    # List
    list_reader_format(manager)

    # Destroy
    scene.Destroy()
    manager.Destroy()


if __name__ == '__main__':
    main()

from fbx import * でFBX SDK Pythonを読み込んでいます。
list_reader_format() という関数で、FBX SDK Pythonが読み込み可能なファイル形式の一覧を標準出力に表示しています。

無事にNode.jsからFBX SDK Pythonを呼べるDockerイメージを作ることができました!

Docker Hubに公開してみる

せっかくなので、Docker Hubに公開してみます。

# ログインする
docker login

# Dockerイメージをプッシュする
docker push seguropus/fbx-sdk-python-nodejs

以下に公開されました。
https://hub.docker.com/r/seguropus/fbx-sdk-python-nodejs

さいごに

本記事作成にあたり、以下のページを参考にしました。ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Node.js]モジュール定義について整理(exports-require / export-import)

はじめに

あるファイルに定義した関数等を別のファイルで使いたいときにどうするか。

Node.jsでは二つのやり方がある。

  1. exportsで公開してrequireで読み込む(CommonJS)
  2. exportで公開してimportで読み込む(ES2015)

常に使えるのは1の方法。Babelを使うなら2の方法でも可能。
どちらを選んでもメリット・デメリットがあるわけではない(と思う)ので、お好きなほうで。

個人的には、常に使える1の方法がいいような気がしている。

使用例

exportsで公開してrequireで読み込む(CommonJS)

一つの関数だけをエクスポート

module.js
// 関数を定義
const f = () => {
    console.log('Hello!')
}

// 関数を公開
module.exports = f
client.js
const f = require('./module')
f()  // Hello!

エクスポートするのは関数に限らず、文字列や数値でも良い。

複数の関数をエクスポート

module.js
// 関数を定義
const f1 = () => console.log('Hello!')
const f2 = () => console.log('World!')

// 別名をつけて、二つの関数を公開
module.exports = {
    sayHello: f1,
    sayWorld: f2
}
client.js
const mod = require('./module')
mod.sayHello()  // Hello!
mod.sayWorld() // World!

クラスをエクスポート

module.js
// クラスを定義
class Person {
    constructor(name) {
        this.name = name
    }

    greet(params) {
        console.log(`Hello, ${this.name}!`)
    }
}

// 公開
module.exports = Person
client.js
const Person = require('./module')
p = new Person('aki')
p.greet() // Hello, aki!

おまけ:選択してインポート

module.js
const f1 = () => console.log('Hello!')
const f2 = () => console.log('World!')

module.exports = {
    sayHello: f1,
    sayWorld: f2
}
client.js
// インポートする対象を選択する
const {sayHello} = require('./module')
sayHello() // Hello!

exportで公開してimportで読み込む(ES2015)

下記の参考資料を参照。いつか書く。

参考資料

1. exports - require(CommonJS)

2. export - import((ES2015))

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[JavaScript][Node.js]メモ:アロー関数の文法

アロー関数(ES2015)の書き方。

f = () => console.log('Hello!')
f()  // Hello!
評価した値を返す
// 式(Expressions)の評価結果が戻り値になる
sum = (a, b) => a + b
r = sum(1, 2)
console.log(r) // 3
returnで値を返す
// {}で文(Statements)を作り、returnで値を返す
sum = (a, b) => { 
  return a + b 
}
r = sum(1, 2)
console.log(r) // 3
引数のカッコを省略
// 引数が一つの時のみカッコを省略可能(0個もしくは2つ以上のときは省略不可)
say = word => console.log(word + '!')
say('Yeah')  // Yeah!
即時関数
(() => console.log('Hello!'))() 
引数としてのアロー関数
arr = [1,2,3,4,5]

// かっこよくない
console.log(arr.map(function(e) { return e + 1 }))  // [ 2, 3, 4, 5, 6 ]

// かっこいい
console.log(arr.map(e => e + 1))  // [ 2, 3, 4, 5, 6 ]

リンク(mozilla)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[JavaScript][Node.js]アロー関数のサンプル集

アロー関数(ES2015)ってかっこいいですよね。

ふつうに
f = () => (console.log('Hello!'))
f()  // Hello!
カッコは省略可能
f = () => console.log('Hello!')
f()  // Hello!
returnの省略
// 一行の時だけ
sum = (a, b) => a + b
r = sum(1, 2)
console.log(r) // 3
returnの省略(NGパターン)
// {}で括ると省略できない。()ならOK。
sum = (a, b) => { a + b }
r = sum(1, 2)
console.log(r) // undefined
複数行
// returnは必須
sum = (a, b) => { 
  return a + b 
}
r = sum(1, 2)
console.log(r) // 3
引数のカッコを省略
// 引数が一つの時のみカッコを省略可能(0個もしくは2つ以上のときは省略不可)
say = word => console.log(word + '!')
say('Yeah')  // Yeah!
即時関数
(() => console.log('Hello!'))() 
引数としてのアロー関数
arr = [1,2,3,4,5]

// かっこよくない
console.log(arr.map(function(e) { return e + 1 }))  // [ 2, 3, 4, 5, 6 ]

// かっこいい
console.log(arr.map(e => e + 1))  // [ 2, 3, 4, 5, 6 ]

リンク(mozilla)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[JavaScript][Node.js]アロー関数のサンプル集

アロー関数(ES2015)ってかっこいいですよね。

ふつうに
f = () => (console.log('Hello!'))
f()  // Hello!
カッコは省略可能
f = () => console.log('Hello!')
f()  // Hello!
returnの省略
// 一行の時だけ
sum = (a, b) => a + b
r = sum(1, 2)
console.log(r) // 3
returnの省略(NGパターン)
// {}で括ると省略できない。()ならOK。
sum = (a, b) => { a + b }
r = sum(1, 2)
console.log(r) // undefined
複数行
// returnは必須
sum = (a, b) => { 
  return a + b 
}
r = sum(1, 2)
console.log(r) // 3
引数のカッコを省略
// 引数が一つの時のみカッコを省略可能(0個もしくは2つ以上のときは省略不可)
say = word => console.log(word + '!')
say('Yeah')  // Yeah!
即時関数
(() => console.log('Hello!'))() 
引数としてのアロー関数
arr = [1,2,3,4,5]

// かっこよくない
console.log(arr.map(function(e) { return e + 1 }))  // [ 2, 3, 4, 5, 6 ]

// かっこいい
console.log(arr.map(e => e + 1))  // [ 2, 3, 4, 5, 6 ]

リンク(mozilla)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Typescript Node.js メソッドの結果をキャッシュす

背景

APIの結果を指定した時間キャッシュさせたかったので、シンプルにキャッシュできるクラスを自前で実装する。

使用ライブラリ

  • ioredis
  • zlib

実装

import ioredis from 'ioredis'
import zlib from 'zlib'

class Client {
  private redis: ioredis.Redis = new ioredis('redis://exammple:6379')

  async fetch<T>(key: string, func: () => Promise<T>, expire): Promise<T> {
    const cached = await this.redis.get(key)
    // キャッシュがあれば、解凍して返却
    if (cached) {
      const cacheString = await new Promise<string>((resolve, reject) =>
        zlib.inflate(Buffer.from(cached, 'base64'), (err, buffer) => {
          if (err) return reject(err)
          resolve(buffer.toString())
        })
      )
      return JSON.parse(cacheString)
    }
    // キャッシュがなければ、メソッドの結果を圧縮してredisに保存
    const result = await func()
    const cache = await new Promise<string>((resolve, reject) =>
      zlib.deflate(JSON.stringify(result), (err, buffer) => {
        if (err) return reject(err)
        resolve(buffer.toString('base64'))
      })
    )
    await this.redis.setex(key, expire, cache)
    return result
  }
}

export const cacheClient = new Client()

使用例

await cacheClient.fetch('cache-key', () => { heavyTask() }, 3000)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Typescript Node.js メソッドの結果をキャッシュする

背景

APIの結果を指定した時間キャッシュさせたかったので、シンプルにキャッシュできるクラスを自前で実装する。

使用ライブラリ

  • ioredis
  • zlib

実装

import ioredis from 'ioredis'
import zlib from 'zlib'

class Client {
  private redis: ioredis.Redis = new ioredis('redis://exammple:6379')

  async fetch<T>(key: string, func: () => Promise<T>, expire): Promise<T> {
    const cached = await this.redis.get(key)
    // キャッシュがあれば、解凍して返却
    if (cached) {
      const cacheString = await new Promise<string>((resolve, reject) =>
        zlib.inflate(Buffer.from(cached, 'base64'), (err, buffer) => {
          if (err) return reject(err)
          resolve(buffer.toString())
        })
      )
      return JSON.parse(cacheString)
    }
    // キャッシュがなければ、メソッドの結果を圧縮してredisに保存
    const result = await func()
    const cache = await new Promise<string>((resolve, reject) =>
      zlib.deflate(JSON.stringify(result), (err, buffer) => {
        if (err) return reject(err)
        resolve(buffer.toString('base64'))
      })
    )
    await this.redis.setex(key, expire, cache)
    return result
  }
}

export const cacheClient = new Client()

使用例

await cacheClient.fetch('cache-key', () => { heavyTask() }, 3000)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.jsとDynamoDBで日時データの処理

DynamoDBに日時データを持たせる2つの方法

  1. データ型をStringにして2016-02-152015-12-21T17:42:34Zのように文字列で持たせる。
  2. データ型をNumberにして1579740176030のように数値で持たせる。

2.項のNumber型の実用例としては、エポック時間 (1970 年 1 月 1 日の 00:00:00 UTC 以降の秒数) を利用することができる。(UNIXTIMEの詳細)

Node.jsでエポック時間を扱う

現在の日時をDateオブジェクトで取得する

const date = new Date();
console.log(date);   // 2020-01-23T01:09:41.444Z
console.log(typeof date);    // 'object'

現在の日時をエポック時間で取得する

const date = Date.now();
console.log(date);   // 1579740176030
console.log(typeof date);    // 'number'

DynamoDBから取得したエポック時間をDateオブジェクトへ変換する

const unixtime = 1579740176030;  // DynamoDBから取得したエポック時間と想定
const date = new Date(unixtime);
console.log(date);   // 2020-01-23T01:09:41.444Z
console.log(typeof date);    // 'object'
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.js、Web Speech APIを使って音声認識を出力

概要

WebSpeechAPIを使用して聞き取った音声の文字おこしをブラウザ上に表示させます。

作成方法

1.WebSpeechAPIを含むhtmlフォルダの作成

新規フォルダを作成し、その中にindex.htmlを作成。

index.html
<!-- index_voice.html -->

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>音声認識サンプル</title>
</head>
<body>
    <h2>音声認識サンプル</h2>
    <button id="btn">start</button>
    <div id="content"></div>

<script>
//ここに音声認識の処理を書いていく

</script>
<body>
<html>

音声認識をするための処理をscriptタグ内に書いていきます。

index.html
//ここに音声認識の処理を書いていく
const speech = new webkitSpeechRecognition();
speech.lang = 'ja-JP';
</script>

音声認識を実行する準備はこの2行で完了です。
webkitSpeechRecognition()を定義し、langをja-JPにすることで日本語に対応したWeb Speech APIが使えるようになります。
そしてこれをイベントで実行出来るようにしていきます。

index.html
//ここに音声認識の処理を書いていく
const speech = new webkitSpeechRecognition();
speech.lang = 'ja-JP';

//---------------追記---------------//
const btn = document.getElementById('btn');
const content = document.getElementById('content');

btn.addEventListener('click' , function() {
    // 音声認識をスタート
    speech.start();
});

speech.addEventListener('result' , function(e) {
   // 音声認識で取得した情報を、コンソール画面に表示
   console.log(e);
//---------------追記---------------//
    // 音声認識で取得した情報を、HTMLに表示
    const text = e.results[0][0].transcript;
    content.innerText = text;
    //--------------------------------//
});
//--------------------------------//

</script>

2.サーバー立ち上げ
node.js Expressを使ってローカルサーバーの立ち上げを行います。
Web speech Api-sampleというフォルダー作成します。

初期設定します。

npm init -y

必要なライブラリをインストールします。

npm i body-parser express

index.jsを作成し、こちらのコードをコピペします。

var express = require('express');
var app = express();

// public というフォルダに入れられた静的ファイルはそのまま表示
app.use(express.static(__dirname + '/public'));

// bodyParser
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.post('/post', function(req, res) {
  for (key in req.body) {
    console.log(key, '=', req.body[key]);
  }
  res.end();
});
app.listen(process.env.PORT || 8080);

console.log("server start! (heroku)");

こちらをindex.jsで保存します。
index.js と同じ階層に public フォルダを作りその中に 先ほど作成したindex.html を格納します。

node index.js

実行します。

http://localhost:8080/ でアクセス。
スタートボタンをおして話し、終わりにまたボタンを押すと。
音声認識.PNG

これでひとまず完了です。次に使用してHerokuにデプロイしていきます。(続く)

考察

まずは音声認識APIをつかってアウトプットすることができました。音声の聞き取りの精度もパソコンを目の前にあるような状況、会議とかだったら問題なく拾える制度でした。
次は、Vue.jsでデザインを整えていきます。
また、いまのままではただの文字の羅列になるので、これをどう編集させていくか、プログラムで機能を追加できていければと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WSL と VSCode と Windows Terminal でコマンドプロンプトにさようなら

macOS から Windows に戻ってきて、「コマンドプロンプトかー、せめて PowerShell だよな」ということで PowerShell Core を使っていたのですが、そういえば WSL(Windows Subsystem for Linux) があったな、と思い出して、開発環境は全面的に WSL を使っていくことにしましたので、その手順をメモしておきます。

なおここで言う開発環境とは、Webアプリのフロントエンドを Angular で、バックエンドを node.js で開発することを指します(また、実際のところ Linux にはあまり詳しくないので、WSL とディストリとシェルを混同して表記している自信があります)。

1. WSL の導入

を参考に、WSL を導入します。

2. Windows Terminal の導入

Windows Terminal は Windows 向けのターミナルクライアントで、現在はまだ Preview 版ですが、なかなか使いやすくカスタマイズ性も高いので愛用しています。

WSL をインストールすると、Windows Terminal にも「Ubuntu」という Profile が増えます。
Windows Terminal を起動したときに、Ubuntu の Terminal がデフォルトで開かれるようにするには、「ctrl + ,」を押して profiles.json を開き、defaultProfile を変更します。

他にも、

  • colorScheme を設定して見やすい色に変更
  • startingDirectory で初期のディレクトリを設定
  • keybindings を追加して ctrl+cctrl+v でコピペできるように(既定では ctrl+shift+c/v)

を変更しています。Windows Terminal はこの profiles.json を編集することでいろいろカスタマイズできるので楽しいですね。参考までに私の設定を載せます。

profiles.json

{
    "$schema": "https://aka.ms/terminal-profiles-schema",

    "defaultProfile": "{2c4de342-38b7-51cf-b940-2309a097f518}",

    "profiles":
    [
        <中略>
        {
            "guid": "{2c4de342-38b7-51cf-b940-2309a097f518}",
            "hidden": false,
            "name": "Ubuntu",
            "colorScheme": "Campbell",
            "cursorShape": "emptyBox",
            "acrylicOpacity": 0.85,
            "useAcrylic": true,
            "cursorColor": "#FFFFFF",
            "fontFace": "Cascadia",
            "fontSize": 12,
            "startingDirectory": "C:\\dev",
            "source": "Windows.Terminal.Wsl"
        }
    ],
    "schemes": [
        {
            "name": "Campbell",
            "foreground": "#F2F2F2",
            "background": "#0C0C0C",
            "colors": [
                "#0C0C0C",
                "#C50F1F",
                "#13A10E",
                "#C19C00",
                "#0037DA",
                "#881798",
                "#3A96DD",
                "#CCCCCC",
                "#767676",
                "#E74856",
                "#16C60C",
                "#F9F1A5",
                "#3B78FF",
                "#B4009E",
                "#61D6D6",
                "#F2F2F2"
            ]
        }
<中略>
    ],

    // Add any keybinding overrides to this array.
    // To unbind a default keybinding, set the command to "unbound"
    "keybindings": [
        { "command": "copy", "keys": [ "ctrl+c" ] },
        { "command": "paste", "keys": [ "ctrl+v" ] }
    ]
}

3. VScode の Default Terminal を WSL に

WSL を導入すると Visual Studio Code の Terminal にも "wsl" が増えているので、ctrl + shift + pTerminal:Select default shell で、"wsl" に変更します。

なお、.code-workspace に Terminal を指定することでワークスペース毎に Terminal を切り替えることもできるようですが、wsl が入っていない環境もあるので、私はあまりオススメしません。

4. VSCode のタスク実行を WSL で

VScode の Default Terminal を WSL に変更しても、VScode のタスクの実行は、Windows 側で行われてしまうようで、これも WSL 上で実行させたいです。残念ながらこれを解決するには「WSL に依存したタスク」の記述が必要になります。

./vscode/tasks.json

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "ng-serve",
      "type": "shell",
      "isBackground": true,
      "command": "ng serve",
      "problemMatcher": {
        "owner": "custom",
        "pattern": {
            "regexp": "^$"
        },
        "background": {
          "activeOnStart": true,
          "beginsPattern": ".*Angular Live Development Server.*",
          "endsPattern": ".*Compiled successfully.*"
        }
      }
    },
    {
      "label": "ng-serve-wsl",
      "type": "shell",
      "isBackground": true,
      "command": "\"ng serve\"",
      "options": {
        "shell": {
          "executable": "C:\\Windows\\System32\\wsl.exe",
          "args": [ "bash -ic" ]
        }
      },
      "problemMatcher": {
        "owner": "custom",
        "pattern": {
            "regexp": "^$"
        },
        "background": {
          "activeOnStart": true,
          "beginsPattern": ".*Angular Live Development Server.*",
          "endsPattern": ".*Compiled successfully.*"
        }
      }
    }
  ]
}

例えば、上記の tasks.json の例では、Angular のデバッグを行うための ng-serve というタスクを定義していますが、これは Windows 上で動作してしまうので、それに加えて WSL 上で動作させる設定を加えた ng-serve-wsl というタスクも用意しています。

WSL版の違いは、options: { } で設定した wsl.exe の情報で、これはコマンドプロンプトで、

wsl.exe bash -ic "ng serve"

を実行することに相当します。
重要なのは bash の実行引数 -i で、これを指定しないと .bashrc が読み込まれない(= PATH が設定されない)ため、ng が command not found エラーになってしまいます。

5. WSL 側に開発環境を構築する

git, node, npm, angular, python, aws-cli, firebase-cli などの開発に必要なツールは、WSL の方に入れていきます。
Ubuntu であれば大抵は apt-get でインストールします。

まとめとちょっと面倒(冗長)なところ

WSL2 まで待っていようかなと思っていたのですが、WSL1 でも使ってみたらとても便利でした。

開発時によく使う Terminal, VSCode については、ほとんど Windows である事を意識せずに過ごす事ができるようになりました。
スクリプトなどは macOS と同じものがほぼ動きますし、Linux で動作する CI のリハーサルも容易になりました。

Windows との相互運用性が思っていたよりも高く、コマンドに .exe をつければ WSL 上からでも Windows コマンドが実行できるので、かゆいところに手が届いてるなと感じました(WSL から msbuild.exe を叩けば Windows でしか動作しない .NET アプリもビルドできるはずだし、そもそも WSL 側に .NET Core を入れてクロスプラットフォームで動作する .NET プログラムの動作確認をする事ももちろん可能です)。

ちょっと冗長なところとしては、

git の認証情報を Win/WSL どちらにも記憶させる必要がある

bash で git コマンドを叩くこともあれば、Gitクライアントアプリ(GitKraken や VSCode など)を使うこともあり、認証情報ストアを共通にする方法は知らないので、今のところ両方に記憶させています。

Windows パスから UNIX パスへの変換

「Windows のエクスプローラでパス名をコピーして、bash で cd する」ために、次のようなコマンドが必要です。

cd $(wslpath -u "C:\dev\hoge")

Windows のパス文字列を wslpath 関数で UNIX パスに変換できるので、これを咬ませてディレクトリ移動しています。

チームの他のメンバは Windows の人が多い

ため、彼らの環境でも動作する事を前提にスクリプトを記述する必要がある場合、その動作確認のために PowerShell も併用し、結局 Windows 側にも node をインストールせざるを得なかったりします。
まあ、これはチームの事情です。
それでも、Windows Terminal は、タブで WSL と PowerShell どちらも管理できるので使いやすいです。

別解というか本命

VSCode 前提ですが、Remote - WSL Extension を利用することで、プロジェクトの開発をすべて WSL上 で行えるようになります。上記で説明した Terminal も、タスクの実行もすべてです。

現在はまだ Preivew 版のため安定性が不安であること、リモート前提なため WSL でもなんか遅そう(主観です)な事、VSCodeの拡張機能がモノによってはリモート用をさらにインストールする必要があるなど、やや気になる点があるために少し試しただけで保留としました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む