Git Product home page Git Product logo

cmd-ts's People

Contributors

americas avatar beaucollins avatar chuckwondo avatar deanshub avatar dependabot[bot] avatar elderapo avatar github-actions[bot] avatar igalklebanov avatar ldub avatar mutewinter avatar netanelgilad avatar nevir avatar plumdog avatar renovate[bot] avatar schniz avatar splintor 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

cmd-ts's Issues

Rebuild website

I noticed that the docs for flags are still wrong -- this was fixed by f6f0ce9, but the website hasn't been rebuilt since then, or is being rebuilt on an even older commit, so the fix doesn't show up. This is a point of friction for someone new trying to use this library :)

Add `aliases` to `subcommands`

Unless I'm missing something or using things incorrectly, it is not possible to specify aliases for "parent" commands that use subcommands. Only "leaf" commands can be aliased. It would be nice to be able to alias "parent" commands.

Grammatical error in error messages

When there are "with highlight" and "with no highlight" errors, the text that precedes the "with no highlight" error(s) is one of the following, depending on plurality:

Along the following error:
Along the following errors:

The messages should include the word "with":

Along with the following error:
Along with the following errors:

How to deal with floats

I've only found this in docs:

- Some arguments may be an integer; so providing a float should result in an error

however when I pass a float ex. 0.22 it parses to 0.

Support for required arguments

There doesn't seem to be any support for requiring that arguments are present? I skimmed the implementation briefly, but I'm not seeing anything about requiring positionals.

This is also a bigger issue since the implementation uses a handler instead of returning optional values; since the handler will never get called, I have to do my own validation outside of cmd-ts, which โ€ฆ largely defeats the purpose of it. :P

Action Required: Fix Renovate Configuration

There is an error with this repository's Renovate configuration that needs to be fixed. As a precaution, Renovate will stop PRs until it is resolved.

Error type: undefined. Note: this is a nested preset so please contact the preset author if you are unable to fix it yourself.

How to execute shell commands inside `handler`?

I tried doing this:

import { command, optional, positional, run, string } from "cmd-ts"
import { execa } from "execa"

const cmd = command({
  name: "gitty",
  description: "automate git",
  version: "0.0.1",
  args: {
    query: positional({ type: optional(string), displayName: "query" }),
  },
  handler: async (args) => {
      const { stdout } = await execa("echo", ["unicorns"])
      console.log(stdout)

  },
})

run(cmd, process.argv.slice(2))

But then if you try run it, you won't get echo unicorns as output.

If you run below code outside the handler:

      const { stdout } = await execa("echo", ["unicorns"])
      console.log(stdout)

It works as expected. Not sure why that happens or how to fix.

readme cleanup request

From what I can see, the README.md file doesn't mention that any docs exist.
But they do exist, they are just hidden inside of the "docs" subdirectory. That's a big problem.

Additionally, right now, there is a bunch of info in the README.
Should this information be merged into the docs?
I think a good end state might be for the README file to be empty with a link to the docs.

Negative Numbers and Strings starting with "-" are not parsed

Hello everyone, thanks to @Schniz for this neat library!

I've run into a problem with negative numbers and strings starting with "-". Given this configuration:

command({
    name: 'show-my-number',
    args: {
        myNumber: positional({
            type: number,
            displayName: 'my-number',
        }),
    },
    handler: (args) => {
        console.log(`This is my number: ${args.myNumber}`)
    }
})

When I run show-my-number -10, I get:

error: found 2 errors

-10
^ Unknown arguments

Along the following error:
2. No value provided for my-number

This also occurs when setting the type of myNumber to string, as well as when using optional instead of positional.

Issues with flags

import * as cli from "cmd-ts";

cli.flag({
  long: "skip",
  type: cli.boolean, // First of all it's possible to specify type here. Unessesery I think.
  defaultValue: () => true, // Default value gets printed in help but is not actually a default in handler.
  defaultValueIsSerializable: true,
})

Optional positional arguments

