Giter Club home page Giter Club logo

listr's People

Contributors

alexpsi avatar andywer avatar arfatsalman avatar athomann avatar ayshvab avatar chris-olszewski avatar insipx avatar k15a avatar kostasx avatar linusu avatar litomore avatar mathiasbynens avatar nickymeuleman avatar okonet avatar rap2hpoutre avatar reconbot avatar samverschueren avatar scott113341 avatar southpolesteve avatar sudo-suhas avatar tylors avatar unkillbob avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

listr's Issues

Parallel Tasks

Great lib! I'm running into a few cases where I would like to execute tasks in parallel. Would be great if there was support for this. One idea is options object to the Listr constructor function such as { parallel: true }. I'm working right now to see if I can hack this together or drop down to ora and do it just with that lib. Will report back here if I find a good solution.

Detect observable issues

Due to an issue in is-observable, we have to fall back to duck typing as isObservable() always returns false.

The current implementation.

const isObservable = obj => Boolean(obj && typeof obj.subscribe === 'function' && obj.constructor.name === 'Observable');

Apparently it's not entirely correct

From @TylorS

This actually breaks the stuff that I'm working on because of the changes to isObservable. The stream library I'm using is most.js and it's constructor.name is Stream and not Observable. However it does implement Observable interfaces.

From @andywer

So how about that: We check with the current custom logic and fallback to is-observable or vice versa. If one of both is true we can be almost certain it is an Observable.

PS: I can also open an issue on zen-observable to use the symbol-observable so is-observable will work with the zen-observable as well.

// @jamestalmage @sindresorhus @jfmengels

Allow passing a value to `then` of the main Listr instance

In the following example:

new Listr(tasks, { concurrent })
                    .run({
                        errors: []
                    })
                    .then((res) => {
                        console.log(res)
                    })
                    .catch((error) => {
                        console.error(error)
                        process.exit(1)
                    })

the res is always undefined even then I resolve internal tasks with some value.

Or alternatively add context to the then so it can be accessed.

Any way to preserve colors in the output?

I'm trying to incorporate this module to lint-staged: lint-staged/lint-staged#32
and running into a few issues. I'd like to have all errors from subsequent tasks (i.e. linters) be outputted to the console "as is".

So, the "right" way of doing it ATM is like:

return new Promise((resolve, reject) => {
                        execa(res.bin, res.args)
                            .then(() => {
                                resolve(`${linter} passed!`)
                            })
                            .catch(err => {
                                reject(`
๐Ÿšจ  ${linter} found some errors. Please fix them and try committing again.

${err.stdout}
`
                                )
                            })
                    })

See https://github.com/okonet/lint-staged/blob/47099e87d059f79ebc7d8bbe5c0e80069704f5bf/src/runScript.js#L14-L27

This works fine, but the output looses all the colors. This sucks!

To preserve colors, it seems there is no other way as spawning a process with stdio: "inherit" option passed in. This makes output colored, but at the same time breaks the output of this module. It now appears twice and (worst thing) it cuts some of the stdout output from the task.

2016-07-23 at 11 37

I'm wondering what would be a better work around this.

See also #16

Support skipping a task while performing the main function

From sindresorhus/np#114 (comment)

It should be possible to skip a task from the main function. Not sure about the implementation details though. We can't use this.skip('message') inside the task function because they don't play nice with fat-arrow functions. So maybe a second 'task' object as argument?

const tasks = new Listr([
    {
        title: 'Install with yarn',
        task: (ctx, task) => execa('yarn')
                .catch(() => {
                        task.skip('Yarn is not available on your system');
                });
    },
]);

Feedback much appreciated!

// @sindresorhus

Status icons in Windows 7 command line tool

Maybe I do it wrong, but if I open Windows command line with cmd and run the following JS script with node script.js, I got instead of success status icons only V.

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

const tasks = new listr([
    {
        title: 'Task 1',
        task: () => Promise.resolve('Foo')
    },
    {
        title: 'Can be skipped',
        skip: () => {
            if (Math.random() > 0.5) {
                return 'Reason for skipping';
            }
        },
        task: () => 'Bar'
    },
    {
        title: 'Task 3',
        task: () => Promise.resolve('Bar')
    }
]);

tasks.run().catch(err => {
    console.error(err);
});

Skippable Tasks

Allow tasks to be labelled as "skipped" instead of completed, with an optional message explaining why.

See sindresorhus/np#64 (comment)

The explanation message should probably persist instead of being swallowed by log-update.

Allow nestable subtasks

It would be cool if you could return a Listr instance from a task, and have all the subtasks display temporarily, then collapse when complete.

