Coder Social home page Coder Social logo

volijs / nestedtypes Goto Github PK

View Code? Open in Web Editor NEW
94.0 25.0 17.0 5.29 MB

BackboneJS compatibility layer for Type-R data framework.

Home Page: https://volicon.github.io/Type-R/

License: MIT License

JavaScript 21.84% HTML 44.23% CoffeeScript 0.12% TypeScript 33.80%
backbone nestedtypes data-management models reactive-programming type-system serialization schema-validation observable

nestedtypes's Introduction

NestedTypes 2.0 is BackboneJS compatibility layer for the Type-R data framework. Type-R is Model/Collection core written from the scratch with the TypeScript and has no side dependencies.

NestedTypes adds support for REST (standard BackboneJS API), Underscore methods, and Backbone 1.1 classes.

If you're upgrading from the version 1.3, there are compatibility issues. Mostly due to the fact that the Type-R and NestedTypes 2.0 is built around the concept of aggregation trees. NestedTypes 1.3 code won't work without refactoring.

Important Notice

Staring with v2.0 Type-R includes generic I/O abstraction which is far superior to the legacy BackboneJS I/O. NestedTypes & NestedReact will be maintained as the BackboneJS compatibility layer as long as Verizon/Volicon systems have legacy Backbone code. Therefore:

  • NestedTypes docs won't be updated. Use Type-R documentation as you primary source of documentation.
  • Functional-wise, there's no reason to prefer NestedTypes over the Type-R any more. If you don't need BackboneJS backward compatibility, move to the Type-R which doesn't have any legacy dependencies like jQuery and underscore.

Features

Post-backbone data framework. 10 times faster, first-class support for nested models and collections and relations by id.

  • ES6 classes support.
  • Deeply observable changes.
  • First-class support for aggregation and relations by id.
  • Attribute type annotations and dynamic type safety.
  • More than 10 times faster than BackboneJS and 2-4 times faster than NestedTypes 1.3 in all browsers.

Installation & Requirements

All modern JS engines are supported (IE10+, Safari, Firefox, Edge, Chrome, nodejs). May work in IE9 but not tested.

npm install nestedtypes

underscore and jquery are hard dependencies.

For lighter framework version without dependencies and Backbone compatibility shim check out Type-R.

Quick API Reference

Central concept in NestedTypes is Record type, which is the JS class with following capabilities:

  • Class members are deeply observable.
  • It is serializable to JSON by default.
  • Class members are typed and their changes are guardered with run-time type checks.

Model is the Record subclass representing REST API endpoint. Models, records, and their collections are used as building blocks to describe both application's UI state and its data layer.

Record definition looks like normal ES6 class definition, but it's mandatory to declare attributes. It looks like this:

import { define, Record } from 'nestedtypes'

@define // <- decorator to perform class transformation
class User extends Model {
    urlRoot : '/api/users',

    static attributes = { // <- attributes declaration
        name : '', // <- can be either default value
        email : String, // <- or any JS type constructor
        isActive : true,
        lastLogin : Date.value( null ) // <- or both
    }
}

const user = new User({ id : 5 }); // <- constructor takes optional attributes hash as an argument
user.fetch().done( () => { // GET /api/users/5
    user.name = 'John';
    user.save(); // PUT /api/users/5
});

Record's Attributes Type Annotations Basics

All record's attributes must be declared with static attributes = { [ attrName ] : TypeAnnotation } member. Type annotation can be one of the following:

  • attrName : Constructor. Such as attrName : Date.
  • attrName : Constructor.value( defaultValue ). Such as lastLogin : Date.value( null ).
  • attrName : defaultValue. In this case, attribute type will be inferred from the value, so isActive : true has the same effect as isActive : Boolean.value( true ).

Record attributes can be accessed directly, like user.name = x. When attribute is assigned, the type of the the value is checked and being converted to the declared type with its constructor invocation if it's necessary.

For the assignments like user.isActive = x, where isActive is declared as Boolean

  • it is assigned as is if x is null or boolean.
  • for primitive types, it's converted with plain constructor invokation like Boolean( x ).
  • For non-primitives convertion will invoke constructor with new, like new Date( x ).