Thanks for the great library, I was looking for something resembling cmdliner (OCaml's library) and was happy to find cmd-ts!

A question I have which I wasn't able to figure out by myself: is it possible to implement optional positional arguments?

I know this is kinda possible with restPositional combinator but this outputs a list and not a single value.

Customizable output

It'd be pretty neat if there were some way I could customize the various bits of output. Like one thing, padding all the subcommand names so they align properly. Introducing chalk colorization here and there. I would just like to be able to pass in things into run to allow customizing the "rendering" of various bits.

flag docs issue

Snippet from docs:

import { command, boolean, flag } from 'cmd-ts';

const myFlag = option({
  type: boolean,
  long: 'my-flag',
  short: 'f',
});

const cmd = command({
  name: 'my flag',
  args: { myFlag },
});

flag is unused. Also, the example is not type-correct:

Argument of type '{ name: string; args: { myFlag: any; }; }' is not assignable to parameter of type 'CommandConfig<{ myFlag: any; }, HandlerFunc<{ myFlag: any; }>>'.
  Property 'handler' is missing in type '{ name: string; args: { myFlag: any; }; }' but required in type 'CommandConfig<{ myFlag: any; }, HandlerFunc<{ myFlag: any; }>>'.ts(2345)

Ability to throw something to trigger logging message w/specific exit code, no stack trace

I would like to abort a CLI handler by throwing something, but control the exit code, and not see a stack trace.

I can think of two ways this might work:

a) ability to throw new CliError('message to log', exitCode)
b) optional errorHandler callback receives a thrown error and decides how to handle it. For example, any errors from an underlying http client can be logged in a specific format and exit with a specific exit code

I tried to use Exit for this, but it's not exported, and comments say it's an internal implementation detail of cmd-ts.

export class Exit {

I'm using runSafely, but I had to copy the implementation of run into my code, and it's undocumented. Could this be as simple as explaining runSafely in the README?

allow `subcommands` to be built/executed lazily

Something like:

  1. dynamic command implementations will allow to lazily load the dependency tree instead of forcing cmd-ts apps to be imported sync

    subcommands({
      cmds: { hello: async () => (await import('./my-command')).cmd }
    })
  2. async functions for the different commands can allow to lazily bootstrap the CLI