Different icons for tasks success/fail

Suggestion :: Add parameters for tasks to use alternate icons

eg. if one of my tasks fails, I am in need of showing a warning icon, instead of an 'x', as the rest of the tasks can finish successfully, even with the other task failing.

Weird output

running

const Listr = require('listr');

const tasks = new Listr([
  {
    title: 'Task1',
    task: function() {
      return new Promise(function(resolve) {
        setTimeout(resolve, 1000);
      });
    }
  },
  {
    title: 'Task Two',
    task: function() {
      return new Promise(function(resolve) {
        setTimeout(resolve, 700);
      });
    }
  },
  {
    title: 'Task Next',
    task: function() {
      return new Promise(function(resolve) {
        setTimeout(resolve, 700);
      }).then(function() {
        return new Promise(function(resolve) {
          console.log('running ...');
          setTimeout(resolve, 700);
        });
      });
    }
  }
]);

tasks.run();

outputs:

node index

 โœ” Task1
 โœ” Task1
 โœ” Task Two
 โœ” Task Next

even though Task1 is set only once. Also, running ... does not gets printed at all

EDIT:

$ node -v
v6.3.0

Maybe provide some fallback if not on a TTY

Hey!

For https://github.com/andywer/npm-launch I was thinking about a plain-text fallback in case !require('tty').isatty(process.stdout). Right now you get all those control characters in the output when piping to a file or similar. In my particular case I have Jenkins/Travis builds in mind.

But maybe you are interested to solve that edge case directly in Listr?

bildschirmfoto 2016-07-14 um 00 29 48

Improve arrows

screen shot 2016-07-06 at 22 57 56

The first arrow would be nicer as figures.pointerSmall (โ€บ) instead.

The second arrow showing terminal output would be better as $.

Persist final `next` output from Observable in output

Hello,

