Giter Club home page Giter Club logo

connect-assetmanager's Introduction

connect-assetmanager

Middleware for Connect (node.js) for handling your static assets.

Installation

Via npm:

$ npm install connect-assetmanager

Handy pre/post hooks

Make sure to check out connect-assetmanager-handlers for useful hooks you can use (inline base64 for image, vendor prefix fixes for example)

What does it allow you to do?

  • Merge and minify CSS/javascript files
  • Auto regenerates the cache on file change so no need for restart of server or manual action.
  • Run pre/post manipulation on the files
    • Use regex to match user agent so you can serve different modified versions of your packed assets based on the requesting browser.
  • Supplies a reference to the modified dates for all groups through assetManager().cacheTimestamps[groupName] as well as md5 hashes assetManager().cacheHashes[groupName] which can be used for cache invalidation in templates.
  • Wildcard add files from dir

Nifty things you can do with the pre/post manipulation

  • Replace all url(references to images) with inline base64 data which remove all would be image HTTP requests.
  • Strip all IE specific code for all other browsers.
  • Fix all the vendor prefixes (-ms -moz -webkit -o) for things like border-radius instead of having to type all each and every time.

Speed test (it does just fine)

Running with

> connect app -n 4

Common data

Concurrency Level:      240
Complete requests:      10000
Failed requests:        0
Write errors:           0

Small (reset.css)

Document Path:          /static/test/small
Document Length:        170 bytes

