Git Product home page Git Product logo

mima's Introduction

MiMa

MiMa (for "Migration Manager") is a tool for identifying binary incompatibilities in Scala libraries.

It's pronounced MEE-ma.

What it is?

MiMa can report binary modifications that may cause the JVM to throw a java.lang.LinkageError (or one of its subtypes, like AbstractMethodError) at runtime. Linkage errors are usually the consequence of modifications in classes/members signature.

MiMa compares all classfiles of two released libraries and reports all source of incompatibilities that may lead to a linkage error. MiMa provides you, the library maintainer, with a tool that can greatly automate and simplify the process of ensuring the release-to-release binary compatibility of your libraries.

A key aspect of MiMa to be aware of is that it only looks for syntactic binary incompatibilities. The semantic binary incompatibilities (such as adding or removing a method invocation) are not considered. This is a pragmatic approach as it is up to you, the library maintainer, to make sure that no semantic changes have occurred between two binary compatible releases. If a semantic change occurred, then you should make sure to provide this information as part of the new release's change list.

In addition, it is worth mentioning that binary compatibility does not imply source compatibility, i.e., some of the changes that are considered compatible at the bytecode level may still break a codebase that depends on it. Interestingly, this is not an issue intrinsic to the Scala language. In the Java language binary compatibility does not imply source compatibility as well. MiMa focuses on binary compatibility and currently provides no insight into source compatibility.

See also: TASTy-MiMa

For Scala 3, in addition to binary compatible, TASTy compatibility becomes increasingly important. Another tool, TASTy-MiMa, is designed to automatically check TASTy compatibility in much the same way that MiMa checks binary compatibility.

Keep in mind that TASTy-MiMa is still young, as of early 2023. It is likely to contain bugs.

Usage

SBT

MiMa's sbt plugin supports sbt 1.x only. (Use v0.3.0 for sbt 0.13.x.)

To use it add the following to your project/plugins.sbt file:

addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "<version>")

Add the following to your build.sbt file:

mimaPreviousArtifacts := Set("com.example" %% "my-library" % "<version>")

and run mimaReportBinaryIssues to see something like the following:

[info] Found 4 potential binary incompatibilities
[error]  * method rollbackTransactionResource()resource.Resource in object resource.Resource does not have a   correspondent in new version
[error]  * method now()scala.util.continuations.ControlContext in trait resource.ManagedResourceOperations does not    have a correspondent in old version
[error]  * abstract method now()scala.util.continuations.ControlContext in interface resource.ManagedResource does not have a correspondent in old version
[error]  * method rollbackTransactionResource()resource.Resource in trait resource.MediumPriorityResourceImplicits does not have a correspondent in new version
[error] {file:/home/jsuereth/project/personal/scala-arm/}scala-arm/*:mima-report-binary-issues: Binary compatibility check failed!
[error] Total time: 15 s, completed May 18, 2012 11:32:29 AM

Mill

A MiMa plugin for Mill is maintained at lolgab/mill-mima.

To use it add the following to your build.sc:

import $ivy.`com.github.lolgab::mill-mima::x.y.z`
import com.github.lolgab.mill.mima._

Please check this page for further information.

Filtering binary incompatibilities

When MiMa reports a binary incompatibility that you consider acceptable, such as a change in an internal package, you need to use the mimaBinaryIssueFilters setting to filter it out and get mimaReportBinaryIssues to pass, like so:

import com.typesafe.tools.mima.core._

mimaBinaryIssueFilters ++= Seq(
  ProblemFilters.exclude[MissingClassProblem]("com.example.mylibrary.internal.Foo"),
)

You may also use wildcards in the package and/or the top Problem parent type for such situations:

mimaBinaryIssueFilters ++= Seq(
  ProblemFilters.exclude[Problem]("com.example.mylibrary.internal.*"),
)

IncompatibleSignatureProblem

Most MiMa checks (DirectMissingMethod, IncompatibleResultType, IncompatibleMethType, etc) are against the "method descriptor", which is the "raw" type signature, without any information about generic parameters.

The IncompatibleSignature check compares the Signature, which includes the full signature including generic parameters. This can catch real incompatibilities, but also sometimes triggers for a change in generics that would not in fact cause problems at run time. Notably, it will warn when updating your project to scala 2.12.9+ or 2.13.1+, see this issue for details.

You can opt-in to this check by setting:

import com.typesafe.tools.mima.plugin.MimaKeys._

ThisBuild / mimaReportSignatureProblems := true

Annotation-based exclusions

The mimaExcludeAnnotations setting can be used to tell MiMa to ignore classes, objects, and methods that have a particular annotation. Such an annotation might typically have "experimental" or "internal" in the name.

The setting is a Seq[String] containing fully qualified annotation names.

Example:

mimaExcludeAnnotations += "scala.annotation.experimental"

Caveat: mimaExcludeAnnotations is only implemented on Scala 3.

Setting different mimaPreviousArtifacts

From time to time you may need to set mimaPreviousArtifacts according to some conditions. For instance, if you have already ported your project to Scala 2.13 and set it up for cross-building to Scala 2.13, but still haven't cut a release, you may want to define mimaPreviousArtifacts according to the Scala version, with something like:

mimaPreviousArtifacts := {
  if (CrossVersion.partialVersion(scalaVersion.value) == Some((2, 13)))
    Set.empty
  else
    Set("com.example" %% "my-library" % "1.2.3")
}

or perhaps using some of sbt 1.2's new API:

import sbt.librarymanagement.{ SemanticSelector, VersionNumber }

mimaPreviousArtifacts := {
  if (VersionNumber(scalaVersion.value).matchesSemVer(SemanticSelector(">=2.13")))
    Set.empty
  else
    Set("com.example" %% "my-library" % "1.2.3")
}

Make mimaReportBinaryIssues not fail

The setting mimaFailOnNoPrevious defaults to true and will make mimaReportBinaryIssues fail if mimaPreviousArtifacts hasn't been set.

To make mimaReportBinaryIssues not fail you may want to do one of the following:

  • set mimaPreviousArtifacts on all the projects that should be checking their binary compatibility
  • avoid calling mimaPreviousArtifacts when binary compatibility checking isn't needed
  • set mimaFailOnNoPrevious := false on specific projects that want to opt-out (alternatively disablePlugins(MimaPlugin))
  • set ThisBuild / mimaFailOnNoPrevious := false, which disables it build-wide, effectively reverting back to the previous behaviour

Setting mimaPreviousArtifacts when name contains a "."

To refer to the project name in mimaPreviousArtifacts, use moduleName rather than name, like

mimaPreviousArtifacts := Set(organization.value %% moduleName.value % "0.1.0")

Unlike name, moduleName escapes characters like ., and is the name actually used by publish and publishLocal to publish your project. It's also the value your users should use when adding your project to their dependencies.

mima's People

Contributors

2m avatar alexarchambault avatar armanbilge avatar bantonsson avatar dotta avatar dwijnand avatar gkossakowski avatar harrah avatar havocp avatar jeroentervoorde avatar jrudolph avatar jsravn avatar jsuereth avatar julienrf avatar lefou avatar matthughes avatar mergify[bot] avatar nafg avatar nick-nachos avatar nigredo-tori avatar paulp avatar raboof avatar retronym avatar rossabaker avatar scala-steward avatar sethtisue avatar sjrd avatar som-snytt avatar szeiger avatar xuwei-k 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

mima's Issues

Make MiMa sbt plugin tell you which artifact/project it is processing

Output if I in akka do mima-report-binary-issues from the top level (only akka-actor and akka-testkit have been configured)

mima-report-binary-issues
[info] Resolving com.typesafe.akka#akka-actor;2.0 ...
[info] Resolving org.scala-lang#scala-library;2.9.1-1 ...
[info] Resolving com.typesafe.akka#akka-testkit;2.0 ...
[info] Resolving com.typesafe.akka#akka-actor;2.0 ...
[info] Resolving org.scala-lang#scala-library;2.9.1-1 ...
[info] Found 17 potential binary incompatibilities
[error] * class akka.actor.ReceiveTimeout was concrete; is declared abstract in new version
< SNIP SNAP >
[error] * class akka.routing.CurrentRoutees was concrete; is declared abstract in new version
[info] Found 0 potential binary incompatibilities

Kind of hard to see what's ok and what's not.

Allow ignoring package private classes

It would be great if users could signify they want to ignore all class which are private[X] for some value of X. This is common practice in large projects which have code that is internally visible but not intended for public consumption.

Compare against a number of previous artifacts

It would be good to be able to compare against a number of previous artifacts to detect which version is really affected by this change.

A new feature could for example have been introduced in X.Y.1 and it is affected in X.Y.2, but everybody on X.Y.0 can upgrade without being affected.

case-class-abstract-becomes-concrete-ok fails but sbt doesn't notice

and since sbt doesn't notice, Travis doesn't notice either

[error] com.typesafe.tools.mima.lib.TestFailed: case-class-abstract-becomes-concrete-ok' failed.
[error]     The following problems were not expected
[error]     - the type hierarchy of object A has changed in new version. Missing types {java.lang.Object,java.lang.Object}
...
[success] Total time: 70 s, completed Oct 8, 2015 6:22:22 PM

Don't look at mirror classes in Problem Report

When reporting missing classes/objects, mimalib shows both the object and it's mirror class.

class scala.runtime.Byte does not have a correspondent in new version
object scala.runtime.Byte does not have a correspondent in new version

Wildcards in problem filters

Play has a package called play.core. None of the stuff in here is API, though some of it must be public since Scala code that Play generates uses it. So it shouldn't be checked for binary compatibility, hence I should be able to create a problem filter that blanket excludes all problems in this package, rather than having to add each one manually.

Green-card specific false-positive issues

The MiMa will have to stay conservative and err on the side of caution, so in order to integrate checking into the Akka build process we need a way to signal that certain well-understood issues are okay, while all others shall fail the build. Ideally such an exception record has to include the issue, the name of the investigator and the comment why it shall be ignored, and the list of exceptions should be part of the normal build settings.

The type hierarchy of object Foo has changed in new version. Missing types `{java.lang.Object,java.lang.Object}`

After bumping the Scala version from 2.10.2 to 2.10.5, one functional test started to fail. Here is the reported incompatibility:

[trace] Stack trace suppressed: run last case-class-abstract-becomes-concrete-ok/*:testFunctional for the full output.
[error] (case-class-abstract-becomes-concrete-ok/*:testFunctional) com.typesafe.tools.mima.lib.TestFailed: case-class-abstract-becomes-concrete-ok' failed.
[error]     The following problems were not expected
[error]     - the type hierarchy of object A has changed in new version. Missing types {java.lang.Object,java.lang.Object}

This is clearly wrong, since all classes extends java.lang.Object.

The reason why the test started to fail after bumping the Scala version is that the companion object of a case classes has a slightly different hierarchy in 2.10.5.

While fixing this ticket, let's also make sure that functional tests as part of PR validation from now on.

Adding an abstract method to an interface should not be reported as a binary incompatibility

MiMa is currently too conservative, and it reports a binary incompatibility when an abstract method is added to an interface, or an abstract class.

Suppose we have the following:

public interface A {
}

the following evolution of interface A is binary compatible

public interface A {
   public String foo();
}

Currently, MiMa reports an error: "- abstract method foo()java.lang.String in interface A does not have a correspondent in old version", and it shouldn't.

Interestingly, this bug is also the reason why adding a Java8 default methods to an interface is reported as a binary incompatibility (see #68).

Remove duplication between cli and sbt-plugin code

There is some code duplication between cli and sbt-plugin code (argument parsing and more). It should be refactored out. Library could be used to do argument parsing (e.g. scallop) to remove fuzzy matching on strings.

Various levels of warning

BC issues should have various "severity" levels that users can configure which level they are interested in. Right now MIMA is very strict, and some users might "know better" than the tool. We should allow configuring.

IvyActions.update is deprecated

this is the only warning preventing us from enabling -Xfatal-warnings

[warn] /Users/tisue/migration-manager/sbtplugin/src/main/scala/com/typesafe/tools/mima/plugin/SbtMima.scala:75:
  method update in object IvyActions is deprecated: This is no longer public.
[warn]     val report = IvyActions.update(
[warn]                             ^

update is used to pull down the previous version of the artifact being tested, as a basis for comparing to the current one

Support for Java8 default methods

I believe that by fixing #66, MiMa now fully supports JDK8 binaries. However, it'd be good to add a few tests exercising in particular Java8 default methods.

Adding a Java8 default method to an interface is reported by MiMa as a binary incompatibility. This is wrong, and should be corrected.

No warning reported for pattern matching evolution that can result in MatchError

I have a change from

trait DialogSource[+A] { def show(): A }

implicit final class OptionPane(val source: (javax.swing.JOptionPane, String))
  extends DialogSource[Any] {
  def show(): Any = {
    val (pane, title) = source
    val jdlg = pane.createDialog(title)
    jdlg.setVisible(true)
    pane.getValue
  }
}

to

trait MyPane[A] {
  def peer: javax.swing.JOptionPane
  def result: A
}

implicit final class OptionPane[A](val source: (MyPane[A], String))
  extends DialogSource[A] {
  def show(): A = {
    val (pane, title) = source
    val jdlg  = pane.peer.createDialog(title)
    jdlg.setVisible(true)
    pane.result
  }
}

This is not recognised as a binary incompatibility. Obviously code compiled against the first version will produce a MatchError when run against the second version, because the tuple extraction doesn't work any more.

Also a type constructor parameter was added. I'm surprised this doesn't result in a warning, too.

MimaKeys.previousArtifact doesn't respect binary cross versioning

Using %% in previousArtifact does not work:

MimaKeys.previousArtifact  := Some("org.scala-lang.modules" %% "scala-swing" % "1.0.1")
[warn]  ::::::::::::::::::::::::::::::::::::::::::::::
[warn]  ::          UNRESOLVED DEPENDENCIES         ::
[warn]  ::::::::::::::::::::::::::::::::::::::::::::::
[warn]  :: org.scala-lang.modules#scala-swing;1.0.1: not found
[warn]  ::::::::::::::::::::::::::::::::::::::::::::::

The sbt plugin ignores the crossVersion value of the ModuleID. The following works, but shouldn't (it uses both %% and an explicit cross version):

MimaKeys.previousArtifact  := Some("org.scala-lang.modules" %% "scala-swing_2.11" % "1.0.1")

Ignore bridge methods

Method flagged bridge in the bytecode should be ignored to avoid reporting twice the same issue.

Simple example, take the following case class

case class Result(succeeded: Int)

Will generate the following two (bytecode) apply methods:

public apply(I)LResult;
public [bridge] apply(Ljava/lang/Object;)LResult;

If the signature of the Result case class change, the two apply methods will be likely to change as well.

Right now the tool reports two errors if the signature of the two methods changed (for example, if a new parameter was added to the case class), though the underlying cause is the same. Therefore, there is no need to report that twice.

We should swallow the error reported for the bridge method.

Update: After discussing this with Martin, the good solution is not to report the error twice only if a method with the same name (+ same number of parameters and same return type) is already reported.

Allow ignoring private inner classes

Even though private inner classes are runtime public, many projects consider them internal API's. It would be great if MIMA could recognize and automatically exclude these classes.

Add a deprecation working-mode

It would be useful if MiMa could have a working mode for checking correct deprecation cycle between two library versions.

In its simplest form, it should report a warning when an API method that was not marked as deprecated is removed.

traits, classes with special characters are skipped

For example, if you remove the methods x, y, z, or t in the below, the change won't get reported.

trait A_* {
  def foo = 0
  def x = 42
}

object A_* {
  def y = 42
}

final class B_*/ {
  def z = 42
}

