Coder Social home page Coder Social logo

esamattis / node-hbsfy Goto Github PK

View Code? Open in Web Editor NEW
256.0 6.0 29.0 186 KB

This package is not maintained anymore and does not receive security updates

Home Page: https://npmjs.org/package/hbsfy

License: Other

Makefile 0.47% JavaScript 96.23% Shell 0.77% HTML 2.54%

node-hbsfy's Introduction

Build Status

hbsfy

Handlebars precompiler plugin for Browserify without magic.

Compiles Handlebars templates to plain Javascript. The compiled templates only have one copy of the Handlebars runtime so they are lightweight and fast!

Usage

Install hbsfy locally to your project:

npm install --save-dev hbsfy

You will also need Handlebars installed. Handlebars 1, 2, 3, and 4 are supported for now (use 4 for best results):

npm install --save-dev handlebars

Then use it as Browserify transform module with -t:

browserify -t hbsfy main.js > bundle.js

where main.js can be like:

var template = require("./template.hbs");
document.body.innerHTML = template({ name: "Epeli" });

and template.hbs:

<h1>Hello {{name}}!</h1>

Options

Custom Extension

You can use --extensions or -e subarg option to configure custom extensions for the transform:

browserify -t [ hbsfy -e html,htm ] main.js > bundle.js

Alternate Precompiler/Compiler

You can specify how the templates are precompiled by using -p or --precompiler, which might also be used with the -c or --compiler option, like so:

browserify -t [ hbsfy -p ember-template-compiler -c Ember.Handlebars ] main.js > bundle.js

By default the precompiler is the handlebars node module and the compiler is "require('hbsfy/runtime')".

Options for the precompiler can be passed using a precompilerOptions key.

Example:

Enable myUltimateHelper only

browserify -t [ hbsfy --precompilerOptions [ --knownHelpersOnly --knownHelpers [ --myUltimateHelper ] ] ]  main.js > bundle.js

See Handlebars API reference for details.

Common JS Partial Resolution

Using the --traverse or -t option will cause partials to be resolved using node's module resolution algorithm. Be sure to prefix relative paths with ./ or ../ as needed. Otherwise the algorithm assumes a node_module is being referenced.

Example:

browserify -t [ hbsfy -t ] main.js > bundle.js
<!-- main.hbs -->
<div>{{> ./path/to/partial.hbs }}</div>
<!-- path/to/partial.hbs -->
<p>I'm a partial</p>

Inline Partials

If you are using Common JS partial resolution (setting the --traverse flag) and you are using Handlebars 4.0.0 or later, you can still use inline partials. Make sure to not use inline partial names that conflict with node_module dependencies. The inline partial will be used over a dependency reference.

package.json

Transform can be configured from the package.json too.

{
  "browserify": {
    "transform": [
      [
        "hbsfy",
        {
          "extensions": [
            "html"
          ],
          "precompilerOptions": {
            "knownHelpersOnly": true,
            "knownHelpers": {
              "myUltimateHelper": true
            }
          }
        }
      ]
    ]
  }
}

The precompiler and compiler keys are naturally available too.

