Repository: pawelgalazka/runjs Branch: master Commit: 6258ca6cbfa5 Files: 24 Total size: 31.1 KB Directory structure: gitextract_wxdegntt/ ├── .babelrc ├── .gitignore ├── .huskyrc.json ├── .npmignore ├── .npmrc ├── .prettierrc.json ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bin/ │ └── task.js ├── jest.config.js ├── package.json ├── src/ │ ├── index.spec.ts │ └── index.ts ├── test/ │ ├── __snapshots__/ │ │ └── e2e.spec.ts.snap │ ├── e2e.spec.ts │ └── sandbox/ │ ├── package.json │ ├── scripts/ │ │ ├── color.js │ │ ├── error.js │ │ └── hello.js │ └── tasksfile.js ├── tsconfig.json └── tslint.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .babelrc ================================================ { "presets": ["@babel/preset-env", "@babel/preset-typescript"] } ================================================ FILE: .gitignore ================================================ .idea .vscode .DS_Store node_modules npm-debug.log /coverage /lib ================================================ FILE: .huskyrc.json ================================================ { "hooks": { "pre-commit": "lint-staged" } } ================================================ FILE: .npmignore ================================================ *.spec.ts /src /test /.huskyrc.json /.travis.yml /.prettierrc.json /.vscode /.babelrc /test /jest.config.js /tslint.json /tsconfig.json ================================================ FILE: .npmrc ================================================ save-exact=true ================================================ FILE: .prettierrc.json ================================================ { "semi": false, "singleQuote": true } ================================================ FILE: .travis.yml ================================================ language: node_js node_js: - "8.15.0" - "10.15.0" ================================================ FILE: CHANGELOG.md ================================================ ## 5.1.0 *API:* - `sh` function now accepts different set of options. Most importantly it does not accept `stdio` option anymore. By default it will be always in `stdio=pipe` mode for `stdout` and `stderr` process streams. It will also print out results to the terminal unless `silent=true` option given. - `sh` function accepts also `transform` option which allows to transform output of shell process. Usefull if we want to add prefixes to the output. - introducing `prefixTransform` function. It is dedicated for usage along with `sh` function as value for `transform` option. ## 5.0.0 *API:* - `help` function as second argument now accepts only `string`, for third argument accepts detailed help information about `options` and `params` - removing `option` and `options` helper, now `options` will be passed always as a first argument to the task - introducing `rawArgs` function, which returns raw, unparsed args which were provided to the task - renaming `run` function to `sh` - introducing `cli` function which exposes tasks functions to the cli *Mechanics:* - `options` are always passed as a first argument to the task function - calling tasks occurs through `npx task` not `npx run` script or by calling `tasksfile.js` directly (`node tasksfile.js`). Tasks file now behaves like a CLI script - to be able to call tasks from `tasksfile.js` they must be exposed by `cli` function. Exporting tasks functions won't do the job anymore. - to be able to call a task through `npx task`, entry to `npm scripts` must be added: `"task": "node ./tasksfile.js"`. `npx task` always try to execute `task` npm script, it's just an alias. - namespaces now can have `default` task - removing autocomplete feature, as it required too much configuration and it seems not used by the users - full support for `TypeScript`. Types files are included within the project. Using `TypeScript` for `tasksfile` is possible. It just require different entry in `npm scripts`: `"task": "ts-node ./tasksfile.ts"` *Other:* - renaming project from `runjs` to `tasksfile` ## 4.4.0 - source code migrated from `Flow/Babel` to `TypeScript` ## 4.3.0 - adding experimental bash autocompletion feature ## 4.2.0 *Changes:* - introducing `help` utility function - improving task docs generation by annotations *Dev env:* - improving e2e tests - using external module `microcli` as for cli args parsing and `--help` handling ## 4.1.0 - remove log option from `run` api command - upgrade dependent packages - introducing `options` helper, deprecating `option` - better printing of methods list when calling `run` *For development env:* - add tests coverage check - introducing `flow` types ## 4.0.0 **Changes:** - removing `ask` and `generate` helpers from api, to keep runjs codebase more focused about its main purpose - dropping support for `node` < 6.11.1 - support for other than `Babel` transpilers, like `TypeScript` - log option to run function, when `false` it does not log the command - documentation updates - support for `async` / `await` - `option` helper **Migration from 3.x to 4.x procedure:** - make sure you have node version >=6.11.1 - if you use Babel you need to add `"runjs": {requires: ["./node_modules/babel-register"]}` config to your package.json, otherwise Babel transpiler won't be picked up - find alternatives for ask and generate, those are not supported by runjs anymore ## 3.4.1 - changing documentation format for calling `run` without arguments (task documentation) - changing name of the prop for documentation from doc to help - when typing `--help` option with task run it will provide documentation only for that task (`run sometask --help`) ## 3.3.0 - migrating to `yarn` - removing task execution logging (decoration function) as it not working well with exporting pure functions - passing task options through `this.options` inside a task function ## 3.2.1 - fixes within handling dashed arguments when calling tasks, dashed arguments can be "spaced" by "-" or "." now, for example: `--some-argument` or `--some.argument` (#49) ## 3.2.0 - documenting tasks args when calling `run` without arguments - presenting list of available tasks from `runfile.js` in more readable way - passing `stdio` directly to `spawn` / `execSync` - changing run api where now it resolves/returns null by default and resolves/returns with value for option stdio: 'pipe'. This - allows to return colours to the terminal if provided by commands outcome. ## 3.1.1 - Bug fix: pass `process.env` by default to `spawn` and `execSync` ## 3.1.0 - fixes #43 `stderr` maxBuffer exceeded error (use `spawn` for `async` calls not `exec`) - `runfile.js` example update in README - drops support for `node` < 4.8.0 ## 3.0.0 - task name spacing/nesting, better for scaling tasks into many files - task descriptions - `ask` function - handling dash arguments in tasks (for example `--test`, `-t`) - logging tasks arguments to console when executing tasks - fixing exit codes when task not found - `run` function returns an output of a command now - improving documentation - deep code refactor, more unit tests ## 2.6.1 - bugfix: streaming `stderr` also for `async` process ## 2.6.0 - streaming output of an `async` command by default (`run` api function) ## 2.5.1 - presenting straightforward message when no `runfile.js` found ## 2.5.0 - bringing backwards compatibility with `node` >= 4.0.0 (previously `node` >= 6.0.0 required) - `run` command in `async` mode now returns a `Promise` ## 2.4.3 - handling config from `package.json` to define a custom path to `babel-register` - executing async commands through `child_process.span` (better `stdio` handling) ## 2.4.0 - removing watch method from api - RunJS will fallback to pure node now if user `babel-register` not found (falling back to it's own `babel-register` before) adding information to README: Why RunJS ? and other README update ## 2.3.0 - dropping Babel 5 support - handling new `exports.default` after babel update ## 2.2.0 - more explicit exceptions - handling existing `babel-register` or `babel/register` require hooks from the user package ## 2.1.0 - new functions available as part of runjs api: `watch` and `generate` - broader README with extensive `runfile.js` example ## 2.0.0 - dropping `es5` and `coffeescript` support in favor of `es6` (handled by babel) - dropping support for `node` < 4.0 ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2015 Paweł Gałązka Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # tasksfile ![node version](https://img.shields.io/node/v/tasksfile.svg) [![Build Status](https://travis-ci.org/pawelgalazka/tasksfile.svg?branch=master)](https://travis-ci.org/pawelgalazka/tasksfile) [![npm version](https://badge.fury.io/js/tasksfile.svg)](https://badge.fury.io/js/tasksfile) Minimalistic building tool > From version >= 5 RunJS was renamed to Tasksfile. > Link to RunJS version: https://github.com/pawelgalazka/runjs/tree/runjs - [Get started](#get-started) - [Why tasksfile ?](#why-tasksfile-) - [Features](#features) - [Executing shell commands](#executing-shell-commands) - [Handling arguments](#handling-arguments) - [Documenting tasks](#documenting-tasks) - [Namespacing](#namespacing) - [Sharing tasks](#sharing-tasks) - [TypeScript support](#typescript-support) - [API](#api) - [sh](#shcmd-options) - [prefixTransform](#prefixTransformprefix) - [help](#helpfunc-description-annotation) - [rawArgs](#rawArgs) ## Get started Install tasksfile in your project npm install tasksfile --save-dev Create `tasksfile.js` in your root project directory: ```js const { sh, cli } = require('tasksfile') function hello(options, name = 'Mysterious') { console.log(`Hello ${name}!`) } function makedir() { sh('mkdir somedir') } cli({ hello, makedir }) ``` Create `task` entry in your `scripts` section in `package.json`: ```json { "scripts": { "task": "node ./tasksfile.js" } } ``` Call in your terminal through npm scripts: ```bash $ npm run task -- hello Tommy $ npm run task -- makedir $ yarn task hello Tommy $ yarn task makedir ``` or through shorter `npx task` alias: ```bash $ npx task hello Tommy Hello Tommy! $ npx task makedir mkdir somedir ``` ## Why tasksfile ? We have Grunt, Gulp, npm scripts, Makefile. Why another building tool ? Gulp or Grunt files seem overly complex for what they do and the plugin ecosystem adds a layer of complexity towards the simple command line tools underneath. The documentation is not always up to date and the plugin does not always use the latest version of the tool. After a while customizing the process even with simple things, reconfiguring it becomes time consuming. Npm scripts are simple but they get out of hand pretty quickly if we need more complex process which make them quite hard to read and manage. Makefiles are simple, better for more complex processes but they depend on bash scripting. Within `tasksfile` you can use command line calls as well as JavaScript code and npm libraries which makes that approach much more flexible. [More](https://hackernoon.com/simple-build-tools-npm-scripts-vs-makefile-vs-runjs-31e578278162) ## Features ### Executing shell commands Tasksfile gives an easy way to execute shell commands in your tasks by `sh` function in synchronous and asynchronous way: ```js const { sh, cli } = require('tasksfile') function command () { sh('jest') sh(`webpack-dev-server --config webpack.config.js`, { async: true }) } cli({ command }) ``` ```bash $ npx task command ``` Because `./node_modules/.bin` is included in `PATH` when calling shell commands by `sh` function, you can call "bins" from your local project in the same way as in npm scripts. ### Handling arguments Provided arguments in the command line are passed to the function: ```javascript function sayHello (options, who) { console.log(`Hello ${who}!`) } cli({ sayHello }) ``` ```bash $ npx task sayHello world Hello world! ``` You can also provide dash arguments like `-a` or `--test`. Order of them doesn't matter after task name. They will be always available by `options` helper from inside a function. ```javascript function sayHello (options, who) { console.log(`Hello ${who}!`) console.log('Given options:', options) } cli({ sayHello }) ``` ```bash $ npx task sayHello -a --test=something world Hello world! Given options: { a: true, test: 'something' } ``` ### Documenting tasks To display all available tasks for your `tasksfile.js` type `task` in your command line without any arguments: $ npx task --help Commands: echo - echo task description buildjs - Compile JS files Use `help` utility function for your task to get additional description: ```javascript const { cli, help } = require('tasksfile') function buildjs () { } help(buildjs, 'Compile JS files') cli({ buildjs }) ``` $ npx task buildjs --help Usage: buildjs Compile JS files You can provide detailed annotation to give even more info about the task: ```javascript const dedent = require('dedent') const { sh, help } = require('tasksfile') function test (options, file) { } help(test, 'Run unit tests', { params: ['file'], options: { watch: 'run tests in a watch mode' }, examples: dedent` task test dummyComponent.js task test dummyComponent.js --watch ` }) cli({ test }) ``` $ npx task test --help Usage: test [options] [file] Run unit tests Options: --watch run tests in a watch mode Examples: task test dummyComponent.js task test dummyComponent.js --watch ### Namespacing To better organise tasks, it is possible to call them from namespaces: ```js const test = { unit () { console.log('Doing unit testing!') } } cli({ test }) ``` ```bash $ npx task test:unit Doing unit testing! ``` This is especially useful if `tasksfile.js` gets too large. We can move some tasks to external modules and import them back to a namespace: `./tasks/test.js`: ```javascript function unit () { console.log('Doing unit testing!') } function integration () { console.log('Doing unit testing!') } function default() { unit() integration() } module.exports = { unit, integration, default } ``` `tasksfile.js` ```js const test = require('./tasks/test') cli({ test }) ``` ```bash $ npx task test:unit Doing unit testing! $ npx task test Doing unit testing! Doing integration testing! ``` If we don't want to put imported tasks into a namespace, we can always use spread operator: ```js cli({ ...test }) ``` ```bash $ npx task unit Doing unit testing! ``` With ES6 modules import/export syntax this becomes even simpler: ```js // export with no namespace export * from './tasks/test' // no namespace // export with namespace import * as test from './tasks/test' export { test } // add namespace ``` ```bash $ npx task unit $ npx task test:unit ``` ### Sharing tasks Because `tasksfile.js` is just a node.js module and `tasksfile` just calls exported functions from that module based on cli arguments, nothing stops you to move some repetitive tasks across your projects to external npm package and just reuse it. `shared-tasksfile` module: ```js function shared1 () { console.log('This task is shared!') } function shared2 () { console.log('This task is shared!') } module.exports = { shared1, shared2 } ``` Local `tasksfile.js` ```js const shared = require('shared-tasksfile') function local () { console.log('This task is local!') } cli({ ...shared, local }) ``` ```bash $ npx task shared1 $ npx task shared2 $ npx task local ``` ### TypeScript support It's very easy to run your tasks in `TypeScript` if you have `TypeScript` already in your project. Just: - change your `tasksfile.js` to `tasksfile.ts` and adjust the code - install `ts-node`: `npm install --save-dev ts-node` - change command in your `package.json`: ```json { "scripts": { "task": "ts-node ./tasksfile.ts" } } ``` `Tasksfile` project already has `TypeScript` declaration files in source files. ## API For inside `tasksfile.js` usage. #### sh(cmd, options) Run given command as a child process and log the call in the output. `./node_modules/.bin/` is included into `PATH` so you can call installed scripts directly. Function will return output of executed command. ```js const { sh } = require('tasksfile') ``` *Options:* ```ts interface IShellOptions { // current working directory cwd?: string // environment key-value pairs env?: NodeJS.ProcessEnv // timeout after which execution will be cancelled timeout?: number // default: false, if true it runs command asynchronously and returns a Promise async?: boolean // if true, it will send output directly to parent process (stdio="inherit"), it won't return the output though // usefull if default piping strips too much colours when printing to the terminal // if enabled, transform option won't work nopipe?: boolean // if true, it won't print anything to the terminal but it will still return the output as a string silent?: boolean // function which allows to transform the output, line by line // usefull for adding prefixes to async commands output transform?: (output: string) => string } ``` #### prefixTransform(prefix) Transform function which can be used as `transform` option of `sh` function. It allows to add prefixes to shell output. *Example:* ```js const { cli, sh, prefixTransform } = require('tasksfile') function test() { sh('echo "test"', { transform: prefixTransform('[prefix]') }) } cli({ test }) ``` ```sh $ npx task test echo "test" [prefix] test ``` #### help(func, description, annotation) Define help annotation for task function, so it will be printed out when calling task with `--help` option and when calling `run` without any arguments. ```js const { help } = require('tasksfile') ``` ```javascript help(build, 'Generate JS bundle') help(test, 'Run unit tests', { params: ['file'], options: { watch: 'run tests in a watch mode' }, examples: ` task test dummyComponent.js task test dummyComponent.js --watch ` }) ``` $ npx task build --help $ npx task test --help #### rawArgs() Returns arguments / options passed to task in a raw, unparsed format. ```javascript const { cli, rawArgs } = require('tasksfile') function hello(options) { console.log('RAW ARGS', rawArgs()) } cli({ hello }) ``` ```sh $ npx task hello 1 2 3 --test RAW ARGS ['1', '2', '3', '--test'] ``` ================================================ FILE: bin/task.js ================================================ #!/usr/bin/env node const { execSync } = require('child_process') const path = require('path') const packageJson = require(path.resolve('./package.json')) const taskScript = packageJson.scripts.task try { execSync(`${taskScript} ${process.argv.slice(2).join(' ')}`, {shell: true, stdio: 'inherit'}) } catch (error) { process.exit(1) } ================================================ FILE: jest.config.js ================================================ // For a detailed explanation regarding each configuration property, visit: // https://jestjs.io/docs/en/configuration.html module.exports = { // A list of paths to directories that Jest should use to search for files in. roots: ["/src/", "/test/"], // The test environment that will be used for testing testEnvironment: "node", // A list of reporter names that Jest uses when writing coverage reports coverageReporters: [ "text", "text-summary" ], // An object that configures minimum threshold enforcement for coverage results coverageThreshold: { "global": { "lines": 30 } } }; ================================================ FILE: package.json ================================================ { "name": "tasksfile", "version": "5.1.1", "description": "Minimalistic task runner for node.js", "keywords": [ "build", "system", "make", "tool" ], "main": "lib/index.js", "types": "lib/index.d.ts", "bin": { "task": "bin/task.js" }, "scripts": { "lint": "tslint -c tslint.json 'src/*.ts' 'test/**/*.ts'", "build": "tsc", "test": "yarn lint && yarn build && yarn clean:sandbox && jest --coverage", "test:unit": "jest ./src/", "test:e2e": "jest ./test/", "clean": "rm -rf node_modules && yarn clean:build && yarn clean:sandbox", "clean:build": "rm -rf ./lib", "clean:sandbox": "rm -rf ./test/e2e/sandbox/node_modules && mkdir -p ./test/sandbox/node_modules/.bin", "sandbox:task": "cd ./test/sandbox && ../../bin/task.js" }, "lint-staged": { "src/*.{ts,tsx}": [ "tslint --fix", "git add" ] }, "engines": { "node": ">=6.11.1" }, "repository": { "type": "git", "url": "git+https://github.com/pawelgalazka/tasksfile.git" }, "author": "Pawel Galazka", "license": "MIT", "bugs": { "url": "https://github.com/pawelgalazka/tasksfile/issues" }, "homepage": "https://github.com/pawelgalazka/tasksfile#readme", "dependencies": { "@pawelgalazka/cli": "2.0.3", "@pawelgalazka/shell": "2.0.0", "chalk": "2.3.0" }, "devDependencies": { "@babel/core": "7.2.2", "@babel/preset-env": "7.3.1", "@babel/preset-typescript": "7.1.0", "@types/jest": "24.0.0", "@types/lodash.padend": "4.6.4", "@types/node": "10.12.18", "husky": "1.3.1", "jest": "24.1.0", "lint-staged": "8.1.0", "prettier": "1.15.3", "tslint": "5.12.1", "tslint-config-prettier": "1.17.0", "tslint-plugin-prettier": "2.0.1", "typescript": "3.2.2" } } ================================================ FILE: src/index.spec.ts ================================================ import { shell } from '@pawelgalazka/shell' import chalk from 'chalk' import { sh } from './index' const shellMock = shell as jest.Mock jest.mock('@pawelgalazka/shell') process.env = { DEFAULT_ENV: 'default env' } describe('sh()', () => { let logger: any beforeEach(() => { jest.resetAllMocks() logger = { error: jest.fn(), log: jest.fn(), title: jest.fn(), warning: jest.fn() } }) it('calls original @pawelgalazka/shell function with the same command', () => { sh('test command', undefined, logger) expect(shellMock).toHaveBeenCalledTimes(1) expect(shellMock).toHaveBeenCalledWith('test command', expect.anything()) }) it('logs executed command', () => { sh('test command', undefined, logger) expect(logger.log).toHaveBeenCalledTimes(1) expect(logger.log).toHaveBeenCalledWith(chalk.bold('test command')) }) it('calls original @pawelgalazka/shell with default options values', () => { sh('test command', undefined, logger) expect(shellMock).toHaveBeenCalledTimes(1) expect(shellMock).toHaveBeenCalledWith(expect.anything(), { env: { DEFAULT_ENV: 'default env', PATH: expect.anything() } }) }) it('calls original @pawelgalazka/shell with given options values', () => { sh( 'test command', { async: true, cwd: 'cwd-dir', env: { CUSTOM_ENV: 'custom env' }, stdio: 'pipe', timeout: 1000 }, logger ) expect(shellMock).toHaveBeenCalledTimes(1) expect(shellMock).toHaveBeenCalledWith(expect.anything(), { async: true, cwd: 'cwd-dir', env: { CUSTOM_ENV: 'custom env', PATH: expect.anything() }, stdio: 'pipe', timeout: 1000 }) }) it('adds ./node_modules/.bin to $PATH', () => { sh('test command', undefined, logger) expect(shellMock).toHaveBeenCalledWith( expect.anything(), expect.objectContaining({ env: expect.objectContaining({ PATH: expect.stringContaining('node_modules/.bin:') }) }) ) }) }) ================================================ FILE: src/index.ts ================================================ import { cli as cliEngine, CLIError, CommandsModule, Middleware, useMiddlewares } from '@pawelgalazka/cli' import { Logger } from '@pawelgalazka/cli/lib/utils/logger' import { IAsyncShellOptions, IShellOptions, ISyncShellOptions, shell, ShellError } from '@pawelgalazka/shell' import chalk from 'chalk' import path from 'path' export { help, rawArgs } from '@pawelgalazka/cli' export { prefixTransform } from '@pawelgalazka/shell' const commandNotFoundHandler: Middleware = next => args => { const { command } = args if (!command) { throw new CLIError( 'Command not found. Type "npx task --help" for more information.' ) } next(args) } const shellErrorHandler: ( logger: Logger ) => Middleware = logger => next => args => { const { reject } = args const nextReject = (error: Error) => { if (error instanceof ShellError) { logger.error(error.message) process.exit(1) } else { reject(error) } } try { next({ ...args, reject: nextReject }) } catch (error) { nextReject(error) } } export function sh( command: string, options: IAsyncShellOptions, logger?: Logger ): Promise export function sh( command: string, options?: ISyncShellOptions, logger?: Logger ): string | null export function sh( command: string, options: IShellOptions = {}, logger: Logger = new Logger() ) { const binPath = path.resolve('./node_modules/.bin') // Include in PATH node_modules bin path const nextPath = [ binPath, (options.env && options.env.PATH) || process.env.PATH ].join(path.delimiter) const nextOptions = { ...options, env: { ...(options.env || process.env), PATH: nextPath } } logger.log(chalk.bold(command)) return shell(command, nextOptions) } export function cli(definition: CommandsModule) { return cliEngine( definition, useMiddlewares([commandNotFoundHandler, shellErrorHandler(new Logger())]) ) } ================================================ FILE: test/__snapshots__/e2e.spec.ts.snap ================================================ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`tasksfile displays commands list if only --help option provided and no task name 1`] = ` " Commands: asyncAwait color echo - Simple echo task error errorAsyncAwait nested nested:echo [p1 p2] - Description of nested task npmBin shell " `; ================================================ FILE: test/e2e.spec.ts ================================================ /* eslint-env jest */ import { execSync as orgExecSync } from 'child_process' describe('tasksfile', () => { const scriptPath = '../../bin/task.js' function execSync(cmd: string) { return orgExecSync(cmd, { cwd: './test/sandbox', env: { ...process.env, FORCE_COLOR: '0' }, stdio: 'pipe' }).toString() } it('executes simple task', () => { expect(execSync(`${scriptPath} echo 1 2 3 --foo --bar`)).toEqual( "echo [ { foo: true, bar: true }, '1', '2', '3' ]\n" ) }) it('executes shell commands in a task', () => { const output = execSync(`${scriptPath} shell`) expect(output).toContain( 'echo "sync terminal"\nsync terminal\necho "sync silent"\noutput sync silent' ) expect(output).toContain('\nasync terminal\n') expect(output).toContain('\noutput async silent\n') }) it('executes task from a namespace', () => { expect(execSync(`${scriptPath} nested:echo 1 2 3 --foo --bar`)).toEqual( "nested echo [ { foo: true, bar: true }, '1', '2', '3' ]\n" ) }) it('executes default task from a namespace', () => { expect(execSync(`${scriptPath} nested 1 2 3 --foo --bar`)).toEqual( "nested default [ { foo: true, bar: true }, '1', '2', '3' ]\n" ) }) it('includes ./node_modules/.bin to PATH when executing bin scripts', () => { execSync('cp -p ./scripts/hello.js ./node_modules/.bin/hello') expect(execSync(`${scriptPath} npmBin`)).toEqual('hello\nHello!\n') }) it('executes async/await task', () => { expect(execSync(`${scriptPath} asyncAwait`)).toContain( 'echo "async and await"\noutput async and await\n\nafter await\n' ) }) it('displays help for a task', () => { expect(execSync(`${scriptPath} echo --help`)).toEqual( 'Usage: echo \n\nSimple echo task\n\n' ) }) it('displays detailed help', () => { expect(execSync(`${scriptPath} nested:echo --help`)).toEqual( 'Usage: nested:echo [options] [p1 p2]\n\nDescription of nested task\n\n' + 'Options:\n\n --foo foo option description\n' + ' --bar bar option description\n' ) }) it('displays error from executed task', () => { expect(() => execSync(`${scriptPath} error`)).toThrow( `Command failed: ${scriptPath} error` ) }) it('displays error from executed async task', () => { expect(() => execSync(`${scriptPath} errorAsyncAwait`)).toThrow( `Command failed: ${scriptPath} errorAsyncAwait` ) }) it('displays commands list if only --help option provided and no task name', () => { expect(execSync(`${scriptPath} --help`)).toMatchSnapshot() }) }) ================================================ FILE: test/sandbox/package.json ================================================ { "name": "sandbox", "private": true, "version": "1.0.0", "scripts": { "task": "node ./tasksfile.js" } } ================================================ FILE: test/sandbox/scripts/color.js ================================================ const chalk = require('chalk') console.log(chalk.yellow('This should be in yellow color')) ================================================ FILE: test/sandbox/scripts/error.js ================================================ throw new Error('error message') ================================================ FILE: test/sandbox/scripts/hello.js ================================================ #!/usr/bin/env node console.log('Hello!') ================================================ FILE: test/sandbox/tasksfile.js ================================================ const { sh, help, cli } = require('../../lib') help(echo, 'Simple echo task') function echo(...args) { console.log('echo', args) } const nested = { echo(...args) { console.log('nested echo', args) }, default (...args) { console.log('nested default', args) } } help(nested.echo, 'Description of nested task', { options: { foo: 'foo option description', bar: 'bar option description' }, params: ['p1', 'p2'] }) function shell() { sh('echo "sync terminal"') console.log('output', sh('echo "sync silent"', {silent: true})) sh('echo "async terminal"', { async: true }) sh('echo "async silent"', { async: true, silent: true}).then(output => console.log('output', output) ) } function npmBin() { sh('hello') } async function asyncAwait() { const output = await sh('echo "async and await"', { async: true, silent: true }) console.log('output', output) console.log('after await') } function error() { sh('node ./scripts/error.js', { async: true }) sh('node ./scripts/error.js') } async function errorAsyncAwait() { await Promise.reject(new Error('async await error')) } function color() { sh('node ./scripts/color.js') sh('node ./scripts/color.js', { async: true }) } cli({ echo, shell, nested, npmBin, asyncAwait, errorAsyncAwait, error, color }) ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { /* Basic Options */ "target": "es2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ "lib": ["es2017"], /* Specify library files to be included in the compilation. */ "declaration": true, /* Generates corresponding '.d.ts' file. */ "sourceMap": true, /* Generates corresponding '.map' file. */ "outDir": "./lib", /* Redirect output structure to the directory. */ /* Strict Type-Checking Options */ "strict": true, /* Enable all strict type-checking options. */ /* Additional Checks */ "noUnusedLocals": true, /* Report errors on unused locals. */ "noUnusedParameters": true, /* Report errors on unused parameters. */ "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ /* Module Resolution Options */ "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ }, "include": [ "src/**/*" ], "exclude": [ "**/*.spec.ts" ] } ================================================ FILE: tslint.json ================================================ { "extends": [ "tslint:latest", "tslint-config-prettier" ], "rules": { "prettier": true, "max-classes-per-file": false, "no-console": false, "no-empty": false, "no-shadowed-variable": false, "no-submodule-imports": false }, "rulesDirectory": ["tslint-plugin-prettier"] }