Windowsにdirenvをインストールしてディレクトリごとの環境変数設定を実現したい

  1. リリースよりdirenv.windows-amd64.exeをダウンロード
  2. direnv.exeにリネームし、C:¥Program Files¥Git¥usr¥binに配置する
  3. Git bashを開いてecho ~でホームディレクトリを確認、ホームディレクトリに.bashrcを以下の内容で作成
   alias direnv="/usr/bin/direnv.exe"
   eval "$(direnv hook bash)"
  1. source .bashrcで再読み込み
    (ここからはオプション、設定できてるか確認)
  2. 適当なディレクトリhogeを作成
  3. hoge以下に.envrcを作成して以下を記述
   export KEY=value
  1. direnv allow.envrcを読み込み
  2. echo $KEYで値が出力されれば完了

Chromeブラウザのユーザープロファイル名を変更する

前提:Alfredのブラウザプロファイル切り替えワークフローを使っている

問題: ユーザープロファイルが適切な名前になっていない

課題: ユーザープロファイルの変更が反映されない

  • ブラウザの設定からだとどうにも変えられない
  • そのほかの手法を試す必要がある

解決手法: コマンドラインから変更できるPythonスクリプトを作成した

ソースコード

大元のAlfredWorkflow書いてる人のコードを参考に作成しました。

参考: 使い方

> python change_profile_name.py -b chrome -n kichinosukey -i 1

JavaScript SyntaxError: The requested module ‘./notes.mjs’ does not provide an export named ‘default’から考える適切なimport/exportの使い方

まずは該当コードを確認しよう。自作モジュール notes.mjsからいくつかの関数をインポートしたかった。

import chalk from "chalk";
import yargs from "yargs";
import notes from "./notes.mjs"

yargs(process.argv.slice(2))
    .usage('Usage: $0 <command> [options]')
    .command('add', 'add note', 
    function (yargs) {
        return yargs
        .option('title', {
            alias: 't',
            default: '',
            demandOption: true})
        .option('body', {
            alias: 'b',
            default: '',
            demandOption: true})
        }, 
    function(argv){
            notes.addNotes(argv.title, argv.body)
            // console.log(`Title: ${argv.title} Body: ${argv.title}`)
        }
    )
    .example('$0 add --title hoge', 'add title "hoge"')
    .help('h')
    .alias('h', 'help')
    .argv;

しかしながらこのコードを実行しようとすると以下エラーが発生。

node app.mjs add --title hoge --body huga
file:///Users/user/node-js-basics/notes-app/app.mjs:3
import notes from "./notes.mjs"
       ^^^^^
SyntaxError: The requested module './notes.mjs' does not provide an export named 'default'
    at ModuleJob._instantiate (node:internal/modules/esm/module_job:127:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:193:5)
    at async Promise.all (index 0)
    at async ESMLoader.import (node:internal/modules/esm/loader:341:24)
    at async loadESM (node:internal/process/esm_loader:88:5)
    at async handleMainPromise (node:internal/modules/run_main:61:12)

Node.js v17.6.0

正直このシンタックスエラーの意味がよくわからなかった。「default」ってなんぞや・・・。

ちなみに自作モジュールがこちら。

import * as fs from 'fs'
import { add } from './utils.mjs'

const getNotes = function() {
    return 'Your notes...'
}

export const addNotes = function(title, body) {
    const notes = loadNotes()
    console.log(notes)
}

const loadNotes = function() {
    try {
        const dataBuffer = fs.readFileSync('notes.json')
        const dataJSON = JSON.toString(dataBuffer)
        return JSON.parse(dataJSON)
    } catch(e) {
        return []
    }
}

そこで我らがMDNドキュメントを見ると目から鱗。importの構文を全く理解できていなかったことが発覚。

import defaultExport from "module-name";
import * as name from "module-name";

先述のコードは上記の1行目と同じ表現になるがこちらは以下のように説明されている。

defaultExport:モジュールからのデフォルトのエクスポートを参照する名前。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/import

つまりexport defaultで宣言したもののみがこの構文でimportできると理解(知らんけど)。

続いて2行目については以下のように説明されている。

name: インポートを参照するとき名前空間のように用いられるモジュールオブジェクトの名前。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/import

