Code Monkey home page Code Monkey logo

console's Introduction

Webmozart Console

Build Status Build status Scrutinizer Code Quality Latest Stable Version Total Downloads Dependency Status

Latest release: 1.0.0-beta5

PHP >= 5.3.9

A usable, beautiful and easily testable console toolkit written in PHP.

Goal

The goal of this package is:

  • to build PHP applications similar to the "git" command
  • with a minimum amount of code
  • that are testable
  • robust
  • and beautiful.

None of the existing console libraries matched these requirements, so I refactored the Symfony Console component into what you can see here.

Installation

Use Composer to install the package:

$ composer require webmozart/console:~1.0@beta

Basic Configuration

Console applications are configured via configuration classes. As example, we will create the "git" command in PHP:

use Webmozart\Console\Config\DefaultApplicationConfig;

class GitApplicationConfig extends DefaultApplicationConfig
{
    protected function configure()
    {
        parent::configure();
        
        $this
            ->setName('git')
            ->setVersion('1.0.0')
            
            // ...
        ;
    }
}

This basic configuration tells the console that the executable is called "git" and that the current version is 1.0.0. Let's create the "git" executable now:

#!/usr/bin/env php
<?php

use Webmozart\Console\ConsoleApplication;

if (file_exists($autoload = __DIR__.'/../../../autoload.php')) {
    require_once $autoload;
} else {
    require_once __DIR__.'/../vendor/autoload.php';
}

$cli = new ConsoleApplication(new GitApplicationConfig());
$cli->run();

The complicated autoload block makes sure that the autoload file is found both when you run the executable directly in your package and when your package is installed in another project via Composer.

Change the permissions of your executable and try to run it:

$ chmod a+x bin/git
$ bin/git
Git version 1.0.0
...

Commands

So far, our application doesn't do much. Let's add a command "log" that displays the latest commits:

class GitApplicationConfig extends DefaultApplicationConfig
{
    protected function configure()
    {
        $this
            // ...
            
            ->beginCommand('log')
                ->setDescription('Show the latest commits')
                ->setHandler(new LogCommandHandler())
            ->end()
        ;
    }
}

As you can see, the execution of the command is delegated to a LogCommandHandler. Since the handler is a separate class, it can easily be tested in isolation. Let's implement a basic handler:

use Webmozart\Console\Api\Args\Args;
use Webmozart\Console\Api\Command\Command;
use Webmozart\Console\Api\IO\IO;

class LogCommandHandler
{
    public function handle(Args $args, IO $io, Command $command)
    {
        // Simulate the retrieval of the commits
        $commits = array(
            'commit1',
            'commit2',
            'commit3',
        );
        
        foreach ($commits as $commit) {
            $io->writeLine($commit);
        }
        
        return 0;
    }
}

The handle() method of our command handler retrieves up to three arguments:

  • Args $args: The arguments and options passed when calling the command.
  • IO $io: The I/O, which gives access to the standard input, the standard output and the error output.
  • Command $command: The currently executed command.

You can leave away the arguments that you don't need.

Every handler should return 0 if it was processed successfully and any integer between 1 and 255 if it failed.

Let's run the command:

$ bin/git log
commit1
commit2
commit3

Arguments

Next we'll add an argument <branch> to the "log" command with which we can select the branch to display:

use Webmozart\Console\Api\Args\Format\Argument;

class GitApplicationConfig extends DefaultApplicationConfig
{
    protected function configure()
    {
        $this
            // ...
            
            ->beginCommand('log')
                // ...
                
                ->addArgument('branch', Argument::OPTIONAL, 'The branch to display', 'master')
            ->end()
        ;
    }
}

We added an optional argument "branch" with the default value "master". We can access the value of the argument through the Args instance in the handler:

class LogCommandHandler
{
    public function handle(Args $args, IO $io)
    {
        $io->writeLine('Branch: '.$args->getArgument('branch'));
        $io->writeLine('--');
        
        // ...
    }
}

Let's run the command with the argument:

$ bin/git log 1.0
Branch: 1.0
--
commit1
commit2
commit3