If it's impossible to convert the value it may be assigned with NaN or Invalid Date (or depending on the type update will be rejected), and there will be an error in the console.

Therefore, it's guaranteed that Record attributes always have declared type.

Collections

Every model has corresponding Collection type declared implicitly. Collections implements Backbone Collection API.

var users = new User.Collection();

users.fetch().done( () => {
    console.log( users.length );
});

When Record is extended, its collection is extended too. The creation of implicit Colleciton type is equivalent to this:

@define
class Users extends Record.Collection {} 

@define
class User extends Record {
    static Collection = Users;
    // ...
}

You can use this pattern when you need to add custom members to the record's collection.

Nested Records and Collections

Nested records and collections are declared with mentioning constructor in attribute type annotation. All changes in nested objects are deeply observable; any change in children will cause change events in a parent.

Records and collections emiting the standard set of Backbone events, with following differences:

  • Collections does not bubble change:[attribute] event from the model by default (change event is bubbled); event bubbling needs to be enabled for every particular event with static itemEvents = { 'change:attr1' : true, ... } declaration.
  • Collections have changes event which is semantically similar to the model's change.

Transactions

Record's change event (and collection's changes event) are transactional. Whatever some changes are made as the reaction on any of change event, it won't cause additional change event for the owner.

Also, you can explicitly group the sequence of changes to the single transaction:

    some.record.transaction( record => {
        record.a = 1;
        record.b = 2;
        ...
    }); // some.record will emit single 'change' event if there was any changes.

    // Execute collection.each in the scope of transaction.
    todoCollection.updateEach( item => item.done = true ); // One 'changes' event will be emitted. 

Aggregation

Record can aggregate other records and collections in its attributes.

@define
class Team extends Record {
    static attributes = {
        members : User.Collection
        leader : User
    }
}

const team = new Team();
team.members.add( new User({ name : 'John' }) );

Aggregated members are:

  • serialized as nested JSON.
  • following operations recursively when the operation happens to its owner.

Aggregated members forms the tree of exclusive ownership. The same record or collection instance cannot be aggregated in two places at the same time, and this rule is checked and enforced.

Shared nested objects

Records may have nested members which belongs to different ownership trees. Special type annotations are required to mark attribute as shared.

  • RecordType.shared or CollectionType.shared. Reference to collection or record which may be aggregated somewhere else. null by default.
  • CollectionType.Refs constructor. Collection of records which may be aggregated somewhere else. Defaults to empty collection.

CollectionType.Refs is an equivalent to CollectionType.shared.value( [] ) when used as attribute type annotation.

Shared types are:

  • not a part of record's ownership tree.
  • not serialized.
  • Not a subject of recursive operations. They are empty by default, not cloned, not validated, and not disposed when the corresponding operation applied to the parent.

In all other aspects, they are indistinguishable from aggregated records and collections.

@define
class Team extends Record {
    static attributes = {
        members : User.Collection.Refs
        leader : User.shared
    }
}

Relationship by id

It's possible to create serializable reference to the shared object which is represented in JSON as a record's id (or array of ids). Special type annotation is required to point out the master collection which will be used to resolve ids to the records.

  • RecordType.from( masterCollection ) represents an id reference to the model.
  • CollectionType.subsetOf( masterCollection ) represents the collection of models id references.

id-reference types behaves as shared types, but:

  • they are serializable as an object id (or array of ids for collections).
  • they are not observable (internal changes do not trigger change events on the record).

