Giter Site home page Giter Site logo

tsickle's Introduction

Tsickle - TypeScript to Closure Translator Build Status

Tsickle converts TypeScript code into a form acceptable to the Closure Compiler. This allows using TypeScript to transpile your sources, and then using Closure Compiler to bundle and optimize them, while taking advantage of type information in Closure Compiler.

What conversion means

A (non-exhaustive) list of the sorts of transformations Tsickle applies:

  • inserts Closure-compatible JSDoc annotations on functions/classes/etc
  • converts ES6 modules into goog.module modules
  • generates externs.js from TypeScript d.ts (and declare, see below)
  • declares types for class member variables
  • translates export * from ... into a form Closure accepts
  • converts TypeScript enums into a form Closure accepts
  • reprocesses all jsdoc to strip Closure-invalid tags

In general the goal is that you write valid TypeScript and Tsickle handles making it valid Closure Compiler code.

Warning: unsupported

Google uses tsickle internally to minify its apps (including those using Angular) using Closure Compiler. We have little experience using tsickle in the other JavaScript ecosystems that are seen outside of Google, and there is generally no support for using it from our side.

Usage

Tsickle is a library, designed to be used by a larger program that interacts with TypeScript and the Closure compiler.

Some known clients are:

  1. Within Google we use tsickle inside the Bazel build system. That code is published as open source as part of Bazel's nodejs/TypeScript build rules.
  2. tscc wraps tsickle and closure compiler, and interops with rollup.
  3. We publish a simple demo program in the demo/ subdirectory.

Design details

Output format

Tsickle is designed to do whatever is necessary to make the code acceptable by Closure compiler. We view its output as a necessary intermediate form for communicating to the Closure compiler, and not something for humans. This means the tsickle output may be kind of ugly to read. Its only real use is to pass it on to the compiler.

For one example, the syntax of types tsickle produces are specific to Closure. The type {!Foo} means "Foo, excluding null" and a type alias becomes a var statement that is tagged with @typedef.

Tsickle emits modules using Closure's goog.module module system. This system is similar to but different from ES modules, and was supported by Closure before the ES module system was finalized.

Differences from TypeScript

Closure and TypeScript are not identical. Tsickle hides most of the differences, but users must still be aware of some differences.

declare

Any declaration in a .d.ts file, as well as any declaration tagged with declare ..., is intepreted by Tsickle as a name that should be preserved through Closure compilation (i.e. not renamed into something shorter). Use it any time the specific string names of your fields are significant. That would most often happen when the object either coming from outside your program, or being passed out of the program.

Example:

declare interface JSONResult {
    username: string;
}
let r = JSON.parse(input) as JSONResult;
console.log(r.username);

By adding declare to the interface (or if it were in a .d.ts file), Tsickle will inform Closure that it must use exactly the field name .username (and not e.g. .a) in the output JS. This matters for this example because the input JSON probably uses the string 'username' and not whatever name Closure would invent for it. (Note: declare on an interface has no additional meaning in pure TypeScript.)

Exporting decorators

An exporting decorator is a decorator that has @ExportDecoratedItems in its JSDoc.

The names of elements that have an exporting decorator are preserved through the Closure compilation process by applying an @export tag to them.

Example:

/** @ExportDecoratedItems */
function myDecorator() {
  // ...
}

@myDecorator()
class DoNotRenameThisClass { ... }

Development

Dependencies

One-time setup

Run yarn to install dependencies.

Build & Test commands

  • yarn build builds the code base.
  • Run tsc --watch for an interactive, incremental, and continuous build.
  • yarn lint checks for lint.
  • yarn test runs unit tests, e2e tests and checks for lint (but make sure to yarn build first or run tsc!). Set the TESTBRIDGE_TEST_ONLY environment variable to filter what golden tests to run.

TypeScript AST help

https://astexplorer.net/ and https://ts-ast-viewer.com/ are convenient tools to visualize and inspect a TypeScript AST.