The second argument of the addArgument() method accepts a bitwise combination of different flags:

Constant Description
Argument::OPTIONAL The argument is optional. If you don't pass a default value, the default value is null.
Argument::REQUIRED The argument must be passed when calling the command.
Argument::MULTI_VALUED The argument may be passed multiple times. The command handler receives an array of all passed values.
Argument::STRING The argument is parsed as string (the default).
Argument::BOOLEAN The argument is parsed as boolean.
Argument::INTEGER The argument is parsed as integer.
Argument::FLOAT The argument is parsed as float.
Argument::NULLABLE Convert "null" to null.

Options

Options are additional, optional settings that you can pass to your command. Let's add the option --max=<limit> to our command which limits the number of displayed commits to the passed limit:

use Webmozart\Console\Api\Args\Format\Option;

class GitApplicationConfig extends DefaultApplicationConfig
{
    protected function configure()
    {
        $this
            // ...
            
            ->beginCommand('log')
                // ...
                
                ->addOption('max', null, Option::REQUIRED_VALUE, 'The maximum number of commits', 25)
            ->end()
        ;
    }
}

The configuration of options is very similar to the configuration of arguments. We created a --max option, which requires a value. If the option is not set by the user, its value defaults to 25.

We can access the passed value with the getOption() method in our command handler:

class LogCommandHandler
{
    public function handle(Args $args, IO $io)
    {
        // ...
        
        $io->writeLine('Limit: '.$args->getOption('max').' commits');
        
        // ...
    }
}

Additionally, you will frequently need the isOptionSet() method, which tells you whether the user actually passed the option when calling the command.

Let's run the command with the option:

$ bin/git log --max 10
Branch: master
Limit: 10 commits
--
commit1
commit2
commit3

Options support short names that consist of a single character only. Instead of two leading dashes, short option names are only prefixed with one dash, for example: -m. Let's add this alias to our option by setting the second argument of the addOption() method:

use Webmozart\Console\Api\Args\Format\Option;

class GitApplicationConfig extends DefaultApplicationConfig
{
    protected function configure()
    {
        $this
            // ...
            
            ->beginCommand('log')
                // ...
                
                ->addOption('max', 'm', Option::REQUIRED_VALUE, 'The maximum number of commits', 25)
            ->end()
        ;
    }
}

Now the command can also be run like this:

$ bin/git log -m 10

Like for arguments, options support a bitwise combination of different flags that control how the option is processed:

Constant Description
Option::NO_VALUE The option accepts no value. Used for simple on/off settings.
Option::OPTIONAL_VALUE The option accepts a value, but the value is option.
Option::REQUIRED_VALUE The option value needs to be set when passing the option.
Option::MULTI_VALUED The option may be passed multiple times. The command handler receives an array of all passed values.
Option::STRING The option is parsed as string (the default).
Option::BOOLEAN The option is parsed as boolean.
Option::INTEGER The option is parsed as integer.
Option::FLOAT The option is parsed as float.
Option::NULLABLE Convert "null" to null.
Option::PREFER_LONG_NAME The help lists the long form (--max) as suggested way of passing the option (the default).
Option::PREFER_SHORT_NAME The help lists the short form (-m) as suggested way of passing the option.

Dependencies

Very often, our command handlers rely on external services to access information or execute business logic. These services can be injected through the constructor of the command handler. For example, assume that we need a CommitRepository to access the commits listed in the "log" command:

class LogCommandHandler
{
    private $repository;
    
    public function __construct(CommitRepository $repository)
    {
        $this->repository = $repository;
    }
    
    public function handle(Args $args, IO $io)
    {
        $commits = $this->repository->findByBranch($args->getArgument('branch'));
        
        // ...
    }
}

Since the CommitRepository is injected into the command handler, we can easily replace the repository with a mock object when we test the handler.

We also need to change the configuration to inject the repository:

class GitApplicationConfig extends DefaultApplicationConfig
{
    protected function configure()
    {
        $this
            // ...
            
            ->beginCommand('log')
                // ...
                
                ->setHandler(new LogCommandHandler(new CommitRepository()))
            ->end()
        ;
    }
}