Time taken for tests:   0.588 seconds
Total transferred:      4380001 bytes
HTML transferred:       1700000 bytes
Requests per second:    17005.50 [#/sec] (mean)
Time per request:       14.113 [ms] (mean)
Time per request:       0.059 [ms] (mean, across all concurrent requests)
Transfer rate:          7273.84 [Kbytes/sec] received

Larger (jQuery.js)

Document Path:          /static/test/large
Document Length:        100732 bytes

Time taken for tests:   10.817 seconds
Total transferred:      1012772490 bytes
HTML transferred:       1009913368 bytes
Requests per second:    924.51 [#/sec] (mean)
Time per request:       259.597 [ms] (mean)
Time per request:       1.082 [ms] (mean, across all concurrent requests)
Transfer rate:          91437.43 [Kbytes/sec] received

Options

path (string) - required

The path to the folder containing the files.

path: __dirname + '/'

files (array) - required

An array of strings containing the filenames of all files in the group.

If you want to add all files from the path supplied add '*'. It will insert the files at the position of the *. You can also use a regexp to match files or use external urls.

files: ['http://code.jquery.com/jquery-latest.js', /jquery.*/ , '*', 'page.js']

route (regex as string) - required

The route that will be matched by Connect.

route: '/\/assets\/css\/.*\.css'

dataType (string), ['javascript', 'css']

The type of data you are trying to optimize, 'javascript' and 'css' is built into the core of the assetManager and will minify them using the appropriate code.

dataType: 'css'

preManipulate (array containing functions)

There are hooks in the assetManager that allow you to programmaticly alter the source of the files you are grouping. This can be handy for being able to use custom CSS types in the assetManager or fixing stuff like vendor prefixes in a general fashion.

'preManipulate': {
    // Regexp to match user-agents including MSIE.
    'MSIE': [
        generalManipulation
        , msieSpecificManipulation
    ],
    // Matches all (regex start line)
    '^': [
        generalManipulation
        , fixVendorPrefixes
        , fixGradients
        , replaceImageRefToBase64
    ]
}

postManipulate (array containing functions)

Same as preManipulate but runs after the files are merged and minified.

The functions supplied look like this:

function (file, path, index, isLast, callback) {
    if (path.match(/filename\.js/)) {
        callback(null, file.replace(/string/mig, 'replaceWithThis'));
    } else {
        callback(null, file);
    }
}

serveModify (req, res, response, callback)

Allows you do to modify the cached response on a per request basis.

function(req, res, response, callback) {
    if (externalVariable) {
        // Return empty asset
        response.length = 1;
        response.contentBuffer = new Buffer(' ');
    }
    callback(response);
}

stale (boolean)

Incase you want to use the asset manager with optimal performance you can set stale to true.

This means that there are no checks for file changes and the cache will therefore not be regenerated. Recommended for deployed code.

debug (boolean)

When debug is set to true the files will not be minified, but they will be grouped into one file and modified.

Example usage

var sys = require('sys');
var fs = require('fs');
var Connect = require('connect');
var assetManager = require('connect-assetmanager');
var assetHandler = require('connect-assetmanager-handlers');

var root = __dirname + '/public';


var Server = module.exports = Connect.createServer();

Server.use('/',
    Connect.responseTime()
    , Connect.logger()
);

var assetManagerGroups = {
    'js': {
        'route': /\/static\/js\/[0-9]+\/.*\.js/
        , 'path': './public/js/'
        , 'dataType': 'javascript'
        , 'files': [
            'jquery.js'
            , 'jquery.client.js'
        ]
    }, 'css': {
        'route': /\/static\/css\/[0-9]+\/.*\.css/
        , 'path': './public/css/'
        , 'dataType': 'css'
        , 'files': [
            'reset.css'
            , 'style.css'
        ]
        , 'preManipulate': {
            // Regexp to match user-agents including MSIE.
            'MSIE': [
                assetHandler.yuiCssOptimize
                , assetHandler.fixVendorPrefixes
                , assetHandler.fixGradients
                , assetHandler.stripDataUrlsPrefix
            ],
            // Matches all (regex start line)
            '^': [
                assetHandler.yuiCssOptimize
                , assetHandler.fixVendorPrefixes
                , assetHandler.fixGradients
                , assetHandler.replaceImageRefToBase64(root)
            ]
        }
    }
};

var assetsManagerMiddleware = assetManager(assetManagerGroups);
Server.use('/'
    , assetsManagerMiddleware
    , Connect.static(root)
);

connect-assetmanager's People

Contributors

cliffano avatar daaku avatar davidpadbury avatar dawnerd avatar laurisvan avatar mape avatar mikl avatar puls avatar ryanrolds avatar thegoleffect 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

connect-assetmanager's Issues

Safari hangs randomly on page refresh

Hi,

I've noticed that Safari hangs randomly on page refresh. Using Web Inspector, it seems like CSS and JS file requests remain on 'pending' state, and eventually get a 304 after a lot of time (some minutes).

I've experienced this issue with Safari 5.1.3 and 5.0.3.

Minification breaks popper.js; how to circunvent minification in a given .js

The minification from jsmin, breaks popper.js: this code:

  return +styles['border' + sideA + 'Width'].split('px')[0] + +styles['border' + sideB + 'Width'].split('px')[0];

and the result becomes:

return +styles['border'+sideA+'Width'].split('px')[0]++styles['border'+sideB+'Width'].split('px')[0];

The syntax error comes from the ++

I would love a way to:

  • Use the already minified popper.min.js (provided by them), which is ok (the space between the + is in place)
  • Tell connect-assetmanager hey, dont jsmin this file!!! it's already minified just right

Add configuration options for file watching

It'd be great to have options for configuring file watching. For development purposes it can be useful to pass watchFile an interval and it might be useful to turn of file watching altogether in production.

runtime files

I had following configuration for static js files

var assetManagerGroups = {
        'js': {
        'route': /\/static\/js\/main.js/
        , 'path': './public/javascripts/'
        , 'dataType': 'javascript'
        , 'files': ['*']
        , debug:true
        }, 'css': {
        'route': /\/static\/css\/main.css/
        , 'path': './public/stylesheets/'
        , 'dataType': 'css'
        , 'files':['*']
        , debug:true        
        }
    };

If I add a new js files in /public/javascripts/ without restarting the server,the new file is not appended to main.js.

Small typo : contentLenght

Hi

Nothing really serious since the mistake is consistent across the code, but there are a few occurrences of response.contentLeng_ht_ instead of response.contentLeng_th_

user agent can be null, causing an error

This can be null (from assettmanager.js):

            var userAgent = req.headers['user-agent'];

which results in an error a few lines down:

                    if (!found && userAgent.match(new RegExp(match, 'i'))) {
                                            ^          
                    TypeError: Cannot call method 'match' of undefined

I'm guessing some user with super privacy is hiding his user agent, or some bot.

Failure when using regexs in files array

Getting this error when I use RegExp in the files array, even as indicated in the document examples:

TypeError: Object /jquery.*/ has no method 'match'
    at /usr/lib/node/.npm/connect-assetmanager/0.0.18/package/lib/assetmanager.js:92:15
    at Array.forEach (native)
    at /usr/lib/node/.npm/connect-assetmanager/0.0.18/package/lib/assetmanager.js:91:17
    at /usr/lib/node/.npm/connect-assetmanager/0.0.18/package/lib/assetmanager.js:18:6
    at Array.forEach (native)
    at Object.forEach (/usr/lib/node/.npm/connect-assetmanager/0.0.18/package/lib/assetmanager.js:16:22)
    at Function.<anonymous> (/usr/lib/node/.npm/connect-assetmanager/0.0.18/package/lib/assetmanager.js:89:12)
    at next (/usr/lib/node/.npm/step/0.0.4/package/lib/step.js:51:23)
    at check (/usr/lib/node/.npm/step/0.0.4/package/lib/step.js:73:14)
    at /usr/lib/node/.npm/step/0.0.4/package/lib/step.js:86:20

Which makes sense - the code at that point is assuming all entries are strings (marker is line 92 of assetmanager.js):

function(err, contents) {
                settings.forEach(function (group, groupName) {
                        if (!group.stale) {
                                group.files.forEach(function (file) {
------>                              if (file.match(/^http:\/\//)) {
                                                return;
                                        }
                                        fs.watchFile(group.path + file, function (old, newFile) {
                                                if (old.mtime.toString() != newFile.mtime.toString()) {
                                                        self.generateCache(groupName);
                                                }
                                        });
                                });
                        }
                });
                self.generateCache();
        });

Finally - this bit in the Readme doesn't immediately make sense, can you clarify?

If you want to add all files from the path supplied add ''. It will insert the files at the position of the . 

Shouldn't YUI run after everything else?

hi mape, i'm back for more... I'm trying to run YUI in the postManipulate group so that it runs just once instead of once per file, and it runs after all the other scripts have done their thing changing the css or js.

I'm getting a new error about the file being null. i had to do this because when it runs before it launches a whole bunch of yuicompressors at once and the threads seem to never die, pegging my cpu at 100%.

group.route within assetManager function not RegExp

In 0.0.24 there is a bug where unless you instantiate your regular express string as a RegExp object, the

if(group.route.test(req.url))

within the settings.forEach loop will error out stating that group.route does not have a method test. You can currently get around this by instantiating your regex as a RegExp object in the config by doing new RegExp(your regex string).

Either it should be instantiated by the plugin as a RegExp object or the example should be changed to show that you must instantiate it yourself.

Cache not auto refreshed.

Just got handed a project which uses this module..none of the files using the assetmanager when modified (locally via webstorm) are auto regenerated.. I have to restart the server every time .. highly annoying and completely makes me want to rewrite ditching this

Asset Manager has a bunch of leaked globals

Hi Mape - I just used Gleak and found that AssettManager has a bunch of accidental global variables. I think the use of this outside the closure is causing it.

found global leak: tls
found global leak: cacheTimestamps
found global leak: cacheHashes
found global leak: generateCache
found global leak: manipulate
found global leak: getFile

File does not match req.url

No matter what I do, when I enter a js file (http://localhost:3000/static/js/backbone.js) in the url, it shows the last file asset manager cached. So in this case it is showing jquery.js even though I entered backbone. Here is the code I am using:

var sys = require('sys');
var fs = require('fs');
var Connect = require('connect');
var assetManager = require('connect-assetmanager');
var assetHandler = require('connect-assetmanager-handlers');

var root = __dirname + '/public';

var Server = module.exports = Connect.createServer();

Server.use('/',
Connect.responseTime()
, Connect.logger()
);

var assetManagerGroups = {
'js': {
'route': //static/js/.*.js/
, 'path': './public/js/'
, 'dataType': 'javascript'
, 'files': [
'jquery.js',
'backbone.js'
]
}
};

var assetsManagerMiddleware = assetManager(assetManagerGroups);
Server.use('/'
, assetsManagerMiddleware
, Connect.static(root)
);
Server.listen(3000);

TypeError: Cannot read property 'headers' of undefined

Started the server, after few HTTP requests it is shown:

Caught exception: TypeError: Cannot read property 'headers' of undefined
TypeError: Cannot read property 'headers' of undefined
    at Request.callback (/var/www/express-boilerplate/node_modules/connect-assetmanager/lib/assetmanager.js:263:37)
    at Request.<anonymous> (/var/www/express-boilerplate/node_modules/connect-assetmanager/node_modules/request/main.js:154:67)
    at Request.emit (events.js:64:17)
    at ClientRequest.<anonymous> (/var/www/express-boilerplate/node_modules/connect-assetmanager/node_modules/request/main.js:151:13)
    at ClientRequest.emit (events.js:64:17)
    at Socket.<anonymous> (http.js:1214:11)
      at Socket.emit (events.js:64:17)
    at Array.<anonymous> (net.js:830:27)
    at EventEmitter._tickCallback (node.js:126:26)

Where do I see the configs you were talking about? I did not touch anything other than siteConfig.js

Does it support multiple JS assets?

I've been trying to build two separate JS assets for separate pages like the following

var assetManagerGroups = {
'js': {
'route': //static/js/[0-9]+/..js/
, 'path': './public/js/'
, 'dataType': 'javascript'
, 'files': [
'jquery.js'
, 'jquery.client.js'
]
}
, 'js2': {
'route': //static/js/[0-9]+/.
.js/
, 'path': './public/js/'
, 'dataType': 'javascript'
, 'files': [
'jquery.js'
]
}
};

It generates separate hashes for the js and js2 assets but the contents of js2 seem to be the same as that of js (always the first entry in list). What am I doing wrong?

How do I change cache control headers on responses?

So far as I can tell, there is no way for me to set my own cache-control headers on assets.

I've tried using serveModify, but it looks to me like any settings I put here for 'Cache-Control' or 'Expires' are overwritten by lines 388 / 399:

'Cache-Control': 'public,max-age=' + 31536000,
'Expires': response.expires || (new Date(new Date().getTime()+63113852000)).toUTCString(),

So am I correct in saying I am unable to set a bespoke max-age for an asset?

I don't want my files to have a max-age of a year, I would prefer something closer to an hour as they may well change.

I'm not sure however if I've misunderstood something in the implementation but it appeared to me that others may have used the serveModify method to overwrite the default headers but I do not seem able to?

Using watch() instead of watchFile()

Hi everyone,

after running asset manager for the first time, it throws an error telling me to use fs.watch() instead of fs.watchFile(). The error occurs in line 114. After replacing watchFile() with watch() it works fine.

Bye

Files skipped during the "match" test loop

The following line 94 in assetmanager causes elements of the files array to be skipped if non-match elements are encountered:

group.files.splice(index, 1);

That line modifies the underlying array, and causes forEach to skip elements (on chrome, anyway.) Example:

var arr = [0,1,2,3,4]

arr.forEach(function(elem,index){
  if(elem === 1){
    arr.splice(index, 1);
  }
  else{
    console.log(elem);
  }
}

...yields:

0
3                 // '2' has been mistakenly skipped, because forEach's internal iterator isn't updated after the removal of '1'
4
undefined // forEach stupidly walks off the end of the now-shortened array

Corrupts bootstrap.js

Using connect-assetmanager in a node.js/Express app.

This works:

js: {
  dataType: 'javascript',
  path: __dirname + '/../public/javascript/',
  files: [//'jquery-1.7.1.js'
         'bootstrap.min.js'
  ],
  route: /\/static\/javascript\/script\.js/
}

(Note using the already minimized version of boostrap.js, and commented out the jquery file for testing).

This fails:

js: {
  dataType: 'javascript',
  path: __dirname + '/../public/javascript/',
  files: [//'jquery-1.7.1.js'
         'bootstrap.js'
  ],
  route: /\/static\/javascript\/script\.js/
}

Note the non-minimized bootstrap.js.

The failure is in the browser (Chrome). At line 120 in the connect-assetmanager compressed portion of bootstrap.js there's a syntax error:

119 isActive=$parent.hasClass('open')
120 clearMenus()!isActive&&$parent.toggleClass('open')
    Uncaught SyntaxError: Unexpected identifier
121 return false}}

Shocked by this, as I would have expected bootstrap.js to be a super common use case for connect-assetmanager. Happy to be shown that i've done something wrong.

TypeError: Cannot call method 'match' of undefined

Not sure how to reproduce this, it happened as a one-off in a production environment.

TypeError: Cannot call method 'match' of undefined
   at 
.../node_modules/connect-assetmanager/lib/assetmanager.js:302:31
   at Array.forEach (native)
   at 
.../node_modules/connect-assetmanager/lib/assetmanager.js:301:36
   at 
.../node_modules/connect-assetmanager/lib/assetmanager.js:18:6
   at Array.forEach (native)
   at Object.forEach 
.../node_modules/connect-assetmanager/lib/assetmanager.js:16:22)
   at Object.assetManager [as handle] 
.../node_modules/connect-assetmanager/lib/assetmanager.js:287:12)
   at next 
.../node_modules/express/node_modules/connect/lib/http.js:201:15)

