Coder Social home page Coder Social logo

handlebars-iron's Introduction

handlebars-iron

Handlebars middleware for the Iron web framework.

Build Status

This library, together with handlebars, iron and hyper, works on both stable and nightly rust.

Both iron and handlebars has backward-incompatible change during 0.x releases. So you will need to choose handlebars-iron version based on those two versions you were using:

handlebars-iron handlebars iron
0.14.x 0.16.x 0.2.x
0.15.x 0.18.x 0.3.x
0.16.0 0.19.x 0.3.x
0.17.x 0.19.x 0.4.x
0.18.x 0.20.x (serde 0.8) 0.4.x
0.19.x 0.22.x 0.4.x
0.20.x 0.23.x 0.4.x
0.21.x 0.24.x 0.4.x
0.22.x 0.24.x 0.5.x
0.23.x 0.25.x (serde 0.9) 0.5.x
0.24.x 0.26.x (serde 1.0) 0.5.x
0.25.x 0.29.x 0.5.x
0.26.x 0.32.x 0.6.x
0.27.x 1.x 0.6.x
0.28.x 2.x 0.6.x
0.29.x 3.x 0.6.x

Usage

Add HandlebarsEngine to your Iron middleware chain as an "after" middleware.

  /// HandlebarsEngine will look up all files with "./examples/templates/**/*.hbs"
  let mut hbse = HandlebarsEngine::new();
  hbse.add(Box::new(DirectorySource::new("./examples/templates/", ".hbs")));

  // load templates from all registered sources
  if let Err(r) = hbse.reload() {
    panic!("{}", r);
  }

  chain.link_after(hbse);

If you want register your own custom helpers, you can initialize the HandlebarsEngine from a custom Handlebars registry.

  let mut hbse = HandlebarsEngine::new();
  hbse.add(Box::new(DirectorySource::new("./examples/templates/", ".hbs")));
  hbse.handlebars_mut().register_helper("helper", my_helper);

  // load templates from all registered sources
  if let Err(r) = hbse.reload() {
    panic!("{}", r);
  }

  chain.link_after(hbse);

You can find more information about custom helper in handlebars-rust document.

In your handler, set Template to response. As required by Handlebars-rust, your data should impl serde::Serialize.

For DirectorySource, handlebars engine will walk the directory specified by prefix, try to register all templates matches the suffix, and extract its name as template name. For instance, ./examples/templates/some/path/index.hbs will be registered as some/path/index.

/// render data with "index" template
/// that is "./examples/templates/index.hbs"
fn hello_world(_: &mut Request) -> IronResult<Response> {
    let mut resp = Response::new();

    let data = ...
    resp.set_mut(Template::new("index", data)).set_mut(status::Ok);
    Ok(resp)
}

By using Template::with You can also render some template without actually register it. But this is not recommended because template string needs to be parsed every time. Consider using a MemorySource if possible.

/// render data with "index" template
/// that is "./examples/templates/index.hbs"
fn hello_world(_: &mut Request) -> IronResult<Response> {
    let mut resp = Response::new();

    let data = ...
    resp.set_mut(Template::with("<h1>{{title}}</h1>", data)).set_mut(status::Ok);
    Ok(resp)
}

Since this is simple library, you may run this example with RUST_LOG=handlebars_iron=info cargo run --example server first, and documentation then.

Rust and its ecosystem are still in early stage, this project might been broken for various reasons. I will try my best to keep this library compiles with latest Rust nightly before the 1.0 final release. If you find anything bad, pull requests and issue reporting are always welcomed.

Live reload

During development you may want to live-reload your templates without having to restart your web server. Here comes the live-reload feature.

Since live-reload may only be useful in development phase, we have made it a optional feature. In order to enable it, you will need to add feature watch in your cargo declaration:

[features]
## create a feature in your app
watch = ["handlebars-iron/watch"]

[dependencies]
handlebars-iron = ...

Check examples/watch_server.rs for further information. To test it: RUST_LOG=handlebars_iron=info cargo run --example watch_server --features watch.

Using handlebars-iron?

Add your project to our adopters.

License

MIT, of course.

handlebars-iron's People

Contributors

alextalker avatar bbigras avatar carols10cents avatar cmbrandenburg avatar cmsd2 avatar cydrobolt avatar euclio avatar golddranks avatar jonil avatar leoyvens avatar mvdnes avatar sunng87 avatar untitaker avatar viraptor 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

handlebars-iron's Issues

Handlebars and Mount and StaticFile