If your application grows, a lot of objects will be created whenever the configure() method is executed - even if the commands that need these objects are not executed. Let's change our call to setHandler() to a closure so that the handler is executed on demand:

class GitApplicationConfig extends DefaultApplicationConfig
{
    protected function configure()
    {
        $this
            // ...
            
            ->beginCommand('log')
                // ...
                
                ->setHandler(function () {
                    return new LogCommandHandler(new CommitRepository());
                })
            ->end()
        ;
    }
}

Now, the LogCommandHandler and its dependencies are only created when "log" command is executed.

Sub-Commands

The "log" command was a very simple example, but many real-world use cases are more complicated than that. Consider the "git remote" command, which is split into several sub-commands:

$ git remote
$ git remote add ...
$ git remote rename ...
$ git remote remove ...

Such sub-commands can be introduced with the beginSubCommand() method in the configuration:

class GitApplicationConfig extends DefaultApplicationConfig
{
    protected function configure()
    {
        $this
            // ...
            
            ->beginCommand('remote')
                ->setDescription('Manage the remotes of your Git repository')
                ->setHandler(function () {
                    return new RemoteCommandHandler(new RemoteManager());
                })
                
                ->beginSubCommand('list')
                    ->setHandlerMethod('handleList')
                ->end()
                
                ->beginSubCommand('add')
                    ->setHandlerMethod('handleAdd')
                    ->addArgument('name', Argument::REQUIRED, 'The remote name')
                    ->addArgument('url', Argument::REQUIRED, 'The remote URL')
                ->end()
                
                // ...
            ->end()
        ;
    }
}

Like regular commands, sub-commands accept options, arguments and command handlers. However, instead of creating one command handler per sub-command, it is often more convenient to create a single handler with one method per sub-command. The handler method can be selected with setHandlerMethod().

The basic implementation of our RemoteCommandHandler is very similar to the command handler of the "log" command:

class RemoteCommandHandler
{
    private $manager;
    
    public function __construct(RemoteManager $manager)
    {
        $this->manager = $manager;
    }
    
    public function handleList(Args $args, IO $io)
    {
        $remotes = $this->manager->getRemotes();
        
        // ...
        
        return 0;
    }
    
    public function handleAdd(Args $args)
    {
        $name = $args->getArgument('name');
        $url = $args->getArgument('url');
        
        $this->manager->addRemote($name, $url);
        
        return 0;
    }
}

If we inject a working RemoteManager, we can execute both commands now:

$ git remote add origin http://example.com
$ git remote list

If we want to execute the "list" command by default if no sub-command is selected, we need to mark it as default command with markDefault():

class GitApplicationConfig extends DefaultApplicationConfig
{
    protected function configure()
    {
        $this
            // ...
            
            ->beginCommand('remote')
                // ...
                
                ->beginSubCommand('list')
                    // ...
                    
                    ->markDefault()
                ->end()
                
                // ...
            ->end()
        ;
    }
}

Now we can run the list command with:

$ git remote

At last, you can use markAnonymous() if you want to run the command just with git remote, but not with git remote list.

Colors and Styles

The console supports colors and styles in the output that you pass to the IO class. Let's output some bold text for example:

$io->writeLine("Current branch: <b>$branch</b>");

Just like in HTML, the styles are inserted in your text using markup tags. The following styles are defined by default:

Tag Description
<b> Bold text
<u> Underlined text
<bu> Bold and underlined text
<c1> Colored text (default: cyan)
<c2> Colored text (default: yellow)
<warn> Black text with a yellow background
<error> White text with a red background

You can change the existing styles or add custom styles using the addStyle() method in your configuration:

use Webmozart\Console\Api\Formatter\Style;

class GitApplicationConfig extends DefaultApplicationConfig
{
    protected function configure()
    {
        $this
            // ...
            
            ->addStyle(Style::tag('c3')->fgMagenta())
        ;
    }
}

Here is a list of methods that can be called on the Style class:

Method Description
bold() Bold text
underlined() Underlined text
blinking() Blinking text
inverse() Invert the foreground and background color
hidden() Hidden text
fgDefault() Use the system's default text color
fgBlack() Black text
fgRed() Red text
fgGreen() Green text
fgYellow() Yellow text
fgBlue() Blue text
fgMagenta() Magenta text
fgCyan() Cyan text
fgWhite() White text
bgDefault() Use the system's default background color
bgBlack() Black background
bgRed() Red background
bgGreen() Green background
bgYellow() Yellow background
bgBlue() Blue background
bgMagenta() Magenta background
bgCyan() Cyan background
bgWhite() White background

Tables

You can draw tables with the Table class:

use Webmozart\Console\UI\Component\Table;

$table = new Table();

$table->setHeaderRow(array('Remote Name', 'Remote URL'));

foreach ($remotes as $remote) {
    $table->addRow(array($remote->getName(), $remote->getUrl()));
}

$table->render($io);

Tables support word wrapping, so that overlong text does not break the output. The default output of the above table is similar to this:

+-------------+-------------------------------+
| Remote Name | Remote URL                    |
+-------------+-------------------------------+
| origin      | http://example.com/repository |
| fork        | http://fork.com/repository    |
+-------------+-------------------------------+

You can change the style of the table by passing a custom TableStyle to the constructor. Either build your own TableStyle or use one of the predefined ones:

use Webmozart\Console\UI\Component\Table;
use Webmozart\Console\UI\Component\TableStyle;

$table = new Table(TableStyle::solidBorder());
┌──────────────┬───────────────────────────────┐
│ Remote Name  │ Remote URL                    │
├──────────────┼───────────────────────────────┤
│ origin       │ http://example.com/repository │
│ fork         │ http://fork.com/repository    │
└──────────────┴───────────────────────────────┘

The following predefined table styles exist:

Style Description
TableStyle::asciiBorder() Uses ASCII characters for the border (the default)
TableStyle::solidBorder() Uses Unicode characters for the border
TableStyle::borderless() No border

Using Symfony Helpers

Since this library is a complete refactor of the Symfony Console component you can not use the Symfony Console helpers out of the box. If you want to use them you need to wrap the input and output accordingly:

use Webmozart\Console\Adapter\ArgsInput;
use Webmozart\Console\Adapter\IOOutput;

$colors = $helper->ask(new ArgsInput($args->getRawArgs(), $args), new IOOutput($io), $question);

If you find this to cumbersome and you use it many times in your code, consider using traits:

use Webmozart\Console\Adapter\ArgsInput;
use Webmozart\Console\Adapter\IOOutput;

trait QuestionTrait
{
    protected function ask(Args $args, IO $io, Question $question)
    {
        // You could cache these instances, but you probably won't need it
        $helper = new QuestionHelper();

        return $helper->ask(new ArgsInput($args->getRawArgs(), $args), new IOOutput($io), $question);
    }
}

class MyCommandHandler
{
    use QuestionTrait;

    public function handle(Args $args, IO $io)
    {
        $question = new ChoiceQuestion(
            'Please select your favorite colors (defaults to red and blue)',
            array('red', 'blue', 'yellow'),
            '0,1'
        );

        $question->setMultiselect(true);

        $colors = $this->ask($args, $io, $question);
    }
}

Authors

Contribute

Contributions to are very welcome!

Support

If you are having problems, send a mail to [email protected] or shout out to @webmozart on Twitter.

License

All contents of this package are licensed under the MIT license.

console's People

Contributors

djmattyg007 avatar lyrixx avatar pkruithof avatar rommsen avatar sstok avatar stof avatar tgalopin avatar tolry avatar webmozart avatar wouterj 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

console's Issues

Error: Call to a member function getOptions() on null

I currently try to setup an cli application with this library. I stepped through the instructions from the README file and now wanted to test my setup, but I run into this error:

(paths are slightly shortened for readability)

PHP Fatal error:  Uncaught Error: Call to a member function getOptions() on null in .../vendor/webmozart/console/src/Api/Config/Config.php:122
Stack trace:
#0 .../vendor/webmozart/console/src/ConsoleApplication.php(122): Webmozart\Console\Api\Config\Config->getOptions()
#1 .../Bin/melior(30): Webmozart\Console\ConsoleApplication->__construct(Object(Melior\CLI\MeliorApplicationConfig))
#2 {main}
  thrown in .../vendor/webmozart/console/src/Api/Config/Config.php on line 122

