Coder Social home page Coder Social logo

siokas / denomander Goto Github PK

View Code? Open in Web Editor NEW
143.0 5.0 9.0 515 KB

Deno command-line interfaces made easy

Home Page: https://doc.deno.land/https/deno.land/x/denomander/docs.ts

License: MIT License

TypeScript 99.70% Dockerfile 0.30%
deno command-line-interface typescript cli cli-app command-line-parser arguments-parser deno-arguments-parser deno-tools

denomander's Introduction

Github Actions deno version Published on nest.land

Denomander is a solution for Deno command-line interfaces. It is inspired by tj's commander.js for Node.js.

Denomander is a Deno project so deno must be installed.

Alternatively, there is a Dockerfile in the root of the project to create an image running deno. To use it just build the Docker file docker build -t deno . Now you can run all the deno commands docker run --rm -v $PWD:/app/ deno test

Installation

Using deno.land

import Denomander from "https://deno.land/x/denomander/mod.ts";

Using nest.land

import Denomander from "https://x.nest.land/denomander/mod.ts";

Usage Example

First, in your deno script, create a program, optionally passing a name, description and version. If not you can change them afterwards by setting the app_name, app_description and app_version variables.

const program = new Denomander({
  app_name: "My App Name",
  app_description: "My App Description",
  app_version: "1.0.1",
});

There are three option types: commands, options and required options.

Options

To set an option just call the option() method passing the short and long flags separated by a space and the description. The value can be accessed as properties.

program
  .command("serve", "Simple Server")
  .option("-a --address", "Define the address")
  .option("-p --port", "Define the port")
  .parse(Deno.args);

if (program.address) {
  const port = program.port || "8000";
  console.log(`Server is running on ${program.address}:${port}`);
}

You may define the option's short and long flags by separating them with a space, comma or | (vertical bar or "pipe").

program
  .command("serve", "Start up the server")
  .option("-a, --address", "Define the address")
  .option("-p | --port", "Define the port")
  .parse(Deno.args);

console.log(`Server is running on ${program.address}:${program.port}`);

Required Options

The implementation of required option is exactly same as the optional option but you have to call the requiredOption() method instead.

program
  .command("serve", "Start up the server")
  .requiredOption("-p --port", "Define the port")
  .option("-a --address", "Define the address")
  .parse(Deno.args);

// The port is required so it must have a value
let address = program.address || "localhost";
console.log(`Server run on ${address}:${program.port}`);

Global Options and Base Command Options

You have the option to define options which belong to all commands (global option) and options which belong to no command (base command option ex. --help, --version).

program
  .baseOption("-q --quiet", "Do not output any message")
  .globalOption("-c --color", "Define the output color")
  .parse(Deno.args);

Custom Option Processing

You may specify a function to do custom processing of option values. The callback function receives a parameter of the pre-processed value.

function parseInteger(value: string): number {
  return parseInt(value);
}

function uppercase(text: string): string {
  return text.toUpperCase();
}

program
  .command("multiply", "Multiply x and y options")
  .option("-x --xnumber", "First Number", parseInteger)
  .option("-y --ynumber", "First Number", parseInteger)
  .action(() => {
    console.log(program.xnumber * program.ynumber);
  });

program
  .command("commit", "Commit Description")
  .requiredOption("-m --message", "Commit Message", uppercase)
  .action(() => {
    console.log(program.message);
  });

Default Option Value

You may define a default value for options (in case no value is passed by the user, the app returns the specified default value as the value of the option)

program
  .command("foo", "Foo Test")
  .option("-d --default", "Default Value", uppercase, "bar")
  .action(() => {
    console.log(program.default);
  });

Option choices

You may define a list (array) of accepted choices for each option. If the user enters anything that is not in this list, a validation error (OPTION_CHOICE_ERROR) is thrown. To define accepted choices, you have to create a custom option object and call the choices() method passing the array of the accepted choices:

const fruits = new Option({
  flags: "-f --fruits",
  description: "Choose one of accepted choices",
}).choices(["apple", "banana", "orange"]);

program
  .command("choose")
  .addOption(fruits)
  .action(() => {
    console.log(`You chose ${program.fruits}`);
  });

Commands

There are two ways to implement the commands. The first is to use an action handler by calling the action() method immediately after the command definition passing the callback function and the second is with custom one-line implementation. Multiple command arguments are now supported!

To define a command just call the .command() method and pass the command name (optionally you may also pass the description and a callback function but if not you may define them afterwards in their own methods). After the command you have the option to declare argument(s) inside brackets []. If you want a not required argument just append a question mark (?) after the name of the argument.