How to use it with Mount, does it support it?

  let mut hbse = HandlebarsEngine::new();
  hbse.add(Box::new(DirectorySource::new("./views/", ".hbs")));
  if let Err(r) = hbse.reload() {
    panic!("{}", r.description());
  }

  let mut router = Router::new();
  router.get("/", index);

  let mut mount = Mount::new();
  mount
    .mount("/", router)
    mount("/css/", Static::new(Path::new("static/css/")));

   println!("Server running at http://localhost:3000/");
   Iron::new(mount).http("localhost:3000").unwrap();

Here how can I add hbse to mount or how to get them work together?

Ability to access request extensions in Helpers

I want to be able to access the current request (in particular the extensions field) from within the template helpers I registered. The end goal is to make url_for from iron-router available in templates.

I realize that this is extremely hard and I'm not sure if handlebars-iron is even supposed to solve it. Flask solves the general problem by adding threadlocals everywhere, but I don't think that's the way to go for a framework and language where performance, control and data flow is more explicit than elsewhere.

Glob matching fails silently on non-existing dir

When setting a non-existing directory for the prefix, I think it would be desirable that glob matching would panic and report the error. I just noticed that it fails silently, since I ran cargo run inside src/ whereas I usually run it from the cargo root dir.

I also thought that it would be maybe nice to change glob to std::fs::WalkDir iterator to eliminate a dependency, but that is marked as unstable, and I don't know whether it'll stabilise until the 1.0 beta. But what do you think about using it when it stabilises?

no method named `register_helper` found

I try registering Helper

#![allow(dead_code)]
use hbs::{HandlebarsEngine, DirectorySource};
use handlebars::{Handlebars, RenderError, RenderContext, Helper, Context};

pub fn template_render(paths: Vec<&str>) -> HandlebarsEngine {
    let mut template = HandlebarsEngine::new();

    // Add helpers
    template.register_helper("link", Box::new(link_helper));

    // add a directory source, all files with .html suffix
    // will be loaded as template
    for path in paths.iter() {
        template.add(Box::new(DirectorySource::new(path, ".html")));
    }

    // load templates from all registered sources
    if let Err(r) = template.reload() {
        panic!("{}", r);
    }
    template
}

And I got error:

error: no method named `register_helper` found for type `hbs::HandlebarsEngine` in the current scope
  --> src/middleware/render.rs:11:14
   |
11 |     tempalte.register_helper("link", Box::new(link_helper));
   |              ^^^^^^^^^^^^^^^

error: aborting due to previous error

How to fix that?

Actively Developed?

Is this still being Actively developed on? because I really do enjoy this Library, keep up the good work!

Where to put CSS/JS files?

I have a template called index.hbs in my templates folder and now I'm trying to include some CSS and JS files in the HTML. However, no matter where I put my CSS/JS folder, the server can never find these files. Where am I supposed to put them for them to get picked up?

Compilation error with `serde_type`

So when using the serde_type feature I get a compilation error. The compilation error actually originates in the handlebars crate but since I do not depend on it directly I was not sure where I should submit the issue.

Compiling handlebars v0.18.1
error: a type named `Json` has already been imported in this module [--explain E0252]
  --> /home/azerupi/.cargo/registry/src/github.com-1ecc6299db9ec823/handlebars-0.18.1/src/template.rs:11:5
   |>
9  |> use serialize::json::Json;
   |>     --------------------- previous import of `Json` here
10 |> #[cfg(feature = "serde_type")]
11 |> use serde_json::value::Value as Json;
   |>     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: a trait named `ToJson` has already been imported in this module [--explain E0252]
 --> /home/azerupi/.cargo/registry/src/github.com-1ecc6299db9ec823/handlebars-0.18.1/src/registry.rs:9:5
  |>
7 |> use serialize::json::ToJson;
  |>     ----------------------- previous import of `ToJson` here
8 |> #[cfg(feature = "serde_type")]
9 |> use serde::ser::Serialize as ToJson;
  |>     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: a type named `Json` has already been imported in this module [--explain E0252]
  --> /home/azerupi/.cargo/registry/src/github.com-1ecc6299db9ec823/handlebars-0.18.1/src/render.rs:10:5
   |>
8  |> use serialize::json::Json;
   |>     --------------------- previous import of `Json` here
9  |> #[cfg(feature = "serde_type")]
10 |> use serde_json::value::Value as Json;
   |>     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: a type named `Json` has already been imported in this module [--explain E0252]
 --> /home/azerupi/.cargo/registry/src/github.com-1ecc6299db9ec823/handlebars-0.18.1/src/helpers/helper_each.rs:4:31
  |>
2 |> use serialize::json::{Json, ToJson};
  |>                       ---- previous import of `Json` here