Fatal error: Uncaught Error: Call to a member function getOptions() on null in .../vendor/webmozart/console/src/Api/Config/Config.php on line 122

Error: Call to a member function getOptions() on null in .../vendor/webmozart/console/src/Api/Config/Config.php on line 122

Call Stack:
    0.0022     359184   1. {main}() .../Bin/melior:0
    0.5231    2424888   2. Webmozart\Console\ConsoleApplication->__construct() .../Bin/melior:30
    0.5775    3043232   3. Melior\CLI\MeliorApplicationConfig->getOptions() .../vendor/webmozart/console/src/ConsoleApplication.php:122

This occures with the full setup but also with rudimentary setup (just setting name and version for the app). I tried to debug this up to some point, but I had no success by now. The error message always stays the same and I don't really get why, thus I'm assuming a bug in the library.

As far as I can see, the ConsoleApplication somehow invokes the method getOptions() on my configuration class but I can't find an instruction to add this method manually, neither there is a corresponding method in the DefaultApplicationConfig class.

You can find all related files that are involved in the setup here: https://gist.github.com/MCStreetguy/ef98687affd1adc75d7584ce18fa46e4

Thanks in advance for participation in my problem, Max.

Table separator

Is there a way to render table separator, just like in Symfony Console?

$table->addRow(new TableSeparator);

Wrong display of long option description

When I add an option with a long description the text wraps incorrectly.

See the description of the --project-folder option.
screen-img

->addOption(
    'project-folder',
    null,
    Option::OPTIONAL_VALUE,
    'The root folder of the project, this is where the ".dancer" folder is located. '.
    'When omitted the folder is automatically searched by the first occurrence of the ".dancer" folder
    in the parent directory structure, and falls back to the current working.'
)

Is the project still alive?

Sorry to bother but the project hasn't received an update in a very long time. I'm looking for something like this and I wonder if this is still being developed. Thanks.

[meta] Integrate hoa/console

Hello :-),

This is more a meta issue in order to discuss about your wishes to integrate Hoa\Console inside this awesome project (cf. your initial tweet and the tweet about opening an issue).

Recapitulare