    subcommands({
      cmds: () => Promise.resolve({ hello: ... })
    })
  3. mixing them both can allow extremely dynamic applications ๐Ÿ˜ฎ

Add a `conditional` combinator

Needs design though. I thought about something like

conditional({ long: string, short?: string }, ArgParser<T>) // ArgParser<T | undefined>

So you can describe something like

command({
  // ...,
  args: {
    // ...,
    deploy: conditional(
      {
        long: 'deploy'
      },
      object({
        tag: ... // can be required if `--deploy` was provided!
      })),
  },
  // then it'll be more declarative
  handler({ deploy }) {
    if (deploy) {
      deploy.tag // ๐Ÿ˜
    }
  }
}

Add a `object` combinator

The basic should be the same as command, and maybe command should use it internally:

object({
  someName: ArgParser<T>,
  otherName: ArgParser<R>,
}) => ArgParser<{ someName: T, otherName: R }>

Suggestion: quote option values when reporting errors

Positional arguments and options' values should be quoted when reporting errors to avoid confusion whenever the values include spaces.

The current lack of quoting makes it hard to parse what exactly was part of what argument/option. There is highlighting in the output, but if it were to be copied over (like when asking for help online) that does not carry over.

Example

Consider the following CLI:

mycat --prefix <text> --transform <case> <file>

Where:

  • <text> is any text (no particular parsing/validation)
  • <file> is an existing file path
  • <case> is one of 'upper', 'lower', or 'asis' (default)

Then the following scenarios in the current version:

$> mycat --prefix 'My beautiful text:' --transform "messing around" nonexistent\ file\ with\ spaces
error: found 2 errors

  mycat --prefix My beautiful text: --transform messing around nonexistent file with spaces
                                                               ^ File does not exist

  mycat --prefix My beautiful text: --transform messing around nonexistent file with spaces
                                    ^ Invalid value 'messing around'. Expected one of: 'upper', 'lower', or 'asis'

It would be much better if it instead outputed:

$> mycat --prefix 'My beautiful text:' --transform "messing around" nonexistent\ file\ with\ spaces
error: found 2 errors

  mycat --prefix 'My beautiful text:' --transform 'messing around' 'nonexistent file with spaces'
                                                                   ^ File does not exist

  mycat --prefix 'My beautiful text:' --transform 'messing around' 'nonexistent file with spaces'
                                      ^ Invalid value 'messing around'. Expected one of: 'upper', 'lower', or 'asis'

Possible extensions/issues

Extra care need to be put if the input value features quotes as well, which might need to not break the quoting in the output.

Options/Flags at any level

It would be nice to be able to provide non-positional options and flags at any level of the subcommand hierarchy.

The most obvious use for this is providing flags/options at the root, such as verbosity and other flags.

Having to define a -v flag on every command seems bit wasteful. It seems that all flags/options from parents could be folded into the handler params.

Documentation with 'directory imports' is unclear

This would probably be better as a Discussion, sorry; but they don't seem enabled on this repo.

The documentation uses:

import { ExistingPath } from 'cmd-ts/batteries/fs';

โ€ฆ I haven't been around the JS ecosystem much recently, but I cannot, for the life of me, get this to work out-of-the-box in 2023 JavaScript? (Node 18 or Node 20; TypeScript 5.1.6.)

  1. Using it directly in an .mts file leads to Node complaining โ€ฆ

    $ node bin.mjs
    node:internal/process/esm_loader:46
          internalBinding('errors').triggerUncaughtException(
                                    ^
    
    Error [ERR_UNSUPPORTED_DIR_IMPORT]: Directory import '/Users/ec/Sync/Code/jsonc-merge/node_modules/cmd-ts/batteries/fs' is not supported resolving ES modules imported from /Users/ec/Sync/Code/jsonc-merge/bin.mjs
    Did you mean to import cmd-ts/dist/cjs/batteries/fs.js?
        at new NodeError (node:internal/errors:405:5)
        at finalizeResolution (node:internal/modules/esm/resolve:218:17)
        ... {
      code: 'ERR_UNSUPPORTED_DIR_IMPORT',
      url: 'file:///Users/ec/Sync/Code/jsonc-merge/node_modules/cmd-ts/batteries/fs'
    }
    
    Node.js v20.5.0

(I also tried --experimental-specifier-resolution=node, although I don't think that's relevant in 2023 โ€ฆ)

  1. I tried to manually resolve the path, following the package.json in the otherwise-empty cmd-ts/batteries/fs folder, but that just leads to a different error about named exports:

    $ cat bin.mts
    import { command, run, string, restPositionals } from "cmd-ts"
    import { ExistingPath } from "cmd-ts/dist/esm/batteries/fs.js"
    
    // ...
    
    $ node bin.mjs
    file:///Users/ec/Sync/Code/jsonc-merge/bin.mjs:2
    import { ExistingPath } from "cmd-ts/dist/esm/batteries/fs.js";
             ^^^^^^^^^^^^
    SyntaxError: Named export 'ExistingPath' not found. The requested module 'cmd-ts/dist/esm/batteries/fs.js' is a CommonJS module, which may not support all module.exports as named exports.
    CommonJS modules can always be imported via the default export, for example using:
    
    import pkg from 'cmd-ts/dist/esm/batteries/fs.js';
    const { ExistingPath } = pkg;
    
        at ModuleJob._instantiate (node:internal/modules/esm/module_job:122:21)
        at async ModuleJob.run (node:internal/modules/esm/module_job:188:5)
        at async DefaultModuleLoader.import (node:internal/modules/esm/loader:228:24)
        at async loadESM (node:internal/process/esm_loader:40:7)
        ...
    
    Node.js v20.5.0
  2. Okay, fine; I follow the instructions in that error-message to manually destructure the package-root โ€ฆ and get another error, because now, having manually resolved that path, Node no longer sees "type": "module" for that file, I guess?

    $ cat bin.mts
    import { command, run, string, restPositionals } from "cmd-ts"
    import CmdBatteriesFs from "cmd-ts/dist/esm/batteries/fs.js"
    const { ExistingPath } = CmdBatteriesFs
    
    // ...
    
    $ node bin.mjs                                         
    (node:46165) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
    (Use `node --trace-warnings ...` to show where the warning was created)
    /Users/ec/Sync/Code/jsonc-merge/node_modules/cmd-ts/dist/esm/batteries/fs.js:1
    import { extendType, string } from '..';
    ^^^^^^
    
    SyntaxError: Cannot use import statement outside a module
        at internalCompileFunction (node:internal/vm:73:18)
        at wrapSafe (node:internal/modules/cjs/loader:1153:20)
        at Module._compile (node:internal/modules/cjs/loader:1197:27)
        at Module._extensions..js (node:internal/modules/cjs/loader:1287:10)
        at Module.load (node:internal/modules/cjs/loader:1091:32)
        at Module._load (node:internal/modules/cjs/loader:938:12)
        at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:165:29)
        at ModuleJob.run (node:internal/modules/esm/module_job:192:25)
        at async DefaultModuleLoader.import (node:internal/modules/esm/loader:228:24)
        at async loadESM (node:internal/process/esm_loader:40:7)
    
    Node.js v20.5.0

So: How the heck do I actually use cmd-ts/batteries/fs from an ESM in a TypeScript project? (And, probably, the documentation around this could be improved โ€ฆ) :x

tsconfig.json
{
   "$schema": "https://json.schemastore.org/tsconfig",

   "compilerOptions": {
      "strict": true,
      "target": "es2019",
      "module": "Node16",

      "declaration": true,
      "declarationMap": true,
      "inlineSourceMap": true,

      "isolatedModules": true,
      "esModuleInterop": true,
      "forceConsistentCasingInFileNames": true
   }
}

Allow `help` to exit with status other than 1

I would like to be able to exit with status 0 when specifying -h or --help, but it appears that 1 is hard-coded as the exit status. Perhaps providing a means for specifying a different exit status when help is invoked would be nice.

Shell completions

Was wondering if anyone created some sort of shell completion generator which works with cmd-ts

Batteries cannot be imported with `"module": "NodeNext"`

import {File} from 'cmd-ts/batteries/fs';

Can be imported when using tsconfig "module": "CommonJS", but in "NodeNext" it breaks with the error: Cannot find module 'cmd-ts/batteries/fs' or its corresponding type declarations.

I'm not sure if this is an issue with how cmd-ts uses a package.json to redirect to dist, or if it's a bug in TypeScript's resolver. I'm using TS 4.9.5

Deno Support

Awesome library. I'm currently using cmd-ts within a Deno application. Everything behaves beautifully with one exception: at the end of every run is an error related to (what appears to be) a NodeJS cleanup hook (?).

error: Uncaught TypeError: __Process$.exit is not a function
    at N.run (https://cdn.esm.sh/v66/[email protected]/es2021/cmd-ts.js:2:4443)
    at Module.ve (https://cdn.esm.sh/v66/[email protected]/es2021/cmd-ts.js:9:66)
    at async file:///Users/harrysolovay/Desktop/capi/cli/bin.ts:8:1

Using dryRun suppresses this error... but then there's no console output. Any tips on smoothing this out?

Suggestion: optional `summary` parameter for `command` and `subcommands`

This is a suggestion to add an optional summary configuration field in command and subcommands which would be referenced when a short description is needed/preferable, like when listings choices of subcommands.

This would allow having both a concise description when a command or subcommand appears in a list in another command's help, and a long-form description with all the necessary details to keep in mind when using the CLI when viewing the command's help.

Example

consider the following example:

#!/bin/env node
// machine.ts

import { command, subcommands, binary } from "cmd-ts";

const brewCoffeeCmd = command({
  name: "brew coffee",
  description: // long description, maybe featuring notes or example usage
`Brew a cup of coffee.

Lorem ipsum dolor sit amet...
`,
  summary: "Brew a cup of coffee",
  args: { /* args */ },
  handler: () => {
    console.log("Brewing coffee...");
  },
});

const brewCocoaCmd = command({
  name: "brew cocoa",
  description: // long description, maybe featuring notes or example usage
`Brew a cup of hot cocoa.

Lorem ipsum dolor sit amet...
`,
  summary: "Brew a cup of hot cocoa",
  args: { /* args */ },
  handler: () => {
    console.log("Brewing hot cocoa...");
  },
});

const brewCmd = subcommands({
  name: "brew",
  description: // long description, maybe featuring notes or example usage
`Brew a cup of a selection of hot drinks.

Lorem ipsum dolor sit amet...
`,
  summary: "Brew a hot drink",
  cmds: {
    coffee: brewCoffeeCmd,
    cocoa: brewCocoaCmd,
  },
});

const payCmd = command({
  name: "pay",
  description: // long description, maybe featuring notes or example usage
`Pay for a drink.

Lorem ipsum dolor sit amet...
`,
  summary: "Pay your drink",
  args: { /* args */ },
  handler: () => {
    console.log("Payment complete, enjoy your drink!");
  },
});

const machineCmd = subcommands({
  name: "machine",
  description: "coffee machine CLI utility",
  summary: "available but unused?",
  cmds: {
    brew: brewCmd,
    pay: payCmd,
  },
});

void run(binary(machineCmd), process.argv);

Which would generate the following help pages:

$> machine --help
machine <subcommand>
> coffee machine CLI utility

Where <subcommand> can be one of:

- brew - Brew a hot drink
- pay - Pay your drink

$> machine brew --help
brew <subcommand>
> Brew a cup of a selection of hot drinks.

Lorem ipsum dolor sit amet...

Where <subcommand> can be one of:

- coffee - Brew a cup of coffee
- cocoa - Brew a cup of hot cocoa

$> machine pay --help
pay
> Pay for a drink.

Lorem ipsum dolor sit amet...

and so on.

Adding ability to accept unknown arguments

Usecase:
Say I'm using cmd-ts to wrap another CLI call.
And I don't want to map each of the 2nd CLI's arguments, but just forward them from the original call.

something like:

cmdTs.command({
	name: 'runner',
	acceptUnknownArguments: true,
	args: {
		myArg: flag({
			type: boolean,
			long: 'my-arg',
		}),
		handler: ({ myArg, unknownArgs }) => {
			if (myArg) {
				execa(`node ./otherCli ${unknownArgs.join(' ')}`)
			}
		},
	},
})

Can we export the type of `args` ?

Hi ๐Ÿ‘‹ Neat project!

It'd be great to be able to write

import { command, run, string, positional } from 'cmd-ts';

// here, we have a handler decoupled from the means of acquiring input
import { myHandler } from './handler'

const app = command({
  name: 'my-first-app',
  args: {
    someArg: positional({ type: string, displayName: 'some arg' }),
  },
  handler: myHandler // I guess this makes me a sucker for tacit programming
});

run(app, process.argv.slice(2));

Thereby allowing myHandler to be invoked in tests without the cmd-ts integration, but unless we can derive the type of args that will be passed to the handler function, we'll have to duplicate the type of args manually, which is brittle and error-prone.

Is there currently a way to export the args type?

Project Status

I am looking for the right TS command framework for me and am curious about the future of this project.

Example of how to use as non-terminal command framework

Hey, if you wouldn't mind, could you show me a little example of how to use cmd-ts such that I could get back a value from the executed command, or otherwise capture its output in an asynchronously-safe way?

I'm wanting to use cmd-ts outside of the context of the CLI, such as backing Discord/IRC bots and the like. It would be really helpful to see a short example if it is possible.

Thanks.

Option to display help when no arguments are passed

As a convenience factor on binaries that contain many subcommands, I find it very helpful to print out help if someone calls the command with zero arguments.

Would be really helpful to have an option (or example) to allow this!

Contravariance for oneOf and defaultValue

Example

import * as cmd from "cmd-ts";
cmd.command({
  name: 'regression',
  args: {
    target: cmd.option({
      type: cmd.oneOf(['stable', 'canary']),
      long: 'target',
      defaultValue() {
        return 'stable'; // Error
      }
    }),
});

TS Result: Type '() => string' is not assignable to type '() => "stable" | "canary"'.

Expected

No Error.

Accept return value: 'stable' as 'stable' | 'carary'

Explorable Interface

Currently, the objects returned by command and subcommand do not feature properties that let you crawl the command structure. It would be helpful if this structure's interface allowed for iterating over it and getting metadata, such as names, descriptions, child commands, etc.

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Warning

These dependencies are deprecated:

Datasource Name Replacement PR?
npm request Unavailable

Rate-Limited

These updates are currently rate-limited. Click on a checkbox below to force their creation now.

  • chore(deps): update dependency @types/request to v2.48.12
  • fix(deps): update dependency debug to v4.3.6
  • chore(deps): update actions/cache action to v4
  • chore(deps): update actions/checkout action to v4
  • chore(deps): update actions/setup-node action to v4
  • chore(deps): update dependency eslint to v9
  • chore(deps): update dependency eslint-plugin-prettier to v5
  • chore(deps): update dependency execa to v9
  • chore(deps): update dependency husky to v9
  • chore(deps): update dependency node to v20
  • chore(deps): update dependency prettier to v3
  • chore(deps): update dependency typescript to v5
  • chore(deps): update dependency vitest to v2
  • chore(deps): update pnpm/action-setup action to v4
  • chore(deps): update typescript-eslint monorepo to v8 (major) (@typescript-eslint/eslint-plugin, @typescript-eslint/parser)
  • ๐Ÿ” Create all rate-limited PRs at once ๐Ÿ”

Edited/Blocked

These updates have been manually edited so Renovate will no longer make changes. To discard all commits and start over, click on a checkbox.

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

github-actions
.github/workflows/build.yml
  • actions/checkout v3
  • actions/setup-node v3
  • pnpm/action-setup v2.2.2
  • actions/cache v3
.github/workflows/release.yml
  • actions/checkout v3
  • pnpm/action-setup v2.2.2
  • actions/setup-node v3
  • changesets/action v1
nodenv
.node-version
  • node 16.16.0
npm
package.json
  • chalk ^4.0.0
  • debug ^4.3.4
  • didyoumean ^1.2.2
  • strip-ansi ^6.0.0
  • @changesets/cli 2.23.2
  • @swc-node/register 1.5.1
  • @types/debug 4.1.7
  • @types/didyoumean 1.2.0
  • @types/fs-extra 9.0.13
  • @types/node-fetch 2.6.2
  • @types/request 2.48.8
  • @typescript-eslint/eslint-plugin 5.30.7
  • @typescript-eslint/parser 5.30.7
  • cargo-mdbook 0.4.4
  • docs-ts 0.6.10
  • eslint 8.20.0
  • eslint-config-prettier 8.5.0
  • eslint-plugin-import 2.26.0
  • eslint-plugin-prettier 4.2.1
  • esm 3.2.25
  • execa 6.1.0
  • fs-extra 10.1.0
  • husky 8.0.1
  • infer-types 0.0.2
  • node-fetch 2.6.7
  • prettier 2.7.1
  • request 2.88.2
  • tempy 3.0.0
  • typedoc 0.23.8
  • typescript 4.7.4
  • vitest 0.18.1

  • Check this box to trigger a request for Renovate to run again on this repository

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.