Edited at

JSer のためのタスクランナー MEMI

More than 1 year has passed since last update.


TL;DR

NPM


  • https://github.com/3846masa/memi

  • JSer のためのタスクランナー MEMI

  • ES module で 1タスク 1Function で書ける

  • 依存関係を自動でインストールしてくれる


なんで作ったのか

きっかけは mimorisuzuko/memi

要約すると,



  • JavaScript で書けるタスクランナーが欲しい


    • Makefile やシェルスクリプトは,凝った処理を書くのが怠い



  • どのディレクトリでも手間なく使いたい


    • Node.js に関係ないディレクトリで node_modulespackage.json を置きたくない



  • タスクで使うモジュールのグローバルインストールは避けたい



    • npm root -g の場所とは別のところに置きたい




既存のタスクランナーと問題点


  • Makefile


    • シェルスクリプトで書かないといけないので,ちょっと複雑なことをしようとすると怠い

    • Windows で make を入れるのが怠い



  • npm scripts



    • package.json を置きたくない



  • gulp


    • 使うモジュールもグローバルかローカルに入れないといけない


    • node_modules を置きたくない

    • グローバルインストールは増やしたくない




JSer のためのタスクランナー MEMI

Memifile.js でタスクを定義できる.

import fs from 'fs-extra';

import path from 'path';
import execa from 'execa';

export default {
async clean() {
await fs.remove(path.join(__dirname, 'dist'));
},
async build() {
await this.clean();
await execa.shell('webpack');
},
};

ネストが気になる人や Default export 排除派は, Named exports でも書ける.

import fs from 'fs-extra';

import path from 'path';
import execa from 'execa';

export async function clean() {
await fs.remove(path.join(__dirname, 'dist'));
}

export async function build() {
await clean();
await execa.shell('webpack');
}

ついでに, CommonJS 方式でも書ける.

const fs = require('fs-extra');

const path = require('path');
const execa = require('execa');

module.exports = {
async clean() {
await fs.remove(path.join(__dirname, 'dist'));
},
async build() {
await this.clean();
await execa.shell('webpack');
},
};

タスクは,memi <taskname> で実行する.例えば, build したいなら memi build とすれば動く.

もちろんサブディレクトリ内にいても,Memifile.js を再帰的に探しに行くので問題ない.

これを応用すると,ホームディレクトリに Memifile.js を入れておくことで,どこでも使えるコマンドも作れる.

memi <taskname> arg1 arg2 ... とすると,Function の引数に arg1 などが渡されるので,ちゃんとしたコマンドを作ることもできる.

Inquirer なども使えるので,対話式のコマンドも難なく作れる.

import inquirer from 'inquirer';

import cowsay from 'cowsay';

export default {
async echo(text) {
if (!text) {
const res = await inquirer.prompt([{
name: 'text',
message: "How's it going?",
}]);
text = res.text;
}
console.log(cowsay.say({ text }));
},
};

さらにここからが MEMI の真骨頂で,依存するモジュールを自動でインストールしてくれる

例えば,上の Memifile.js では, fs-extraexeca がインストールされる.

インストール先は $HOME/.memi%USERPROFILE%\.memi になるので,グローバルと混ざらないし,ローカルに入れる必要もない.


MEMI は 小さいスクリプトを手間なく書き始められて ,出来が良ければ そのままホームディレクトリに突っ込んで,いつでも使える CLI コマンドにできる

気軽にも使えるし,本格的にも使えるタスクランナー.

ぜひ使ってみてほしい.


余談: Qiita API と MEMI

この記事は VSCode から書いて API 経由で投稿したが,それも MEMI でざっくりコマンドを作った.

import 'dotenv/config';

import fs from 'fs-extra';
import fm from 'front-matter';
import axios from 'axios';
import opn from 'opn';

export async function qiita(file) {
if (!file) {
return;
}

const { attributes, body } = fm(await fs.readFile(file, 'utf8'));
const { data: res } = await axios.request({
method: 'post',
url: 'https://qiita.com/api/v2/items',
data: {
body,
title: attributes.title,
tags: attributes.tags.map(name => ({ name })) || [],
private: true,
},
headers: {
Authorization: `Bearer ${process.env.QIITA_TOKEN}`,
'Content-Type': 'application/json',
},
});

console.log(res.url);
opn(res.url);
}

memi qiita filename.md とすれば,投稿されて URL がブラウザで開く.

しっかり作るなら更新機能とかも欲しくなるが,取っ掛かりとしてはこの程度から始めるのでも良い気がする.


余談: MEMI とは

発案者曰く,


柿崎芽実さんはけやき坂46のメンバーで,memiとmakeが似てるから選びました.推しは齊藤京子です.