Git Product home page Git Product logo

ghcjs's Introduction

Introduction

GHCJS is a Haskell to JavaScript compiler that uses the GHC API.

Quick Start - Developing GHCJS

GHCJS contains a library, ghcjs, which contains the JavaScript code generator and a slightly customized variant of the ghc library, and several executable programs.

The repository has several submodules and some files must be generated before the package can be installed.

prerequisites

GHC

You need the same major version of GHC as the version of the GHCJS branch you're building.

cabal-install

cabal-install 3.0 is supported

emscripten emsdk

GHCJS uses a C toolchain, mostly for build system related tasks like the C preprocessor, Autoconf scripts and tools like hsc2hs. Direct support for using compiled foreign libraries from Haskell code may follow at a later date.

Please follow the installation instructions at https://emscripten.org/docs/getting_started/index.html

GHCJS requires the "upstream" emscripten backend, which is the default now. The earlier "fastcomp" backend will not work.

getting and preparing the source tree

$ git clone https://github.com/ghcjs/ghcjs.git
$ cd ghcjs
$ git submodule update --init --recursive

building the compiler

GHCJS depends on a few "local" packages in the source tree. You can use cabal-install and stack to set up a build environment that contains these packages.

Cabal new-install

After the source tree has been prepared, the package can be installed. You may want ensure that binaries of earlier versions are overwritten:

cabal v2-install --overwrite-policy=always --install-method=copy --installdir=inplace/bin

At the time of writing, cabal-install does not support creating symbolic links on Windows, even though this is the default installation method. A workaround is telling it to copy the executables instead:

cabal v1-install --prefix=inplace

v1 style Cabal sandbox

v1 style cabal sandboxes are also supported

if you want to build with a Cabal sandbox, use the makeSandbox.sh script to add the local packages.

$ cabal v1-sandbox init
$ cabal v1-install

stack

or you can use stack:

$ stack --system-ghc --skip-ghc-check install --local-bin-dir=inplace/bin

Booting GHCJS

The ghcjs-boot program builds the "boot" libraries, like ghc-prim, base and template-haskell with GHCJS. After booting, GHCJS can compile regular Haskell programs and packages.

ghcjs-boot needs to be able to find the emscripten toolchain, a nodejs executable. The easiest way to do this is by running the emsdk_env.sh script. After that, you can run ghcjs-boot by pointing it to the boot libraries (the directory containing the boot.yaml file)

$ source ~/emsdk/emsdk_env.sh
$ ./inplace/bin/ghcjs-boot -s ./lib/boot

GHCJS executables and library paths

After booting, you can add the directory containing the GHCJS binaries to your executable PATH. The ghcjs-boot program prints the location after finishing building the libraries.

You can also create a symbolic link for the ghcjs and ghcjs-pkg programs, or use the --with-compiler and --with-hc-pkg flags when using cabal-install

Generating a source distribution

if you work on boot packages that need some for an upstream library, make sure to update the patches in /lib/patches first

$ ./utils/updatePatches.sh

then regenerate the packages

$ ./utils/makePackages.sh

ghcjs's People

Contributors

alexanderkjeldaas avatar ali-abrar avatar andrewthad avatar angerman avatar basvandijk avatar bgamari avatar bitonic avatar chrisdone avatar cstrahan avatar dalaing avatar ddssff avatar dfordivam avatar elvishjerricco avatar fgaray avatar gregwebs avatar hamishmack avatar hdgarrood avatar iand675 avatar kfigiela avatar lgastako avatar luigy avatar luite avatar mgsloan avatar mikemckibben avatar mostalive avatar mstksg avatar osa1 avatar pedromartins avatar sviperll avatar tehnix avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ghcjs's Issues

HTML5 Canvas support

Although I guess HTML5 canvas support probably need a solution to javascript FFI, I would like to raise this issue for having discussion about the implementation idea. One lightweight implementation may be to make wrappers for javascript function and organize them as a cabal package like ghcjs-dom. To make a cool example using HTML5 canvas can boost lots of interest in this project.