3 |> #[cfg(feature = "serde_type")]
4 |> use serde_json::value::{self, Value as Json};
  |>                               ^^^^^^^^^^^^^

error: a type named `Json` has already been imported in this module [--explain E0252]
 --> /home/azerupi/.cargo/registry/src/github.com-1ecc6299db9ec823/handlebars-0.18.1/src/helpers/helper_lookup.rs:4:5
  |>
2 |> use serialize::json::Json;
  |>     --------------------- previous import of `Json` here
3 |> #[cfg(feature = "serde_type")]
4 |> use serde_json::value::Value as Json;
  |>     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: a type named `Json` has already been imported in this module [--explain E0252]
   --> /home/azerupi/.cargo/registry/src/github.com-1ecc6299db9ec823/handlebars-0.18.1/src/context.rs:7:31
    |>
2   |> use serialize::json::{Json, ToJson};
    |>                       ---- previous import of `Json` here
...
7   |> use serde_json::value::{self, Value as Json};
    |>                               ^^^^^^^^^^^^^

error: duplicate definitions with name `call`: [--explain E0201]
  --> /home/azerupi/.cargo/registry/src/github.com-1ecc6299db9ec823/handlebars-0.18.1/src/helpers/helper_each.rs:87:5
   |>
87 |>     fn call(&self,
   |>     ^
note: previous definition of `call` here
  --> /home/azerupi/.cargo/registry/src/github.com-1ecc6299db9ec823/handlebars-0.18.1/src/helpers/helper_each.rs:17:5
   |>
17 |>     fn call(&self,
   |>     ^

error: duplicate definitions with name `wraps`: [--explain E0201]
  --> /home/azerupi/.cargo/registry/src/github.com-1ecc6299db9ec823/handlebars-0.18.1/src/context.rs:79:5
   |>
79 |>     pub fn wraps<T: Serialize>(e: &T) -> Context {
   |>     ^
note: previous definition of `wraps` here
  --> /home/azerupi/.cargo/registry/src/github.com-1ecc6299db9ec823/handlebars-0.18.1/src/context.rs:73:5
   |>
73 |>     pub fn wraps<T: ToJson>(e: &T) -> Context {
   |>     ^

error: aborting due to 2 previous errors
error: Could not compile `handlebars`.

At first sight, it seems as enabling serde_type does not disable rustc-serialize causing the double definitions.

EDIT: It seems to compile fine with default-features = false but intuitively I would have assumed that just enabling serde_type would take care of this. Would it be possible disable rustc-serialize when serde_type is enabled?

FR: Improve usage for inlined templates

I'm currently writing an application that should run as a single binary. Right now I'm acquiring the RwLock for the registry explicitly, and call register_template_string(..., include_str!(...)) for each template.

I've found two rather easy to fix usability problems specific to my usecase:

  • When trying to use HandlebarsEngine::from with a custom registry, it cleared out all manually registered templates.
  • Handlebars-rust still tries to stat against a directory, but in my case this isn't necessary.

And then the idea of loading/parsing/compiling templates at compile-time comes to mind, but I suspect that the necessary APIs aren't stable yet in Rust, and that it's too late for those kind of major feature requests.

Regarding the two usability problems, I am not sure what to do. Perhaps HandlebarsEngine could make the prefix and suffix fields of type Option<String> to indicate that the raw registry should be used? Such a state would also disallow usage of reload and disable its initial invocation.

DirectorySource path handling is unexpected

DirectorySource treats the path argument in a very literal way. Specifically if there's a file "templates/index.hbs", then

DirectorySource::new("templates", ".hbs")

will register template "/index", but

DirectorySource::new("templates/", ".hbs")

will register template "index". I'd expect both to do the same thing (without initial slash), because the path points at the exact same directory.

DirectorySource does not use correct path types

The DirectorySource type is constructed using UTF-8 strings. At the least, it should use std::ffi::OsStr, but probably it should use std::path::Path.

This issue is related to #23, where the idea was mentioned but seems to have been dropped.

Because this would be a breaking change, maybe it would make sense to rethink the DirectorySource API. Consider the following constructors:

pub fn new_with_extension<D, E>(directory: D, extension: E) -> DirectorySource
    where D: Into<std::path::PathBuf>,
          E: AsRef<std::ffi::OsStr>
{ unimplemented!(); }

pub fn new_with_filter<D, F>(directory: D, filter: F) -> DirectorySource
    where D: Into<std::path::PathBuf>,
          F: Fn(&std::path::Path) -> bool
{ unimplemented!(); }

The new_with_extension method would replace the current new method, whereby the application wants to use all files in a directory (and its subdirectories) with a given extension.

Whereas, the new_with_filter method would allow applications more flexibility in terms of filtering files based on any custom criteria.

Update crates.io version

The current version of this library on Github builds fine (with a few warnings), but the version on crates.io doesn't build. You need to bump the version and re-release it.

Update to handlebars 1.0

Handlebars 1.0 has been released. It would be great to update this crate to the new handlebars.

Improve Template errors reporting

Currently, mistakes at the templates syntax really dangerous zone.

For example:

  let mut template = HandlebarsEngine::new();
  hbs.add(Box::new(DirectorySource::new(path, ".hbs")));
  if let Err(r) = hbs.reload() {
      ###  panic!("{:?}", r.description());
  }

And for example template with wrong helper name:
mytemplate.hbs

<div>
    {{#myhelper 123}}{{/myhelper}}
</div>

Compilation successful.
I tried run:
thread 'main' panicked at '"TemplateError"', src/middleware/render.rs:50

I suggesting extremely improve error reporting, for example:

Template parse error:
    |> mytemplate.hbs 2:5
    |> {{#myhelper 123}}{{/myhelper}}
        --------------------------
Error: unsupported helper name.

Same for any other template errors.

My reasons:

  • easy and clear debugging
  • fast development
  • clear understanding what is going on.
  • anything else

Relicense under dual MIT/Apache-2.0

Why?

The MIT license requires reproducing countless copies of the same copyright
header with different names in the copyright field, for every MIT library in
use. The Apache license does not have this drawback, and has protections from
patent trolls and an explicit contribution licensing clause. However, the
Apache license is incompatible with GPLv2. This is why Rust is dual-licensed as
MIT/Apache (the "primary" license being Apache, MIT only for GPLv2 compat), and
doing so would be wise for this project. This also makes this crate suitable
for inclusion in the Rust standard distribution and other project using dual
MIT/Apache.

How?

To do this, get explicit approval from each contributor of copyrightable work
(as not all contributions qualify for copyright) and then add the following to
your README:

## License

Licensed under either of
 * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
 * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.

### Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you shall be dual licensed as above, without any
additional terms or conditions.

and in your license headers, use the following boilerplate (based on that used in Rust):

// Copyright (c) 2015 t developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

And don't forget to update the license metadata in your Cargo.toml!

Contributor checkoff

Add a development mode where templates are always reloaded from disk

If I make a change in a template, I currently have to restart the server to see the change appear. It would be nice if there was some development mode that always loaded the latest version of the template from disk, so I wouldn't have to rerun my binary every time I make a template change.

Template is not rendered in AfterMiddleware

I'm trying to implement a custom 404 handler that renders a handlebars template. I have the following code:

struct ErrorHandler;

impl AfterMiddleware for ErrorHandler {
    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        if let Some(_) = err.error.downcast::<NoRoute>() {
           Ok(Response::with((status::NotFound, Template::new("not_found", ()))));
        } else {
            Err(err)
        }
    }
}

I have made sure that the template is registered (my other templates work correctly), and that the middleware is linked. However, there is no body in the response. "Not found" is returned in the body if I replace the Template::new with "Not found".

Add the ability to gzip content before Responding

It's easy enough to add gzip to non-handlebars iron responses:
https://github.com/gsquire/compress (which might be a great example of how to use the other library)

BUT, when using handlebars, it's much trickier to do. I was working on my own AfterMiddleware handler that would mutate the output of handlerbars.. but still being new to Rust, and the AfterMIddleware piece has been a little tricky. Should be fairly simple given you already have that piece working if it's directly integrating into the library... and probably an appreciated feature by all.
https://github.com/alexcrichton/flate2-rs

Here's the snippet of code I already have working, to be added to the AfterMiddleware, potentially adding gzip as an extra option:
let raw_accept_encoding = request.headers.get_raw("Accept-Encoding").unwrap();

    for accept_encoding in raw_accept_encoding
    {
        println!("raw_accept_encoding: {}", str::from_utf8(accept_encoding).unwrap());

        if str::from_utf8(accept_encoding).unwrap().to_lowercase().contains("gzip")
        {
            response.headers.set(ContentEncoding(vec![Encoding::Gzip]));
            let mut encoder = GzEncoder::new(Vec::new(), Compression::Default);
            encoder.write_all("Insert Handlerbars output here");

           let compressed_bytes = encoder.finish().unwrap();  //except don't unwrap
            //then return compressed_bytes. 
      }

}

[Suggestion] Implementing Handler for Templates

Hey!

This library is awesome - thank you for your continued work on it.

I'm making this issue to ask if you think it is worth implementing Handler for Template? Currently I am doing something like this in my code:

pub struct TemplateHandler {
    path: &'static str
}

impl TemplateHandler {
    pub fn new(path: &'static str) -> TemplateHandler {
        TemplateHandler { path: path }
    }
}

impl Handler for TemplateHandler {
    fn handle(&self, _: &mut Request) -> IronResult<Response> {
        let mut resp = Response::new();
        resp.set_mut(Template::new(self.path, ())).set_mut(status::Ok);
        Ok(resp)
    }
}

// Mount the templating
mount.mount("/",
            router!(
    get "/" => TemplateHandler::new("index"),
    get "/kmeans" => TemplateHandler::new("kmeans"),
    get "/dbscan" => TemplateHandler::new("dbscan"),
));

Maybe I'm missing the point but I think it would be nice to provide this behavior within this library. I know there are many use cases that will not simply be rendering a web page without providing new content. But I think this is common enough that it is valuable.

An alternative could be to implement Handler for a new wrapper type as I have - perhaps called RenderTemplate?

In any case, it is not too much extra code to add on and the library is very useful as is!

Watching is unreliable

Quite often (perhaps half the time) watch() is missing instances of me saving files. If I save it again, then it normally (but still not quite always) works.

Passing array with object as first element as object array as second is broken.

Shown by the following example:

Add the following two files to the examples and examples/templates as array.rs and array.hbs respectively:

extern crate iron;
extern crate env_logger;
extern crate handlebars_iron as hbs;
extern crate rustc_serialize;

use iron::prelude::*;
use iron::{status};
use hbs::{Template, HandlebarsEngine};
use rustc_serialize::json::{ToJson, Json};
use std::collections::BTreeMap;

struct Team {
    name: String,
    pts: u16
}

impl ToJson for Team {
    fn to_json(&self) -> Json {
        let mut m: BTreeMap<String, Json> = BTreeMap::new();
        m.insert("name".to_string(), self.name.to_json());
        m.insert("pts".to_string(), self.pts.to_json());
        m.to_json()
    }
}

fn make_data () -> Json {
    let sub_teams = vec![ Team { name: "Jiangsu Sainty".to_string(),
                             pts: 43u16 },
                      Team { name: "Beijing Guoan".to_string(),
                             pts: 27u16 },
                      Team { name: "Guangzhou Evergrand".to_string(),
                             pts: 22u16 },
                      Team { name: "Shandong Luneng".to_string(),
                             pts: 12u16 } ];

    let team = (Team { name: "Main Team".to_string(),
                       pts: 123u16 },
                sub_teams);
    team.to_json()
}

/// the handler
fn hello_world(_: &mut Request) -> IronResult<Response> {
    let mut resp = Response::new();

    let data = make_data();
    resp.set_mut(Template::new("array", data)).set_mut(status::Ok);
    Ok(resp)
}

fn main() {
    env_logger::init().unwrap();

    let mut chain = Chain::new(hello_world);
    chain.link_after(HandlebarsEngine::new("./examples/templates/", ".hbs"));
    println!("Server running at http://localhost:3000/");
    Iron::new(chain).http("localhost:3000").unwrap();
}
<html>
  <head>
    <title>Array Test</title>
  </head>
  <body>
    Main: <b>{{[0].name}}</b>: {{[0].pts}}
    <ul>
    {{#each this.[1]}}
      <li class="{{#if @first}}champion{{/if}}">
      {{@index}}. <b>{{name}}</b>: {{pts}} -- {{this}}
      </li>
    {{/each}}
    </ul>
    {{this.[1]}} <- output correct json
  </body>
</html>

The output will have all nulls for the sub-teams, even though it will print four lines with all null, so it is iterating over the right thing. More simple tests of array iteration seem to work, so I suspect something about the complexity of the json being passed is breaking things.

Accessing the sub-team array as just [1] also doesn't work.

0.14 release

0.14 release will sunset legacy APIs (new and from). new2 and from2 will be renamed to new and from.

Also 0.14 will be using iron 0.3 which is incompatible via previous versions.

The functionality of 0.14 will be totally identical with 0.13.x. If you don't want to update to new iron/hyper, you can just stick to 0.13.x.

Handle templates set on IronErrors

Currently, modifying the response of an Err(IronError) to have a template is not processed by the AfterMiddleware in this package, and the response body stays empty.

To process errors as well, catch needs to implemented besides after.

DirectorySource not loading templates in a heirarchy

Loading templates puts them all in a flat namespace instead of a heirarchy like described here in the README.

For DirectorySource, handlebars engine will walk the directory specified by prefix, try to register all templates matches the suffix, and extract its name as template name. For instance, ./examples/templates/some/path/index.hbs will be registered as some/path/index.

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.