Error: watch ENOENT on express server

This is my app.js handlerd by express server:

/**
 * Module dependencies.
 */

var express = require('express');
var http = require('http');
var path = require('path');
var assetManager = require('connect-assetmanager');

var assetManagerGroups = {
  js: {
    route: /\/static\/js\/[0-9]+\/.*\.js/,
    path: './public/javascripts/lib',
    dataType: 'javascript',
    files: [ 'jquery.js' ]
  }
};

var assetsManagerMiddleware = assetManager(assetManagerGroups);

var app = express();

// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.use(express.methodOverride());
app.use(express.cookieParser('your secret here'));
app.use(express.session());
// app.use(assetsManagerMiddleware)
app.use(require('stylus').middleware(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'public')));

// development only
if ('development' == app.get('env')) {
  app.use(express.errorHandler());
}

app.get('/static/js/', express.static(__dirname + '/public/javascripts'));
app.get('/static/css/', express.static(__dirname + '/public/stylesheets'));
app.get('/static/img/', express.static(__dirname + '/public/images'));
app.get('/*', function(req, res){
  res.render('index', { title: 'Express' });
});

http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});

When the var assetsManagerMiddleware = assetManager(assetManagerGroups); line is uncommented, the server throws:

ellmo@ellmo~/node/ss2(master)$ node app.js
Express server listening on port 3000

