npm code reading

rake --tasks (-T) のようにpackage.jsonのタスクを一覧で見たかったのですが
用意されていないと思い込んでしまい、じゃ作るかと思い立ってコードを書いてみました。

まずはnpm code reading

bin/npm

まずは実行ファイル。npm-cli.js を実行しているだけのようです。

// .nodebrew/current/bin/npm

NPM_CLI_JS="$basedir/node_modules/npm/bin/npm-cli.js"

"$NODE_EXE" "$NPM_CLI_JS" "$@"

bin/npm-cli.js

npm ライブラリを使用してコマンド実行しているようです。

//.nodebrew/node/v4.2.4/lib/node_modules/npm/bin/npm-cli.js
var npm = require('../lib/npm.js')

  npm.load(conf, function (er) {
    if (er) return errorHandler(er)
    npm.commands[npm.command](npm.argv, errorHandler)
  })

lib/npm.js

ここでコマンドやエイリアスの設定をしています。
そして各コマンド毎にrequireしています。
drefの中でエイリアスに相当するコマンド名を抜き出してrequire。

// .nodebrew/node/v4.2.4/lib/node_modules/npm/lib/npm.js

  var aliases = {
    'rm': 'uninstall',

  var cmdList = [
    'install',

// 183行目
      var a = npm.deref(c)

// 191行目
      var cmd = require(__dirname + '/' + a + '.js')

作ってみる

package.jsonを読み込むところは他のコマンドを参考に書いてみました。 lib/npm.js にコマンドとエイリアスを追加し、以下のコマンド(ls-script)として作成。

// .nodebrew/node/v4.2.4/lib/node_modules/npm/lib/ls-script.js
// show the scripts of packages

module.exports = exports = lsScript

var path = require('path')
var npm = require('./npm.js')
var readJson = require('read-package-json')

lsScript.usage = 'npm ls-script' +
    '\n\naliases: tasks'

function lsScript (args, cb) {
  readJson(path.join(npm.localPrefix, 'package.json'), function (er, d) {
    if (er && er.code !== 'ENOENT' && er.code !== 'ENOTDIR') return cb(er)
    d = d || {}
    var scripts = Object.keys(d.scripts || {})
    var maxLen =  Math.max.apply(Math, scripts.map(function (el) { return el.length }))
    scripts.forEach(function (script){
      var padding = Array(maxLen - script.length + 1).join(' ')
      console.log(script + padding + ' # ' + d.scripts[script]);
    })
    cb();
  })
}

指定したサイズの文字列を生成するのにArray#joinを使っています。jsはこの辺面倒くさいですね。
 
実行結果はこんな感じ。

$ npm ls-script                                                                                                                                     ⏎
build # git archive --format=zip HEAD --worktree-attributes > build/hl-switcher.zip
test  # ./node_modules/karma/bin/karma start

実はrun-scriptで出来た

一旦満足する結果が出来たので、PRでも出そうかとissueをググったりしていたところ
list npm run-able scripts · Issue #4888 · npm/npm
を見つけました。
run-script (run) で出来たんですね。というか、絶対あるよな。
久しぶりにnpmさわったせいですっかり忘れていました。

せっかくなのでrun-scriptのcode reading

引数なしの場合、list を実行しています。
そして、3つのoptionに対応していました。

// .nodebrew/node/v4.2.4/lib/node_modules/npm/lib/run-script.js

    if (log.level === 'silent') {
      return cb(null, allScripts)
    }

    if (npm.config.get('json')) {
      console.log(JSON.stringify(d.scripts || {}, null, 2))
      return cb(null, allScripts)
    }

    if (npm.config.get('parseable')) {
      allScripts.forEach(function (script) {
        console.log(script + ':' + d.scripts[script])
      })
      return cb(null, allScripts)
    }
# オプションなし
$ npm run
Lifecycle scripts included in hl-switcher:
  test
    ./node_modules/karma/bin/karma start

available via `npm run-script`:
  build
    git archive --format=zip HEAD --worktree-attributes > build/hl-switcher.zip

# silent
$ npm run --silent

# json
$ npm run --json
{
  "build": "git archive --format=zip HEAD --worktree-attributes > build/hl-switcher.zip",
  "test": "./node_modules/karma/bin/karma start"
}

# parsable
$ npm run --parseable
build:git archive --format=zip HEAD --worktree-attributes > build/hl-switcher.zip
test:./node_modules/karma/bin/karma start

まとめ

恥ずかしいPR出さんでよかった。あとcode reading 楽しい。