program
  .command("mv [from] [to] [message?]", "Start the server")
  .action(({ from, to, message }: any) => {
    // Do your actions here
    console.log(`File is moved from ${from} to ${to}`);
    if (message) {
      console.log("message");
    }
  });

program.parse(Deno.args);

// Command action calback is called in all 3 command names (actual command and two aliases)

Parse args with spread operator

You have to option to catch the rest of the args passed from the user (in an array)

program
  .command("find [args...]")
  .action(({ args }: any) => {
    console.log(`Files to find (${args.length}): `);
    console.log(args);
  })
  .description("find file");

program.parse(Deno.args);

// Command example:

// > find file1 file2 file3
// Files to find (3):
// [ "file1", "file2", "file3" ]

Action Handler

The argument(s) passed in the callback function is now an object so you may destructure the object and take your variable which has the same name with your command declaration!

program
  .command("clone [foldername]")
  .description("clone a repo")
  .action(({ foldername }: any) => {
    console.log("The repo is cloned into: " + foldername);
  });

program.parse(Deno.args);

Extra Parameters

Any options available in the program are passed to the callback function.

program
  .command("clone [foldername]")
  .description("clone a repo")
  .option("-b --branch", "Branch to clone")
  .action(({ foldername }: any, { branch }: any) => {
    console.log("Repo: " + foldername);
    console.log("Branch: " + branch);
  });

program
  .command("multiply", "Multiply x and y options")
  .option("-x --xnumber", "First Number", parseInteger)
  .option("-y --ynumber", "First Number", parseInteger)
  .action(({ xnumber, ynumber }: any) => {
    console.log(xnumber * ynumber);
  });

program.parse(Deno.args);

Custom Implementation

program.command("serve", "Start the server");

if (program.serve) {
  console.log("The server has started...");
}

program.parse(Deno.args);

Alias

After the command declaration you have the option to declare as many aliases as you want for this spesific command.

program
  .command("serve", "Start the server")
  .alias("server", "start-server")
  .action(() => {
    console.log("the server is started");
  });

program.parse(Deno.args);

// Command action calback is called in all 3 command names (actual command and two aliases)

Sub Commands

After the command declaration you have the option to declare as many sub-command as you want. You may add an action and description for each one.

const parent = program.command("parent");

parent
  .subCommand("child1", "test")
  .action(() => {
    console.log("parent + child 1 commands");
  })
  .description("Sub Command Implementation");

parent
  .subCommand("child2", "test")
  .action(() => {
    console.log("parent + child 2 commands");
  })
  .description("Another Sub Command Implementation");

program.parse(Deno.args);

Option to Change Default Commands (help, version)

In order to change the default commands (help, version) just call the corresponding method. In case of help pass the command and the description but in case of version you may also pass the actual version of the app and after that the command and the description.

program.setVersion("1.8.1", "-x --xversion", "Display the version of the app");

program.parse(args);

Customize Error Messages

There are two ways to change the error messages. You may pass a fourth argument in new Denomander() constructor (errors object) or you may call the .errorMessages() method again passing the error messages in object.

const program = new Denomander({
  app_name: "My MY App",
  app_description: "My MY Description",
  app_version: "1.0.1",
  errors: {
    INVALID_RULE: "Invalid Rule",
    OPTION_NOT_FOUND: "Option not found!",
    COMMAND_NOT_FOUND: "Command not found!",
    REQUIRED_OPTION_NOT_FOUND: "Required option is not specified!",
    REQUIRED_VALUE_NOT_FOUND: "Required command value is not specified!",
    TOO_MANY_PARAMS: "You have passed too many parameters",
  },
});
program.errorMessages({
  INVALID_RULE: "Invalid Rule",
  OPTION_NOT_FOUND: "Option not found!",
  COMMAND_NOT_FOUND: "Command not found!",
  REQUIRED_OPTION_NOT_FOUND: "Required option is not specified!",
  REQUIRED_VALUE_NOT_FOUND: "Required command value is not specified!",
  TOO_MANY_PARAMS: "You have passed too many parameters",
});

Improved Error Experience (Option to Throw Errors)

From v0.8 by default Denomander app does not throw the errors but instead it outputs the error message in the console and exits the app. If you want to throw all the errors just pass the throw_errors: true option inside the AppDetails in Denomander constructor.

const program = new Denomander({
  app_name: "My App Name",
  app_description: "My App Description",
  app_version: "1.0.1",
  throw_errors: true,
});

Used

Meta

Apostolos Siokas – @siokas_[email protected]

Contributing

Any kind of contribution is welcome!

License

Distributed under the MIT License.

https://github.com/siokas/denomander