Debugging

You can debug tests by passing --node_options=--inspect or --node_options=--inspect-brk (to suspend execution directly after startup).

For example, to debug a specific golden test:

TESTBRIDGE_TEST_ONLY=my_golden_test node --inspect-brk=4332 ./node_modules/.bin/jasmine out/test/*.js

Then open [about:inspect] in Chrome and choose "about:inspect". Chrome will launch a debugging session on any node process that starts with a debugger listening on one of the listed ports. The tsickle tests and Chrome both default to localhost:9229, so things should work out of the box.

The break in specific code locations you can add debugger; statements in the source code.

Updating Goldens

Run UPDATE_GOLDENS=y yarn test to have the test suite update the goldens in test_files/....

Environment variables

Set the environment variable TESTBRIDGE_TEST_ONLY=<REGEX> to limit the golden tests (found in test_files/...) to only run tests with a name matching the regex.

Releasing

On a new branch, run:

# tsickle releases are all minor releases for now, see npm help version.
$ npm version minor

This will update the version in package.json, commit the changes, and create a git tag.

Push the branch, open a pull request, get it reviewed, and wait for it to be merged.

Checkout and pull the latest version from master:

$ git checkout master && git pull

Check if the tag exists. If not, re-tag the commit and push the tag.

$ git tag
# Does this show the tag already? If not, proceed with:
$ git tag v0.32.0 && git push origin v0.32.0  # but use correct version

Once the versioned tag is pushed to GitHub the release (as found on https://github.com/angular/tsickle/releases) will be implicitly created.

From the master branch run:

npm config set registry https://wombat-dressing-room.appspot.com
npm login
npm publish  # runs a clean build & test automatically

tsickle's People

Contributors

12wrigja avatar alexeagle avatar appsforartists avatar b-strauss avatar blickly avatar bowenni avatar brad4d avatar chuckjaz avatar dependabot[bot] avatar drjanitor avatar evmar avatar frigus02 avatar gregmagolan avatar h-joo avatar hess-g avatar jjudd avatar lauraharker avatar lucassloan avatar mhausner avatar mprobst avatar nreid260 avatar renovate-bot avatar rictic avatar rkirov avatar rrdelaney avatar rubenlg avatar tbosch avatar therobinator avatar theseanl avatar vikerman 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  avatar  avatar  avatar  avatar  avatar  avatar

tsickle's Issues

Support array destructuring

eg.

function f([x,y]: number[]) {}

See #96 where I started to do this, but needs more work because number[] must be represented as Array<number> in the closure typedef.

"export enum" with comment is pretty hard to annotate

We try to emit a @typedef for enums when we hit them.
Below is the tree of the various nodes and where the emits happen when parsing something like

// comment here
export enum ...

Note that we emit the @typedef before we hit the ExportKeyword, which means it'll be hard to make the @typedef appear directly adjacent to the enum in the output. I'm beginning to think stripping comments by default is a better strategy...

| | node: EnumDeclaration
| | | emit "/** @typedef {number} */\n"
| | | node: SyntaxList
| | | | node: ExportKeyword
| | | | | emit "\n\n// This additional exported enum is here to exercise the fix for issue #51.\nexport"
| | | node: EnumKeyword
| | | | emit " enum"

add goldens for different steps of the pipeline

We have the following pipeline:

.ts -> (sickle) -> .ts with annotations -> (ts compiler) -> .js with annotations -> (closure compiler) -> minified .js

We currently have goldens only for ".js with annotations". It would be helpful to catch bugs earlier with goldens for ".ts with annotations" and maybe also goldens for "minified .js".

Transpiling causes confusing line numbers in output

When using sickle, the compiler error messages you get are relative to the sickle output, which means that file offsets are wrong.

One idea is to ensure that sickle output has output lines in the same place as input lines, which means generating code that (for example) doesn't use newlines. That at least makes line numbers line up.

