cheekybits / genny Goto Github PK
View Code? Open in Web Editor NEWElegant generics for Go
License: MIT License
Elegant generics for Go
License: MIT License
It would be nice to not pollute a package's namespace with exported types that are not useable from outside of the package due to the use of internal types for the generic types. One way to do this is to add a 'private' flag to the parse.Generics function and to switch from specificLg to specificSm in subIntoLiteral if this flag is set.
I would like
//go:generate genny -in=$GOFILE -out=$GOFILE_gen gen "Datatype=string,int"
type Datatype generic.Type
type GetterDatatype interface {
GetDataType(column string) (DataType, err)
}
type HasGetters interface {
GetterDatatype
}
To expand into
type GetterInt interface {
GetInt(column string) (int, err)
}
type GetterString interface {
GetString(column string) (string, err)
}
type HasGetters interface {
GetterInt
GetterString
}
But, of course, it doesn't. Is this a worthy enhancement? Is there a workaround to define HasGetters (other than knowing ahead of time what the datatypes are)?
If I have something like the following and replace X with int
type _X_ generic.Type
type Cell_X_ struct {
Value int
}
func m(_X_) {}
func example() {
a := Cell_X_{}
m(Cell_X_{})
}
The following code which wont compile is generated.
type CellInt struct {
Value int
}
func m(int) {}
func example() {
a := CellInt{}
m(Cellint{})
}
Is this a limitation in how genny works or a bug?
If users write a comment above generic.Type
(as per golint
), we should remove it when generating specific code to avoid orphan comments.
I found go test TestCustomTypesMap fails on Debian build farm: alpha powerpc ppc64 x32.
As a tentative workaround, I'll set to ignore this failure for the next upload to Debian.
Please kindly check whether this can be fixed. Thank you!
error log:
=== RUN TestCustomTypesMap
generic_simplemap_test.go:32:
Error Trace: generic_simplemap_test.go:32
Error: Should be false
Test: TestCustomTypesMap
--- FAIL: TestCustomTypesMap (0.00s)
if genny fails to generate a code due to not being able to locate the file.
it still generates a empty file leading to subsequent calls to fail with the follow error
btree.gen.go:1:1: expected 'package', found 'EOF'
should probably not create the file until we are ready to write its contents.
This works:
wget -q -O - "https://github.com/metabition/gennylib/raw/master/maps/concurrentmap.go" | genny gen "KeyType=BUILTINS ValueType=BUILTINS"
Would be much simpler if it could be this:
genny -out="outfile.go" get maps/concurrentmap "KeyType=BUILTINS ValueType=BUILTINS"
Also validate that go generate
works:
//go:generate genny -out="outfile.go" get maps/concurrentmap "KeyType=BUILTINS ValueType=BUILTINS"
Show example of how to use genny with Go's 1.4 go generate
feature.
When generating a file which utilizes reflect, I noticed that reflect gets strips out of my import list.
Generated file
Source file
check code below , I just want to define 2 type separately in 1 file
however it generate ReadPairInt8 ReadPairInt16 many times,
could u find a way fix it, like if there is only 1 type in one function , do not repeat it
or define a generic.SingleType
`
//go:generate genny -in=$GOFILE -out=../util/$GOFILE gen "T1=int8,int16,int32,int64,int,uint8,uint16,uint32,uint64,uint T2=float32,float64"
package util
import (
"strconv"
"github.com/cheekybits/genny/generic"
)
type T1 generic.Type
func ReadPairT1(data string) (x, y T1) {
splits := SplitParams(data)
lx, _ := strconv.Atoi(splits[0])
ly, _ := strconv.Atoi(splits[1])
x = T1(lx)
y = T1(ly)
return
}
type T2 generic.Type
func ReadPairT2(data string) (x, y T2) {
splits := SplitParams(data)
lx, _ := strconv.ParseFloat(splits[0], 32)
ly, _ := strconv.ParseFloat(splits[1], 32)
x = T2(lx)
y = T2(ly)
return
}
`
The help text and the readme seem to be implying that I can use -pkg
to change the package statement at the top of the file, but this doesn't seem to work. I'm using the same template to generate a bunch of API clients for various microservices in different packages, and being able to name the microservice-specific subpackages for the microservices would be nice. At the moment I have to name them all client
and alias.
If -pkg
is supposed to do something else, then consider this a potential feature request. It's barely mentioned in the first place, so i'm not discounting me misunderstanding what it's for.
I have some imports like:
import (
"fmt"
"environment/types"
"environment/utils"
)
But those stmt disappeared from output (sometimes) even if the semantics of my code is completely vaild
I encounter an error when using a generic type to index a slice or as the len
argument to make
non-integer len argument in make([]IntType) - IntType
non-integer slice index n
My workaround has been to add a generic.Integer
type, in addition to the existing generic.Number
type. An alternative might be to change the generic.Number
definition to:
type Number int
Another alternative is to add a // +build ignore
tag to the template file, but then I think it can't be used for generic testing.
Eg
type GenericType generic.Type
type NameGenericType Interface {
DoSomthingGenericType()
}
func Stuff(ngt NameGenericType) {
ngt.DoSomthingGenericType()
}
When generating with uint8 produces
type uint8 interface {
DoSomthingUint8()
}
func Stuff(ngt uint8) {
ngt.DoSomthinguint8()
}
Hi!
Looks like I misunderstood the concepts of genny and passed a specific type with a package name as genny arguments. The problem is that an error message is not pointed to the cause directly it's not trivial to debug. Actually, I resorted to binary search to figure it out.
Code:
//go:generate genny -pkg=test -in=../templates/main.go -out=main.go gen "MyType=package.SpecificType"
Error:
Failed to goimports the generated code: ../templates/main.go:10:12: expected type, found '.' (and 1 more errors)
main.go:3: running "genny": exit status 4
Problem
If your genny templates include renamed imports they'll be incorrectly removed from the output. Initially I thought this was a bug in imports.Parse
but when running goimports
(which I believe uses the imports
lib as it's backend) i don't see the same behavior.
I think what's going on is that because genny
passes the generated code into imports.Parse
without any imports included the renamed packages aren't able to resolve.
Repro case genny-test
Possible solution
-trustImports
which will bypass the import processing from imports
; I've made this modification locally and it works well enough. Two issues with it a) you lose the implicit formatting that imports
does for you b) it doesn't remove the generic
import that you pick up from your template. I'm side stepped (b) by not compiling my templates and just leaving the generic
import out.imports.Process
. I think this should let imports
do its thing without genny
intervention.I know 1 works, I didn't bother trying 2 as I found a suitable work around for my case.
Work around
Since 1 & 2 both require library changes some work arounds for anyone else that may hit this
package $name
differs from directory); this may necessitate changes in other places the package is imported but often works...genny
and implement solution 1 or 2 (and provide a PR ๐)This is what I'm trying to do, works fine for simple types, but fails with slices:
package providers
import (
"github.com/cheekybits/genny/generic"
)
//go:generate genny -in=$GOFILE -out=gen_$GOFILE gen "T=[]string"
type T generic.Type
type TProvider interface {
Get() (T, error)
}
thanks
The import code.google.com/p/go.tools/imports
has been moved to golang.org/x/tools/imports
at https://github.com/cheekybits/genny/blob/master/parse/parse.go#L15
The package cannot be built without this amendment.
Like the title suggests, I think it would be nice to keep the thing. Both as (human, not computer) memory, and in case you need to change something later.
Right now genny get is hard-coded to getting from github.com/metabition/gennylib.
Seems like it would be a good idea to allow getting generics from any url.
When using go generate, genny does not remove the original go generate comment causing subsequent go generates to fail after a gen-gen-file.go
is created. This creates the nuisance of having to manually remove the go generate line in generated files.
The code generated by genny
generates the object name (ObjInt
) with the specific type in
lower case when it should be capitalized. This occurs when the object name is the
parameter of new
function. Does not occur if preceded by a space:
package dsp
import "github.com/cheekybits/genny/generic"
//go:generate genny -in=$GOFILE -out=gen-$GOFILE gen "NumberType=int"
type NumberType generic.Number
type ObjNumberType struct {
v NumberType
}
func NewNumberType() *ObjNumberType {
// Works
//return &{ObjNumberType}
// Works (there is a space preceding the object name)
//return new( ObjNumberType)
// Does not work -> n := new(Objint) (Lowercase Int)
n := new(ObjNumberType)
return n
}
Input
package trie
import "github.com/cheekybits/genny/generic"
type ValueType generic.Type
//Node represents a node in the trie
type Node struct {
branches [256]*Node
val generic.Type
terminal bool
}
// Walk returns the node reached along edge c, if one exists. If node doesn't
// exist we return nil
func (n *Node) Walk(c byte) *Node {
return n.branches[c]
}
// Terminal indicates whether n is terminal in the trie (that is, whether the path from the root to n
// represents an element in the set). For instance, if the root node is terminal, then []byte{} is in the
// trie.
func (n *Node) Terminal() bool { return n.terminal }
// Val gives the value associated with this node. It panics if n is not terminal.
func (n *Node) Val() generic.Type {
if !n.terminal {
panic("Val called on non-terminal node")
}
return n.val
}
Generation command
genny -in node.go gen "ValueType=int"
Error
Failed to goimports the generated code: node.go:28:2: expected declaration, found 'if'
//go:generate genny -in=$GOFILE -out=gen-$GOFILE gen "KeyType=string,int ValueType=string,int"
go generate ./...
command with above comment reproduce
running "genny": exec: "genny": executable file not found in $PATH
messege for me.
However, if I do that command with
//go:generate $GOPATH/bin/genny -in=$GOFILE -out=gen-$GOFILE gen "KeyType=string,int ValueType=string,int"
this comment, genny generation works for me well.
I want to use //go:generate genny -in=$GOFILE -out=gen-$GOFILE gen "KeyType=string,int ValueType=string,int"
this comment which is document's recommended comment.
How can I use this one with the best?
below is my go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/jayden/Library/Caches/go-build"
GOENV="/Users/jayden/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/jayden/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/jayden/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/Users/jayden/Desktop/vitalcare-backend/go/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/sn/k8tknb_14qdb9nwxb6qd7cv80000gn/T/go-build483121234=/tmp/go-build -gno-record-gcc-switches -fno-common
Thanks!
Here is a fun issue:
context
, errors
, and github.com/go-resty/resty
in a fairly complicated source file inside a pre-modules monorepo setup that has to do fun things to GOPATH to work.golang.org/x/tools/imports
apparently tries to work out what you need to import in order to automatically add it codecontext
and github.com/go-resty/resty
, dropping errors
and causing code to not compile.My temporary fix is to fork Genny in order to remove the continue statements used to skip import statements. This can't work on multiple generated types in the same invocation, but I'm generating one type per file for a REST client for our internal microservices, so it lets me make progress.
Having the import statements does fix it.
I think that golang.org/x/tools/imports
adding imports back in hid the issue. The first thing i tried to do before I dug in far enough to figure out what is actually going on was make a test case. I couldn't cause this to fail in an isolated setting. I think that mostly it's smart enough to magically work out what it needs to do just enough that dropping all import statements can work in most cases.
I can't share the monorepo or the code, and since I can't get a standalone test case working I'm not sure how to let someone else reproduce it. If necessary, I may be able to get permission to share the specific file being used as input, since it contains no code specific to our setup. Dropping all import statements can be seen by modifying the Generics
function in parse.go to return just before invocation of golang.org/x/tools/imports
.
Since the way Genny works means that we can't have variation in the import statements themselves based off the type parameters (or, at least as far as I know it doesn't), I think the fix here is to refactor the code cleanup to run per typeset rather than on the entire concatenated output. Flags can then be passed to the first one telling it that it's the first one, so that it knows not to drop package and import statements, then unconditionally drop from all the others.
Furthermore, it looks like there's some other oddities that probably should be handled in the same refactor, as I think they can be solved in a similar way:
packageContainer
(i.e. packageContainer := NewPackageContainer()
). Currently I think that gets through because such code is frequently indented.import "foo"
and I don't think this code handles that either.If refactored to run code cleanup once per typeset, it's guaranteed to be package, imports, code in that order possibly with comments, which should handle the latter points and make it safe to strip whitespace. Specifically, I believe ignoring everything until a non-comment non-package non-import line is encountered and then just including everything as-is after that point will do it.
I'll try to find the time to refactor if I'm headed in the right direction, but it might be better if someone who is familiar with the internals does it, plus I haven't got a lot of time on my hands so it might be a while. If anyone can at least let me know if this all seems right before I put the time in to refactor, I'd appreciate it.
It appears than non-built-in import statements are being lost on generation. As an example, take a "simpletest.go" file that looks like this:
package test
import (
"fmt"
"github.com/gorilla/mux"
"github.com/cheekybits/genny/generic"
)
type MyType generic.Type
func MyTypeFunction() {
_ := mux.NewRouter()
fmt.Println("Hello")
}
if I then run the command cat simpletest.go | genny gen "MyType=ConcreteType" > simpletest_gen.go
I get the following output:
// This file was automatically generated by genny.
// Any changes will be lost if this file is regenerated.
// see https://github.com/cheekybits/genny
package test
import "fmt"
func ConcreteTypeFunction() {
_ := mux.NewRouter()
fmt.Println("Hello")
}
You can see the import statement has correctly dropped the genny reference, but also incorrectly removed the mux reference which means the code is now invalid.
Am I doing something wrong?
I was looking at the go generics options available today and noticed in the docs that this project seems to be using a header of:
// This file was automatically generated by genny.
// Any changes will be lost if this file is regenerated.
// see https://github.com/cheekybits/genny
As it were, this header format isn't usually recognized as machine-generated by common linters.
Would it be possible to switch to the community standard header format? Perhaps something like this:
// Code generated by genny - DO NOT EDIT.
// https://github.com/cheekybits/genny
This change would make it such that any linters could recognize that the code isn't human-edited/maintained, and automatically make the appropriate "ignore issues in this file" determination.
Thanks for writing up this tooling, and I hope you're having a fantastic day! :)
template
package xmpl
import (
"github.com/jmoiron/sqlx"
)
type Types []Type
func GetTypes(db *sqlx.DB) (Types, error) {
result := Types{}
err := db.Select(&result, "SELECT * FROM table;")
return result, err
}
generated with
//go:generate genny -in=$GOFILE -out=gen-$GOFILE gen "Type=Operation table=operation"
will replace table
by Operation
, but not operation
:
// This file was automatically generated by genny.
// Any changes will be lost if this file is regenerated.
// see https://github.com/cheekybits/genny
package operation
import "github.com/jmoiron/sqlx"
type Operations []Operation
func GetOperations(db *sqlx.DB) (Operations, error) {
result := Operations{}
err := db.Select(&result, "SELECT * FROM Operation;")
return result, err
}
Using go install
to install genny will not include the change in the latest master branch, namely the -tag
flag features. Is it possible to create a new tag for the latest commit? I believe the -tag
will be helpful in many use cases.
The current header (below) is hardcoded. This needs to be made customisable so that we can give more specific information on how the code is generated in each specific instance (could be through Makefile, through documented process, through go generate, ...).
header:
// This file was automatically generated by genny.
// Any changes will be lost if this file is regenerated.
// see https://github.com/cheekybits/genny
In my template I have these imports:
import (
"errors"
"fmt"
"strings"
"github.com/deoxxa/gojson"
"github.com/macropodhq/go.uuid"
"github.com/foo/bar/entities"
"github.com/foo/bar/fdb"
"github.com/foo/bar/security"
"github.com/foo/simplelog"
)
My generated files have imports looking like this:
import (
"encoding/json"
"strings"
"github.com/facebookgo/stackerr"
uuid "github.com/macropodhq/go.uuid"
"github.com/foo/bar/entities"
"github.com/foo/bar/fdb"
"github.com/foo/bar/security"
"github.com/foo/simplelog"
)
You can see that "errors" has been removed (thus preventing my code from compiling), and "github.com/macropodhq/go.uuid" has been renamed to "uuid" (I didn't ask for it, but OK). Furthermore it has decided to replace the custom json package we use with the standard "Go encoding/json" package. I guess genny is auto-fixing the imports section - but it's doing it wrongly.
To prevent a template file from being included in the build process a build tag can be added at the top:
// +build ignore
package foo
Unfortunately, these aren't removed from the generated output. As a result the generated code is also not included in the build.
i.e.
genny -package="monkey" gen...
Would produce...
package monkey
.
.
.
@westonplatter please update travis after we moved the repo :)
So, I decided to take a look at genny, just poking around, implementing a pretty dumb digraph...
package digraph
import "github.com/cheekybits/genny/generic"
type Node generic.Type
type DigraphNode struct {
nodes map[Node][]Node
}
func NewDigraphNode() *DigraphNode {
return &DigraphNode{
nodes: make(map[Node][]Node),
}
}
func (dig *DigraphNode) Add(n Node) {
if _, exists := dig.nodes[n]; exists {
return
}
dig.nodes[n] = nil
}
func (dig *DigraphNode) Connect(a, b Node) {
dig.Add(a)
dig.Add(b)
dig.nodes[a] = append(dig.nodes[a], b)
}
But when I run
$ cat generic.go | genny gen Node=int
I get
// This file was automatically generated by genny.
// Any changes will be lost if this file is regenerated.
// see https://github.com/cheekybits/genny
package digraph
type DigraphInt struct {
nodes map[int][]Node
}
func NewDigraphInt() *DigraphInt {
return &DigraphInt{
nodes: make(map[int][]Node),
}
}
func (dig *DigraphInt) Add(n int) {
if _, exists := dig.nodes[n]; exists {
return
}
dig.nodes[n] = nil
}
func (dig *DigraphInt) Connect(a, b int) {
dig.Add(a)
dig.Add(b)
dig.nodes[a] = append(dig.nodes[a], b)
}
As you can see, not all instances of the Node type in the code got rewritten.
//go:generate genny -pkg chr -in=../gen/grow.go -out=chargrow.go gen "SliceType=float32,float64,[]int"
package gen
import (
"github.com/cheekybits/genny/generic"
)
func GrowSliceType(sli *[]SliceType, n int) {
dif := n - cap(*sli)
if dif > 0 {
*sli = append((*sli)[:cap(*sli)], make([]SliceType, dif)...)
} else {
*sli = (*sli)[:n]
}
}
error
Failed to goimports the generated code: ../gen/grow.go:36:10: expected '(', found '['
char.go:1: running "genny": exit status 4
Could genny offer a command line option to separate generated code in multiple files?
For example, "SomeType=string,int" will generate gen-source-string.go
and gen-source-int.go
.
The flags description in the Usage section of the README is showing the option name types
instead of flags
.
{types} - (optional) Command line flags (see below)
{flags} - (optional) Command line flags (see below)
The best maintained fork is at https://github.com/mauricelam/genny
I am on windows 10 latest
vscode
The following works fine.
genny -in ./internal/genny/sarulabsdi/interface-types.go -out ./pkg/interface-types.go -pkg pkg gen "InterfaceType=IHello"
// This file was automatically generated by genny.
// Any changes will be lost if this file is regenerated.
// see https://github.com/cheekybits/genny
package pkg
import (
"reflect"
di "github.com/fluffy-bunny/sarulabsdi"
)
// ReflectTypeIHello used when your service claims to implement IHello
var ReflectTypeIHello = di.GetInterfaceReflectType((*IHello)(nil))
// AddIHelloByObj adds a prebuilt obj
func AddIHelloByObj(builder *di.Builder, obj interface{}) {
di.AddSingletonWithImplementedTypesByObj(builder, obj, ReflectTypeIHello)
}
// AddSingletonIHello adds a type that implements IHello
func AddSingletonIHello(builder *di.Builder, implType reflect.Type) {
di.AddSingletonWithImplementedTypes(builder, implType, ReflectTypeIHello)
}
When I run the same command on github it produces the following output.
Where did the following go?
di "github.com/fluffy-bunny/sarulabsdi"
// This file was automatically generated by genny.
// Any changes will be lost if this file is regenerated.
// see https://github.com/cheekybits/genny
package pkg
import "reflect"
// ReflectTypeIHello used when your service claims to implement IHello
var ReflectTypeIHello = di.GetInterfaceReflectType((*IHello)(nil))
// AddIHelloByObj adds a prebuilt obj
func AddIHelloByObj(builder *di.Builder, obj interface{}) {
di.AddSingletonWithImplementedTypesByObj(builder, obj, ReflectTypeIHello)
}
// AddSingletonIHello adds a type that implements IHello
func AddSingletonIHello(builder *di.Builder, implType reflect.Type) {
di.AddSingletonWithImplementedTypes(builder, implType, ReflectTypeIHello)
}
So yea, its working on my machine but not on github actions. I am windows 10, github it ubuntu-latest.
i want to define function with generic.Type define. for example:
define template_demo.go :
func registerModelType() {
MngCreater = append(MngCreater, ModelType(GlobalmgDb))
}
input_file="./template_res_register.go"
out_file="res_register_1.go" ##
real_type="RegisterSignDailyFortuneMg,RegisterSecretChitchatWork" ###RegisterSecretChitchatWork
pkg_name="common_res"
genny -in ${input_file} -out ${out_file} -pkg ${pkg_name} gen "ModelType=${real_type}"
func registerRegisterSecretChitchatWork() {
MngCreater = append(MngCreater, RegisterSecretChitchatWork(GlobalmgDb))
}
cannot use ModelType(GlobalmgDb) (value of type ModelType) as data_cache.CreateRes value in argument to append: ModelType does not implement data_cache.CreateRes (missing method Create)
Remove all mention of last repo
Hi.
Thank you for your providing a neat package!
I would like to extend generic type like this Java example so that I can call method in the
type.
public class Bar<T extends B & java.io.Serializable> {}
and I find I could do similar stuff with Genny. but it is a bit hacky way.
package genny
import "bytes"
// NOTE: this is how easy it is to define a generic type
//Something ..
type Something interface { //generic.Type
String() string //generic.Type
} //generic.Type
// SomethingQueue is a queue of Somethings.
type SomethingQueue struct {
items []Something
}
func NewSomethingQueue() *SomethingQueue {
return &SomethingQueue{items: make([]Something, 0)}
}
func (q *SomethingQueue) Push(item Something) {
q.items = append(q.items, item)
}
func (q *SomethingQueue) Pop() Something {
item := q.items[0]
q.items = q.items[1:]
return item
}
func (q *SomethingQueue) String() string {
var buf bytes.Buffer
for _, item := range q.items {
buf.WriteString(item.String())
}
return buf.String()
}
How about support this use case officially, and add "generic.ignore" for ignoring line?
Hey. I'm having good success with genny but now I have a spazzy-issues.
Code:
func (arr *T) pop () BASETYPE { l := len(*arr)-1; val := (*arr)[l]; *arr = (*arr)[:l]; return val }
Question
One. is there any way to automatically deduce BASETYPE to int from T being []int?
I tried:
func (arr *TSlice) pop () T { l := len(*arr)-1; val := (*arr)[l]; *arr = (*arr)[:l]; return val }
but after running go generate
Tslice is named "intSlice" so I cannot export it, which is my next question:
Two. I'm exporting my package so custom types have to be Uppercased
, so the way I was building slices is no longer work. any suggestions on how to get "intSlice" to "IntSlice" if i generate with "int"? (so I can use it as as the base type)
edit: NVM I should just use a second var. I'm still kind of curious as to the 2nd question, or if theres a basetype() API for genny though. Cheers and thanks for the software, most simple generator I've seen for Go.
The //go:generate genny
comments are left in, they should be removed.
It would be nice to get more hands on genny by adding the hacktoberfest label to open issues, so they show up in searches for issues with the label (state:open label:hacktoberfest).
Also add a contributing.md file to explain how newcomers can get started contributing to genny.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.