denomander's People

Contributors

epmills avatar halvardssm avatar kawarimidoll avatar michael-spengler avatar n10000k avatar njpearman avatar pabloszx avatar siokas avatar symbitic avatar vwkd 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

denomander's Issues

Can not specify a description for arguments

I took a quick look at the API and also into printCommandHelp and figured there's no way to specify a description for an argument. This is a bit of a bummer because it limits the usefulness of the generated help text

Description:
Transform service instances using specified handlers

Command Usage:
transform [path] {Options}

Arguments:
path

Required Options:
-o --out 	 Path to the out repo

Options:
-h --help 	 Help Screen 	 

It would be great if we could specify the same kind of description as for options.

improved error experience

It would be really nice when an error occurs to show the following instead of what is there now:

Error: You specified an option (--https) that is not defined for the command (serve).

or something more like bash

serve: illegal option -- https
usage: serve [-ap]

This would be much cleaner for the end user. The current error made me think that there was an error in my code, when the error was actually in the command line I typed.

Excellent module by the way!

Required options scope are program wise instead of command wise

When a required options is set for a command, it must be provide for all commands instead of just the one adding it

deno run https://raw.githubusercontent.com/siokas/denomander/master/example.ts clone folder

outputs : Error: Required option is not specified!

deno run https://raw.githubusercontent.com/siokas/denomander/master/example.ts clone folder -p 8080

outputs: The repo is cloned into: folder

Command with multiple arguments does not work

When a command is provide with more than one argument, VALIDATION_ARG_NOT_FOUND is thrown

repro steps :

import Denomander from "https://raw.githubusercontent.com/siokas/denomander/master/mod.ts";

const program = new Denomander({
  app_name: "My MY App",
  app_description: "My MY Description",
  app_version: "1.0.1",
});

program
  .command("token [email] [password]")
  .action((email: string, password : any) => {
    console.log(email, password);
  })
  .description("get bearer token");

try {
  program.parse(Deno.args);
} catch (error) {
  console.log(error);
}
deno run .\index.ts token email password

outputs Error: Argument not found in available commands!

Have you heard of deno-args ?

Hi,

Allow me to give you a little feedback, do with it what you want, feel free to ignore.

The quality of the code you produce is very good but I think the main problem with commander.js is that it was designed for vanilla JavaScript. Its API design does not allow type inference...

Maybe you should keep the denomander name and the logo which is really cool and join forces with u/KSXGitHub to work on https://github.com/KSXGitHub/deno-args which look really promising.

I think for commander.js a simple fork using denoify to make it cross-compatible is enough.

However, what you did is cool.

Cheers

Non-null assertions (undefined)

Even though it works fine as it is, there should be no non-null assertions in the project. Currently I use the ❗️ postfix operator but I should consider a better logic here. Maybe a non undefined var or a check-before-use solution.

Bug: Invalid option choice! with multiple required options that have predefined choices

Hi, I have two mandatory actions, but Denomander seems to get confused about what the right choices are, it mixes things up. Did I find a bug?

import { default as Denomander, Option as DenomanderOption } from "https://deno.land/x/[email protected]/mod.ts";

const projectActions: string[] = [
  "clone",
  "reset",
  "flavor",
  "clean",
  "prepare",
  "build",
  "tag",
  "release",
  "exec",
  "wipe",
];

const projectNames: string[] = [
  "core",
  "explorer",
  "wallet",
  "desktop",
  "android",
  "ios",
];

program
  .command("proj", "Executes a given action on a project.")
  .option(
    "-s --slug",
    "Execute the action on a different slug than provided in the env configuration. (optional)"
  )
  .option(
    "-b --base-slug",
    "Execute the action from a different slug origin than provided in the env configuration. (optional)"
  )
  .addOption(
    new DenomanderOption({
      flags: "-a --action",
      description: "Action to execute.",
      isRequired: true,
    }).choices(projectActions)
  )
  .addOption(
    new DenomanderOption({
      flags: "-n --name",
      description: "Name of the project.",
      isRequired: true,
    }).choices([...projectNames, "all"])
  )
  .action(async () => {
  });

program.parse(Deno.args);
> deno run ./demo.ts proj -a flavor -n core
❌ Error (a): Invalid option choice! Argument 'flavor' is invalid. Allowed choices are: core,explorer,wallet,desktop,android,ios,all

@siokas Am I missing something?

Validations

All validation logic seems a bit messy and its not scalable.
.validateArgs() .validateOnCommands() .validateRequiredOptions() .validateOptionsAndCommands()

error: missing field `now` at line 9 column 1