I use zen-observable, and the .next() to update the progress. At the end of the task I would either like to

  • persist the last output from .next()
    or be able to update the task name by some means (i.e. passing a resolution to .complete()) (tc39/proposal-observable#77 makes this a non-viable option)

My use case is going through a number of files looking for a 'match', printing the current status, and it'd be nice if I could print on the console what the match is for each task.

Retrieve errors when `exitOnError` is set to false

From @okonet

I've just tried to integrate it and can't wrap my head on how do I display all errors when all tasks complete. When using default renderer, it just continues silently now, so I was expecting the error argumemt in the catch to contain all my errors. Looking at the code it is not the case. Is there a way of accessing it somehow after the execution? Should it be some internal state I have to take care of? Please point me in the right direction.

Because exitOnError is set to false, at the end, the .catch function is not being invoked and you end up in then. Because the result of the promise is the context object, we could add an errors array and push all the errors into that list. A downside of this approach though is that we should make it a read-only property so that people can't overwrite that property.

@okonet @frederickfogerty thoughts?

Refactor renderers

The entire render stuff should be refactored to easier allow multiple different and even custom outputs.

Add an issue template.

If people are reporting visual bugs, we really need to know which terminal emulator they are using.

Improve arguments handling

For the longest time, I was debugging an issue which was finally fixed by replacing:

new Listr({
    title, task
}, {
    title, task
})

with

new Listr([{
    title, task
}, {
    title, task
}])

Notice the difference? Listr wasn't doing anything in the first instance, I couldn't think of anything better to say than "it doesn't work", no output, no errors, nothing, just a no-op... and so it took so long to debug.

rename `message` to `title`

Currently a task is defined by message and task. We should change message to title it seems more appropriate.

Rendering issue when returning Promise from subtasks

When using collapse option on parent task and returning Promise (via run method) from subtasks, there are some weird rendering issues happening:

Returning Promise

1

Returning instance

2

Is this known issue? What Iโ€™m trying to achieve is to have callback after list of subtasks is completed. Thenable seems like simplest solution for this.

Task returning subtask code which produces error:

task: ( ctx, task ) => {

	const values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

	return new Listr(values.map(( value ) => {
		return {
			title: `Task ${value}`,
			task: () => {
				return new Promise(( resolve ) => {
					setTimeout(() => {
						resolve(value);
					}, 300);
				});
			}
		};
	})).run();

}

Add `enabled` property to a task

From sindresorhus/np#114 (comment)

It would be nice if we could dynamically make tasks visible or invisible. If invisible, they shouldn't be executed. So maybe the property should be called enabled instead? I had something in mind like

const tasks = new Listr([
    {
        title: 'Install with yarn',
        task: ctx => execa('yarn')
                .catch(() => {
                        ctx.yarn = false;
                });
    },
    {
        title: 'Install with npm',
        visible: ctx => ctx.yarn === false,
        task: () => execa('npm', ['test'])
    }
]);

The function should be synchronous because otherwise we could end up in a state where it looks like the tasks aren't doing any work for 5 seconds for instance.

While I'm typing this, I might be more in favour if enabled. As visible might look like the task is invisible but it will still be executed.

Any remarks, improvements or things I overlooked? Feel free to provide feedback.

// @sindresorhus

Support for user input

Sometimes tasks can require some user input from the terminal. Right now, it doesn't seem to be possible to pause the render that will always eat the user input. I'm not sure if this is even possible with a custom renderer. Either way, I think user input should be supported.

Can you point me in a right direction here?

Passing a `context` object to the task functions

In #17, the idea came up for passing a context object as argument of a task function. This would allow you to pass data from one task to another allowing you to create composable tasks.

Do we have to take something special into account when doing this? Because we are targetting Node 4+, are there any reasons why we should pass in a regular Object.create(null) object instead of Map?

A way to issue warnings

Swallowing all output may encourage users to ignore warnings which maybe they shouldn't (linter warnings, etc).

It would be nice if tasks were able to push any number of warnings to the output, that were not swallowed by log-update

Cursor not shown after running all tasks.

I'm using listr with vorpal to make a CLI app, and after running the tasks, the cursor is not being redrawn.

I checked the CLIRenderer that's based on log-update and i realized that function done is not being called after on end function.

Maybe the idea is to change the render fn, adding a done condition:

const render = (tasks, done) => {
    const output = tasks.map(task => task.render());
    logUpdate(output.join('\n'));
    if (done) {
        logUpdate.done();
    }
};

Display only the task that is running

Hey guys,

I want to know if there is a way to show the task only when was run or is running, instead of display all the task list from the beginning.

Add Task on the run

Hi,

I'm having a slightly different use case: I would like to add task to Listr while it's already running (my task list is unknown when the program start).

On my first tries, the 2nd task is rendered but the spinner never shows and the list complete before the return of the second promise. My guess is that it use a reduce to run the tasks and it won't accept new entries while reducing.

I'm working on a recursive approach that would shift the array until null. Do you think it can work? Any advice? Thanks

Improve the plain ascii output

I really like that listr produce by default useful output when redirected to file:

[05:30:10] Upgrading last deals [started]
[05:30:13] Upgrading last deals [completed]
[05:30:13] Find unsaved measures [started]
[05:30:35] Find unsaved measures [completed]
[05:30:35] Saving measures [started]
[05:30:35] Saving measures [completed]
[05:30:35] All measures saved [started]
[05:30:35] All measures saved [completed]

But since I append it to a log file, I would need at least the date to appear before the hour. If we could customize the format with an option or something it would be beautiful...

Promise returning an Obsevable

The case where a promise returns an observable is not handled at the moment which means that something like this

const task = {
    title: 'Clean & Install',
    task: () => del('node_modules').then(() => exec('npm', ['install'])))
};

Where exec returns an execa Observable ends the task after removing the node_modules.

Show all subtasks

Currently subtasks are hidden once they're done. It'd be nice to leave them on once they're done, as an outline of the process. For example instead of:

  • Prerequisite check
  • Git
  • Installing dependencies
  • Publishing package

Done

It could be a more explicative:

  • Prerequisite check
    • Validate version
    • Check for pre-release version
    • Check npm version
    • Check git tag existence
  • Git
    • Check current branch
    • Check local working tree
    • Check remote history
  • Installing dependencies
  • Bumping version
  • Publishing package

Done

The observable interface no longer contains the forEach method

The Observable interface as defined here
No longer contains the method forEach() which is used to determine if an object returned by a task is an observable here.

I'd propose adjusting that line to something like

const isObservable = obj => obj && typeof obj.subscribe === 'function' && typeof obj.constructor.from === 'function'

I don't forsee the from() static method being removed since it's the basis for converting to and from different observable types.

I'd be happy to PR this if it would be accepted.

Thank you for you time ๐Ÿ˜„

`exitOnError` should cancel other tasks if executed concurrently

From #34 (comment)

This really is an edge case though but we should support it. The issue was introduced with #46 but we decided to land that without this fix.

The issue occurs when we have a task that looks like this