つまり任意のモジュールに含まれる要素を任意のオブジェクト名でimportするときはこの表現を使え、と理解(知らんけど)。

答え

冒頭のimprot文の表記をちょこっと変えただけ。

import chalk from "chalk";
import yargs from "yargs";
import * as notes from "./notes.mjs"

併せて読んでおきたい

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/export

Node.js yargsの使い方

yargs

https://www.npmjs.com/package/yargs

yargs-parser

https://www.npmjs.com/package/yargs-parser

yargsとyargs-parserの違い

yargsがyargs-parserを包括している感じでしょうか?実際の挙動を見てみます。

import yargsp from "yargs-parser";
import yargs from "yargs";
import process from "process";

console.log(yargs(process.argv.slice(2)));
console.log(yargs(process.argv.slice(2)).argv);
console.log(yargsp(process.argv.slice(2)));
$ node app.mjs add --title="Things to buy"
YargsInstance {
  customScriptName: false,
  parsed: false,
  '$0': 'app-chap16.mjs',
  argv: [Getter]
}
{ _: [ 'add' ], title: 'Things to buy', '$0': 'app-chap16.mjs' }
{ _: [ 'add' ], title: 'Things to buy' }
  • yargsは引数を与えることでYargInstanceというオブジェクトになります。
  • yargsの引数パース結果はyargs.argvで参照できます
  • yargs-parserはyargsのそれとは若干結果が異なります
    • yargsはパース結果に実行ファイルそのものを含むのに対してyargs-parserは明示的に与えた引数のみを結果に含みます

実装例1. yargsを使った基本的なパース

コマンドラインからコマンドをパースさせます。

yargs(process.argv.slice(2))
    .usage('Usage: $0 <command> [options]')
    .command('count', 'Count the files in a directory', function (yargs) {
        console.log('count items!')
    })
    .command('list', 'list files of a directory:', function (yargs) {
        console.log('list items!')
    })
    .example('$0 count', 'count the lines in the given file')
    .help('h')
    .alias('h', 'help')
    .argv;

以下のコマンドを実行します。想定通りの結果になっているでしょうか?

$ node app.mjs count
count items!

$ node app.mjs list 
list items!

$ node app.mjs --help
Usage: app.mjs <command> [options]

Commands:
  app.mjs count  Count the files in a directory
  app.mjs list   list files of a directory:

Options:
      --version  Show version number                                   [boolean]
  -h, --help     Show help                                             [boolean]

Examples:
  app.mjs count  count the lines in the given file

実装例2. コマンドオプションの設定

countやlistといったコマンドにオプションを追加してやります。以前はbuilderとしてオプションを与えていたみたいですが・・・。

yargs(process.argv.slice(2))
    .usage('Usage: $0 <command> [options]')
    .command('count', 'Count the files in a directory', function (yargs) {
        return yargs.option('directory', {
            alias: 'd',
            default: './'
        })
        console.log('count items!')
    })
    .command('list', 'list files of a directory:', function (yargs) {
        return yargs.option('directory', {
            alias: 'd',
            default: './'
        })
        console.log('list items!')
    })
    .example('$0 count', 'count the lines in the given file')
    .help('h')
    .alias('h', 'help')
    .argv;

以下コマンドを実行してみます。countのみ確認してますが無事オプションは追加されたようです。

$ node app.mjs count --help
app.mjs count

Count the files in a directory

Options:
      --version    Show version number                                 [boolean]
  -h, --help       Show help                                           [boolean]
  -d, --directory                                                [default: "./"]

しかしながらコマンド実行後の挙動が何もないのは寂しいのでコードを変更して実際の挙動を確認していきます。addコマンドのtitleオプションを指定してコンソール上にタイトルを表示させます。

yargs(process.argv.slice(2))
    .usage('Usage: $0 <command> [options]')
    .command('add', 'add note', 
    function (yargs) {
        return yargs.option('title', {
            alias: 't',
            default: '',
            demandOption: true})
        }, 
    function(argv){
        console.log(`Title: ${argv.title}`)
        }
    )
    .example('$0 add --title hoge', 'add title "hoge"')
    .help('h')
    .alias('h', 'help')
    .argv;