object B_*/ {
  def t = 42
}

Problem appears to be in PackageInfo#accessibleClasses skipping these classes because their encoded names, as provided by ClassInfo#name, have $-signs in them.

sbt plugin should let sbt resolve the previous artifact version

Sometimes the hack for resolving the previous artifact version fails and you need to delete the dependency from the ivy cache to make it work again (see http://pastie.org/7388307 for a log of such a failure).

As suggested by @harrah, the mima plugin could require a dependency to the old artifact version to be declared in a separate configuration so that sbt could resolve it in the usual way in the update task.

Scala working mode

MiMa checks for binary incompatibilities as defined by the JVM. This is very strict, and users may want to have a more Scala-centric compatibility check. Specifically, MiMa should use the Scala Pickler annotation to retrieve information that have been lost during compilation.

Example:

  • Private types should not be reported as binary incompatibilities. Scalac does not always maintain private accessors in the generated bytecode.
  • Addition/Removal of local methods.

This is very much related to #1.

Related tickets are #40 and #53

SBT Plugin should *reflectively* pull in MIMA (and resolve it as such)

The SBT plugin should resolve/start MIMA reflectively on a safe classpath that is able to have a different SBT version from the main plugin.

MOST LIKELY - This means the CLI has to be as robust as the SBT plugin so we can feed all config from the plugin to the CLI (just reflectively).

Check for serialization compatibility (in the Java sense)

MiMa currently reports all sources of binary incompatibility, i.e., it ensures that no linkage error is thrown at runtime. Binary compatibility does not imply serialization compatibility, the two are indeed distinct topics.

It would be useful is MiMa could inform the user when two classes are serializable compatible (in the Java sense).

To check serialization compatibility we need to:

  1. Report a warning whenever the serialVersionUID of a class changes.
  2. Report a warning whenever the serialVersionUID of a class is the same, but the class has changed in a way that is not serializable compatible.
  3. Report a warning whenever the serialVersionUID of a class has changed even though there is no real reason for changing it.

Implementing 1) is easy. While 2) and 3) will require some work.