But in cases where we generate a synthetic constructor there's not a particular line in the source we are modifying, so we may be forced to add a new to the source. But maybe even then we could do a rule like "stuff all synthetic code in one line in the very bottom of the class, right before the closing curly brace".

Use ES6 sets and maps

Would let us drop all the hasOwnProperty stuff.

Depends on what version of node we depend on.

Stub decls must appear after "super" call

class Foo {
  constructor(public x: string) {
    super(3);
  }
}

becomes

class Foo {
  constructor(public x: string) {

// Sickle: begin stub declarations.

 /** @type { string} */
this.x;
// Sickle: end stub declarations.

    super(3);
  }
}

It's illegal in TS to have code before the super call apparently.

Sickle chokes on decorators (likely due to a bug in TS < 1.8)

Input:

class DecoratorTest {
  @foo
  private x: number;

  constructor() {}
}

causes a crash due to a failed assertion about offsets.

With some debugging, I found that the resulting syntax tree when parsing this input is bogus. Note how the AtToken shows up twice.

| | | | node: PropertyDeclaration
| | | | | node: AtToken
| | | | | node: SyntaxList
| | | | | | node: Decorator
| | | | | | | node: AtToken
| | | | | | | node: Identifier
| | | | | node: SyntaxList
| | | | | | node: PrivateKeyword
| | | | | node: Identifier

Upgrading our TypeScript dep to typescript@beta (1.8) produces

| | | | node: PropertyDeclaration
| | | | | node: SyntaxList
| | | | | | node: Decorator
| | | | | | | node: AtToken
| | | | | | | node: Identifier
| | | | | node: SyntaxList
| | | | | | node: PrivateKeyword
| | | | | node: Identifier

which is a layout I expect.

sickle should strip all comments

in file_comments.ts we emit jsdoc before existing comment:

/** @return { string} */ // This test verifies that initial comments don't confuse offsets.
 function foo() {
     return 'foo';
 }

we should strip all comments, since we already don't emit comments in properties (see comments.ts golden).

Constructable types

var ctorType: {new(a: number): Foo};

should translate to:

var /** function(new:Foo, number) */ ctorType;

However that format does not support additional properties on ctorType. Apparently this is supported by Closure's internal representation of the type, but there's no syntax for it.

Enum written with incorrect reverse map

from angular2/github/modules/angular2/src/compiler/url_resolver.ts

enum _ComponentIndex {
  Scheme = 1,
  UserInfo,
  Domain,
  Port,
  Path,
  QueryData,
  Fragment
}

TS/sickle produces this ES6 code:

var _ComponentIndex;
(function (_ComponentIndex) {
    _ComponentIndex[_ComponentIndex["Scheme"] = 1] = "Scheme";
    _ComponentIndex[_ComponentIndex["UserInfo"] = 2] = "UserInfo";
    _ComponentIndex[_ComponentIndex["Domain"] = 3] = "Domain";
    _ComponentIndex[_ComponentIndex["Port"] = 4] = "Port";
    _ComponentIndex[_ComponentIndex["Path"] = 5] = "Path";
    _ComponentIndex[_ComponentIndex["QueryData"] = 6] = "QueryData";
    _ComponentIndex[_ComponentIndex["Fragment"] = 7] = "Fragment";
})(_ComponentIndex || (_ComponentIndex = {}));
_ComponentIndex.Scheme = 1;
_ComponentIndex.UserInfo = 0;
_ComponentIndex.Domain = 1;
_ComponentIndex.Port = 2;
_ComponentIndex.Path = 3;
_ComponentIndex.QueryData = 4;
_ComponentIndex.Fragment = 5;

Causes a runtime failure, we have
["components/youtube_app.html", undefined, undefined, undefined, undefined, "components/youtube_app.html", undefined, undefined]
and then parts[_ComponentIndex.Path][0] == '/'
fails because _ComponentIndex.Path is 3 rather than 5.

Default param arguments need to be annotated as optional

function foo(x: number, y: string = 'hi')