Hoa\Console is more a low-level API for the terminal. First of all, every API is based on tput (aka terminal info' or terminal capabitilies). This makes everything much more compatible with the plenty of terminals we have in the wild (see Control the terminal, the right way). Based on that, we —at Hoa— have designed an API for the cursor (Hoa\Console\Cursor), the window (Hoa\Console\Window) and the mouse (Hoa\Console\Mouse). And based on that, we have designed a powerful readline (Hoa\Console\Readline), with, of course, autocompleters. Example:

Autocompleters in action

Apart from that, we have also designed an option parser and reader (resp. Hoa\Console\Parser and Hoa\Console\GetOption). They are “standalone” classes. To make it works easier with the rest of the environment, we provide Hoa\Router\Cli as another class of another library. And Hoa\Console\Dispatcher\Kit with Hoa\Dispatcher (another library) ease the development of complete commands (as defined by your project). Hoa\Router and Hoa\Dispatcher are totally optional, this must be clear.

Finally, there is also processus control with Hoa\Console\Processus: Allowing to interactivly communicate with a processus (->on('output'), ->on('input') etc.).

The documentation of Hoa\Console can be found on Hoa's website: http://hoa-project.net/Literature/Hack/Console.html.

Needs and wishes

Now, we are ready to collaborate to integrate Hoa\Console inside webmozart/console because we think this project provides a very nice high-level API. This kind of API is missing in Hoa since this is out of our goal and it can “hurt” users in some particular cases. I personally think we have a big opportunity to make a great tool here!

The only question I have is: What are your needs and wishes?

/cc @hoaproject/hoackers and especially @jubianchi who did write the Symfony bridge to Hoa\Console with the bundle. Maybe these projects can be re-used.

ChoiceQuestion use <hl> tag when auto-completing

Hey there,

When using the symfony's ChoiceQuestion with this console, we end up with a wrong auto-completion containing <hl> tags.

Example:

Hello world?
  [0] yes
  [1] no
 > y<hl>es</hl>

The fix is quite easy on my side:

$configuration->addStyle(Style::tag('hl')->bold());

But I think it could be nice to implement it directly in the library.

Cheers mate,
Alain

How to set default command?

Hi, thanks for the library!

I have a program with the run command, how to set it as the default command?

My code:

$this->beginCommand('run')
                ->markDefault()
                ->setDescription('Run analizer metrics')
                ->setHandler(new \Insphptor\Program\Commands\RunCommand())
                ->beginSubCommand('export')
                    ->setHandlerMethod('export')
                    ->setDescription('Export result in ' . config()['export'] . ' file')
                    ->addArgument('view', Argument::OPTIONAL, 'The view system', 'default')
                    ->addOption('open', 'o', Option::BOOLEAN, 'Open server with result in view')
                ->end()
        ->end();

The markDefault no work, has alternative for it?

Is the command name character restriction necessary?

I recently set up a command line application with this library and I'm very impressed about it.
The possibilities totally fulfill what I tried to achieve.

But now I stumbled upon a restriction in the command names.
I wanted to create "namespaced" commands, just as e.g. the Flow Framework does.
I tried to register core:cache:flush but I receive an Exception, telling me that only word characters and hyphens could be used as name.

Is there any reasonable cause for this restriction?
Would it be possible to add more chars to the pattern or even leave the naming completely up to the developer?

Any widget like ProgressBar,confirm,select etc. like yii2 Concole component did.

see what yii2 have

clearScreen
clearScreenBeforeCursor
saveCursorPosition
moveCursorUp
updateProgress
select
error
beginAnsiFormat
xtermBgColor
moveCursorDown
stdin
startProgress
scrollDown
scrollUp
ansiFormatCode
renderColoredString
input
moveCursorNextLine
showCursor
wrapText
ansiToHtml
output
stdout
clearLine
endProgress
confirm
hideCursor
isRunningOnWindows
moveCursorForward
restoreCursorPosition
BaseConsole
ansiFormat
xtermFgColor
ansiStrlen
getScreenSize
clearLineBeforeCursor
stderr
moveCursorBackward
streamSupportsAnsiColors
moveCursorTo
escape
clearLineAfterCursor
markdownToAnsi
moveCursorPrevLine
stripAnsiFormat
endAnsiFormat
clearScreenAfterCursor
prompt

I tinks this is very convenience to have the ability to draw these widgets
Any plan to adding this feature?

Is it possible to use Symfony Helpers (i.e. Questions etc.)?

Hi,

awesome library.

I am thinking of ditching symfony console in favor of this one. But we make heavy usage of Symfonys Helpers (http://symfony.com/doc/current/components/console/helpers/questionhelper.html etc.). Here: #7 it says that its available but I am not sure how to use it.

I was thinking about

public function handleAdd(Args $args, IO $io, Command $command)
{
    $helper = new QuestionHelper();
    $question = new ChoiceQuestion(
        'Please select your favorite colors (defaults to red and blue)',
        array('red', 'blue', 'yellow'),
        '0,1'
    );
    $question->setMultiselect(true);
    $colors = $helper->ask($io->getInput(), $io->getOutput(), $question);

but I get
QuestionHelper::ask() must implement interface Symfony\Component\Console\Input\InputInterface, instance of Webmozart\Console\Api\IO\Input given

Is it correct that I can not use the helpers?

Missing methods in ApplicationConfig

During my work on a puli/cli ticket I get the following Scrutinizer bug message:

The method beginOptionCommand does only exist in Webmozart\Console\Api\Config\CommandConfig, but not in Webmozart\Console\Api\Config\ApplicationConfig.

This may be an issue for projects switching from CommandConfig to ApplicationConfig.

It looks like the classes in the Config namespace need an interface or some template methods in the abstract Config class.

@webmozart: What would you prefer to make the classes replaceable by offering the same API?

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.