fix namespace issues

Foreign imports are polluting the global namespace (in gen2 also other things but that's mostly me being sloppy). I'd also like to switch to the h$ prefix instead of $hs_ because it's a bit more compact and a bit nicer to read, and still unlikely to clash with other things.

Proposal:

foreign import ccall "malloc" imports h$malloc
foreign import ccall "@malloc" imports malloc

also prefix all haskell generated names and rts functions with h$

Comments/suggestions welcome.

Hamish mentioned that there might be problems with this (perhaps related to dynamic imports?). I'd like to do this in the gen2 branch first to see what problems we encounter.

Create RTS generator

Generate RTS mechanically using some standalone tool: ghcjs-rts

This will allow simultaneous support of many calling conventions: plain, trampoline, CPS.
This will allow easy renaming and reorganizing RTS to tweak performance.

the road to cabal support

I think the plan is to make ghcjs a separate compiler, installable with cabal install, and to add support for it in the cabal library. Issue here to keep track of things that need to be done:

  • cabal library patch to add ghcjs compiler type that installs generated .js files
  • ghcjs needs to print the right answers when cabal-install asks for them (--numeric-version etc), mostly done
  • base libraries need to be compiled with cabal and properly registered as ghcjs packages when ghcjs is installed
  • A fallback option to install .js files with older cabal libraries might be useful in the short term i'm not sure if this is easily diable

I'm going to work on the third point and finish the second. There might be more things to be done, I don't know much about the linker.

Implementation of primitive values is still lacking

The problem is this line in Generator/Core.hs

expression (StgApp f args) = Js.jumpToMethod (stgIdToJs f) "hscall"
(map stgArgToJs args)

f is sometimes a primitive value, that can not be evaluated. And don't
see how to tell it apart for now...

Replace build instructions with a build script

I've tried to build ghcjs but it seems to be somewhat difficult with all the manual steps and cabal dependencies that exist only on github. Would it be possible to remedy this with a build script?

I'm willing to contribute a Makefile if you can tell me what actually needs to be done to build this from scratch. I'd guess that other random potential contributors would be easier to entice to participate if building this would be easier.

How does that sound to you?

Unclear how to use the ghcjs compiler

The documentation states, that one should use the command

ghcjs

to compile to javascript. But, after following the build instructions, I do not have a ghcjs executable. When I compile a haskell source with the compiler located in $HOME/ghcjs/bin/ I get 2 js files "...plain.js" and "...trampoline.js".

When I include them in an html page as javascript source, chrome browser gives me:

Uncaught ReferenceError: $t is not defined

Setup complexity

Can we simplify ghcjs setup? From the point of view of the developer, the setup process should be:

  1. Install Haskell Platform.
  2. Run cabal install ghcjs

That's it.

compile 32 bit libraries with 64 bit GHC

Ideally, the standalone compiler would be installable with cabal install by everyone, regardless of whether they're using a 32bit or a 64bit GHC. Using 64 bit integer emulation in the GHCJS runtime is not a great solution due to performance and a lot of extra code to maintain.

This ticket is for collecting the remaining issues and to investigate what we can fix in GHC itself, ideally before the 7.8 release.

  • If we manually define WORD_SIZE_IN_BITS to be 32, most of the base libs can be built as if they were 32 bit. Unfortunately the build breaks on generating the Typeable, since the API itself has a hardcoded word size ( see compiler/typecheck$ vim TcGenDeriv.lhs ) when generating the hashes.
  • what kind of work is currently going on for building GHC as a cross compiler? does TH work?

Generate more optimized function calls

When we need to call a function we invoke general apply mechanism. Apply checks in run-time whether function is ready to receive given number of arguments and preform arguments processing if it is not. But this information is often available statically in compile-time. We should pass around an environment with top-level objects and objects bound by local lets, so we will know in compile time if object called now is from environment and we can optimize call if it is allready evaluated or if we are given an exact number of arguments.

Rewrite Generator in Monad or ApplicativeFunctor style

If we switch from pure function to monad actions. We can use Reader monad to pass around environment.

  • This will allow splitting Core module into more modules.
  • This will allow generator to emit more optimized code.

Split Generator.Core into smaller modules

Split Generator.Core into

  • Generator.Binding
  • Generator.Expression
  • Generator.Case

Smaller modules will allow adding more optimizations into code.

Splitting is now impossible cause these parts are mutual recursive. If we pass around some environment all over Generator we will be able to rewrite mentioned parts in some kind of open recursive style:

data Env = Env
  { getCaseExpressionGenerator :: Javascript js => ... -> js
  , getExpressionGenerator :: Javascript js => ... -> js
  , getBindingGenerator :: Javascript js => ... -> js
  }
expression env =
  ... getCaseExpressionGenerator env ...
  ... getExpressionGenerator env ... 
  ... getBindingGenerator env ... 
caseExpression env = 
  ... getCaseExpressionGenerator env ...
  ... getExpressionGenerator env ... 
  ... getBindingGenerator env ... 
binding env =
  ... getCaseExpressionGenerator env ...
  ... getExpressionGenerator env ... 
  ... getBindingGenerator env ... 

And an actual used environment will be

module Generator.TopLevelEnv (topLevelEnv) where

import Generator.Expression (expression)
import Generator.Case (caseExpression)
import Generator.Binding (binding)

topLevelEnv = Env
  { getExpressionGenerator = expression topLevelEnv
  , getCaseExpressionGenerator = caseExpression topLevelEnv
  , getBinding = binding topLevelEnv
  }

Haskell Source Maps

Currently we include the Closure Compilers source maps. These map from the minified java back to the unminified form. It would be nice if we also had Haskell Source maps. These would map the unminified javascript code back to the Haskell source.

First step will be to improve the source-map package so that we can use it to write mappings (see the issue in there about VLQ values).

Next we need to output a source map from the ghcjs generator, then add support in the linker. This would give us a source map file for each unminified bundle created by the linker that points back to the Haskell source.

For bonus points we should write a tool to combine these with the closure compiler output to create a source map that goes all the way from the minified js to the Haskell source.

runghc Setup configure fails

I've installed ghc-paths and monads-tf with Cabal, but I still get an error message.

$ runghc Setup configure
Configuring ghcjs-0.1.0...
Setup: At least the following dependencies are missing:
ghc-paths -any, monads-tf -any

Specs:

ghcjs 0.0.1
cabal 0.8.2
GHC 6.12.3
Mac OS X 10.6.6

change license to MIT

Hi, I'd like to change the license from BSD to MIT because it's slightly friendlier for end users (no attribution requirement in binary dists). Yesod switched earlier this year for the same reason.

@sviperll , @hamishmack are you ok with this change?

z-encoding in RTS

GHC uses encoding called z-encoding.

zEncodeString "Control.Monad.State.Lazy" == "ControlziMonadziStateziLazzy"

Dot is replaced with "zi". "z" is replaced with "zz".

loadModule function in RTS must implement z-encoding by itself, but it isn't.

variableName = moduleName.replace (/./g, "zi"); // Z-encoding string

line is wrong. It should replace all special characters, not only dot.
So It must be changed to

variableName = moduleName.replace(/z/g, "zz").replace (/./g,
"zi"); // Z-encoding string

Fails to build on OS X 10.7

haskell-platform installed by homebrew

ghcjs fails with

"/usr/local/bin/ghc" -M -dep-makefile ghc/stage1/build/.depend.haskell.tmp -include-pkg-deps -H32m -O -package-conf libraries/bootstrapping.conf -hide-all-packages -i -ighc/. -ighc/stage1/build -ighc/stage1/build/autogen -Ighc/stage1/build -Ighc/stage1/build/autogen -optP-include -optPghc/stage1/build/autogen/cabal_macros.h -package array-0.4.0.0 -package base-4.5.1.0 -package bytestring-0.9.2.1 -package directory-1.1.0.2 -package filepath-1.3.0.0 -package ghc-7.4.2 -package process-1.1.0.1 -package unix-2.5.1.1 -Wall -XHaskell98 -XNondecreasingIndentation -XCPP -XPatternGuards -no-user-package-conf -rtsopts -odir ghc/stage1/build -hidir ghc/stage1/build -stubdir ghc/stage1/build -hisuf hi -osuf o -hcsuf hc ghc/./Main.hs
: cannot satisfy -package ghc-7.4.2:
ghc-7.4.2-31e48a04c363f79059e7a7434de9af35 is unusable due to missing or recursive dependencies:
Cabal-1.14.0-2822c5c113bde562460ba4df1e6252e6 bin-package-db-0.0.0.0-728a6587b9d54cd042c9d4b8f017f8bb hoopl-3.8.7.3-0eb99e8c0c3f3964d1b6aade4a55299c hpc-0.5.1.1-aded85d3c95c51273fc3dedd3c91e956

A standard GHC pull builds fine.

Improve Thread Scheduling

Currently Haskell threads a preempted after a hard coded number of iterations through the trampoline. We should be a bit smarter than that. If it is not too slow we should use (new Date).getTime() instead so that code running slow functions will not wind up hogging the CPU.

Also to support long running threads we should have the scheduler yield to JavaScript. Hopefully we can add a call to setTimeout to the scheduler in order to yield to javascript.

Low level JavaScript FFI using JavaScriptCore

I think the native Haskell first approach to the DOM has worked well and we should do the same for a low level JavaScript interface. WebKitGtk comes with JavaScriptCore so that will probably be easiest.

The header files are in /usr/include/webkitgtk-1.0/JavaScriptCore. It's not GObject based and there's no IDL, so we might as well just wrap each function manually. I think the entry point we will need is webkit_web_frame_get_global_context to get a JavaScript context.

Once we have a working native code example that calls JavaScript we can compile it with GHCJS and write some JavaScript functions to implement the calls to JavaScriptCore.

Hopefully this interface will provide a foundation for a high level FFI as described in #3

managing foreign imports

The current FFI code just calls a Javascript function with the same name as the imported function from the C file. Most of these functions are implemented in rts-common.js. These functions pollute the global namespace and this approach is not modular.

A good solution would:

  • Allow existing packages to be used by providing a javascript implementation
  • Have some collection of .js compatibility files and a dependency map for packages that need them (basically what's currently in rts-common.js)
  • Let packages implement their own javascript functions wherever they use c-sources
  • Have existing packages not pollute the global namespace, but still allow any javascript function to be imported easily

Unable to compile ghcjs with the given instructions

Operation system: ubuntu 12.04 64bit
ghc version: gch 7.4.1 (64bit)

I am following the instruction I found in the README.markdown

The first trouble comes with "git pull ghcjs". I get:

remote: Counting objects: 42, done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 26 (delta 22), reused 21 (delta 17)
Unpacking objects: 100% (26/26), done.
From github.com:ghcjs/packages-Cabal

  • [new branch] encoding -> ghcjs/encoding
  • [new branch] ghc-7.2 -> ghcjs/ghc-7.2
  • [new branch] ghc-7.4 -> ghcjs/ghc-7.4
  • [new branch] master -> ghcjs/master
    You asked to pull from the remote 'ghcjs', but did not specify
    a branch. Because this is not the default configured remote
    for your current branch, you must specify a branch on the command line.

So I am doing:

git pull ghcjs ghc-7.4

Then "git branch ghc-7.4 ghcjs/ghc-7.4" gives me:

fatal: A branch named 'ghc-7.4' already exists.

And finaly "perl boot" fails with:

Error: libraries/extensible-exceptions/LICENSE doesn't exist.
Maybe you haven't done './sync-all get'? at boot line 74,
line 57.

incremental marking

GHCJS has to mark the whole heap from time to time to see which objects are still reachable, for running finalizers and reverting CAFs. Marking the whole heap in one go can cause pauses that are too long for interactive programs if the heap is very big.

Haskellium

One of javascript's advantages is the fast turn around it has (from coding to finding out you have type errors :-) ). GHCJS is quite slow to compile and is unlikely to get hugely faster. DART has a similar problem and they are solving it by allowing devs to use http://www.dartlang.org/dartium/

We should find out dartium that works and if possible do the same thing to marry ghci and chromium. The idea being that this would load our haskell and run it using the GHC interpreter, bypassing the conversion to JavaScript and giving us all our cool ghci features.

Hopefully we can use C implementations of whatever DOM FFI's come up with and bypass javascript completely.

can no get webversion of ghcjs-hello to work

Hey,

I am atempting to try out ghcjs. I followed the instruction, installed ghcjs and compiled the ghcjs-hello example.
The exectuable works! I ran ghcjs-min on ~/.cabal/bin/ghcjs-hello.jsexe
Now I did:

mkdir public_html
cd public_html
ln -s ../.cabal ghcjs
python -m SimpleHTTPServer

Now I direct my browser to http://127.0.0.1:8000/ghcjs/bin/ghcjs-hello.jsexe/

I see the console, I can type, but now output! And no reaction to my commands.
I also tried hterm.html or console.html.

The chromium javascript console shows: Paused on excpetion 'TypeError'

Here is the stack trace:

(anonymous function) (./hs3min.js:164)
A (rts.js:129)
(anonymous function) (./hs3min.js:164)
od.pf (rts.js:134)
nd.Tb (rts.js:130)
(anonymous function) (rts.js:130)

I am on xubuntu 64bit (12.04) and I am using chromium.

Regards,
Nathan

HsBaseConfig.h: No such file or directory

I try to generate /base javascripts
ghcjs -odir /base -hidir /base -hide-package base -package-name base -I./include -i./dist-install/build -XMagicHash -XExistentialQuantification -XRank2Types -XScopedTypeVariables -XUnboxedTuples -XForeignFunctionInterface -XUnliftedFFITypes -XDeriveDataTypeable -XGeneralizedNewtypeDeriving -XFlexibleInstances -XStandaloneDeriving -XPatternGuards -XEmptyDataDecls -XNoImplicitPrelude -XCPP Prelude.hs
Is there any simple way to generate HsBaseConfig.h without build ghc source? Or maybe just download somewhere already generated javascripts.

BuildTest script does not work

It has some hardcoded module names and builds an unusable base and ghc-prim package, with missing modules. A better method would probably be to build through cabal

Browser support

The ghcjs-hello example does not work in IE9 yet, could be due to the use of typed arrays in a way that's not yet supported in the RTS. What would be good browser support goals? Supporting very old browsers is not going to be worthwhile since GHCJS requires a fast js engine anyway, but it would be nice if the output could be more or less generally usable.

For me this would be a reasonable goal:

Full support / try to optimize performance for:

  • Chrome 17
  • Safari 5
  • Opera 11.6
  • IE10
  • Firefox 10

(I don't know about exact versions of Chrome and Firefox because of their annoying versioning policies, but people with a fairly recent linux distro, should be included)

Make sure that programs run on:

  • IE9
  • Various mobile browsers on iPhone/iPad, Android

Does this sound ok? How to best test support? Using the (planned) test suite could be tricky, but we could make a simpler test page that just reports the results in the browser, testing for particular issues.

generate multiple calling convention code

Since choosing the calling convention is more a link-time decision, we could generate code for both and then pick one at link time. i want to add this at least to the standalone compiler, this ticket is just a reminder.

Add impressive example

Add example that uses many Haskell libraries:

  • Parsec
  • MTL

I think about an evaluator for beta-eta call-by-name lambda-calculus terms.

distribute .js files with cabal packages

An easy way to distribute .js files to be linked in with cabal packages would be to include them in data-files, how about:

package-datadir/ghcjs/*.js gets linked after rts and shims, before generated haskell code. The javascript file gets run through CPP so it can do things like #if __GLASGOW_HASKELL__ >= 706, #if __GHCJS__ >= 101

avoid symbol name clashes between packages

We should do something to avoid symbol name clashes between packages when linking. Some ideas:

  • Prepend package name immediately to all symbols
  • When linking, give each package a short identifier (1 or 2 characters usually) and add it to all symbols. The main package would get a fixed main identifier

I think the first solution will probably work for static linking, and Closure might deal with those long names for us, but I would like to "dynamically link" things for my own site, without blowing up code size too much. (Dynamic linking = on-demand loading of whole modules or groups, where new code (in a single package) can be added at any time)

Change memory-indexing operation to work in IE

In Firefox, you have "abc"[1] == "b", but in IE "abc"[1] == undefined. Instead, IE uses "abc".charAt(1), which also works in Firefox and the others.

The problem became apparent in unpackCString in Base.js (around line 2160 in my version)

Add Text interface to WebKitGtk

Currently the bindings expect Haskell String and to satisfy the C interface it is converted to/from a UTF-8 CString (a ByteArray). Overhead of this conversion is high and using Text would just add another conversion (Text -> String -> CString UTF-8).

Solution is to find a good way to generate WebKitGtk bindings that take Text and work convert to CString UTF-8 in native code, but replace them at link time with alternative implementations that convert use the fast JavaScript functions we have for converting Text to/from JS strings.

Then we can encourage people concerned with performance to use Text.

64-bit support

RTS now emulates 32-bit Words and Ints that's why you need 32-bit GHC to build basic libraries. So 64-bit systems are not supported right now.

You can use ghcjs on 64-bit systems to build code that doesn't depend on system word width and use generated code with basic libraries generated on 32-bit system.

A faster and more robust module loader (now it loses a lot of time on 404 errors, trying to access modules in the wrong package dir)

We should use packageId to find out required modules. This will allow us to know the exact location of module to load. And this will allow the usage of modules with the same name from different packages, like mtl and monads-tf, or parsec-2 and parsec-3. This is really important in big projects...

PackageID is readilly accessible from GHC API. And it should be trivial to add it.

How to find Source of ReferenceError

Hey,
Now that i got ghcjs running, I am trying out some examples from reactive-banana.
For example SlotMachine.hs.

Doing so I ran into a ReferenceError

<exception>: ReferenceError
arguments: Array[1]
0: "ghc_wrapper_d1sl_getrusage"
length: 1
__proto__: Array[0]

in this line

var $ff=ghc_wrapper_d1sl_getrusage(0,D);

Now I am wondering, how can I find ou more about this missing function? Is it not defined for ghcjs or just not linked? And what function is used in the program that causes this error?

js-sources in packages

Many packages have some c-sources, for ghcjs we would like something similar, but for javascript sources instead. They should be installed with the package, and linked when building bundles. But how to specify them, and what about the c-sources that we can't do anything with?

some ideas:

  • use c-sources field in cabal to search for js files with the same name: what do do when js file is missing? warnings tend to get drowned in other messages.
  • add js-sources field

WebGL/OpenGL Support

Take an existing OpenGL app, compile it with GHCJS and then try to implement the missing FFI calls with WebGL.

Add CPS calling convention

Add support for CPS calling convention.

  • This will eliminate stack overflows.
  • This is expected to increase performance dramatically.
  • This will allow an emulation of multithreading. Javascript setTimeout or setInterval will give a scheduling mechanism.

Make it work on all major browsers

I need help here. I've tested only on 64-bit Firefox 3.6 and Chrome. As I understand it should be really trivial to make it work everywhere. But people seems to have problems with it...

(Depends on issue 28)

remove absolute locations for linker / test suite

ghcjs depends on closure-library and closure-compiler being installed directly in the home directory. additionally ghc is expected to be installed outside the project dir. I think this should be fixed somehow

  • should we install closure and closure-library in the share folder with the package?
  • can we distribute these with the cabal package, or perhaps include a script that downloads them?

Add performance tests

Two people has asked for some performance tests. I don't know what performance tests to add, but it doesn't require much effort...

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.