should produce something like

function foo(/** number */ x, /** string= */ y = 'hi')

Note the equals sign on the 'y' arg. This is because it's optional for the caller to provide it.

Unnamed object parameters

index (1) must be less than size (1)
  Node(PARAM_LIST): /javascript/angular2/github/modules/angular2/src/core/di/provider.js:69:15
    constructor(token, { useClass, useValue, useExisting, useFactory, deps, multi }) {
  Parent(FUNCTION ):/javascript/angular2/github/modules/angular2/src/core/di/provider.js:69:4
    constructor(token, { useClass, useValue, useExisting, useFactory, deps, multi }) {

    at com.google.common.base.Preconditions.checkElementIndex(Preconditions.java:1295)
    at com.google.common.base.Preconditions.checkElementIndex(Preconditions.java:1277)
    at com.google.common.collect.SingletonImmutableList.get(SingletonImmutableList.java:41)

javascript/angular2/github/modules/angular2/src/core/di/provider.js looks like

/**
     * @param token
     * @param { ?} {useClass, useValue, useExisting, useFactory, deps, multi}
     */
    constructor(token, { useClass, useValue, useExisting, useFactory, deps, multi }) {

I don't think that name for parameter 2 is legal.

This is the next blocker for compiling angular2 with closure compiler.

TypeScript ES6 decorator emit leaves class in a position closure doesn't re-write

eg
blaze-bin/third_party/javascript/angular2/github/modules/angular2/src/web_workers/shared/serializer.js
contains

let Serializer = class {
...
};
Serializer = __decorate([
    di_1.Injectable(),
    __metadata('design:paramtypes', [render_store_1.RenderStore])
], Serializer);
exports.Serializer = Serializer;

With language_out=ES5, closure still leaves class keywords in the final JS.

//third_party/javascript/angular2/github/modules/angular2/src/web_workers/shared/serializer.js
goog.loadModule(function(exports) {'use strict';goog.module('angular2$src$web__workers$shared$serializer');
...
let Serializer = class {
...

This breaks us, even in Chrome where class is supported, because it disallows using call or apply on the constructor function of a class.

enums in modules can't be typed

module Foo { enum Bar { ...

eventually produces something like

(function (Foo) {
    /** @typedef {number} */
    var Bar;

But you apparently can't (?) put a @typedef within a scope like that, it has to be top level (?).

Support command-line build workflows

Users other than Google will probably want some sort of sickle executable that they can apply to their source to get Closure output.

Perhaps Sickle should even go as far as being responsible for executing Closure on their behalf -- so you just take your TypeScript output, give it to Sickle, and you get a minified binary out.

Propagate rest (aka ...) params into types

Even in untyped mode, a function like

function foo(...args: string[]) 

becomes

/**
 * @param { ?} args
 * @return { ?}
 */
function foo(...args: string[]) 

which Closure doesn't accept: "WARNING - Missing "..." in type annotation for rest parameter."

Update project description on Github, still says "Tickle"

It looks like maybe this project used to be named Tickle - and it still says that on the Github project description shown prominently. "Tickle - TypeScript to Closure Annotator"

This issue is a reminder to eventually adjust that.

Non-exported enums not accepted by Closure

The first enum below is exported, the second is not.

Closure rejects the second one with
ERROR - The alias Foo is assigned a value more than once.

goog.module('foo');
(function (ErrorType) {
    ErrorType[ErrorType["NONE"] = 0] = "NONE";
    ErrorType[ErrorType["PERMANENT"] = 1] = "PERMANENT";
    ErrorType[ErrorType["RETRYABLE"] = 2] = "RETRYABLE";
    ErrorType[ErrorType["NOT_FOUND"] = 3] = "NOT_FOUND";
})(exports.ErrorType || (exports.ErrorType = {}));
var ErrorType = exports.ErrorType;
ErrorType.NONE = 0;
ErrorType.PERMANENT = 1;
ErrorType.RETRYABLE = 2;
ErrorType.NOT_FOUND = 3;



var Foo;
(function (Foo) {
    Foo[Foo["BAR"] = 0] = "BAR";
})(Foo || (Foo = {}));
Foo.BAR = 0;

John Lenz says: "The goog.module support is being rewritten and the behavior here is likely to change. As is this is simply violation of the current restriction on goog.module top level declarations"

fully support untyped mode

As per our conversation with JS compiler folks, most advanced optimizations do not need types (only disambiguate/ambiguate props do). Sickle can fully support an advanced optimization consumer once we have the following:

  • turn off closure warnings/errors on valid code that is minification safe. #59
  • support a driver that does typechecking first, then sickle run, finally an emit (no typechecking).
  • detect minification unsafe code that passes the TS typechecker. #23

/cc @martine

reorganize closureExternsBlacklist

from @rkirov

This list needs to be more structured (we can keep hacking on it for now). Here is what I did in clutz:

fix which files are considered "platform apis" on both sides:

lib.d.ts for TS (TSsymbols)
closure/externs:COMMON (ClosureSymbols)
modify lib.d.ts to be a strict subset of closure externs

enumerate symbols in missingPlatformSymbols = ClosureSymbols \ TSSymbols ( \ is set difference)
clutz now emits everything not in closure externs + missingPlatformSymbols.

Beyond "platform externs", I don't think the tool should be doing any white/black listing, as we can't have any visibility into what those symbols are. It won't scale to keep adding them here.

Annotate class methods

class Foo {
  bar(x: string, y?: string) {}
}

generates

class Foo {
  bar(x: string, y?: string) {}
// Sickle: begin synthetic ctor.
constructor() {
[... more elided...]

We don't annotate the function for some reason? We need to do so for the optional argument.

Generate structurally typed interfaces for classes (?)

TypeScript uses structural type compatibility throughout, e.g.:

class Foo { bar: string; }
class Baz { bam: string; }
var f: Foo = new Baz(); // legal as all properties match

In Closure, that's only allowed for structural interfaces. While undocumented, according to the code this should be triggered by:

/** @interface @record */
my.StructuralInterface = function() {};

It should be possible to translate all TypeScript types to structural interfaces (for interface) or pairs of interface and class for classes:

interface MyIf {}
class MyClass {}

would generate:

/** @interface @record */
function MyIf() {};
/** @interface @record */
function MyClass_StructuralInterface() {};
/** @constructor @implements {MyClass_StructuralInterface} */
function MyClass() {};

Whether it's more convenient/safer/easier to implement to either invent a new name for the interface, or to invent a new name for the class, and either change the name in all type-positions, or change the name in all value-positions, is unclear at this point, both would probably work.

returning an arrow function triggers ASI

function foo() {
    return (x: string) => 3;
}

produces

/**
 */
function foo() {
    return 
/**
 * @param { string} x
 */
(x: string) => 3;
}

Because we inserted the newline after the return, it triggers ASI and you get

error TS7027: Unreachable code detected.

Synthetic ctor needs to call base class ctor with proper arguments

class SuperTestBase {
  constructor(public x: number) {}
}
class SuperTestDerivedNoCTor extends SuperTestBase {
  foobar: number = 3;
}

Sickle generates a constructor for the derived class here, to have a place to declare the type of foobar.

But the existence of a constructor means that we need to call the constructor of the base class, which means we need to be able to reconstruct the constructor parameters as well as the parameters to super.

I think this means we might need to store a map of classes to the expected parameters of the ctor, which might even mean needing to do cross-file analysis... :~(

I tried a hack like this but it didn't go. I might be able to turn off some type checking and force it though.

constructor(...args: any[]) {
  super(...args);

parseInt has a different type in Closure

Closure defines parseInt as having a required second argument. I think this is to handle the common mistake of doing parseInt(user_input), which does a surprising thing (octal) when user input starts with a leading 0.

For sickle's purposes, we'll need to figure out something. Ideas:

  • adjust the TypeScript typings for parseInt
  • coerce parseInt to undo the Closure check (which is a little disappointing)

Export of enum typedef doesn't pass e2e test

If we generate code like the following, it fails to compile in Closure:

/** @typedef {number} */
export var EnumTest2;
(function (EnumTest2) {
    EnumTest2[EnumTest2["XYZ"] = 0] = "XYZ";
    EnumTest2[EnumTest2["PI"] = 3.14159] = "PI";
})(EnumTest2 || (EnumTest2 = {}));
/** @type {EnumTest2} */
EnumTest2.XYZ = 0;  // <- ***fails here***
/** @type {EnumTest2} */
EnumTest2.PI = 3.14159;

The error message:

[...] ERROR - Bad type annotation. Unknown type EnumTest2
/** @type {EnumTest2} */
           ^

Something about export and typedef interacting poorly maybe?

The other weird thing is that I can't reproduce this with the Google-internal JS compiler, which makes me wonder if it's a bug.

De-reference all properties in `export * from ...`

When compiling export * from 'foo'; to CommonJS module format, we end up with code like this:

function __export(m) { for (var k in m) exports[k] = m; }
__export(require('somemodule'));

Now surprisingly this works in Closure Compiler when replacing require with goog.require. It simply patches the exports to be the module name (which is a global symbol) and then the right properties end up on the right objects (yay!).

However when importing a property that was re-exported like this, Closure Compiler complains about a missing property. It's possible to shut that off (--jscomp_off=missingProperties), but that seems overall dangerous - for example, dead code elimination might kill that property.

If sickle was to "de-reference" and re-export the particular symbols, this would avoid the problem for the time being, and allow us to continue using the hacky CommonJS --> goog.require transformation without having to rely on ES6 modules.

An alternative, possibly cleaner, solution would be to convert all module imports to goog.module and goog.require within sickle (including the * dereferencing). This would break the second compilation pass - TypeScript would no longer understand all those imports. However we have already type checked, so at that point we could just ignore all errors and emit.

WDYT? @martine @rkirov

"declare module" crashes

declare module Foo { enum Bar { ...

after sickle we produce the TypeScript code

declare module Foo {
  [...]
/** @type {Bar} */
(<any>Bar).BAZ =  1;
}

and you can't have statements in a declared module. I think the workaround is to skip them in this case but I don't really know what the original code is trying to do...

Preserve TypeScript type coercions in Closure code

Input:

function foo(str: string) {}
foo(<string>JSON.parse('"foo"'));

Outputs:

// @param {string} str
function foo(str) { }
foo(JSON.parse('"foo"'));

We drop the <string> so Closure doesn't know about the type cast, producing a warning.

emitting declared symbols from modules into a global externs is wrong

currently, we collect all declared namespace and emit them as externs.

// in a.ts
export var a = 0; // so this is indeed a typescript module.
declare var window: {};
declare namespace angular { ... }

sickle outputs var window and var angular in externs.js. However, TS declarations in modules are scoped to the module similarly to plain old JS variable declarations (var foo). So it would be valid TS to have again:

// in b.ts 
export var a = 0; // so this is indeed a typescript module.
declare var window: {};
declare namespace angular { ... }

but sickle output would be wrong. I can't come up with a clean solution here, we are trying to merge multiple scopes (every module is one), into one, and the only solution is renaming, which is going to be PITA.

@martine , @mprobst Ideas?

Handle abstract methods

abstract class Base {
  abstract foo(): void;
  bar() { foo(); }
}

TS drops the decl of foo when generating ES6, but we need to keep it around to make Closure happy.

JSDoc comments need an extra newline

class Foo {
  /** js-doc comment with text in it */
  frotz: number;
}

becomes

class Foo {
  constructor() {
        /**  js-doc comment with text in it @type { number} */
        this.frotz;
    }
}

which is invalid Closure syntax -- we need a newline before that @type bit.

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.