See module-deps documentation for more information as this feature is implemented there (it's a part of Browserify itself).

Programmatic usage

The configure method of the transform can be used to create new transforms with different defaults.

var hbsfy = require("hbsfy").configure({
  extensions: ["html"]
});

var browserify = require("browserify");
var b = browserify("./index.js");
b.transform(hbsfy);
b.bundle().pipe(fs.createWriteStream("./bundle.js"));

Helpers

To register custom helpers, require the runtime and run registerHelper to create helper:

var Handlebars = require("hbsfy/runtime");
Handlebars.registerHelper("upcase", function(s) {
  return s.toUpperCase();
});

Partials

Partials can be created by giving precompiled template to the registerPartial function.

Handlebars.registerPartial('link', require("./partial.hbs"));

Checkout the example folder for details.

Note: if using the --traverse option, partial registration is automatic.

.compile

This synchronous method can be used to enable all hsbfy functionality in another environment, such as node or a test runner (such as mocha).

// mocha-hbs.js
var fs = require("fs");
var hbsfy = require("hbsfy");

require.extensions['.hbs'] = function (module, filename) {
  var file = fs.readFileSync(filename, "utf8");
  var opts = { traverse: true };
  return module._compile(hbsfy.compile(file, opts), filename);
}
$ mocha -r hbs:./mocha-hbs.js tests/

Remember to register your custom helpers as well! Ideally your templates themselves require your helpers and runtime, and call registerHelper. But if they don't, all helpers can be loaded at once as part of the require hook above:

// mocha-hbs.js
var fs = require("fs");
var hbsfy = require("hbsfy");
var runtime = require("hbsfy/runtime");
var helpers = require("./path/to/my/exported/helpers");

Object.keys(helpers).forEach(function (key) {
  runtime.registerHelper(key, helpers[key]);
});

require.extensions['.hbs'] = function (module, filename) {
  var file = fs.readFileSync(filename, "utf8");
  var opts = { traverse: true };
  return module._compile(hbsfy.compile(file, opts), filename);
}

Process output HTML string

This option accepts a function which takes one argument (the template file content) and returns a string which will be used as the source for the precompiled template object. The example below removes leading and trailing spaces to shorten templates.

hbsfy.configure({
  processContent: function(content) {
    content = content.replace(/^[\x20\t]+/mg, '').replace(/[\x20\t]+$/mg, '');
    content = content.replace(/^[\r\n]+/, '').replace(/[\r\n]*$/, '\n');
    return content;
  }
});

Changelog

2.8.1

  • Fix build due to source maps being included in bundle output.

2.8.0

  • Support block partials, ignoring templates with @ prefix. #60

2.7.0

  • Allow inline partials when using --traverse. #54

2.6.0

  • Add processContent option. #50

2.5.0

  • Export findPartials and compile for use in utilities / test frameworks #49.

2.4.1

2.4.0

  • support handlebars 2, 3, 4

2.3.1

  • Handle null nodes when traversing Handlebars AST.

2.3.0

  • Allow resolving / requiring partials using node's module resolution algorithm (--traverse). #47

2.2.1

  • Emit compile errors instead of crashing. #38

2.2.0

  • Support for compiler options #29

2.1.0

  • Subargs options for alternate precompilers and compilers #31

2.0.0

  • Support Browserify subargs
  • The configure method does not mutate the inner state of the module anymore
    • Instead it returns a new transform function.
  • Handlebars is not a peerDependency anymore
    • It must be manually installed
    • This relaxes completely the version binding of Handlebars - it is now possible to try Handlebars 2.0 alpha

1.3.0

  • Support Handlebars 1.3
  • Now uses the official runtime api

1.0.0

  • Remove handlebars-runtime dependency and depend directly on the handlebars module as a peer dependency.
    • Runtime must be now required with require("hbsfy/runtime") instead of require("handlebars-runtime").
    • Thanks to @kamicane for teaching me how to do this.
  • Option to configure template extensions

node-hbsfy's People

Contributors

alanpeabody avatar cesutherland avatar danspam avatar esamattis avatar hughsk avatar jsahlen avatar kentmw avatar kirbysayshi avatar romanyanke avatar sinewyk avatar terinjokes 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

node-hbsfy's Issues

hbsfy with --traverse fails with inline partials

With Handlebars 4.0.0, they introduced inline partials: http://handlebarsjs.com/partials.html#inline-partials

Our project added the --traverse flag so that we could bundle our partials in our browserify bundles (very helpful!).

These don't seem to mix as the inline partials are defined inline inside the same template.

Take this example from the handlebars documentation:

{{#*inline "myPartial"}}
  My Content
{{/inline}}
{{#each children}}
  {{> myPartial}}
{{/each}}

In this example myPartial will assumed to be node_module dependency. My thoughts is it would be nice to be able to give an indication to the hbsfy parser that this is an inline partial and not modify it.

Something like:

{{#*inline "myPartial"}}
  My Content
{{/inline}}
{{#each children}}
  {{> inline:myPartial}}
{{/each}}

I believe this would just require:

if (p.indexOf('inline:') !== 0) {
   ...
}

on line https://github.com/epeli/node-hbsfy/blob/master/index.js#L139

I'll make a pull request if we think this the way to solve this.

Most likely not working with helpers and partials

I'm not entirely sure, but having worked with templatify and customizing it to actually work for our app which still required lots of changes, I believe it is not as simple as you have it set up currently.

As far as I can see the way you are doing it will not include the necessary information to make partials and helpers work. This should give you an idea of how complicated it actually is to get those working.

BTW, I fell for this once too thinking it was that easy.

So I guess a good idea would be to add tests concerning partials and helpers and go from there.

You can use this or more specifically this as a source of information as well.

Good luck :)

Using helpers through command-line

The doc don't clearly stated how to register helpers with the command line (and if it's even possible).

So is there a way to add the following helper to the -t options?
browserify -t [ hbsfy -e html,htm ] main.js > bundle.js

var Handlebars = require("hbsfy/runtime");
Handlebars.registerHelper("upcase", function(s) {
  return s.toUpperCase();
});

comiling templates in the node_modules folder

I understand my setup might sound a bit strange, but please bear with me: I am trying to create a project that exposes some simple common web widgets (js/hbs). I know I should probably bundle the widgets and deploy them, but I was curious to see if I could npm install the widgets project from my other web projects.

Things seemed to be going okay, but then suddenly I started getting errors from browserify:

SyntaxError: Unexpected reserved word

After a little digging, it turned out that these errors were being caused because some of my hbs files were being required in as js files. Some of them were compiling into functions as expected, but others were not. I have not been able to pinpoint exactly what the difference is between the files that are failing, but I didn't want to waste any more time without asking my question.

Is this the expected behavior when requiring in templates from the node_modules folder? Or is there something else going on here?

hbsfy returns BOM character at the beginning of templated strings

var template_hbsfy = require('template.hbs');

// tmpl_string is the exact same string contained in template.hbs, read using $.ajax()
var template = Handlebars.compile(tmpl_string);

// obj is some object
var string1 = template_hbsfy(obj);
var string2 = template(obj);

string1 and string2 are different.

string1 contains a BOM character appended to the beginning ot the string. This is interpreted as an empty text element.

How to use with custom (Ember) compiler with the browserify api?

Hey man, trying to run hbsfy through the browserify api in a gulp task with the Ember template compiler.

This is what my task looks like:

gulp.task('browserify', function() {
    var source = require('vinyl-source-stream');
    var browserify = require('browserify');

    var bundler = browserify({
        entries: ['/js/main.js'],
        debug: true
    });

    return bundler
        .bundle()
        .pipe(source('bundle.js'))
        .pipe(gulp.dest('/build/'));
});

I have my browserify transforms set up in my package.json:

  "browserify": {
    "transform": [
      "browserify-shim",
      "hbsfy"
    ]
  }

But this doesn't compile the templates with the ember-template-compiler. How do I add that in there? Could you maybe add an example to the readme?

peerDep

Hi,

why is there no peerDep to handlebars.
e.g. just like before 2.0.0 but with * version:

"peerDependencies": {
  "handlebars": "*"
}

Errors on comments

Consider the template

template.js

{{!-- a comment --}}
<div class="smt"></div>

app.js

module.exports = {
   template: require("./template.hbs")
};

Compiling will give an error something like (using slightly different templates):

Error: Parsing file /home/gyeates/Dropbox/clearpath/LoLoch/assets/templates/nav.hbs: Line 2: Unexpected identifier
    at Deps.parseDeps (/home/gyeates/Dropbox/clearpath/LoLoch/node_modules/gulp-browserify/node_modules/browserify/node_modules/module-deps/index.js:339:28)
    at /home/gyeates/Dropbox/clearpath/LoLoch/node_modules/gulp-browserify/node_modules/browserify/node_modules/module-deps/index.js:282:29
    at ConcatStream.<anonymous> (/home/gyeates/Dropbox/clearpath/LoLoch/node_modules/gulp-browserify/node_modules/browserify/node_modules/concat-stream/index.js:32:43)
    at ConcatStream.emit (events.js:117:20)
    at finishMaybe (/home/gyeates/Dropbox/clearpath/LoLoch/node_modules/gulp-browserify/node_modules/readable-stream/lib/_stream_writable.js:460:14)
    at endWritable (/home/gyeates/Dropbox/clearpath/LoLoch/node_modules/gulp-browserify/node_modules/readable-stream/lib/_stream_writable.js:469:3)
    at ConcatStream.Writable.end (/home/gyeates/Dropbox/clearpath/LoLoch/node_modules/gulp-browserify/node_modules/readable-stream/lib/_stream_writable.js:436:5)
    at DuplexWrapper.onend (/home/gyeates/Dropbox/clearpath/LoLoch/node_modules/gulp-browserify/node_modules/readable-stream/lib/_stream_readable.js:537:10)
    at DuplexWrapper.g (events.js:180:16)
    at DuplexWrapper.emit (events.js:117:20)

Babel plugin

This is obviously out of scope for this particular package but currently their just isn't a good babel handlebars plugin. I've tried to mess around and create one but truthfully babel plugins confuse me.

For me personally this would resolve #51.

If you'd be interested in making one that would be amazing.
Feel free to close this issue if this isn't something you'd consider doing.

example use-cases of hbsfy

Hi, forgive me if you find this issue untypical, but I simply don't get the idea of where I would need to use hbsfy? I've been using handlebars in SPA projects, I know how do they work. And I do understand the main concept of browserify. But I can't think of any real world example I might use hbsfy in. I read all readme docs and I didn't found it there asa well. Please give me a hint on that.

Helpers not registering

Hi,

I'm having an issue with helpers not registering at all. I have them in a separate file and am importing the runtime as per the instructions:

# helpers.coffee
Handlebars = require 'hbsfy/runtime'
.
.
Handlebars.registerHelper 'duration', (ms) ->
  min = ms / 1000 / 60
  "#{Math.floor min / 60}t #{min % 60}min"

The helper functions show up in the bundle as normal code, when instead they should be included in the bundle in some other way?

 // bundle.js
  Handlebars.registerHelper('duration', function(ms) {
    var min;
    min = ms / 1000 / 60;
    return "" + (Math.floor(min / 60)) + "t " + (min % 60) + "min";
  });

Do they need to be included in a different way? My build script uses the hbsfy transform like this:

// build.js
function bundle() {

  var builtFile = path.join(__dirname, 'build', 'bundle.js');
  var outFile = fs.createWriteStream(builtFile, 'utf8');

  var b = browserify();
  b.require(path.resolve('./js/main.js'), {entry: true});
  b.transform(require("hbsfy"));
  b.bundle({ debug: true })
      .on('end', function () {
        console.log('Build succeeded');
      })
      .on('error', console.error.bind(console))
      .pipe(outFile)
  ;
}

The templates get generated just fine. Any idea what might be the problem? I'm using Handlebars version 1.3.0.

Cheers!

Show original markup in source map

Currently, the compiled markup is shown in the browserify source map. Ideally, the original handlebars markup would be shown.

Note that other browserify transforms such as stringify show the original markup, so this should be doable.

Cheers - and thanks for this very useful transform.

Update deps

I was wondering if we can get Handlebars dep bumped to accommodate the 2.0 alpha?

Quick Templates – compile from string

This plugin is a fantastic piece of work! It's made implementing Handlebars a breeze in a couple of projects I've worked on recently. The only thing I've found wanting is the ability to compile templates direct from a string, as I frequently come across situations where I'd like to compile a quick ‘lil inline template rather than creating a whole new dedicated .hbs file. (Possibly this is not quite a "best-practice" approach, and maybe I should just be using partials, but ugh...)

Anyways, I didn't see this kind of functionality mentioned in the docs, tho I haven't explored the source code myself. I was wondering if there is some secret way to do this with the plugin currently? Or if you– the Right & Honourable dev. –might consider implementing something along these lines in the future? In either case, much ♥︎ for hbsfy!

Returning SafeString in Helper

my helper returns:

return new Handlebars.SafeString(' data-context=\'' + data + '\'');

Doesn't cause an error but the output is not a "SafeString"

way to use helpers from another bundle inside another bundle?

hi,

i would like to know if there is a way you recommend to use helpers from one bundled file in another bundled file, without duplicating the runtime code and helpers to both bundled files.., i could live with exporting the Handlebars object from one bundle but i dont know how to use this then global object in my other bundle.

Q: how to load hbs files dynamically

Hi there,

I'm trying to have my cake and eat it too: using node-hbsfy to precompile templates, but also allow for dynamically/run-time loading of hsb files.

I have a 99% working setup: at compile-time I use node-hbsfy and at run-time the standard Handlebars (bower) package, works like a charm.
There's two issues with this though:

  1. I'm pretty certain there's some code duplication due to the hbsfy runtime and the Handlebars lib both being included into the webapp.
  2. If I register helpers/partials with the hbsfy runtime they're not accessible in the Handlebars lib instance and vice versa.

Any known workarounds/solutions for this? For instance can I swap out the hbsfy runtime for the one shipped with Handlebars or something?

Issues when using with Ember specific Handlebars templates

Ember uses Ember.Handlebars for compilation (ember-template-compiler), since it adds additional helpers. When compiling these templates I get errors like:

Error: Missing helper: 'partial'

Maybe allowing for use of a custom compiler? Or if it's already available, then add docs?

Something like: "hbsfy-precompiler": "ember-template-compiler" in package.json and that would be required instead of handlebars. Something like mothership might be helpful (as well as browserify-shim, by the same author, which uses it).

honor Browserify "paths" when resolving partials?

the ability to resolve partials by relative path (via the --traverse flag) is great, but all the dots and slashes can get cumbersome quickly. I was wondering if there was a to get HBSfy to "honor" my Browserify paths setting somehow?

so for example, say I had the following files:

app/
├ templates/
│ └─item.hbs
└ views
  └ page/
    └─layout.hbs

if I wanted to use the item.hbs partial in my layout.hbs template I'd have to write out the entire relative path to the file. instead, it'd be awesome if hbsfy could somehow resolve partial paths against those I've set in my Browserify options! so that if I'd got it configured something like:

// in a gulpfile.js task or package.json entry or wherever
Browserify({
  entry:["src/index.js"],
  paths:["app/templates"] // HBSfy would resolve paths against this option
  })

now, in my faraway template I could simply write:

<!-- let's say in app/Views/Page/layout.hbs -->
{{#each items}}
  {{> templates/item.hbs }}
{{/each}}

instead of the fully qualified {{> ../../templates/item.hbs }} relative path.


hope that made a shred of sense, but honestly not sure — let me know if I can clarify!

Fatal error on Windows machine

Hi,

I am trying to use hbsfy, but i am getting the following error from my grunt task:
"Fatal error: Line 2: Unexpected identifier"

This problem is probably related to Windows 7 OS, because it works fine on MacOS.

Does any of you tried to use it on Windows? Thanks!

global installation fails

Somebody sent an issue via irc:

im getting an error while installing hbsfy globally, and
this makes it seem like its because of the peerDependencies
in the package.json file… have you had success installing hbsfy globally?

Personally I don't ever install libraries globally, but this should be investigated.

Helper for unit test support

First of all: thanks for hbsfy, it's a really clean solution for precompiling HBS views as part of the build. My only problem was: i have a few modules that require HBS files, and they work great when building the bundle with Browserify + hbsfy, but they can't be unit tested anymore.

controller.coffee

_ = require 'underscore'
view = require 'views/home.hbs'

exports.render = ->
    view {some: 'data'}

controller_spec.coffee

ctrl = require '../src/controllers/controller'

it 'renders stuff', ->
    ctrl.render()

This doesn't work because Node tries to require and parse the HBS file. Maybe there was an easier solution I missed, but I ended up adding this helper, required from my mocha.opts before any test runs:

spec_helper.coffee

fs = require 'fs'
handlebars = require 'handlebars'
runtime = require 'handlebars-runtime'

require.extensions['.hbs'] = (module, filename) ->
    js = handlebars.precompile fs.readFileSync(filename).toString()
    module.exports = runtime.template eval("[#{js}][0]")

Anyway, if this is a common pattern, maybe it could be a helper like hbsfy.addRequireSupport()?

use global Handlebars

hi, is there an easy way to configure grunt-browserify to use hbsfy transform but with the global Handlebars variable available?

Require hook (server side node).

It would be cool to have a require hook built into this (similar to babel, coffee-script, etc).
So that we could do:

require("hbsfy/register");
var myTemplate = require("./index.hbs");

There is already the example for "mocha hbs" but having this built in would help.
This is useful for apps that use handlebars isomorphically.

[email protected] is incompatible with [email protected]

The newer version of handlebars released yesterday is incompatible with [email protected] (which has runtime based on [email protected]):

screen shot 2013-06-01 at 6 55 58 pm

[email protected] ([email protected]) is missing a method added in 1.0.12.

Changing package.json to specify a compatible handlebars version or bumping handlebars-runtime should fix it (I've opened a PR to update node-handlebars-runtime with the latest code: esamattis/node-handlebars-runtime#2)

Hope that helps!

`ParseError: Unexpected end of input` no matter what 😭

It straight up seems not to be working for me, as though I'd failed to add the transform to my command.

browserify -t hbsfy app/init.js > app/build.js

yields:
ParseError: Unexpected end of input

which is the same result as running

browserify app/init.js > app/build.js

template.hbs:2
<h2>I am dying</h2>
      ^
ParseError: Unexpected identifier

template.hbs

<h1>Help me</h1>
<h2>I am dying</h2>

view.js

var Marionette  = require('backbone.marionette');
var template   = require('app/templates/template.hbs');

module.exports = Marionette.ItemView.extend({
    template: template
});

dependencies

"devDependencies": {
    "backbone": "^1.2.1",
    "backbone.marionette": "^2.4.2",
    "browserify": "^10.2.4",
    "handlebars": "^3.0.0",
    "hbsfy": "~2.2.1",
    "jquery": "^2.1.4",
    "underscore": "^1.8.3",
    "watch": "^0.16.0"
},

I'm a lost noob and I can't figure out what on earth is missing. I've tried npm install handlebars@1 and @2 with no sucess...

How to not include runtime with hbsfy

I am trying to write multiple small projects using hbsfy. The problem is that each of these projects includes an instance of the handlebars runtime. If someone tries to consume more than one of these projects, they get multiple copies of the runtime. Is there a way to not include the runtime when using hbsfy? Thanks!

Hbsfy fails to use aliases while using --traverse

I am using the transform aliasify (https://github.com/benbria/aliasify) to stop the crazy ../../../../../../ problem of referencing a file far away.

This is my setup:

  var browserify = require('browserify');
  var b = browserify();
  b.add('./app/init');
  b.transform('aliasify');
  b.transform('hbsfy', { traverse: true });

and this is my package.json aliasify configuration:

{
  "aliasify": {
    "aliases": {
      "app": "./app"
    }
  }
}

and I'm referencing this alias in my templates:

  {{> app/myPartial.hbs }}

And when I run the bundle it still complaining it can't find that module.

I realize that aliasify is separate from hbsfy and there's no reason why they would have to work together. But, I was wondering if people have found a way around the ../../../../../../ problem using --traverse flag

Option to include minified version of the runtime

Using grunt to build with browserify, I'm using both hbsfy and uglifyify as transforms on my code. It appears that the runtime included by hbsfy is not minified.

Could there be an option to have runtime.js point to handlebars/dist/handlebars.runtime.min instead of handlebars/runtime?

I could work on a PR if this sounds interesting.

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.