From b09a4e394e1b1663b6f2b1b9b6303f14f822e3fe Mon Sep 17 00:00:00 2001 From: Vladimir Iakovlev Date: Sat, 13 Aug 2016 17:38:40 +0300 Subject: [PATCH] #N/A: Add `npm_run_script` rule --- README.md | 1 + tests/rules/test_npm_run_script.py | 84 ++++++++++++++++++++++++++++++ thefuck/rules/npm_run_script.py | 31 +++++++++++ 3 files changed, 116 insertions(+) create mode 100644 tests/rules/test_npm_run_script.py create mode 100644 thefuck/rules/npm_run_script.py diff --git a/README.md b/README.md index 9d24ce2d..58be467a 100644 --- a/README.md +++ b/README.md @@ -200,6 +200,7 @@ using the matched rule and runs it. Rules enabled by default are as follows: * `mkdir_p` – adds `-p` when you trying to create directory without parent; * `mvn_no_command` – adds `clean package` to `mvn`; * `mvn_unknown_lifecycle_phase` – fixes misspelled lifecycle phases with `mvn`; +* `npm_run_script` – adds missing `run-script` for custom `npm` scripts; * `npm_wrong_command` – fixes wrong npm commands like `npm urgrade`; * `no_command` – fixes wrong console commands, for example `vom/vim`; * `no_such_file` – creates missing directories with `mv` and `cp` commands; diff --git a/tests/rules/test_npm_run_script.py b/tests/rules/test_npm_run_script.py new file mode 100644 index 00000000..72cb35e8 --- /dev/null +++ b/tests/rules/test_npm_run_script.py @@ -0,0 +1,84 @@ +import pytest +from io import BytesIO +from thefuck.rules.npm_run_script import match, get_new_command +from tests.utils import Command + +stdout = ''' +Usage: npm + +where is one of: + access, add-user, adduser, apihelp, author, bin, bugs, c, + cache, completion, config, ddp, dedupe, deprecate, dist-tag, + dist-tags, docs, edit, explore, faq, find, find-dupes, get, + help, help-search, home, i, info, init, install, issues, la, + link, list, ll, ln, login, logout, ls, outdated, owner, + pack, ping, prefix, prune, publish, r, rb, rebuild, remove, + repo, restart, rm, root, run-script, s, se, search, set, + show, shrinkwrap, star, stars, start, stop, t, tag, team, + test, tst, un, uninstall, unlink, unpublish, unstar, up, + update, upgrade, v, version, view, whoami + +npm -h quick help on +npm -l display full usage info +npm faq commonly asked questions +npm help search for help on +npm help npm involved overview + +Specify configs in the ini-formatted file: + /home/nvbn/.npmrc +or on the command line via: npm --key value +Config info can be viewed via: npm help config + +''' + +run_script_stdout = b''' +Lifecycle scripts included in code-view-web: + test + jest + +available via `npm run-script`: + build + cp node_modules/ace-builds/src-min/ -a resources/ace/ && webpack --progress --colors -p --config ./webpack.production.config.js + develop + cp node_modules/ace-builds/src/ -a resources/ace/ && webpack-dev-server --progress --colors + watch-test + jest --verbose --watch + +''' + + +@pytest.fixture(autouse=True) +def run_script(mocker): + patch = mocker.patch('thefuck.rules.npm_run_script.Popen') + patch.return_value.stdout = BytesIO(run_script_stdout) + return patch.return_value + + +@pytest.mark.usefixtures('no_memoize') +@pytest.mark.parametrize('script', [ + 'npm watch-test', 'npm develop']) +def test_match(script): + command = Command(script, stdout) + assert match(command) + + +@pytest.mark.usefixtures('no_memoize') +@pytest.mark.parametrize('command, run_script_out', [ + (Command('npm test', 'TEST FAIL'), run_script_stdout), + (Command('npm watch-test', 'TEST FAIL'), run_script_stdout), + (Command('npm test', stdout), run_script_stdout), + (Command('vim watch-test', stdout), run_script_stdout)]) +def test_not_match(run_script, command, run_script_out): + run_script.stdout = BytesIO(run_script_out) + assert not match(command) + + +@pytest.mark.usefixtures('no_memoize') +@pytest.mark.parametrize('script, result', [ + ('npm watch-test', 'npm run-script watch-test'), + ('npm -i develop', 'npm run-script -i develop'), + ('npm -i watch-script --path ..', + 'npm run-script -i watch-script --path ..')]) +def test_get_new_command(script, result): + command = Command(script, stdout) + assert get_new_command(command) == result diff --git a/thefuck/rules/npm_run_script.py b/thefuck/rules/npm_run_script.py new file mode 100644 index 00000000..67b86891 --- /dev/null +++ b/thefuck/rules/npm_run_script.py @@ -0,0 +1,31 @@ +import re +from subprocess import Popen, PIPE +from thefuck.utils import for_app, memoize, eager + + +@memoize +@eager +def get_scripts(): + proc = Popen(['npm', 'run-script'], stdout=PIPE) + should_yeild = False + for line in proc.stdout.readlines(): + line = line.decode() + if 'available via `npm run-script`:' in line: + should_yeild = True + continue + + if should_yeild and re.match(r'^ [^ ]+', line): + yield line.strip().split(' ')[0] + + +@for_app('npm') +def match(command): + return ('Usage: npm ' in command.stdout + and not any(part.startswith('ru') for part in command.script_parts) + and command.script_parts[1] in get_scripts()) + + +def get_new_command(command): + parts = command.script_parts[:] + parts.insert(1, 'run-script') + return ' '.join(parts)