Start by reading this: http://docs.oracle.com/javase/6/docs/platform/serialization/spec/version.html

Don't report added toString override as "added method"

When moving from

class Pair(a: String) {}

to

case class Pair(a: String) {}

MiMa reports:

[info] akka-actor: found 1 potential binary incompatibilities (filtered 232)
[error]  * method toString()java.lang.String in object akka.japi.Pair does not have a correspondent in new version
[error]    filter with: ProblemFilters.exclude[MissingMethodProblem]("akka.japi.Pair.toString")

The toString method is generated in the case class. It is not "missing on the old version", as since toString is available on any Object anyway.

Increasing visibility from package-private to public not incompatible?

I was surprised by the following change not triggering an incompatibility warning:

    // old

    package de.sciss.lucre.event

    trait EventLike {
       private[lucre] def --->() : Unit
    }

    // new

    trait EventLike {
       def --->() : Unit
    }

While I can't verify that it's binary compatible, it certainly creates a source incompatibility. I had following dependent library code:

    package de.sciss.lucre.gagaismo

    trait Lala extends EventLike {
       private[lucre] def --->() : Unit
    }

So when I referred to the above code as % "1.0.0+" and the change is from 1.0.0 to 1.0.1, I suddenly get a compilation error:

[error]  method pullUpdate has weaker access privileges; it should be public
[error]       final private[lucre] def pullUpdate( pull: Pull[ S ])( implicit tx: S#Tx ) : Option[ Change[ A ]] = {
[error]                                ^

Run MiMa from Maven

For our Maven lovers we should make it simpler to intergrate MiMa in their build!

Group issues into categories

Binary compatibility issues should be grouped into like categories. If the categories are useful enough, users can pick which categories they care for.

Support filters in UI and CLI

This is a continuation of issue #3

In the sbt plugin, you can filter out binary compat issues that you've confirmed are not real problems for whatever reason.

It might be useful to support something similar if you're not using sbt, for example maybe you have your own compat checker script.

We could either build up filters from a .conf file or we could eval a .scala file containing a filter list. Perhaps this file could be provided on the command line to the UI or CLI. The UI could even allow you to add/remove filters, that would be fancy.

I don't know exactly how people are using these tools so not sure what makes sense.

JDK 8 support

Mima doesn't work on JDK8 bytecode, it fails with errors like:

java.lang.RuntimeException: bad constant pool tag 18 at byte 23
    at com.typesafe.tools.mima.core.ClassfileParser$ConstantPool.errorBadTag(ClassfileParser.scala:201)
    at com.typesafe.tools.mima.core.ClassfileParser$ConstantPool.<init>(ClassfileParser.scala:103)
        ...

CLI shouldn't depend on scala.tools.cmd package from the compiler

At the moment CLI uses scala.tools.cmd package that is shipped with the compiler for command line arguments parsing. This is unmaintained library that compiler itself does not use. The Scala team is likely to not fix any bugs related to it and will deprecate it soon.

MiMa should migrate off that package. See #30 for an example of a bug in that library.

Sbt plugin crashes sbt with "not a directory or jar file: [...] .pom"

> sbt
[info] Loading global plugins from /home/johannes/git/self/config/etc/.sbt/0.13/plugins
[info] Loading project definition from /home/johannes/git/spray/spray/project
[info] Set current project to root (in build file:/home/johannes/git/spray/spray/)
root > p spray-http
[info] Set current project to spray-http (in build file:/home/johannes/git/spray/spray/)
spray-http > mimaReportBinaryIssues
[info] Resolving org.parboiled#parboiled-core;1.1.6 ...
not a directory or jar file: /home/johannes/.ivy2/cache/io.spray/spray-http/poms/spray-http-1.1.0.pom

Reproduction with this branch:

https://github.com/jrudolph/spray/tree/w/use-mima

'Idle' MiMa spins at 10% CPU usage

I like to leave the GUI app open, so I can re-evaluate the changes when working on a new snapshot of my library. However, I noticed (on OS X, via the ActivityMonitor) that the MiMa is constantly using approx. 10% CPU (on a very fast machine), even when not performing any actions, so something must be wrong with the EDT not being idle.

Methods with incompatible results type should not be always "fixable"

Currently, if the return type of a method changes between two library versions, Mima Lib reports an "IncompatibleResultTypeProblem" that has, by default, a "fixable" status. This is too optimistics since the new method return type may not be a subtype of the old one. We should report that the problem is "fixable" only if newMethod.resultType <: oldMethod.resultType. Otherwise a new term should be used to warn the user that the incompatibility can be fixed but a runtime cast exception will be thrown if the method is ever called.

Evolving a final class to be an object is not a binary compatible evolution

Suppose we have the following evolution

v1: final class A
v2: object A

Let's look at the disassembled code

v1:

$ javap A
public final class A {
  public A();
}

while v2:

$ javap A
public final class A {
}

Because the constructor is no longer available, MiMa should report the following binary incompatibility:

- method this()Unit in class A does not have a correspondent in new version

But it currently reports none.

What's surprising is that we even have a test for the above evolution, see https://github.com/typesafehub/migration-manager/tree/master/reporter/functional-tests/src/test/final-class-becomes-object-nok. However, the problems.txt is erroneously empty, as it should contain the incompatibility mentioned above.

Cross version publishing

When publishing cross-versioned (2.9.x and 2.10.x) we should make sure the SBT plugin is not published if the sources are on 2.10 and #27 is not implemented.

MiMa doesn't check the generic signature of a class/method/field

Say you have

class A {
  def foo(): (String,Int) = ("",0)
}

And you evolve it to

class A {
  def foo(): (Int, String) = (0, "")
}

The signature of method foo doesn't change, i.e.,

% javap -private A 
Compiled from "A.scala"
public class A extends java.lang.Object implements scala.ScalaObject{
    public scala.Tuple2 foo();
    public A();
}

MiMa doesn't report any incompatibility in the above case.

One reason for this is that MiMa doesn't currently check the generic signature. However, even if it would, we can still find instances where the generic signature is the same but you still end up with a ClassCastException at runtime. Hence, a good fix for this isn't straightforward.

roll 0.1.8 release

to get the fix for #78 out there

(not sure how to publicize. just on scala-internals? mention in 2.12.0-M4 notes?)

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.