/Users/ellmo/node/ss2/node_modules/connect-assetmanager/node_modules/step/lib/step.js:39
        throw arguments[0];
                       ^
Error: watch ENOENT
    at errnoException (fs.js:1019:11)
    at FSWatcher.start (fs.js:1051:11)
    at Object.fs.watch (fs.js:1076:11)
    at /Users/ellmo/node/ss2/node_modules/connect-assetmanager/lib/assetmanager.js:114:9
    at Array.forEach (native)
    at /Users/ellmo/node/ss2/node_modules/connect-assetmanager/lib/assetmanager.js:105:17
    at /Users/ellmo/node/ss2/node_modules/connect-assetmanager/lib/assetmanager.js:32:6
    at Array.forEach (native)
    at Object.settings.forEach (/Users/ellmo/node/ss2/node_modules/connect-assetmanager/lib/assetmanager.js:30:22)
    at Function.<anonymous> (/Users/ellmo/node/ss2/node_modules/connect-assetmanager/lib/assetmanager.js:103:12)

As you can see it does that after actually starting the server. I wish there was something to read and debug, but there isn't. I'm at a complete loss.

Feature Request: modify mimeType of minified content

It is sometimes needed to modify document Content-Type of the response.
Can you some how modify function serveContent(response) so it will be possible to set custom content-type of the response like this:

assetmanager.js(line: 398) headers['Content-Type'] = response.mimeType || mimeType;

Question: How to have md5 appended to asset urls?

@mape: I'd like to append the md5 to the filename to ensure "cache busting". Is there an easy way to do that using assetmanager? I imagine that an asset_url helper in express could easily generate the md5, but the bundled files are somewhat ephemeral given they don't necessarily exist on disk.. it should probably be implemented via a call to assetmanager to request the md5.

Thoughts?

I cannot edit files in development mode

Hi everyone,

when I have assetmanager running in development, it throws an error in line 115 telling me that I cannot call method 'toString' of undefined. I do not know how to fix this. My workaround is since that error occurs in an if-condition, I set the if-condition to always true.

Bye

Force generateCache to run on every request

Every time I change a javascript file or CSS file it takes two refreshes for it to appear in the browser. It looks like fs.watchFile picks up the change (running OSX) but generateCache is being called after the old content is already served.

Subfolders and regular expressions don't work together

Hey guys,

I've been struggling for the past hour to get connect-assetmanager working with subfolders without any success.
I'm basically trying to get all JS files inside my javascriptsfolder and subfolders as javascripts/controllers or javascripts/models.

So I have this path:

'path': __dirname + '/javascripts/',

And trying to use a regular expression like this:

'files': [/[a-z]+\/[a-z]+\.js/]

It always returns

No match for ...

I've been looking over the code and apparently this can't work with subfolders as I would expect it. If anyone is still looking over those issues and have a solution, I would really appreciate it since there's no alternative today to connect-assetmanager.

Thanks

document the permissions

Can you add any ACL instructions to the readme? Under Ubuntu I have to use sudo to run node when I include your module. Otherwise I get this:

/home/spatches/learningprojects/expressAssetTest/node_modules/connect-assetmanager/node_modules/step/lib/step.js:39
throw arguments[0];
^
Error: watch ENOSPC
at errnoException (fs.js:806:11)
at FSWatcher.start (fs.js:837:11)
at Object.fs.watch (fs.js:861:11)
at module.exports.generateCache.self (/home/spatches/learningprojects/expressAssetTest/node_modules/connect-assetmanager/lib/assetmanager.js:114:9)
at Array.forEach (native)
at module.exports.generateCache.self (/home/spatches/learningprojects/expressAssetTest/node_modules/connect-assetmanager/lib/assetmanager.js:105:17)
at module.exports.settings.forEach (/home/spatches/learningprojects/expressAssetTest/node_modules/connect-assetmanager/lib/assetmanager.js:32:6)
at Array.forEach (native)
at Object.module.exports.settings.forEach (/home/spatches/learningprojects/expressAssetTest/node_modules/connect-assetmanager/lib/assetmanager.js:30:22)
at Function.module.exports.generateCache.self (/home/spatches/learningprojects/expressAssetTest/node_modules/connect-assetmanager/lib/assetmanager.js:103:12)

CSS minified improperly after updating a CSS file

On initial startup, minification of a series of CSS files works properly. However, upon updating anything in any of these css files, the minified CSS is regenerated and broken. Usually a tiny part of one file will be truncated, throwing the styles into chaos.

Any idea what might be causing this? Am I missing a configuration parameter?

router.use( assetManager({ css: { dataType: 'css', path: __dirname +'/static/styles/', files: [ 'tksync.css', 'room.css', 'room/fullscreen.css', 'dialog.css', 'tooltip.css', 'search.css', 'draw.css', 'collection.css', 'farbtastic/farbtastic.css' ], route: /styles\/tk_room.css/ } }) );

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.