masterCollection reference may be either:

  • direct reference to the globally available collection.
  • function returning the reference to the collection.
  • string, which is the symbolic reference to collection (dot-separated path to the collection taken relative to the record's this).
class Team extends Record {
    static attributes = {
        members : User.Collection,
        leader : User.from( 'members' ) // <- leader is serializable reference to the record from members collection.  
    }
}

Owner-references

^ symbol in symbolic reference represents getOwner() call and returns the record owner. Collections are skipped.

Following example expects that Team record will be aggregated (alone or in a collection) together with users collection.

class Team extends Record {
    static attributes = {
        members : User.Collection.subsetOf( '^users' ),
        leader : User.from( 'members' )  
    }
}

Tilda-References and Stores

Symbolic reference staring with ~ is resolved relative to the record called store, which is located with record.getStore() method. For instance, reference ~users will be resolved as this.getStore().users.

getStore() uses following store location algorithm:

  1. It traverse an ownership tree upwards and return the first Store model it has found.
  2. If none of the record's owners is the Store, it returns global store from Nested.store.

Store is the subclass of the Record and behaves as a regular Record. Therefore, resolution of id references depends on the context and you may have as many stores as you like.

Following example expects that there's users collection in some upper record which is inherited from Store, or (if there are none) in the global store:

class Team extends Record {
    static attributes = {
        members : User.Collection.subsetOf( '~users' ),
        leader : User.from( 'members' )  
    }
}

Attribute has-annotations

It's possible to control different aspects of record's attribute behavior through additional metadata. All of them starts with a keyword .has added to the constructor type.

Object describing an attribute is called metatype. Operations on metatypes are immutable (returns new metatype), and can be chained.

// Declare Month metatype.
const Month = Number.value( 1 ).has.check( x => x > 0 && x <= 12 );

attribute : Type.has.toJSON( false | ( x, name ) => json )

Override default serializer for the attribute. false option will exclude attribute from serialization.

attribute : Type.has.parse( ( json, name ) => data )

Override default JSON parser for the attribute.

attribute : Type.has.get( ( value, name ) => value )

Get hook which may transform attribute value on read. Get hooks can be chained.

attribute : Type.has.set( ( value, name ) => value )

Set hook which may transform attribute value before it's assigned. Set hooks can be chained.

attribute : RecordOrCollectionType.has.changeEvents( false )

When nested attribute is changed, don't mark the owner as changed.

attribute : Type.has.events({ [ event ] : handler | handlerName })

Listen to the specified events from the attribute. handler can be either function or the name of the record's method.

attribute : Type.has.check( x => boolean, errorMsg? : any )

Attach check to the attribute. Checks can be chained. Attribute is valid whenever check function returns truthy value.

errorMessage is optional.

attribute : Type.isRequired

Similar to Type.has.check( x => x, 'Required' ).

Validation

Validation is performed recursively on ownership tree. Record and collection shares the same validation API.

record.validate()

Override it to add custom record-level validation. Method shoudl return truthy value in case of validation error.

For attribute level checks see Type.has.check annotation.

record.isValid() : boolean

Checks whenever record is valid.

record.validationError

Return validation error object or null if there are no errors.

nestedtypes's People

Contributors

alexanderevgrafov avatar avaly avatar iterpugov 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

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

nestedtypes's Issues

Optional event bubbling

Event bubbling is a nice feature in nestedTypes, but, unfortunately, it causes problem in my existing Backbone application where I am trying to integrate nestedTypes.

A way to control or disable event bubbling for a certain Model/Collections would be very helpful. Alternatively, being able to distinguish local events from bubbled events would help.

New attribute specification

Add attribute spec creation methods to types themselves. Need to think a bit more about it, but idea looks very promising. Looks like it's not only beautiful, but the right way to implement "polimorphic metatypes".

Model.extend({
    defaults : {
        created : Date,
        modified : Date.value( null )
        enabled : Boolean.value( true ).options({ toJSON : false }),
        counter : Number.options({
               value : 5,
               toJSON : false
        }),
        untyped : NestedTypes.options({ toJSON : false })
    }
})

Make error handling more flexible / configurable

Hi,

this looks pretty much like the library I am looking for, which is lightweight schema and relationship definitions for Backbone. However, since we are using Backbone on the server, the error handling (writing to console.error) is not really suitable for our case. It would be awesome if there was a way to configure this somehow.
I would either propose having a simple switch for logging to console.error, or throw an actual Error.
Or make it so that you can pass in the (4) function(s) yourself, at least the one that actually does console.error.

Another idea would be to set the value that is being returned when calling isValid() on the model.

Update:
I just realized also that my validate is not being called, since there was no version update since d1d070b

Refactor event bubbling logic to be used in custom types

Right now, it can be used by backbone types only. We need to generalize it a bit, and introduce metatype features which would provide:

  • deep update (Type.set method must be defined)
  • change event bubbling (Type.triggerWhenChanged)

Both featured should be controlled separately.

Attribute-level `set` does not work

First of all, thanks for a fantastic Backbone plugin. Apologies if this ends up being user error, but I can't seem to get set to work within NestedTypes.options({...}). get, toJSON, parse, etc. all work fine, but not set. When I set the attribute, it is set with the native set -- the attribute-specific set is never called.

NestedTypes.options({
     get: function() {
         // Is called as expected
     },
     set: function() {
         // Never called, attribute is set with native set
     }
});

I am using version 0.9.0. Any tips appreciated.

Versioning in sync

The version of nestedtypes.js in the master branch is set to 0.7.0

The version of package.json in the master branch is set to 0.6.0

The version published in NPM is 0.5.0.

Please update the versions accordingly and publish all new versions in NPM. Also using git tags to identify versions would be helpful.

Transactional semantic for options.set and .get

Experimental feature - new get/set API, which would have the same effect as attribute-level properties override, but:

  1. Make overriding easier and safer, and
  2. make sure that both will be called from Model's get and set.
var M = Nested.Model.extend({
    defaults : {
        a : Number.options({
            set : function( x ){
                return x; // or undefined to prevent set
            },

            get : function( x ){
                return x;
            }
        })
    }
};

Behaviour:

  • model.get( 'name' ) and model.name should call options.get if it exists.
  • model.get( 'property' ) should call native property.

Therefore, model.get is overriden to call native property.
native property get will either access model.attributes, or call options.get in addition.

  • model.name = x and model.set( 'name', x ) should call options.set before actions are taken.
  • when 'undefined' is returned, set is cancelled.
  • for safety, it should be called after type cast, however it's not clear what to do with backbone types deep update (working option is not to call it in case of deep update).

Inconsistent Backbone parse implementation

Collection.set invoke Collection.parse, while Model.set doesn't. It leads to problems invoking 'parse' in nested models/collection tree.

Looks like it's done in order to prevent parsing of defaults values in the constructor. Because, we don't know which attributes we need to fill with defaults before we actually call 'parse'. However, in subsequent 'set' parse will not be executed for nested attributes in all cases.

During Model.fetch parse will be executed for top level model only.

Solution is not clear. It would be either

  1. to override Model.set, Model.save, Model.fetch, and Model.constructor to invoke parse. In this case we need to execute set two times in Model.constructor, and also server response will be parsed in case of deep update).
  2. override Collection.set, Collection.fetch, and Collection.constructor not to execute parse.
  3. Throw out backbone, because previous solutions have their own problems.

I need to think. First of all, I will continue writing more or less complete unit test, which already detected this problem.

Collection.add() not returning the added model

Backbone.js documentation says about Collection.add():
"Returns the added (or preexisting, if duplicate) models."

This feature can easily be added by saving the return value of func.apply() in Collection.wrapCall() and returning it at the end.

Model.Null( type ) metatype to init attribute with null

  • add 'Attribute' metatype to directly control property, default value, and type of the attribute.
  • implement Model.Null declaration using this metatype
  • change implementation of RefsTo stuff to use this thing.
Model.Null = function( Ctor ){
            return new Attribute({
                ctor : Ctor,
                init : null 
            }); 
        }

Array type behaves wrong on type convertion

Because when "new Array( length )" is being called with a single attribute, it expect it's attribute to be the length.

Consider an idea to wrap object in array if it's not an array instead.

Advanced Views

  • can take an object, which is used as a spec for BaseView.Model.extend( spec )
  • subviews spec, which is used to specify types of subviews
subviews : {
   left : FilterView, 
   middle : PlayerView,
   right : ClipView
}

initialize : function(){
   this.left = { options };
   this.right = { options };
}
  • subviews must behave like an attributes
  • subviews must be inherited from the base view
  • subviews type spec must support some options (get, set, value, type, event bubbling, might be custom listener specs)
  • subviews must be attached automatically in 'render' inside tags with 'subview' attribute
  • render must be template-neutral, expecting function in View.template

Declarative attribute-level toJSON specification

An ability to specify for each model's attribute whenever it will be serialized in toJSON method, and customize serialization algorithm. Like this:

var M = Model.extend({
    defaults : {
          a : String,
          b : Attribute({
                type: Number,
                toJSON : false // <-- won't be serialized
          }),
          c : Attribute({
                type: Number,
                toJSON : function(){ // <-- custom serialization
                      return "Number is " + this.c;
                }
          })
    }
})

How to use without require.js

I love the way this code is structured. This is much more clean than anything I've seen so far. However, I don't use AMD logic in my app.

Can you give me a quick code example for using this in a non-AMD environment?

View: inline events binding

Looks like it's possible to completely move View.events section to template. Attribute 'events' will hold the string of pairs 'eventName:viewFunctionName', which is enough to identify both HTML event sources and event handlers.

When parsing template, template engine should extract events strings, and use them to generate 'events' object. Should be done before actual parsing to grab all options possible, event specs must be constants.

<form>
    <input events="change:changeValue keydown:filter"/>

    <button events="click:save"/>
</form>

Will create following events map:
{
    'change [events="change:changeValue keydown:filter"]' : 'changeValue',
    'keydown [events="change:changeValue keydown:filter"]' : 'filter',
    'click [events="click:save"]' : 'save'
}

Support for defaults' function body

Translate 'defaults' to 'attributes' spec during extend, if attributes spec doesn't exist already.
If defaults is an object, replace it will a function.
If defaults is already a function, extract typeless attributes info.

This enhancement should improve backward compatibility with backbone, and simplify migration to NestedTypes models.

collection add,set with merge true does not merge

merge:true is not correctly merging the attributes. I.e. it does reset all attributes which are not given in this set. The problem seems to be backbone which calls model.set with an already constructed model. This constructed model has all the defaults set. therefore, __setMany functions gets all attributes even the one that were originally not specified.

Backbone.Collection.set creates a new model which has all the defaults set:

 for (i = 0, l = models.length; i < l; i++) {
        if (!(model = this._prepareModel(models[i], options))) continue;

        // If a duplicate is found, prevent it from being added and
        // optionally merge it into the existing model.
        if (existing = this.get(model)) {
          if (options.remove) modelMap[existing.cid] = true;
          if (options.merge) {
            existing.set(model.attributes, options);  //<<< PROBLEM its not called with models[i]
            if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true;
          }

not sure how to fix that

Model.Collection - automatic generation of default collections for models.

This property will be added to each model type upon extend.

MyModel.Collection = NestedTypes.Collection.extend({ model : MyModel });

You can override MyModel.Collection with your own class if you need to customize collection's properties. Either like this:

    val Location = Model.extend({
        defaults: {
            name : String
        },

                collection : {
                      initialize : function(){ ...},
                      // other collection properties
                }
    });

or like this:

MyModel.Collection = NestedTypes.Collection.extend({
     model : MyModel,
     initialize : function(){ ...},
});

Include custom properties in toJSON

Nice plugin, I like the API style your plugin uses.

Currently it looks like custom properties do not get serialized for toJSON calls.

For example,

MyModel = Model.extend({
  properties: {
   myProp: function() {return 'cabbage';}
  }
})
modelInstance = new MyModel();
modelInstance.toJSON() // outputs '{}', expected '{myProp: cabbage}'

defaults [] and {}

Objects used in defaults are shared between Model instances.

Workaround: Use 'Array' and 'Object' instead of [] and {} respectively.

It's actually an annoying problem (now you just can't be safe when using {} and [] in defaults), and needs to be fixed. What I am planning to do:

  • Allow plain JS literals (JSON) only in simplified 'defaults' syntax (just value, no type). Always make a deep copy of that literals.

Most annoying fact is that in plain backbone with function in default these values could be used safely.

Highest priority. Needs to be fixed very soon. Ultimate goal of this project is to make 'naive' programming patterns safe. Not to increase an amount of garbage in developers heads.

New implementation of model's relations

Model.RefTo and Collection.RefsTo will be replaced with Collection.SubsetOf and Model.From.

Both will be lazily resolved on first access, Collection.SubsetOf will be extended with methods helpful for handling sets.

Add global Integer type

  • Must be correct subtype of Number
  • Must round itself to integer in constructor
  • Must behave as primitive types

Rename Backbone.NestedTypes to something short

NestedTypes sucks. It's too long. So far, "Nested" namespace looks the best.

Another option is to export Model as a namespace for the rest of the stuff.

var Model = require( 'nestedtypes' );

var M = Nested.Model.extend({
...

Direct implementation of 'mixin' for Class, Model, and Collection

{
    mixin : object // <- will copy object properties, before master type properties will be applied.
}

{
    mixin : Ctor // <- will copy object.prototype properties
}

{
    mixin : [ obj1, obj2, Ctor ] // <- will copy things in order
}

(?) will execute constructor chain on the master object. Not sure yet is it needed or not. But might be cool.

Make relational patterns explicit.

Extension available in separate module, providing necessary tools to deal with model's references by id. Useful in situations when you want to receive ids and arrays of ids instead of nested models and collections. Usual problem with this approach is that you need to manage the state of several collections required to resolve id references, which could be tricky.

Will NestedRelations its so easy, that you'd be surprised. Lets start with an example with User and Role models.

var User = Nested.Model.extend({
    urlRoot : '/api/users',

    defaults : {
        name : '',
        roles : Nested.Collection.subsetOf( 'roles' )
    }
});

var Role = Nested.Model.extend({
    urlRoot : '/api/roles',

    defaults : {
        name : '',
        users : Nested.Collection.subsetOf( 'users' )
    }
});

And now all what we need to complete an example, is to tell NestedRelations, what are that 'roles' and 'users':

Nested.relations = {
    users : User.Collection,
    roles : Role.Collection
};

That's really it.

Enum type

  • it must support iteration through its option
  • it must perform type casts on assignments.
  • it must support display names, symbolic shortcuts, and serialization ids.
  • syntax must be declarative and minimalistic.

Something like this:

var ChartTypes = Nested.Enum( 'one', 'two', 'three' );

var ChartTypes = Nested.Enum( { id : 'one', label : 'One' }, { id: 2, name: 'two', label: 'Two' }, 'three' );

ChartTypes.each( ... ) // underscore methods must be proxied

ChartTypes.one // elements should be available by name

Should behave as String. Could be implemented internally as Model.from.

Collection.RefTo - handle circular dependencies

Right now it's impossible to handle circular dependencies, if you want to extend precise collection type.

Possible solution would be to clone master collection in the moment when collection is being resolved. It's theoretically possible. Bad part is that collection will be replaced, but good part is that it would be done before collection will be actually returned :). So no one will notice it.

What we could do, actually, is to delay creation of Collection to the moment of time when attribute will be accessed for the first time (lazy construction). Bla-a-ack magic.

A = Model.extend({
    defaults : {
        bs : Nested.Collection.subsetOf( 'bs' )
    }
});

B = Model.extend({
    defaults : {
        as : Nested.Collection.subsetOf( 'as' ),
        a : Nested.Model.from( 'as' )
    }
});

Model get/set: support for deep refs

"deep ref" is symbolic reference to nested object's attribute, like this:
"attrName.attrName.modelIdOrCid.attrName"

  1. Model.get must accept symbolic references.
  2. Model.set( attr, value ) must accept symbolic references too.

Must be implemented in non-recursive way, so it will work on regular nested objects too.

This capability will be beneficial for:

  • nested relations. (Model.get support is required)
  • simple data binding.

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.