以下コマンドを実行していきます。

$ node app.mjs add --help      
app.mjs add

add note

Options:
      --version  Show version number                                   [boolean]
  -h, --help     Show help                                             [boolean]
  -t, --title                                           [required] [default: ""]

$ node app.mjs add --title hoge
Title: hoge

まとめ・所感

  • yargsは多機能パーサー、yargs-parserは単機能パーサーといった理解に落ち着くとします

M1 macでnode.jsの開発環境を整備する

パッケージのインストール

  • node.js

$ brew install node

  • nvm(node version manager)
    • node.jsのバージョンを管理・切り替え

$ brew install nvm

インストール完了後に以下を実施

$ mkdir ~/.nvm # nvmのワーキングディレクトリ
$ vim ~/.zshrc # nvmの環境変数を設定、以下の記述を構成ファイルに追加する

export NVM_DIR=”$HOME/.nvm”
[ -s “/opt/homebrew/opt/nvm/nvm.sh” ] && \. “/opt/homebrew/opt/nvm/nvm.sh” # This loads nvm
[ -s “/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm” ] && \. “/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm” # This loads nvm bash_completion

ただしNVM_DIR は任意の場所に設定できますが、/opt/homebrew/opt/nvm から変更しないままだと、アップグレード/再インストール時に nvm でインストールされた node はすべて壊れてしまう、という警告が出ています。これはどう対応したら良いのだろうか??#TODO

  • npm(node package manager)
    • node.jsのパッケージ管理

$ brew install npm

それぞれインストール完了後にコマンドライン上でnode/nvm/npmと叩いて正しく呼び出せるか確認すると良いでしょう。

ローカルサーバーを立てる

適当なリポジトリを作っておいて公式ドキュメントをもとにコードを写経してみます。

コードを書き終わったら以下コマンドを実行してサーバーを走らせます。

$ node app.js
Server running at http://127.0.0.1:3000/

localhost:3000にアクセスするとHellor Worldが返されます。

デバッグ

先ほどのコードを以下コマンドで実行することでデバッグクライアントが待機するようです。

$ node –inspect app.js
Debugger listening on ws://127.0.0.1:9229/21ab3436-839a-405f-8081-dd3dce1cf83a
For help, see: https://nodejs.org/en/docs/inspector
Server running at http://127.0.0.1:3000/

コマンドのレスポンス1行目がインスペクタクライアントのURLになります。ChromiumベースのWebブラウザでchrome://inspect/#devicesと入力してインスペクタクライアントを開いてサーバーの挙動を確認すると良いかもしれません。

ファイルシステムAPIを使う

htmlを使わない環境下でも使える様々なAPIがあるようです。たとえば以下。

const fs = require('fs');

fs.writeFileSync('notes.txt', 'Hi there!')
  • このrequireはcommonJSというサーバーサイドなどの環境下におけるJSの仕様らしい
    • Wikipedia見る限りそんな主流というわけでもないのか・・・?

ECMAScript形式だと以下のようになる

import * as fs from 'fs';

fs.writeFileSync('notes.txt', 'Hi there!')

ただし上記の形式の場合は以下のように拡張子に”m”をつけて実行する。

$ node app.mjs

外部モジュールの呼び出し

まずはメインファイル

import { name } from './utils.mjs'
console.log(name);

次にutilsファイル

console.log('utils.mjs')
export const name = 'Mike';

以下コマンドを実行するとutils内の関数呼び出しとexportされた変数を参照できていることがわかる。

$ node app.mjs

util.js
Mike

さらに関数のexportも確認する。

console.log('util.js')

export const name = 'Mike';

export const add = function(a, b) {
    return a + b;
};
import { name, add } from './utils.mjs'

console.log(name);

console.log(add(2, 1));

以下コマンドを実行すると関数addを呼び出せていることが確認できる。

$ app.mjs

util.js
Mike
3

気づき(なんでもあり)

  • シングルクォート「’」とバッククォート「`」は文字列の処理において使い分けられる
    • 後者は文字列の変数置換”${hoge}”で使われる
  • requireかimportか
    • commonJSかECMAScriptのどちらに従うかっぽいが後者のやり方がようわからんかった。