const tasks = new Listr([
	{
		title: 'Foo',
		task: () => Promise.reject(new Error('Foo bar'))
	},
	{
		title: 'Bar',
		task: () => {
			return new Listr([
				{
					title: 'Foo Bar Subtask 1',
					task: () => Promise.reject(new Error('Foo bar baz'))
				},
				{
					title: 'Foo Bar Subtask 2',
					task: () => execa('npm', ['install'])
				}
			], {concurrent: true, exitOnError: true});
		}
	},
	{
		title: 'Foo',
		task: () => delay(5000)
	}
], {
	exitOnError: false
});

The root list is configured to not exit on failure. The inner list however is set to fail on error. Because of that, when the inner list is being executed, the first task of that list throws an error. However, because of the combination concurrent and exitOnError, the second task of the inner list is still being executed entirely which means that npm install will still install ALL dependencies instead of aborting.

In order to fix this, we should introduce cancelation. It should work with Observables (not tested yet) because they have built in cancellation. However, promises don't. But thanks to @sindresorhus, who had [some fun with promises(https://github.com/sindresorhus/promise-fun), we can use something like p-cancelable to introduce cancelable promises as well.

Solution

We would attach an extra onCancel(fn: Function) method to the task object which is being passed as second argument to each task. The function passed into the onCancel method will then be executed when the promise should be cancelled.

const tasks = new Listr([
	{
		title: 'Foo',
		task: () => Promise.reject(new Error('Foo bar'))
	},
	{
		title: 'Bar',
		task: () => {
			return new Listr([
				{
					title: 'Foo Bar Subtask 1',
					task: () => Promise.reject(new Error('Foo bar baz'))
				},
				{
					title: 'npm install',
					task: (ctx, task) => {
						const cp = execa('npm', ['install']);

						task.onCancel(() => {
							cp.kill();
						});

						return cp;
					}
				}
			], {exitOnError: true});
		}
	},
	{
		title: 'Foo',
		task: () => delay(5000)
	},
], {
	exitOnError: false
});

Feedback is more then welcome!

Updating a task in place

I am stoked about this module. I think it has potential to be the way to represent any non-trivial CLI behavior. I can imagine it being used in Yeoman, test runners, etc.

One thing I think we will need is to address some of what @jamestalmage says in sindresorhus/np#30 (comment).

when I'm installing something that has a long build step as part of it's install ... I want to see what's causing the hold up

That is to say, tasks have an associated status that is meaningful in some cases. For npm, a progress bar may be useful, but even just seeing which dependency is currently being installed would help explain long installs at a quick glance.

To achieve this, I imagine a method that would allow me to update the task description in place. Or maybe just characters to the right of the task description if you want to be opinionated about it and protect end users from crazy mutations to the task list.

Fails on non-string output

listr will throw an error when an observable task outputs something without a .trim() method, like a number or object. Test case:

const tasks = new Listr([{
  title: 'Count',
  task: () => {
    title: 'Count up',
    task: () => rx.Observable.create(obj => {
      var i = 0;
      var id = setInterval(() => obj.next(i++), 100);
      setTimeout(() => {clearInterval(id); obj.complete(i)}, 1050);
}}]);

Rejecting with a string as argument shows an error

In lint-staged I'm rejecting a promise with a String as docs says here https://github.com/SamVerschueren/listr#promises

But when doing so I get the following output:

{ TypeError: Cannot create property 'context' on string '
๐Ÿšซ pwd found some errors. Please fix them and try committing again.



'
    at tasks.then.catch.err (/Users/okonet/Projects/OSS/sort-staged-example/front/node_modules/listr/index.js:74:17)
    at process._tickCallback (internal/process/next_tick.js:103:7) context: {} }

Note that before it was displaying and this is the correct behavior:

๐Ÿšซ pwd found some errors. Please fix them and try committing again.

I think this is due to this one: 72ee8cc (#37)

Concurrency as a limit

Fantastic package! I just want to propose a feature change which involves specifying a concurrent tasks limit rather than simply passing in true.

This feature would be beneficial to large tasks as they hog memory. Limiting them into chunks saves this memory allowing large tasks to run uninterrupted.

Much like in bluebird's map function

{ concurrency: 3 } // Limit the async chain to run 3 at a time

0.6.0 broke default rendering

Hey @SamVerschueren, I am glad for the new renderers!

But I just experienced an issue when trying to use 0.6.0 for npm-launch. Seems like it doesn't render much anymore when using the default renderer. Maybe it's rendering the listr instance as completed before it actually is?

listr-0 5 0

listr-0 6 0

task only show the last line when return multi line

{
      title: 'Lint',
      task: () =>
        execa('npm', ['run', 'lint']).catch(error => {
          // console.log(error)
          throw new Error(`123\n456`)
        }),
      skip: () => !pkg.scripts.lint && 'missing script lint'
    }

it's only show 456,need i add custom renderers ?

Completing all concurrent tasks if one errors.

I'm running a set of tasks concurrently. At the moment, if one of the tasks fails, the whole execution is stopped.

Is it possible to have them all continue to completion, whether failed or passed?

Thanks

Create single line renderer

A renderer that only shows the current active task. Not sure what to do with subtasks though. Maybe render them like Parent > Child.

Skip subtasks without main task failing

Being able to skip a task is great and this piece of code works fine:

{
  title: 'Creating some-directory folder',
  task: (ctx, task) => {
    if (existsSync(someDirectory)) {
      task.skip('The some-directory folder already exists');
      return;
    }

    mkdirSync(someDirectory);
  },
},

However, say you want to iterate over folders inside of the some-directory folder once it's created, it doesn't seem to be possible right now (please let me know if I'm mistaken) to do something like this:

{
  title: 'Creating folders',
  task: (ctx, task) => {
    const tasks = [];

    // imagine some list of folders to iterate over
    folders.forEach((folder) => {
      const name = folder.name;

      tasks.push({
        title: name,
        task: () => { // passing the ctx and task here doesn't seem to have an effect below
          if (existsSync(`${some-directory}/${name}`)) {
            task.skip(`The ${name} folder already exists`); // this skips the entire main task and using throw new Error seems to work (unless there's a task below this one) but that fails the main task as well.
            return;
          }

          mkdirSync(`${some-directory}/${name}`;
        },
      });
    });

    return new Listr(tasks, {concurrent: true, exitOnError: false});
  },
},

Preferably, say you have a list of 10 folders to check and iterate over, if the first two exist but the rest don't, I'd want to gracefully skip over the first two and create the others, without failing the main task.

cc @SamVerschueren

Automatically fallback in non-TTY environments

This has been playing in my head for quite a while now. So we have support for custom (3rd party) renderers like the current embedded update-renderer and the verbose-renderer. The default renderer is the update renderer, also in a non-TTY environment which doesn't work well with ANSI escape codes.

This means that you will have to write something like this.

new Listr([
    // tasks go here
], {
    renderer: process.isTTY ? 'update': 'verbose'
});

I want to get rid of this and automatically fall back to the verbose renderer if Listr internally detects that it is not running in a TTY. This is perfectly possible and I believe that we should really do this. It doesn't make sense to render fancy stuff with log-update for instance of it doesn't work correctly.

But what should be done with custom renderers then? Let's first recap what a custom renderer is and how it can be used. If you look into the custom renderers section of the readme, you can see that the basic structure looks like this.

class CustomRenderer {

    constructor(tasks, options) { }

    render() { }

    end(err) { }
}

We can then use it and pass it in as renderer.

const custom = require('custom-renderer');

new Listr([
    // tasks go here
], {
    renderer: custom
});

What should we do now when executed in a non-TTY environment?

Option 1. Always use the verbose renderer

We could always use the verbose renderer by default. The drawback is that you will have to live with it that the output always looks the same.

Option 2. Custom renderer lets us know that it supports those environments

The custom renderer could set a property indicating if it supports non-TTY environments.

class CustomRenderer {

    constructor(tasks, options) { }

    get nonTTY() {
        return true;
    }

    render() { }

    end(err) { }
}

If Listr detects that it isn't running in a TTY, it will look check if nonTTY returns true, if that is the case it could safely use that renderer. If it returns a falsy value, it will fall back to the verbose renderer.

Option 3. Fallback renderer

If you pass in a custom renderer, it will automatically use that renderer in all environments. Except if you pass in another renderer as fallback renderer.

new Listr([
    // tasks go here
], {
    renderer: custom,
    fallbackRenderer: 'verbose'
});

Options to be decided. Could as well be something like

new Listr([
    // tasks go here
], {
    renderer: {
        default: custom,
        noTTY: 'verbose'
    }
});

Any feedback or input on these ideas would be more then appreciated!

// @sindresorhus @jfmengels @kevva @andywer @okonet

Skipping a sub-task collapses the parent task

I can't tell if this is broken or intended to work this way. If I have a task with two sub-tasks, and the first task has a skip function which returns a skip message to be displayed, and the the second task succeeds, it would appear that the parent task collapses and โ€œsucceedsโ€.

Is that expected? I would expect it to stay uncollapsed so the skip message is visible.

Option to only show subtasks on failure

In np we have a Git task with some subtasks. Those subtasks finish quickly so the whole thing feels very jumpy. It would be nice if we could just not show them at all, but show in case of a failure.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.