deno 1.36.1 (release, x86_64-pc-windows-msvc)
v8 11.6.189.12
typescript 5.1.6

Running a script with a simple import as per documentation

import Denomander from "https://deno.land/x/denomander/mod.ts";

you get this error

C:\temp>deno run -A --unstable test-deanomander-import.ts
error: missing field `now` at line 9 column 1
    at file:///C:/temp/test-deanomander-import.ts:2:24

C:\temp>

Remove the v prefix from std library imports

The following warnings are shown when downloading denomander:

Download https://deno.land/[email protected]/testing/asserts.ts
Warning std versions prefixed with 'v' will be deprecated soon. Please change your import to https://deno.land/[email protected]/fmt/colors.ts (at https://deno.land/[email protected]/fmt/colors.ts)
Download https://deno.land/[email protected]/fmt/colors.ts
Warning std versions prefixed with 'v' will be deprecated soon. Please change your import to https://deno.land/[email protected]/testing/asserts.ts (at https://deno.land/[email protected]/testing/asserts.ts)
Download https://deno.land/[email protected]/testing/asserts.ts
Warning std versions prefixed with 'v' will be deprecated soon. Please change your import to https://deno.land/[email protected]/flags/mod.ts (at https://deno.land/[email protected]/flags/mod.ts)

Cannot use options with name "action"

When using .option("-a --action", "Name of Action") how can i get hold of the option value? It gets overwritten by the action function itself ... In general i dont think its a good idea to expose the options on the program object itself. Why not make everything available at the first param of the action callback ?

Required arguments cannot be hyphenated

The following program complains, Required option is not specified!, even when it is. As far as I can tell, it has to do with the presence of a hyphen in the option.

import Denomander from "https://deno.land/x/denomander/mod.ts";

new Denomander({ app_name: '', app_description: '', app_version: '' })
    .requiredOption('--hyphenated-option', '')
    .parse(Deno.args);

Also, in case I might be preemptive, I suggest only using the invalid issue label for irrelevant issues, as suggested here.

"rest" operator

Is it possible to add an option that pics up all the remaining args? like

program.command("test [args...]").action(({args}) => {
  console.log(args);
});
$ deno run test.ts test foo bar foo2 bar2
foo bar foo2 bar2

Command throws error when parsing number

Hi! I found a bug which happens when passing a number to a command.

How to reproduce:

import Denomander from "https://deno.land/x/denomander/mod.ts";

const program = new Denomander();
program.command("foo [bar]", "Foo")
program.globalOption("--baz", "Baz")
program.parse(Deno.args);

deno run mod.ts foo 1

The error:

error: Uncaught TypeError: text.substr is not a function
    return text.substr(0, 2).replace(/-/g, "") +
                ^
    at Function.stripDashes (https://deno.land/x/[email protected]/src/Helper.ts:5:17)
    at https://deno.land/x/[email protected]/src/Util.ts:112:43
    at Array.find (<anonymous>)
    at Function.findCommandFromArgs (https://deno.land/x/[email protected]/src/Util.ts:111:18)
    at https://deno.land/x/[email protected]/src/Executor.ts:28:30
    at Array.forEach (<anonymous>)
    at Executor.defaultCommands (https://deno.land/x/[email protected]/src/Executor.ts:27:26)
    at Denomander.execute (https://deno.land/x/[email protected]/src/Kernel.ts:130:10)
    at Denomander.run (https://deno.land/x/[email protected]/src/Kernel.ts:218:8)
    at Denomander.parse (https://deno.land/x/[email protected]/src/Denomander.ts:15:10)

Also, when passing an option e.g. deno run mod.ts foo bar --baz buz, both bar and baz will have the value buz instead of foo=bar and baz=buz

why 3 legs?

why the dino in the logo has 3 legs? why not 2 or 4? if you draw the back side of the front right leg, why not also for the back leg?

image

Options value as an object

There should be an object to hold all the option values passed from the arguments. Currently the options are accessible only from the main Denomander object instance.

Consider something similar to this:
const programOptions = program.opts();
console.log(programOptions.name);

examples/example.ts cause error

examples/example.ts throws an error.

screenshot 2021-07-09 14 16 41

OPTION_CHOICE is required in errors object.

  const program = new Denomander(
    {
      app_name: "My MY App",
      app_description: "My MY Description",
      app_version: "1.0.1",
      errors: {
        INVALID_RULE: "Invalid Rule",
        OPTION_NOT_FOUND: "Option not found!",
        COMMAND_NOT_FOUND: "Command not found!",
        REQUIRED_OPTION_NOT_FOUND: "Required option is not specified!",
        REQUIRED_VALUE_NOT_FOUND: "Required command value is not specified!",
        TOO_MANY_PARAMS: "You have passed too many parameters",
+       OPTION_CHOICE: "Invalid option choice!",
      },
    },
  );

The requested module 'denomander/mod.ts' does not provide an export named 'Denomander'

Not able to use import map feature. Please advise.

$ deno run --import-map=import-maps.json my-first-deno-program.js -h
error: Uncaught SyntaxError: The requested module 'denomander/mod.ts' does not provide an export named 'Denomander'
export { Denomander } from "denomander/mod.ts";
         ^
    at <anonymous> (file:///lang/deno/dependencies.js:1:10)
$ cat import-maps.json
{
  "imports": {
    "denomander/": "https://deno.land/x/[email protected]/"
  }
}
$ cat dependencies.js
export { Denomander } from "denomander/mod.ts";
$ cat my-first-deno-program.js
import { Denomander } from "./dependencies.js";

const program = new Denomander({
  app_name: "My App Name",
  app_description: "My App Description",
  app_version: "1.0.1",
});
program
  .command("serve", "Start the server")
  .alias("server", "start-server")
  .action(() => {
    console.log("the server is started");
  });
$ deno info https://deno.land/x/[email protected]/mod.ts | head
local: /home/.cache/deno/deps/https/deno.land/5be3b5383be15d79eb1fd49da8662bc89f5d3df67ccbac67808450d7f673a7e3
emit: /home/.cache/deno/gen/https/deno.land/5be3b5383be15d79eb1fd49da8662bc89f5d3df67ccbac67808450d7f673a7e3.js
type: TypeScript
dependencies: 25 unique (total 133.73KB)
$ cat /home/.cache/deno/deps/https/deno.land/5be3b5383be15d79eb1fd49da8662bc89f5d3df67ccbac67808450d7f673a7e3
import Denomander from "./src/Denomander.ts";
export { default as Option } from "./src/CustomOption.ts";
export { error_log, success_log, warning_log } from "./src/utils/print.ts";

export default Denomander;

'import type' errors from TypeScript in Deno 1.4.0

The reason for the error can be seen here: https://stackoverflow.com/a/63887967

The error message is as follows.

error: TS1371 [ERROR]: This import is never used as a value and must use 'import type' because the 'importsNotUsedAsValues' is set to 'error'.
import { Denomander } from "./Denomander.ts";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    at https://deno.land/x/[email protected]/src/interfaces.ts:1:1

TS1371 [ERROR]: This import is never used as a value and must use 'import type' because the 'importsNotUsedAsValues' is set to 'error'.
import { Arguments } from "./Arguments.ts";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    at https://deno.land/x/[email protected]/src/Validator.ts:2:1

TS1371 [ERROR]: This import is never used as a value and must use 'import type' because the 'importsNotUsedAsValues' is set to 'error'.
import { Kernel } from "./Kernel.ts";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    at https://deno.land/x/[email protected]/src/Validator.ts:3:1

TS1371 [ERROR]: This import is never used as a value and must use 'import type' because the 'importsNotUsedAsValues' is set to 'error'.
import { Command } from "./Command.ts";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    at https://deno.land/x/[email protected]/src/Validator.ts:4:1

TS1371 [ERROR]: This import is never used as a value and must use 'import type' because the 'importsNotUsedAsValues' is set to 'error'.
import { ValidatorContract } from "./interfaces.ts";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    at https://deno.land/x/[email protected]/src/Validator.ts:5:1

TS1371 [ERROR]: This import is never used as a value and must use 'import type' because the 'importsNotUsedAsValues' is set to 'error'.
import { Option } from "./Option.ts";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    at https://deno.land/x/[email protected]/src/Validator.ts:13:1

Optional command arguments

Hi! Is there a way to allow for optional arguments?

Right now, if I use command
program.command("command [value]")
it will result in an error if I don't provide a value.

However, I would like to have this value optional, is this possible in the current version?

Add version to deno.land/x

Hi @siokas !

I am using Denomander for Nessie, but I prefer to use deno.land/x over nest.land to keep it as vanilla as possible. Could you add the new version tags to the package at deno.land/x ?

Thank you for your work with this package!

addOption isRequired/choices not working

program
  .command("open", "Opens a foo.")
  .addOption(
    new Option({
      flags: "-f --foo",
      description: "Open something",
      isRequired: true,
      choices: ["a", "b", "c"],
    })
  )
  .action(async () => {
	  console.log(program.foo);
  });

Testing:

> ./my-test.ts open
undefined
> ./my-test.ts open -f d
d

In both cases it should complain that either -f is missing or d is not a valid choice. This doesn't work.

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.